aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-09-20 17:19:26 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-11-09 00:16:44 -0500
commit27ac0ffeac80ba6b9580529568d06144df044366 (patch)
treea2e523716039784271250387d6e94ec4f01daec5
parent146a8595c6399ee6ab4b5cc34c0d28aa4835fdc5 (diff)
locks: break delegations on any attribute modification
NFSv4 uses leases to guarantee that clients can cache metadata as well as data. Cc: Mikulas Patocka <mikulas@artax.karlin.mff.cuni.cz> Cc: David Howells <dhowells@redhat.com> Cc: Tyler Hicks <tyhicks@canonical.com> Cc: Dustin Kirkland <dustin.kirkland@gazzang.com> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--drivers/base/devtmpfs.c4
-rw-r--r--fs/attr.c25
-rw-r--r--fs/cachefiles/interface.c4
-rw-r--r--fs/ecryptfs/inode.c4
-rw-r--r--fs/hpfs/namei.c2
-rw-r--r--fs/inode.c6
-rw-r--r--fs/nfsd/vfs.c8
-rw-r--r--fs/open.c22
-rw-r--r--fs/utimes.c9
-rw-r--r--include/linux/fs.h2
10 files changed, 69 insertions, 17 deletions
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 1b8490e2fbde..0f3820121e02 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -216,7 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
216 newattrs.ia_gid = gid; 216 newattrs.ia_gid = gid;
217 newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; 217 newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
218 mutex_lock(&dentry->d_inode->i_mutex); 218 mutex_lock(&dentry->d_inode->i_mutex);
219 notify_change(dentry, &newattrs); 219 notify_change(dentry, &newattrs, NULL);
220 mutex_unlock(&dentry->d_inode->i_mutex); 220 mutex_unlock(&dentry->d_inode->i_mutex);
221 221
222 /* mark as kernel-created inode */ 222 /* mark as kernel-created inode */
@@ -322,7 +322,7 @@ static int handle_remove(const char *nodename, struct device *dev)
322 newattrs.ia_valid = 322 newattrs.ia_valid =
323 ATTR_UID|ATTR_GID|ATTR_MODE; 323 ATTR_UID|ATTR_GID|ATTR_MODE;
324 mutex_lock(&dentry->d_inode->i_mutex); 324 mutex_lock(&dentry->d_inode->i_mutex);
325 notify_change(dentry, &newattrs); 325 notify_change(dentry, &newattrs, NULL);
326 mutex_unlock(&dentry->d_inode->i_mutex); 326 mutex_unlock(&dentry->d_inode->i_mutex);
327 err = vfs_unlink(parent.dentry->d_inode, dentry, NULL); 327 err = vfs_unlink(parent.dentry->d_inode, dentry, NULL);
328 if (!err || err == -ENOENT) 328 if (!err || err == -ENOENT)
diff --git a/fs/attr.c b/fs/attr.c
index 1449adb14ef6..267968d94673 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -167,7 +167,27 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)
167} 167}
168EXPORT_SYMBOL(setattr_copy); 168EXPORT_SYMBOL(setattr_copy);
169 169
170int notify_change(struct dentry * dentry, struct iattr * attr) 170/**
171 * notify_change - modify attributes of a filesytem object
172 * @dentry: object affected
173 * @iattr: new attributes
174 * @delegated_inode: returns inode, if the inode is delegated
175 *
176 * The caller must hold the i_mutex on the affected object.
177 *
178 * If notify_change discovers a delegation in need of breaking,
179 * it will return -EWOULDBLOCK and return a reference to the inode in
180 * delegated_inode. The caller should then break the delegation and
181 * retry. Because breaking a delegation may take a long time, the
182 * caller should drop the i_mutex before doing so.
183 *
184 * Alternatively, a caller may pass NULL for delegated_inode. This may
185 * be appropriate for callers that expect the underlying filesystem not
186 * to be NFS exported. Also, passing NULL is fine for callers holding
187 * the file open for write, as there can be no conflicting delegation in
188 * that case.
189 */
190int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
171{ 191{
172 struct inode *inode = dentry->d_inode; 192 struct inode *inode = dentry->d_inode;
173 umode_t mode = inode->i_mode; 193 umode_t mode = inode->i_mode;
@@ -243,6 +263,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
243 error = security_inode_setattr(dentry, attr); 263 error = security_inode_setattr(dentry, attr);
244 if (error) 264 if (error)
245 return error; 265 return error;
266 error = try_break_deleg(inode, delegated_inode);
267 if (error)
268 return error;
246 269
247 if (inode->i_op->setattr) 270 if (inode->i_op->setattr)
248 error = inode->i_op->setattr(dentry, attr); 271 error = inode->i_op->setattr(dentry, attr);
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
index 43eb5592cdea..5088a418ac4d 100644
--- a/fs/cachefiles/interface.c
+++ b/fs/cachefiles/interface.c
@@ -449,14 +449,14 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
449 _debug("discard tail %llx", oi_size); 449 _debug("discard tail %llx", oi_size);
450 newattrs.ia_valid = ATTR_SIZE; 450 newattrs.ia_valid = ATTR_SIZE;
451 newattrs.ia_size = oi_size & PAGE_MASK; 451 newattrs.ia_size = oi_size & PAGE_MASK;
452 ret = notify_change(object->backer, &newattrs); 452 ret = notify_change(object->backer, &newattrs, NULL);
453 if (ret < 0) 453 if (ret < 0)
454 goto truncate_failed; 454 goto truncate_failed;
455 } 455 }
456 456
457 newattrs.ia_valid = ATTR_SIZE; 457 newattrs.ia_valid = ATTR_SIZE;
458 newattrs.ia_size = ni_size; 458 newattrs.ia_size = ni_size;
459 ret = notify_change(object->backer, &newattrs); 459 ret = notify_change(object->backer, &newattrs, NULL);
460 460
461truncate_failed: 461truncate_failed:
462 mutex_unlock(&object->backer->d_inode->i_mutex); 462 mutex_unlock(&object->backer->d_inode->i_mutex);
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 1c628f023041..c36c44824471 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -882,7 +882,7 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
882 struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); 882 struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
883 883
884 mutex_lock(&lower_dentry->d_inode->i_mutex); 884 mutex_lock(&lower_dentry->d_inode->i_mutex);
885 rc = notify_change(lower_dentry, &lower_ia); 885 rc = notify_change(lower_dentry, &lower_ia, NULL);
886 mutex_unlock(&lower_dentry->d_inode->i_mutex); 886 mutex_unlock(&lower_dentry->d_inode->i_mutex);
887 } 887 }
888 return rc; 888 return rc;
@@ -983,7 +983,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
983 lower_ia.ia_valid &= ~ATTR_MODE; 983 lower_ia.ia_valid &= ~ATTR_MODE;
984 984
985 mutex_lock(&lower_dentry->d_inode->i_mutex); 985 mutex_lock(&lower_dentry->d_inode->i_mutex);
986 rc = notify_change(lower_dentry, &lower_ia); 986 rc = notify_change(lower_dentry, &lower_ia, NULL);
987 mutex_unlock(&lower_dentry->d_inode->i_mutex); 987 mutex_unlock(&lower_dentry->d_inode->i_mutex);
988out: 988out:
989 fsstack_copy_attr_all(inode, lower_inode); 989 fsstack_copy_attr_all(inode, lower_inode);
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index 345713d2f8f3..1b39afdd86fd 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -407,7 +407,7 @@ again:
407 /*printk("HPFS: truncating file before delete.\n");*/ 407 /*printk("HPFS: truncating file before delete.\n");*/
408 newattrs.ia_size = 0; 408 newattrs.ia_size = 0;
409 newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; 409 newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
410 err = notify_change(dentry, &newattrs); 410 err = notify_change(dentry, &newattrs, NULL);
411 put_write_access(inode); 411 put_write_access(inode);
412 if (!err) 412 if (!err)
413 goto again; 413 goto again;
diff --git a/fs/inode.c b/fs/inode.c
index ce48c359ce9e..4bcdad3c9361 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1603,7 +1603,11 @@ static int __remove_suid(struct dentry *dentry, int kill)
1603 struct iattr newattrs; 1603 struct iattr newattrs;
1604 1604
1605 newattrs.ia_valid = ATTR_FORCE | kill; 1605 newattrs.ia_valid = ATTR_FORCE | kill;
1606 return notify_change(dentry, &newattrs); 1606 /*
1607 * Note we call this on write, so notify_change will not
1608 * encounter any conflicting delegations:
1609 */
1610 return notify_change(dentry, &newattrs, NULL);
1607} 1611}
1608 1612
1609int file_remove_suid(struct file *file) 1613int file_remove_suid(struct file *file)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 27ba21b5f383..94b5f5d2bfed 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -427,7 +427,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
427 goto out_nfserr; 427 goto out_nfserr;
428 fh_lock(fhp); 428 fh_lock(fhp);
429 429
430 host_err = notify_change(dentry, iap); 430 host_err = notify_change(dentry, iap, NULL);
431 err = nfserrno(host_err); 431 err = nfserrno(host_err);
432 fh_unlock(fhp); 432 fh_unlock(fhp);
433 } 433 }
@@ -988,7 +988,11 @@ static void kill_suid(struct dentry *dentry)
988 ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; 988 ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
989 989
990 mutex_lock(&dentry->d_inode->i_mutex); 990 mutex_lock(&dentry->d_inode->i_mutex);
991 notify_change(dentry, &ia); 991 /*
992 * Note we call this on write, so notify_change will not
993 * encounter any conflicting delegations:
994 */
995 notify_change(dentry, &ia, NULL);
992 mutex_unlock(&dentry->d_inode->i_mutex); 996 mutex_unlock(&dentry->d_inode->i_mutex);
993} 997}
994 998
diff --git a/fs/open.c b/fs/open.c
index fffbed40dbe9..4b3e1edf2fe4 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -57,7 +57,8 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
57 newattrs.ia_valid |= ret | ATTR_FORCE; 57 newattrs.ia_valid |= ret | ATTR_FORCE;
58 58
59 mutex_lock(&dentry->d_inode->i_mutex); 59 mutex_lock(&dentry->d_inode->i_mutex);
60 ret = notify_change(dentry, &newattrs); 60 /* Note any delegations or leases have already been broken: */
61 ret = notify_change(dentry, &newattrs, NULL);
61 mutex_unlock(&dentry->d_inode->i_mutex); 62 mutex_unlock(&dentry->d_inode->i_mutex);
62 return ret; 63 return ret;
63} 64}
@@ -464,21 +465,28 @@ out:
464static int chmod_common(struct path *path, umode_t mode) 465static int chmod_common(struct path *path, umode_t mode)
465{ 466{
466 struct inode *inode = path->dentry->d_inode; 467 struct inode *inode = path->dentry->d_inode;
468 struct inode *delegated_inode = NULL;
467 struct iattr newattrs; 469 struct iattr newattrs;
468 int error; 470 int error;
469 471
470 error = mnt_want_write(path->mnt); 472 error = mnt_want_write(path->mnt);
471 if (error) 473 if (error)
472 return error; 474 return error;
475retry_deleg:
473 mutex_lock(&inode->i_mutex); 476 mutex_lock(&inode->i_mutex);
474 error = security_path_chmod(path, mode); 477 error = security_path_chmod(path, mode);
475 if (error) 478 if (error)
476 goto out_unlock; 479 goto out_unlock;
477 newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); 480 newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
478 newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; 481 newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
479 error = notify_change(path->dentry, &newattrs); 482 error = notify_change(path->dentry, &newattrs, &delegated_inode);
480out_unlock: 483out_unlock:
481 mutex_unlock(&inode->i_mutex); 484 mutex_unlock(&inode->i_mutex);
485 if (delegated_inode) {
486 error = break_deleg_wait(&delegated_inode);
487 if (!error)
488 goto retry_deleg;
489 }
482 mnt_drop_write(path->mnt); 490 mnt_drop_write(path->mnt);
483 return error; 491 return error;
484} 492}
@@ -522,6 +530,7 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
522static int chown_common(struct path *path, uid_t user, gid_t group) 530static int chown_common(struct path *path, uid_t user, gid_t group)
523{ 531{
524 struct inode *inode = path->dentry->d_inode; 532 struct inode *inode = path->dentry->d_inode;
533 struct inode *delegated_inode = NULL;
525 int error; 534 int error;
526 struct iattr newattrs; 535 struct iattr newattrs;
527 kuid_t uid; 536 kuid_t uid;
@@ -546,12 +555,17 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
546 if (!S_ISDIR(inode->i_mode)) 555 if (!S_ISDIR(inode->i_mode))
547 newattrs.ia_valid |= 556 newattrs.ia_valid |=
548 ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; 557 ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
558retry_deleg:
549 mutex_lock(&inode->i_mutex); 559 mutex_lock(&inode->i_mutex);
550 error = security_path_chown(path, uid, gid); 560 error = security_path_chown(path, uid, gid);
551 if (!error) 561 if (!error)
552 error = notify_change(path->dentry, &newattrs); 562 error = notify_change(path->dentry, &newattrs, &delegated_inode);
553 mutex_unlock(&inode->i_mutex); 563 mutex_unlock(&inode->i_mutex);
554 564 if (delegated_inode) {
565 error = break_deleg_wait(&delegated_inode);
566 if (!error)
567 goto retry_deleg;
568 }
555 return error; 569 return error;
556} 570}
557 571
diff --git a/fs/utimes.c b/fs/utimes.c
index f4fb7eca10e8..aa138d64560a 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -53,6 +53,7 @@ static int utimes_common(struct path *path, struct timespec *times)
53 int error; 53 int error;
54 struct iattr newattrs; 54 struct iattr newattrs;
55 struct inode *inode = path->dentry->d_inode; 55 struct inode *inode = path->dentry->d_inode;
56 struct inode *delegated_inode = NULL;
56 57
57 error = mnt_want_write(path->mnt); 58 error = mnt_want_write(path->mnt);
58 if (error) 59 if (error)
@@ -101,9 +102,15 @@ static int utimes_common(struct path *path, struct timespec *times)
101 goto mnt_drop_write_and_out; 102 goto mnt_drop_write_and_out;
102 } 103 }
103 } 104 }
105retry_deleg:
104 mutex_lock(&inode->i_mutex); 106 mutex_lock(&inode->i_mutex);
105 error = notify_change(path->dentry, &newattrs); 107 error = notify_change(path->dentry, &newattrs, &delegated_inode);
106 mutex_unlock(&inode->i_mutex); 108 mutex_unlock(&inode->i_mutex);
109 if (delegated_inode) {
110 error = break_deleg_wait(&delegated_inode);
111 if (!error)
112 goto retry_deleg;
113 }
107 114
108mnt_drop_write_and_out: 115mnt_drop_write_and_out:
109 mnt_drop_write(path->mnt); 116 mnt_drop_write(path->mnt);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6e36e7118ec1..ab2a0ca82dc5 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2278,7 +2278,7 @@ extern void emergency_remount(void);
2278#ifdef CONFIG_BLOCK 2278#ifdef CONFIG_BLOCK
2279extern sector_t bmap(struct inode *, sector_t); 2279extern sector_t bmap(struct inode *, sector_t);
2280#endif 2280#endif
2281extern int notify_change(struct dentry *, struct iattr *); 2281extern int notify_change(struct dentry *, struct iattr *, struct inode **);
2282extern int inode_permission(struct inode *, int); 2282extern int inode_permission(struct inode *, int);
2283extern int generic_permission(struct inode *, int); 2283extern int generic_permission(struct inode *, int);
2284 2284