blob: 0a41952d1e795a827e7e87b03543f4408ea0078d [file] [log] [blame] [edit]
/* 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.
*/
//#define __KDEBUG
#include <linux/module.h>
#include <linux/kernel.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";
static const char cachefiles_xattr_cull_index[] =
XATTR_USER_PREFIX "CacheFiles.cull_index";
static const char cachefiles_xattr_atime_base[] =
XATTR_USER_PREFIX "CacheFiles.atime_base";
/*
* check the type label on the root object
* - done using xattrs
*/
int cachefiles_check_root_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 %pd [%lu] (err %d)\n",
dentry, 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 %pd [%lu] (err %d)\n",
dentry, 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 %pd [%lu] type %s not %s\n",
dentry, 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 [cs=%d]",
auxdata->len, le32_to_cpu(auxdata->cull_slot));
ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
&auxdata->cull_slot, auxdata->len, 0);
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("UPD #%u [cs=%u]",
auxdata->len, le32_to_cpu(auxdata->cull_slot));
ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
&auxdata->cull_slot, 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 cull slot number and type label */
ret = vfs_getxattr(dentry, cachefiles_xattr_cache,
&auxbuf->cull_slot, 512 + 5);
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 < 5)
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 - 5;
_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:
auxdata->cull_slot = auxbuf->cull_slot;
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;
}
/*
* read the slot number from a file
*/
unsigned cachefiles_read_cull_slot(struct cachefiles_cache *cache,
struct dentry *dentry)
{
__le32 slot;
int ret;
_enter("");
/* read the current cull slot number and type label */
ret = vfs_getxattr(dentry, cachefiles_xattr_cache, &slot, 4);
if (ret < 4) {
_leave(" = no cull slot");
return CACHEFILES_NO_CULL_SLOT;
}
_leave(" = %d", le32_to_cpu(slot));
return le32_to_cpu(slot);
}
/*
* 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;
}
/*
* check the label on the culling index
* - done using xattrs
*/
int cachefiles_check_cull_index(struct cachefiles_cache *cache)
{
struct dentry *dentry = cache->cull_index->f_path.dentry;
char label[3], xlabel[3];
int ret;
ASSERT(dentry);
ASSERT(dentry->d_inode);
snprintf(label, 3, "%02x", cache->cx_entsize);
_enter("{%s}", label);
/* attempt to install a label directly */
ret = vfs_setxattr(dentry, cachefiles_xattr_cull_index, label, 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)",
dentry->d_name.len, dentry->d_name.len,
dentry->d_name.name, dentry->d_inode->i_ino,
-ret);
goto error;
}
/* read the current label */
ret = vfs_getxattr(dentry, cachefiles_xattr_cull_index, xlabel, 3);
if (ret < 0) {
if (ret == -ERANGE)
goto bad_label_length;
pr_err("Can't read xattr on %*.*s [%lu] (err %d)",
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_label_length;
if (xlabel[0] != label[0] || xlabel[1] != label[1])
goto bad_label;
ret = 0;
error:
_leave(" = %d", ret);
return ret;
bad_label_length:
pr_err("Cache cull index xattr length incorrect");
ret = -EIO;
goto error;
bad_label:
xlabel[2] = 0;
pr_err("Cache cull index xattr specifies entry size of 0x%s not 0x%s",
xlabel, label);
ret = -EIO;
goto error;
}
/*
* get/set the atime base from the cull_atimes file
* - done using xattrs
*/
int cachefiles_get_set_atime_base(struct cachefiles_cache *cache)
{
struct dentry *dentry = cache->cull_atimes->f_path.dentry;
char label[16 + 1], *end;
int ret;
ASSERT(dentry);
ASSERT(dentry->d_inode);
cache->atime_base = get_seconds();
sprintf(label, "%016lx", cache->atime_base);
_enter("{%s}", label);
/* attempt to install a label directly */
ret = vfs_setxattr(dentry, cachefiles_xattr_atime_base, label, 16,
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)",
dentry->d_name.len, dentry->d_name.len,
dentry->d_name.name, dentry->d_inode->i_ino,
-ret);
goto error;
}
/* read the current label */
ret = vfs_getxattr(dentry, cachefiles_xattr_atime_base, label, 17);
if (ret < 0) {
if (ret == -ERANGE)
goto bad_label_length;
pr_err("Can't read xattr on %*.*s [%lu] (err %d)",
dentry->d_name.len, dentry->d_name.len,
dentry->d_name.name, dentry->d_inode->i_ino,
-ret);
goto error;
}
/* attempt to parse the atime base */
if (ret != 16)
goto bad_label_length;
cache->atime_base = simple_strtoull(label, &end, 16);
if (end - label != 16 || *end != '\0') {
pr_err("Failed to parse xattr on %*.*s [%lu]",
dentry->d_name.len, dentry->d_name.len,
dentry->d_name.name, dentry->d_inode->i_ino);
ret = -EIO;
goto error;
}
ret = 0;
error:
_leave(" = %d [%lx]", ret, cache->atime_base);
return ret;
bad_label_length:
pr_err("Cache atime_base xattr length incorrect");
ret = -EIO;
goto error;
}
/**
* force update a slot to be the one specified.
* caller is responsible for locking cache->xattr_mutex.
*
* @return 0 on success, a negated errno value otherwise.
*/
int cachefiles_reset_slot(struct dentry *dentry, unsigned slot)
{
struct cachefiles_xattr *xbuf;
int ret = 0;
ssize_t len;
_enter("%p, %u", dentry, slot);
xbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, cachefiles_gfp);
if (!xbuf) {
_leave(" = -ENOMEM");
return -ENOMEM;
}
len = vfs_getxattr(dentry, cachefiles_xattr_cache, &xbuf->cull_slot,
512 + sizeof(struct cachefiles_xattr));
if (len < 0) {
pr_err("Failed to read xattrs on %s", dentry->d_name.name);
ret = len;
goto error;
}
xbuf->cull_slot = slot;
ret = vfs_setxattr(dentry, cachefiles_xattr_cache, &xbuf->cull_slot, len,
XATTR_REPLACE);
if (ret) {
pr_err("Failed to replace xattrs on %s", dentry->d_name.name);
if (ret == ENOMEM)
ret = -ENOMEM;
else
ret = -EIO;
goto error;
}
error:
kfree(xbuf);
return ret;
}