linux/fs/9p/fid.c
<<
>>
Prefs
   1/*
   2 * V9FS FID Management
   3 *
   4 *  Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
   5 *  Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License version 2
   9 *  as published by the Free Software Foundation.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to:
  18 *  Free Software Foundation
  19 *  51 Franklin Street, Fifth Floor
  20 *  Boston, MA  02111-1301  USA
  21 *
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/errno.h>
  26#include <linux/fs.h>
  27#include <linux/slab.h>
  28#include <linux/sched.h>
  29#include <linux/idr.h>
  30#include <net/9p/9p.h>
  31#include <net/9p/client.h>
  32
  33#include "v9fs.h"
  34#include "v9fs_vfs.h"
  35#include "fid.h"
  36
  37/**
  38 * v9fs_fid_add - add a fid to a dentry
  39 * @dentry: dentry that the fid is being added to
  40 * @fid: fid to add
  41 *
  42 */
  43
  44int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
  45{
  46        struct v9fs_dentry *dent;
  47
  48        p9_debug(P9_DEBUG_VFS, "fid %d dentry %s\n",
  49                 fid->fid, dentry->d_name.name);
  50
  51        dent = dentry->d_fsdata;
  52        if (!dent) {
  53                dent = kmalloc(sizeof(struct v9fs_dentry), GFP_KERNEL);
  54                if (!dent)
  55                        return -ENOMEM;
  56
  57                spin_lock_init(&dent->lock);
  58                INIT_LIST_HEAD(&dent->fidlist);
  59                dentry->d_fsdata = dent;
  60        }
  61
  62        spin_lock(&dent->lock);
  63        list_add(&fid->dlist, &dent->fidlist);
  64        spin_unlock(&dent->lock);
  65
  66        return 0;
  67}
  68
  69/**
  70 * v9fs_fid_find - retrieve a fid that belongs to the specified uid
  71 * @dentry: dentry to look for fid in
  72 * @uid: return fid that belongs to the specified user
  73 * @any: if non-zero, return any fid associated with the dentry
  74 *
  75 */
  76
  77static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any)
  78{
  79        struct v9fs_dentry *dent;
  80        struct p9_fid *fid, *ret;
  81
  82        p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n",
  83                 dentry->d_name.name, dentry, uid, any);
  84        dent = (struct v9fs_dentry *) dentry->d_fsdata;
  85        ret = NULL;
  86        if (dent) {
  87                spin_lock(&dent->lock);
  88                list_for_each_entry(fid, &dent->fidlist, dlist) {
  89                        if (any || fid->uid == uid) {
  90                                ret = fid;
  91                                break;
  92                        }
  93                }
  94                spin_unlock(&dent->lock);
  95        }
  96
  97        return ret;
  98}
  99
 100/*
 101 * We need to hold v9ses->rename_sem as long as we hold references
 102 * to returned path array. Array element contain pointers to
 103 * dentry names.
 104 */
 105static int build_path_from_dentry(struct v9fs_session_info *v9ses,
 106                                  struct dentry *dentry, char ***names)
 107{
 108        int n = 0, i;
 109        char **wnames;
 110        struct dentry *ds;
 111
 112        for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
 113                n++;
 114
 115        wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
 116        if (!wnames)
 117                goto err_out;
 118
 119        for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
 120                wnames[i] = (char  *)ds->d_name.name;
 121
 122        *names = wnames;
 123        return n;
 124err_out:
 125        return -ENOMEM;
 126}
 127
 128static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
 129                                               uid_t uid, int any)
 130{
 131        struct dentry *ds;
 132        char **wnames, *uname;
 133        int i, n, l, clone, access;
 134        struct v9fs_session_info *v9ses;
 135        struct p9_fid *fid, *old_fid = NULL;
 136
 137        v9ses = v9fs_dentry2v9ses(dentry);
 138        access = v9ses->flags & V9FS_ACCESS_MASK;
 139        fid = v9fs_fid_find(dentry, uid, any);
 140        if (fid)
 141                return fid;
 142        /*
 143         * we don't have a matching fid. To do a TWALK we need
 144         * parent fid. We need to prevent rename when we want to
 145         * look at the parent.
 146         */
 147        down_read(&v9ses->rename_sem);
 148        ds = dentry->d_parent;
 149        fid = v9fs_fid_find(ds, uid, any);
 150        if (fid) {
 151                /* Found the parent fid do a lookup with that */
 152                fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
 153                goto fid_out;
 154        }
 155        up_read(&v9ses->rename_sem);
 156
 157        /* start from the root and try to do a lookup */
 158        fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
 159        if (!fid) {
 160                /* the user is not attached to the fs yet */
 161                if (access == V9FS_ACCESS_SINGLE)
 162                        return ERR_PTR(-EPERM);
 163
 164                if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
 165                                uname = NULL;
 166                else
 167                        uname = v9ses->uname;
 168
 169                fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
 170                                       v9ses->aname);
 171                if (IS_ERR(fid))
 172                        return fid;
 173
 174                v9fs_fid_add(dentry->d_sb->s_root, fid);
 175        }
 176        /* If we are root ourself just return that */
 177        if (dentry->d_sb->s_root == dentry)
 178                return fid;
 179        /*
 180         * Do a multipath walk with attached root.
 181         * When walking parent we need to make sure we
 182         * don't have a parallel rename happening
 183         */
 184        down_read(&v9ses->rename_sem);
 185        n  = build_path_from_dentry(v9ses, dentry, &wnames);
 186        if (n < 0) {
 187                fid = ERR_PTR(n);
 188                goto err_out;
 189        }
 190        clone = 1;
 191        i = 0;
 192        while (i < n) {
 193                l = min(n - i, P9_MAXWELEM);
 194                /*
 195                 * We need to hold rename lock when doing a multipath
 196                 * walk to ensure none of the patch component change
 197                 */
 198                fid = p9_client_walk(fid, l, &wnames[i], clone);
 199                if (IS_ERR(fid)) {
 200                        if (old_fid) {
 201                                /*
 202                                 * If we fail, clunk fid which are mapping
 203                                 * to path component and not the last component
 204                                 * of the path.
 205                                 */
 206                                p9_client_clunk(old_fid);
 207                        }
 208                        kfree(wnames);
 209                        goto err_out;
 210                }
 211                old_fid = fid;
 212                i += l;
 213                clone = 0;
 214        }
 215        kfree(wnames);
 216fid_out:
 217        if (!IS_ERR(fid))
 218                v9fs_fid_add(dentry, fid);
 219err_out:
 220        up_read(&v9ses->rename_sem);
 221        return fid;
 222}
 223
 224/**
 225 * v9fs_fid_lookup - lookup for a fid, try to walk if not found
 226 * @dentry: dentry to look for fid in
 227 *
 228 * Look for a fid in the specified dentry for the current user.
 229 * If no fid is found, try to create one walking from a fid from the parent
 230 * dentry (if it has one), or the root dentry. If the user haven't accessed
 231 * the fs yet, attach now and walk from the root.
 232 */
 233
 234struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
 235{
 236        uid_t uid;
 237        int  any, access;
 238        struct v9fs_session_info *v9ses;
 239
 240        v9ses = v9fs_dentry2v9ses(dentry);
 241        access = v9ses->flags & V9FS_ACCESS_MASK;
 242        switch (access) {
 243        case V9FS_ACCESS_SINGLE:
 244        case V9FS_ACCESS_USER:
 245        case V9FS_ACCESS_CLIENT:
 246                uid = current_fsuid();
 247                any = 0;
 248                break;
 249
 250        case V9FS_ACCESS_ANY:
 251                uid = v9ses->uid;
 252                any = 1;
 253                break;
 254
 255        default:
 256                uid = ~0;
 257                any = 0;
 258                break;
 259        }
 260        return v9fs_fid_lookup_with_uid(dentry, uid, any);
 261}
 262
 263struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
 264{
 265        struct p9_fid *fid, *ret;
 266
 267        fid = v9fs_fid_lookup(dentry);
 268        if (IS_ERR(fid))
 269                return fid;
 270
 271        ret = p9_client_walk(fid, 0, NULL, 1);
 272        return ret;
 273}
 274
 275static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, uid_t uid)
 276{
 277        struct p9_fid *fid, *ret;
 278
 279        fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
 280        if (IS_ERR(fid))
 281                return fid;
 282
 283        ret = p9_client_walk(fid, 0, NULL, 1);
 284        return ret;
 285}
 286
 287struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
 288{
 289        int err;
 290        struct p9_fid *fid;
 291
 292        fid = v9fs_fid_clone_with_uid(dentry, 0);
 293        if (IS_ERR(fid))
 294                goto error_out;
 295        /*
 296         * writeback fid will only be used to write back the
 297         * dirty pages. We always request for the open fid in read-write
 298         * mode so that a partial page write which result in page
 299         * read can work.
 300         */
 301        err = p9_client_open(fid, O_RDWR);
 302        if (err < 0) {
 303                p9_client_clunk(fid);
 304                fid = ERR_PTR(err);
 305                goto error_out;
 306        }
 307error_out:
 308        return fid;
 309}
 310
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.