330 lines
7.3 KiB
C
330 lines
7.3 KiB
C
|
/* 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;
|
||
|
}
|