| /* 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); | 
 |  | 
 | 	clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); | 
 | 	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; | 
 |  | 
 | 	if (!dentry) | 
 | 		return -ESTALE; | 
 |  | 
 | 	_enter("%p,#%d", object, auxdata->len); | 
 |  | 
 | 	/* attempt to install the cache metadata directly */ | 
 | 	_debug("SET #%u", auxdata->len); | 
 |  | 
 | 	clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); | 
 | 	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, | 
 | 				     i_size_read(d_backing_inode(dentry))); | 
 | 	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, | 
 | 					   i_size_read(d_backing_inode(dentry))); | 
 |  | 
 | 		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; | 
 | } |