| /* 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(dentry->d_inode); |
| |
| 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 %*.*s [%lu] (err %d)\n", |
| dentry->d_name.len, dentry->d_name.len, |
| dentry->d_name.name, dentry->d_inode->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 %*.*s [%lu] (err %d)\n", |
| dentry->d_name.len, dentry->d_name.len, |
| dentry->d_name.name, dentry->d_inode->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", |
| dentry->d_inode->i_ino); |
| ret = -EIO; |
| goto error; |
| |
| bad_type: |
| xtype[2] = 0; |
| pr_err("Cache object %*.*s [%lu] type %s not %s\n", |
| dentry->d_name.len, dentry->d_name.len, |
| dentry->d_name.name, dentry->d_inode->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(dentry->d_inode); |
| 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(dentry->d_inode); |
| |
| 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)", |
| dentry->d_inode->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)", |
| dentry->d_inode->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", |
| dentry->d_inode->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)", |
| dentry->d_inode->i_ino, -ret); |
| } |
| |
| _leave(" = %d", ret); |
| return ret; |
| } |