/* CacheFiles path walking and related routines
*
* 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/mount.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/slab.h>
#include "internal.h"
#define CACHEFILES_KEYBUF_SIZE 512
/*
* dump debugging info about an object
*/
static noinline
void __cachefiles_printk_object(struct cachefiles_object *object,
const char *prefix,
u8 *keybuf)
{
struct fscache_cookie *cookie;
unsigned keylen, loop;
printk(KERN_ERR "%sobject: OBJ%x\n",
prefix, object->fscache.debug_id);
printk(KERN_ERR "%sobjstate=%s fl=%lx swfl=%lx ev=%lx[%lx]\n",
prefix, fscache_object_states[object->fscache.state],
object->fscache.flags, object->fscache.work.flags,
object->fscache.events,
object->fscache.event_mask & FSCACHE_OBJECT_EVENTS_MASK);
printk(KERN_ERR "%sops=%u inp=%u exc=%u\n",
prefix, object->fscache.n_ops, object->fscache.n_in_progress,
object->fscache.n_exclusive);
printk(KERN_ERR "%sparent=%p\n",
prefix, object->fscache.parent);
spin_lock(&object->fscache.lock);
cookie = object->fscache.cookie;
if (cookie) {
printk(KERN_ERR "%scookie=%p [pr=%p nd=%p fl=%lx]\n",
prefix,
object->fscache.cookie,
object->fscache.cookie->parent,
object->fscache.cookie->netfs_data,
object->fscache.cookie->flags);
if (keybuf)
keylen = cookie->def->get_key(cookie->netfs_data, keybuf,
CACHEFILES_KEYBUF_SIZE);
else
keylen = 0;
} else {
printk(KERN_ERR "%scookie=NULL\n", prefix);
keylen = 0;
}
spin_unlock(&object->fscache.lock);
if (keylen) {
printk(KERN_ERR "%skey=[%u] '", prefix, keylen);
for (loop = 0; loop < keylen; loop++)
printk("%02x", keybuf[loop]);
printk("'\n");
}
}
/*
* dump debugging info about a pair of objects
*/
static noinline void cachefiles_printk_object(struct cachefiles_object *object,
struct cachefiles_object *xobject)
{
u8 *keybuf;
keybuf = kmalloc(CACHEFILES_KEYBUF_SIZE, GFP_NOIO);
if (object)
__cachefiles_printk_object(object, "", keybuf);
if (xobject)
__cachefiles_printk_object(xobject, "x", keybuf);
kfree(keybuf);
}
/*
* mark the owner of a dentry, if there is one, to indicate that that dentry
* has been preemptively deleted
* - the caller must hold the i_mutex on the dentry's parent as required to
* call vfs_unlink(), vfs_rmdir() or vfs_rename()
*/
static void cachefiles_mark_object_buried(struct cachefiles_cache *cache,
struct dentry *dentry)
{
struct cachefiles_object *object;
struct rb_node *p;
_enter(",'%*.*s'",
dentry->d_name.len, dentry->d_name.len, dentry->d_name.name);
write_lock(&cache->active_lock);
p = cache->active_nodes.rb_node;
while (p) {
object = rb_entry(p, struct cachefiles_object, active_node);
if (object->dentry > dentry)
p = p->rb_left;
else if (object->dentry < dentry)
p = p->rb_right;
else
goto found_dentry;
}
write_unlock(&cache->active_lock);
_leave(" [no owner]");
return;
/* found the dentry for */
found_dentry:
kdebug("preemptive burial: OBJ%x [%s] %p",
object->fscache.debug_id,
fscache_object_states[object->fscache.state],
dentry);
if (object->fscache.state < FSCACHE_OBJECT_DYING) {
printk(KERN_ERR "\n");
printk(KERN_ERR "CacheFiles: Error:"
" Can't preemptively bury live object\n");
cachefiles_printk_object(object, NULL);
} else if (test_and_set_bit(CACHEFILES_OBJECT_BURIED, &object->flags)) {
printk(KERN_ERR "CacheFiles: Error:"
" Object already preemptively buried\n");
}
write_unlock(&cache->active_lock);
_leave(" [owner marked]");
}
/*
* record the fact that an object is now active
*/
static int cachefiles_mark_object_active(struct cachefiles_cache *cache,
struct cachefiles_object *object)
{
struct cachefiles_object *xobject;
struct rb_node **_p, *_parent