From patchwork Thu Jul 1 05:19:50 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Aneesh Kumar K.V" X-Patchwork-Id: 109005 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o615KNaY032559 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 1 Jul 2010 05:20:59 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-1.v29.ch3.sourceforge.com) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1OUCC7-0005mz-SW; Thu, 01 Jul 2010 05:20:15 +0000 Received: from sfi-mx-4.v28.ch3.sourceforge.com ([172.29.28.124] helo=mx.sourceforge.net) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1OUCC5-0005mB-TO for v9fs-developer@lists.sourceforge.net; Thu, 01 Jul 2010 05:20:13 +0000 X-ACL-Warn: Received: from e23smtp09.au.ibm.com ([202.81.31.142]) by sfi-mx-4.v28.ch3.sourceforge.com with esmtps (TLSv1:AES256-SHA:256) (Exim 4.69) id 1OUCC4-0000dY-08 for v9fs-developer@lists.sourceforge.net; Thu, 01 Jul 2010 05:20:13 +0000 Received: from d23relay03.au.ibm.com (d23relay03.au.ibm.com [202.81.31.245]) by e23smtp09.au.ibm.com (8.14.4/8.13.1) with ESMTP id o615JxEU022385 for ; Thu, 1 Jul 2010 15:19:59 +1000 Received: from d23av01.au.ibm.com (d23av01.au.ibm.com [9.190.234.96]) by d23relay03.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id o615K2YZ1745038 for ; Thu, 1 Jul 2010 15:20:02 +1000 Received: from d23av01.au.ibm.com (loopback [127.0.0.1]) by d23av01.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id o615K1uQ013966 for ; Thu, 1 Jul 2010 15:20:01 +1000 Received: from skywalker.in.ibm.com ([9.124.35.50]) by d23av01.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id o615K0qI013767; Thu, 1 Jul 2010 15:20:00 +1000 From: "Aneesh Kumar K.V" To: v9fs-developer@lists.sourceforge.net, viro@zeniv.linux.org.uk, Linus Torvalds Date: Thu, 1 Jul 2010 10:49:50 +0530 Message-Id: <1277961590-5603-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.2.rc1 X-Spam-Score: -0.2 (/) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.2 AWL AWL: From: address is in the auto white-list X-Headers-End: 1OUCC4-0000dY-08 Subject: [V9fs-developer] [PATCH] fs/9p: Prevent parallel rename when doing fid_lookup X-BeenThere: v9fs-developer@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: v9fs-developer-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 01 Jul 2010 05:20:59 +0000 (UTC) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 5d6cfcb..50a907a 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -97,6 +97,48 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) return ret; } +static int build_path_from_dentry(struct v9fs_session_info *v9ses, + struct dentry *dentry, char ***names) +{ + char **wnames; + int n = 0, i = 0; + struct dentry *ds; + + read_lock(&v9ses->rename_lock); + for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) + n++; + + wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); + if (!wnames) { + n = 0; + goto err_out; + } + for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent) { + wnames[i] = kmalloc(strlen(ds->d_name.name) + 1, GFP_KERNEL); + if (!wnames[i]) + goto err_out; + + strcpy(wnames[i], ds->d_name.name); + } + *names = wnames; + read_unlock(&v9ses->rename_lock); + return n; +err_out: + read_unlock(&v9ses->rename_lock); + for (; i < n; i++) + kfree(wnames[i]); + kfree(wnames); + return -ENOMEM; +} + +static void kfree_wnames(char **wnames, int n) +{ + int i; + for (i = 0; i < n; i++) + kfree(wnames[i]); + kfree(wnames); +} + /** * v9fs_fid_lookup - lookup for a fid, try to walk if not found * @dentry: dentry to look for fid in @@ -112,7 +154,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) int i, n, l, clone, any, access; u32 uid; struct p9_fid *fid, *old_fid = NULL; - struct dentry *d, *ds; + struct dentry *ds; struct v9fs_session_info *v9ses; char **wnames, *uname; @@ -139,46 +181,62 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) fid = v9fs_fid_find(dentry, uid, any); if (fid) return fid; - + /* + * we don't have a matching fid. To do a TWALK we need + * parent fid. We need to prevent rename when we want to + * look at the parent. + */ + read_lock(&v9ses->rename_lock); 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++; - - 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 (v9fs_proto_dotu(v9ses) || - v9fs_proto_dotl(v9ses)) + if (fid) { + /* + * Found the parent fid do a lookup with that. + * We want to do the walk without holding rename_lock + */ + char *dentry_name; + dentry_name = kmalloc(strlen(dentry->d_name.name) + 1, + GFP_KERNEL); + if (dentry_name) { + strcpy(dentry_name, dentry->d_name.name); + read_unlock(&v9ses->rename_lock); + fid = p9_client_walk(fid, 1, (char **)&dentry_name, 1); + kfree(dentry_name); + goto dentry_out; + } else { + read_unlock(&v9ses->rename_lock); + return ERR_PTR(-ENOMEM); + } + } + read_unlock(&v9ses->rename_lock); + /* start from the root and try to do a lookup */ + fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any); + if (!fid) { + /* the user is not attached to the fs yet */ + if (access == V9FS_ACCESS_SINGLE) + return ERR_PTR(-EPERM); + + if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) uname = NULL; - else - uname = v9ses->uname; - - fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, - v9ses->aname); + else + uname = v9ses->uname; - if (IS_ERR(fid)) - return fid; - - v9fs_fid_add(ds, fid); - } - } else /* walk from the parent */ - n = 1; + fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, + v9ses->aname); + if (IS_ERR(fid)) + return fid; - if (ds == dentry) + v9fs_fid_add(dentry->d_sb->s_root, fid); + } + /* If we are root just return the fid */ + if (dentry->d_sb->s_root == 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; - + n = build_path_from_dentry(v9ses, dentry, &wnames); + if (n < 0) { + fid = ERR_PTR(n); + goto err_out; + } clone = 1; i = 0; while (i < n) { @@ -193,16 +251,18 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) */ p9_client_clunk(old_fid); } - kfree(wnames); - return fid; + kfree_wnames(wnames, n); + goto err_out; } old_fid = fid; i += l; clone = 0; } + kfree_wnames(wnames, n); - kfree(wnames); +dentry_out: v9fs_fid_add(dentry, fid); +err_out: return fid; } diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 3c49201..b41bcef 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -237,6 +237,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, __putname(v9ses->uname); return ERR_PTR(-ENOMEM); } + rwlock_init(&v9ses->rename_lock); rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY); if (rc) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index bec4d0b..dee4f26 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -104,6 +104,7 @@ struct v9fs_session_info { struct p9_client *clnt; /* 9p client */ struct list_head slist; /* list of sessions registered with v9fs */ struct backing_dev_info bdi; + rwlock_t rename_lock; }; struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 2286cc6..509cdba 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -750,6 +750,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; v9ses = v9fs_inode2v9ses(dir); + /* We can walk d_parent because we hold the dir->i_mutex */ dfid = v9fs_fid_lookup(dentry->d_parent); if (IS_ERR(dfid)) return ERR_CAST(dfid); @@ -856,28 +857,34 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, retval = PTR_ERR(newdirfid); goto clunk_olddir; } - if (v9fs_proto_dotl(v9ses)) { retval = p9_client_rename(oldfid, newdirfid, (char *) new_dentry->d_name.name); if (retval != -ENOSYS) goto clunk_newdir; } + if (old_dentry->d_parent != new_dentry->d_parent) { + /* + * 9P .u can only handle file rename in the same directory + */ - /* 9P can only handle file rename in the same directory */ - if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { P9_DPRINTK(P9_DEBUG_ERROR, "old dir and new dir are different\n"); retval = -EXDEV; goto clunk_newdir; } - v9fs_blank_wstat(&wstat); wstat.muid = v9ses->uname; wstat.name = (char *) new_dentry->d_name.name; retval = p9_client_wstat(oldfid, &wstat); clunk_newdir: + if (!retval) { + write_lock(&v9ses->rename_lock); + /* successful rename */ + d_move(old_dentry, new_dentry); + write_unlock(&v9ses->rename_lock); + } p9_client_clunk(newdirfid); clunk_olddir: diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index b593ece..1f085cf 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -285,4 +285,5 @@ struct file_system_type v9fs_fs_type = { .get_sb = v9fs_get_sb, .kill_sb = v9fs_kill_super, .owner = THIS_MODULE, + .fs_flags = FS_RENAME_DOES_D_MOVE, };