X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2F9p%2Ffid.c;h=dfebdbe7440e6f31f627fdd41718cf55c459a55d;hb=877c357e7511395bc923ec9efc2e8b021a17ed79;hp=a9b6301a04fcce2d0c5302ecfbb66a0de9cf8770;hpb=c1f1625860847b57a0450a28d112423c2af675ff;p=linux-2.6 diff --git a/fs/9p/fid.c b/fs/9p/fid.c index a9b6301a04..dfebdbe744 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -1,6 +1,7 @@ /* * V9FS FID Management * + * Copyright (C) 2007 by Latchesar Ionkov * Copyright (C) 2005, 2006 by Eric Van Hensbergen * * This program is free software; you can redistribute it and/or modify @@ -26,168 +27,184 @@ #include #include #include +#include +#include -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" /** - * v9fs_fid_insert - add a fid to a dentry + * v9fs_fid_add - add a fid to a dentry + * @dentry: dentry that the fid is being added to * @fid: fid to add - * @dentry: dentry that it is being added to * */ -int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry) +int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) { - struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; - dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid, - dentry->d_iname, dentry); - if (dentry->d_fsdata == NULL) { - dentry->d_fsdata = - kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (dentry->d_fsdata == NULL) { - dprintk(DEBUG_ERROR, "Out of memory\n"); + struct v9fs_dentry *dent; + + P9_DPRINTK(P9_DEBUG_VFS, "fid %d dentry %s\n", + fid->fid, dentry->d_iname); + + dent = dentry->d_fsdata; + if (!dent) { + dent = kmalloc(sizeof(struct v9fs_dentry), GFP_KERNEL); + if (!dent) return -ENOMEM; - } - fid_list = (struct list_head *)dentry->d_fsdata; - INIT_LIST_HEAD(fid_list); /* Initialize list head */ + + spin_lock_init(&dent->lock); + INIT_LIST_HEAD(&dent->fidlist); + dentry->d_fsdata = dent; } - fid->uid = current->uid; - list_add(&fid->list, fid_list); + spin_lock(&dent->lock); + list_add(&fid->dlist, &dent->fidlist); + spin_unlock(&dent->lock); + return 0; } /** - * v9fs_fid_create - allocate a FID structure - * @dentry - dentry to link newly created fid to + * v9fs_fid_find - retrieve a fid that belongs to the specified uid + * @dentry: dentry to look for fid in + * @uid: return fid that belongs to the specified user + * @any: if non-zero, return any fid associated with the dentry * */ -struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) +static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) { - struct v9fs_fid *new; - - dprintk(DEBUG_9P, "fid create fid %d\n", fid); - new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); - if (new == NULL) { - dprintk(DEBUG_ERROR, "Out of Memory\n"); - return ERR_PTR(-ENOMEM); + struct v9fs_dentry *dent; + struct p9_fid *fid, *ret; + + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n", + dentry->d_iname, dentry, uid, any); + dent = (struct v9fs_dentry *) dentry->d_fsdata; + ret = NULL; + if (dent) { + spin_lock(&dent->lock); + list_for_each_entry(fid, &dent->fidlist, dlist) { + if (any || fid->uid == uid) { + ret = fid; + break; + } + } + spin_unlock(&dent->lock); } - new->fid = fid; - new->v9ses = v9ses; - new->fidopen = 0; - new->fidclunked = 0; - new->iounit = 0; - new->rdir_pos = 0; - new->rdir_fcall = NULL; - init_MUTEX(&new->lock); - INIT_LIST_HEAD(&new->list); - - return new; + return ret; } /** - * v9fs_fid_destroy - deallocate a FID structure - * @fid: fid to destroy - * - */ - -void v9fs_fid_destroy(struct v9fs_fid *fid) -{ - list_del(&fid->list); - kfree(fid); -} - -/** - * v9fs_fid_lookup - return a locked fid from a dentry + * v9fs_fid_lookup - lookup for a fid, try to walk if not found * @dentry: dentry to look for fid in * - * find a fid in the dentry, obtain its semaphore and return a reference to it. - * code calling lookup is responsible for releasing lock - * - * TODO: only match fids that have the same uid as current user - * + * Look for a fid in the specified dentry for the current user. + * If no fid is found, try to create one walking from a fid from the parent + * dentry (if it has one), or the root dentry. If the user haven't accessed + * the fs yet, attach now and walk from the root. */ -struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) +struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) { - struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; - struct v9fs_fid *return_fid = NULL; + int i, n, l, clone, any, access; + u32 uid; + struct p9_fid *fid; + struct dentry *d, *ds; + struct v9fs_session_info *v9ses; + char **wnames, *uname; + + v9ses = v9fs_inode2v9ses(dentry->d_inode); + access = v9ses->flags & V9FS_ACCESS_MASK; + switch (access) { + case V9FS_ACCESS_SINGLE: + case V9FS_ACCESS_USER: + uid = current->fsuid; + any = 0; + break; + + case V9FS_ACCESS_ANY: + uid = v9ses->uid; + any = 1; + break; + + default: + uid = ~0; + any = 0; + break; + } - dprintk(DEBUG_9P, " dentry: %s (%p)\n", dentry->d_iname, dentry); + fid = v9fs_fid_find(dentry, uid, any); + if (fid) + return fid; - if (fid_list) - return_fid = list_entry(fid_list->next, struct v9fs_fid, list); + ds = dentry->d_parent; + fid = v9fs_fid_find(ds, uid, any); + if (!fid) { /* walk from the root */ + n = 0; + for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) + n++; - if (!return_fid) { - dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); - return_fid = ERR_PTR(-EBADF); - } + fid = v9fs_fid_find(ds, uid, any); + if (!fid) { /* the user is not attached to the fs yet */ + if (access == V9FS_ACCESS_SINGLE) + return ERR_PTR(-EPERM); - if(down_interruptible(&return_fid->lock)) - return ERR_PTR(-EINTR); + if (v9fs_extended(v9ses)) + uname = NULL; + else + uname = v9ses->uname; - return return_fid; -} + fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, + v9ses->aname); -/** - * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it - * @dentry: dentry to look for fid in - * - * find a fid in the dentry and then clone to a new private fid - * - * TODO: only match fids that have the same uid as current user - * - */ + if (IS_ERR(fid)) + return fid; -struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) -{ - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); - struct v9fs_fcall *fcall = NULL; - int fid, err; - - base_fid = v9fs_fid_lookup(dentry); - - if(IS_ERR(base_fid)) - return base_fid; - - if(base_fid) { /* clone fid */ - fid = v9fs_get_idpool(&v9ses->fidpool); - if (fid < 0) { - eprintk(KERN_WARNING, "newfid fails!\n"); - new_fid = ERR_PTR(-ENOSPC); - goto Release_Fid; + v9fs_fid_add(ds, fid); } + } else /* walk from the parent */ + n = 1; - err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); - if (err < 0) { - dprintk(DEBUG_ERROR, "clone walk didn't work\n"); - v9fs_put_idpool(fid, &v9ses->fidpool); - new_fid = ERR_PTR(err); - goto Free_Fcall; - } - new_fid = v9fs_fid_create(v9ses, fid); - if (new_fid == NULL) { - dprintk(DEBUG_ERROR, "out of memory\n"); - new_fid = ERR_PTR(-ENOMEM); + if (ds == dentry) + return fid; + + wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); + if (!wnames) + return ERR_PTR(-ENOMEM); + + for (d = dentry, i = (n-1); i >= 0; i--, d = d->d_parent) + wnames[i] = (char *) d->d_name.name; + + clone = 1; + i = 0; + while (i < n) { + l = min(n - i, P9_MAXWELEM); + fid = p9_client_walk(fid, l, &wnames[i], clone); + if (IS_ERR(fid)) { + kfree(wnames); + return fid; } -Free_Fcall: - kfree(fcall); + + i += l; + clone = 0; } -Release_Fid: - up(&base_fid->lock); - return new_fid; + kfree(wnames); + v9fs_fid_add(dentry, fid); + return fid; } -void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) +struct p9_fid *v9fs_fid_clone(struct dentry *dentry) { - v9fs_t_clunk(v9ses, fid->fid); - v9fs_fid_destroy(fid); + struct p9_fid *fid, *ret; + + fid = v9fs_fid_lookup(dentry); + if (IS_ERR(fid)) + return fid; + + ret = p9_client_walk(fid, 0, NULL, 1); + return ret; }