Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 27ac0ff

Browse files
J. Bruce FieldsAl Viro
authored andcommitted
locks: break delegations on any attribute modification
NFSv4 uses leases to guarantee that clients can cache metadata as well as data. Cc: Mikulas Patocka <[email protected]> Cc: David Howells <[email protected]> Cc: Tyler Hicks <[email protected]> Cc: Dustin Kirkland <[email protected]> Acked-by: Jeff Layton <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 146a859 commit 27ac0ff

File tree

10 files changed

+69
-17
lines changed

10 files changed

+69
-17
lines changed

drivers/base/devtmpfs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
216216
newattrs.ia_gid = gid;
217217
newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
218218
mutex_lock(&dentry->d_inode->i_mutex);
219-
notify_change(dentry, &newattrs);
219+
notify_change(dentry, &newattrs, NULL);
220220
mutex_unlock(&dentry->d_inode->i_mutex);
221221

222222
/* mark as kernel-created inode */
@@ -322,7 +322,7 @@ static int handle_remove(const char *nodename, struct device *dev)
322322
newattrs.ia_valid =
323323
ATTR_UID|ATTR_GID|ATTR_MODE;
324324
mutex_lock(&dentry->d_inode->i_mutex);
325-
notify_change(dentry, &newattrs);
325+
notify_change(dentry, &newattrs, NULL);
326326
mutex_unlock(&dentry->d_inode->i_mutex);
327327
err = vfs_unlink(parent.dentry->d_inode, dentry, NULL);
328328
if (!err || err == -ENOENT)

fs/attr.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,27 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)
167167
}
168168
EXPORT_SYMBOL(setattr_copy);
169169

170-
int 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+
*/
190+
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
171191
{
172192
struct inode *inode = dentry->d_inode;
173193
umode_t mode = inode->i_mode;
@@ -241,6 +261,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
241261
return 0;
242262

243263
error = security_inode_setattr(dentry, attr);
264+
if (error)
265+
return error;
266+
error = try_break_deleg(inode, delegated_inode);
244267
if (error)
245268
return error;
246269

fs/cachefiles/interface.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,14 +449,14 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
449449
_debug("discard tail %llx", oi_size);
450450
newattrs.ia_valid = ATTR_SIZE;
451451
newattrs.ia_size = oi_size & PAGE_MASK;
452-
ret = notify_change(object->backer, &newattrs);
452+
ret = notify_change(object->backer, &newattrs, NULL);
453453
if (ret < 0)
454454
goto truncate_failed;
455455
}
456456

457457
newattrs.ia_valid = ATTR_SIZE;
458458
newattrs.ia_size = ni_size;
459-
ret = notify_change(object->backer, &newattrs);
459+
ret = notify_change(object->backer, &newattrs, NULL);
460460

461461
truncate_failed:
462462
mutex_unlock(&object->backer->d_inode->i_mutex);

fs/ecryptfs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
882882
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
883883

884884
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);
886886
mutex_unlock(&lower_dentry->d_inode->i_mutex);
887887
}
888888
return rc;
@@ -983,7 +983,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
983983
lower_ia.ia_valid &= ~ATTR_MODE;
984984

985985
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);
987987
mutex_unlock(&lower_dentry->d_inode->i_mutex);
988988
out:
989989
fsstack_copy_attr_all(inode, lower_inode);

fs/hpfs/namei.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
407407
/*printk("HPFS: truncating file before delete.\n");*/
408408
newattrs.ia_size = 0;
409409
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
410-
err = notify_change(dentry, &newattrs);
410+
err = notify_change(dentry, &newattrs, NULL);
411411
put_write_access(inode);
412412
if (!err)
413413
goto again;

fs/inode.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,11 @@ static int __remove_suid(struct dentry *dentry, int kill)
16031603
struct iattr newattrs;
16041604

16051605
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);
16071611
}
16081612

16091613
int file_remove_suid(struct file *file)

fs/nfsd/vfs.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
427427
goto out_nfserr;
428428
fh_lock(fhp);
429429

