| /* CacheFiles extended attribute management | |
| * | |
| * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
| * Written by David Howells (dhowells@redhat.com) | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public Licence | |
| * as published by the Free Software Foundation; either version | |
| * 2 of the Licence, or (at your option) any later version. | |
| */ | |
| #include <linux/module.h> | |
| #include <linux/sched.h> | |
| #include <linux/file.h> | |
| #include <linux/fs.h> | |
| #include <linux/fsnotify.h> | |
| #include <linux/quotaops.h> | |
| #include <linux/xattr.h> | |
| #include <linux/slab.h> | |
| #include "internal.h" | |
| static const char cachefiles_xattr_cache[] = | |
| XATTR_USER_PREFIX "CacheFiles.cache"; | |
| /* | |
| * check the type label on an object | |
| * - done using xattrs | |
| */ | |
| int cachefiles_check_object_type(struct cachefiles_object *object) | |
| { | |
| struct dentry *dentry = object->dentry; | |
| char type[3], xtype[3]; | |
| int ret; | |
| ASSERT(dentry); | |
| ASSERT(d_backing_inode(dentry)); | |
| if (!object->fscache.cookie) | |
| strcpy(type, "C3"); | |
| else | |
| snprintf(type, 3, "%02x", object->fscache.cookie->def->type); | |
| _enter("%p{%s}", object, type); | |
| /* attempt to install a type label directly */ | |
| ret = vfs_setxattr(dentry, cachefiles_xattr_cache, type, 2, | |
| XATTR_CREATE); | |
| if (ret == 0) { | |
| _debug("SET"); /* we succeeded */ | |
| goto error; | |
| } | |
| if (ret != -EEXIST) { | |
| pr_err("Can't set xattr on %pd [%lu] (err %d)\n", | |
| dentry, d_backing_inode(dentry)->i_ino, | |
| -ret); | |
| goto error; | |
| } | |
| /* read the current type label */ | |
| ret = vfs_getxattr(dentry, cachefiles_xattr_cache, xtype, 3); | |
| if (ret < 0) { | |
| if (ret == -ERANGE) | |
| goto bad_type_length; | |
| pr_err("Can't read xattr on %pd [%lu] (err %d)\n", | |
| dentry, d_backing_inode(dentry)->i_ino, | |
| -ret); | |
| goto error; | |
| } | |
| /* check the type is what we're expecting */ | |
| if (ret != 2) | |
| goto bad_type_length; | |
| if (xtype[0] != type[0] || xtype[1] != type[1]) | |
| goto bad_type; | |
| ret = 0; | |
| error: | |
| _leave(" = %d", ret); | |
| return ret; | |
| bad_type_length: | |
| pr_err("Cache object %lu type xattr length incorrect\n", | |
| d_backing_inode(dentry)->i_ino); | |
| ret = -EIO; | |
| goto error; | |
| bad_type: | |
| xtype[2] = 0; | |
| pr_err("Cache object %pd [%lu] type %s not %s\n", | |
| dentry, d_backing_inode(dentry)->i_ino, | |
| xtype, type); | |
| ret = -EIO; | |
| goto error; | |
| } | |
| /* | |
| * set the state xattr on a cache file | |
| */ | |
| int cachefiles_set_object_xattr(struct cachefiles_object *object, | |
| struct cachefiles_xattr *auxdata) | |
| { | |
| struct dentry *dentry = object->dentry; | |
| int ret; | |
| ASSERT(dentry); | |
| _enter("%p,#%d", object, auxdata->len); | |
| /* attempt to install the cache metadata directly */ | |
| _debug("SET #%u", auxdata->len); | |
| ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | |
| &auxdata->type, auxdata->len, | |
| XATTR_CREATE); | |
| if (ret < 0 && ret != -ENOMEM) | |
| cachefiles_io_error_obj( | |
| object, | |
| "Failed to set xattr with error %d", ret); | |
| _leave(" = %d", ret); | |
| return ret; | |
| } | |
| /* | |
| * update the state xattr on a cache file | |
| */ | |
| int cachefiles_update_object_xattr(struct cachefiles_object *object, | |
| struct cachefiles_xattr *auxdata) | |
| { | |
| struct dentry *dentry = object->dentry; | |
| int ret; | |
| ASSERT(dentry); | |
| _enter("%p,#%d", object, auxdata->len); | |
| /* attempt to install the cache metadata directly */ | |
| _debug("SET #%u", auxdata->len); | |
| ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | |
| &auxdata->type, auxdata->len, | |
| XATTR_REPLACE); | |
| if (ret < 0 && ret != -ENOMEM) | |
| cachefiles_io_error_obj( | |
| object, | |
| "Failed to update xattr with error %d", ret); | |
| _leave(" = %d", ret); | |
| return ret; | |
| } | |
| /* | |
| * check the consistency between the backing cache and the FS-Cache cookie | |
| */ | |
| int cachefiles_check_auxdata(struct cachefiles_object *object) | |
| { | |
| struct cachefiles_xattr *auxbuf; | |
| enum fscache_checkaux validity; | |
| struct dentry *dentry = object->dentry; | |
| ssize_t xlen; | |
| int ret; | |
| ASSERT(dentry); | |
| ASSERT(d_backing_inode(dentry)); | |
| ASSERT(object->fscache.cookie->def->check_aux); | |
| auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, GFP_KERNEL); | |
| if (!auxbuf) | |
| return -ENOMEM; | |
| xlen = vfs_getxattr(dentry, cachefiles_xattr_cache, | |
| &auxbuf->type, 512 + 1); | |
| ret = -ESTALE; | |
| if (xlen < 1 || | |
| auxbuf->type != object->fscache.cookie->def->type) | |
| goto error; | |
| xlen--; | |
| validity = fscache_check_aux(&object->fscache, &auxbuf->data, xlen); | |
| if (validity != FSCACHE_CHECKAUX_OKAY) | |
| goto error; | |
| ret = 0; | |
| error: | |
| kfree(auxbuf); | |
| return ret; | |
| } | |
| /* | |
| * check the state xattr on a cache file | |
| * - return -ESTALE if the object should be deleted | |
| */ | |
| int cachefiles_check_object_xattr(struct cachefiles_object *object, | |
| struct cachefiles_xattr *auxdata) | |
| { | |
| struct cachefiles_xattr *auxbuf; | |
| struct dentry *dentry = object->dentry; | |
| int ret; | |
| _enter("%p,#%d", object, auxdata->len); | |
| ASSERT(dentry); | |
| ASSERT(d_backing_inode(dentry)); | |
| auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, cachefiles_gfp); | |
| if (!auxbuf) { | |
| _leave(" = -ENOMEM"); | |
| return -ENOMEM; | |
| } | |
| /* read the current type label */ | |
| ret = vfs_getxattr(dentry, cachefiles_xattr_cache, | |
| &auxbuf->type, 512 + 1); | |
| if (ret < 0) { | |
| if (ret == -ENODATA) | |
| goto stale; /* no attribute - power went off | |
| * mid-cull? */ | |
| if (ret == -ERANGE) | |
| goto bad_type_length; | |
| cachefiles_io_error_obj(object, | |
| "Can't read xattr on %lu (err %d)", | |
| d_backing_inode(dentry)->i_ino, -ret); | |
| goto error; | |
| } | |
| /* check the on-disk object */ | |
| if (ret < 1) | |
| goto bad_type_length; | |
| if (auxbuf->type != auxdata->type) | |
| goto stale; | |
| auxbuf->len = ret; | |
| /* consult the netfs */ | |
| if (object->fscache.cookie->def->check_aux) { | |
| enum fscache_checkaux result; | |
| unsigned int dlen; | |
| dlen = auxbuf->len - 1; | |
| _debug("checkaux %s #%u", | |
| object->fscache.cookie->def->name, dlen); | |
| result = fscache_check_aux(&object->fscache, | |
| &auxbuf->data, dlen); | |
| switch (result) { | |
| /* entry okay as is */ | |
| case FSCACHE_CHECKAUX_OKAY: | |
| goto okay; | |
| /* entry requires update */ | |
| case FSCACHE_CHECKAUX_NEEDS_UPDATE: | |
| break; | |
| /* entry requires deletion */ | |
| case FSCACHE_CHECKAUX_OBSOLETE: | |
| goto stale; | |
| default: | |
| BUG(); | |
| } | |
| /* update the current label */ | |
| ret = vfs_setxattr(dentry, cachefiles_xattr_cache, | |
| &auxdata->type, auxdata->len, | |
| XATTR_REPLACE); | |
| if (ret < 0) { | |
| cachefiles_io_error_obj(object, | |
| "Can't update xattr on %lu" | |
| " (error %d)", | |
| d_backing_inode(dentry)->i_ino, -ret); | |
| goto error; | |
| } | |
| } | |
| okay: | |
| ret = 0; | |
| error: | |
| kfree(auxbuf); | |
| _leave(" = %d", ret); | |
| return ret; | |
| bad_type_length: | |
| pr_err("Cache object %lu xattr length incorrect\n", | |
| d_backing_inode(dentry)->i_ino); | |
| ret = -EIO; | |
| goto error; | |
| stale: | |
| ret = -ESTALE; | |
| goto error; | |
| } | |
| /* | |
| * remove the object's xattr to mark it stale | |
| */ | |
| int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, | |
| struct dentry *dentry) | |
| { | |
| int ret; | |
| ret = vfs_removexattr(dentry, cachefiles_xattr_cache); | |
| if (ret < 0) { | |
| if (ret == -ENOENT || ret == -ENODATA) | |
| ret = 0; | |
| else if (ret != -ENOMEM) | |
| cachefiles_io_error(cache, | |
| "Can't remove xattr from %lu" | |
| " (error %d)", | |
| d_backing_inode(dentry)->i_ino, -ret); | |
| } | |
| _leave(" = %d", ret); | |
| return ret; | |
| } |