430-
host_err = notify_change(dentry, iap);
430+
host_err = notify_change(dentry, iap, NULL);
431431
err = nfserrno(host_err);
432432
fh_unlock(fhp);
433433
}
@@ -988,7 +988,11 @@ static void kill_suid(struct dentry *dentry)
988988
ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
989989

990990
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);
992996
mutex_unlock(&dentry->d_inode->i_mutex);
993997
}
994998

fs/open.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
5757
newattrs.ia_valid |= ret | ATTR_FORCE;
5858

5959
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);
6162
mutex_unlock(&dentry->d_inode->i_mutex);
6263
return ret;
6364
}
@@ -464,21 +465,28 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
464465
static int chmod_common(struct path *path, umode_t mode)
465466
{
466467
struct inode *inode = path->dentry->d_inode;
468+
struct inode *delegated_inode = NULL;
467469
struct iattr newattrs;
468470
int error;
469471

470472
error = mnt_want_write(path->mnt);
471473
if (error)
472474
return error;
475+
retry_deleg:
473476
mutex_lock(&inode->i_mutex);
474477
error = security_path_chmod(path, mode);
475478
if (error)
476479
goto out_unlock;
477480
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
478481
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
479-
error = notify_change(path->dentry, &newattrs);
482+
error = notify_change(path->dentry, &newattrs, &delegated_inode);
480483
out_unlock:
481484
mutex_unlock(&inode->i_mutex);
485+
if (delegated_inode) {
486+
error = break_deleg_wait(&delegated_inode);
487+
if (!error)
488+
goto retry_deleg;
489+
}
482490
mnt_drop_write(path->mnt);
483491
return error;
484492
}
@@ -522,6 +530,7 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
522530
static int chown_common(struct path *path, uid_t user, gid_t group)
523531
{
524532
struct inode *inode = path->dentry->d_inode;
533+
struct inode *delegated_inode = NULL;
525534
int error;
526535
struct iattr newattrs;
527536
kuid_t uid;
@@ -546,12 +555,17 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
546555
if (!S_ISDIR(inode->i_mode))
547556
newattrs.ia_valid |=
548557
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
558+
retry_deleg:
549559
mutex_lock(&inode->i_mutex);
550560
error = security_path_chown(path, uid, gid);
551561
if (!error)
552-
error = notify_change(path->dentry, &newattrs);
562+
error = notify_change(path->dentry, &newattrs, &delegated_inode);
553563
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+
}
555569
return error;
556570
}
557571

fs/utimes.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static int utimes_common(struct path *path, struct timespec *times)
5353
int error;
5454
struct iattr newattrs;
5555
struct inode *inode = path->dentry->d_inode;
56+
struct inode *delegated_inode = NULL;
5657

5758
error = mnt_want_write(path->mnt);
5859
if (error)
@@ -101,9 +102,15 @@ static int utimes_common(struct path *path, struct timespec *times)
101102
goto mnt_drop_write_and_out;
102103
}
103104
}
105+
retry_deleg:
104106
mutex_lock(&inode->i_mutex);
105-
error = notify_change(path->dentry, &newattrs);
107+
error = notify_change(path->dentry, &newattrs, &delegated_inode);
106108
mutex_unlock(&inode->i_mutex);
109+
if (delegated_inode) {
110+
error = break_deleg_wait(&delegated_inode);
111+
if (!error)
112+
goto retry_deleg;
113+
}
107114

108115
mnt_drop_write_and_out:
109116
mnt_drop_write(path->mnt);

include/linux/fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2278,7 +2278,7 @@ extern void emergency_remount(void);
22782278
#ifdef CONFIG_BLOCK
22792279
extern sector_t bmap(struct inode *, sector_t);
22802280
#endif
2281-
extern int notify_change(struct dentry *, struct iattr *);
2281+
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
22822282
extern int inode_permission(struct inode *, int);
22832283
extern int generic_permission(struct inode *, int);
22842284

0 commit comments

Comments
 (0)