diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2006-08-22 20:06:22 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-09-22 23:24:52 -0400 |
commit | 4f390c152bc87165da4b1f5b7d870b46fb106d4e (patch) | |
tree | 643b5d12f76bd7d3688380fbaf69f607a34a06bf | |
parent | d3db90e270791b21cd00d3c094884bffa907cc9e (diff) |
NFS: Fix double d_drop in nfs_instantiate() error path
If the LOOKUP or GETATTR in nfs_instantiate fail, nfs_instantiate will do a
d_drop before returning. But some callers already do a d_drop in the case
of an error return. Make certain we do only one d_drop in all error paths.
This issue was introduced because over time, the symlink proc API diverged
slightly from the create/mkdir/mknod proc API. To prevent other coding
mistakes of this type, change the symlink proc API to be more like
create/mkdir/mknod and move the nfs_instantiate call into the symlink proc
routines so it is used in exactly the same way for create, mkdir, mknod,
and symlink.
Test plan:
Connectathon, all versions of NFS.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/dir.c | 16 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 26 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 31 | ||||
-rw-r--r-- | fs/nfs/proc.c | 29 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 5 |
5 files changed, 59 insertions, 48 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 084e8cb41c84..affd3ae52e55 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1147,23 +1147,20 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, | |||
1147 | struct inode *dir = dentry->d_parent->d_inode; | 1147 | struct inode *dir = dentry->d_parent->d_inode; |
1148 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); | 1148 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); |
1149 | if (error) | 1149 | if (error) |
1150 | goto out_err; | 1150 | return error; |
1151 | } | 1151 | } |
1152 | if (!(fattr->valid & NFS_ATTR_FATTR)) { | 1152 | if (!(fattr->valid & NFS_ATTR_FATTR)) { |
1153 | struct nfs_server *server = NFS_SB(dentry->d_sb); | 1153 | struct nfs_server *server = NFS_SB(dentry->d_sb); |
1154 | error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr); | 1154 | error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr); |
1155 | if (error < 0) | 1155 | if (error < 0) |
1156 | goto out_err; | 1156 | return error; |
1157 | } | 1157 | } |
1158 | inode = nfs_fhget(dentry->d_sb, fhandle, fattr); | 1158 | inode = nfs_fhget(dentry->d_sb, fhandle, fattr); |
1159 | error = PTR_ERR(inode); | 1159 | error = PTR_ERR(inode); |
1160 | if (IS_ERR(inode)) | 1160 | if (IS_ERR(inode)) |
1161 | goto out_err; | 1161 | return error; |
1162 | d_instantiate(dentry, inode); | 1162 | d_instantiate(dentry, inode); |
1163 | return 0; | 1163 | return 0; |
1164 | out_err: | ||
1165 | d_drop(dentry); | ||
1166 | return error; | ||
1167 | } | 1164 | } |
1168 | 1165 | ||
1169 | /* | 1166 | /* |
@@ -1448,8 +1445,6 @@ static int | |||
1448 | nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | 1445 | nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) |
1449 | { | 1446 | { |
1450 | struct iattr attr; | 1447 | struct iattr attr; |
1451 | struct nfs_fattr sym_attr; | ||
1452 | struct nfs_fh sym_fh; | ||
1453 | struct qstr qsymname; | 1448 | struct qstr qsymname; |
1454 | int error; | 1449 | int error; |
1455 | 1450 | ||
@@ -1473,12 +1468,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name); | |||
1473 | 1468 | ||
1474 | lock_kernel(); | 1469 | lock_kernel(); |
1475 | nfs_begin_data_update(dir); | 1470 | nfs_begin_data_update(dir); |
1476 | error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, | 1471 | error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr); |
1477 | &attr, &sym_fh, &sym_attr); | ||
1478 | nfs_end_data_update(dir); | 1472 | nfs_end_data_update(dir); |
1479 | if (!error) | 1473 | if (!error) |
1480 | error = nfs_instantiate(dentry, &sym_fh, &sym_attr); | ||
1481 | else | ||
1482 | d_drop(dentry); | 1474 | d_drop(dentry); |
1483 | unlock_kernel(); | 1475 | unlock_kernel(); |
1484 | return error; | 1476 | return error; |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 9e8258ece6fd..d85ac427c326 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -544,23 +544,23 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) | |||
544 | } | 544 | } |
545 | 545 | ||
546 | static int | 546 | static int |
547 | nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, | 547 | nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path, |
548 | struct iattr *sattr, struct nfs_fh *fhandle, | 548 | struct iattr *sattr) |
549 | struct nfs_fattr *fattr) | ||
550 | { | 549 | { |
551 | struct nfs_fattr dir_attr; | 550 | struct nfs_fh fhandle; |
551 | struct nfs_fattr fattr, dir_attr; | ||
552 | struct nfs3_symlinkargs arg = { | 552 | struct nfs3_symlinkargs arg = { |
553 | .fromfh = NFS_FH(dir), | 553 | .fromfh = NFS_FH(dir), |
554 | .fromname = name->name, | 554 | .fromname = dentry->d_name.name, |
555 | .fromlen = name->len, | 555 | .fromlen = dentry->d_name.len, |
556 | .topath = path->name, | 556 | .topath = path->name, |
557 | .tolen = path->len, | 557 | .tolen = path->len, |
558 | .sattr = sattr | 558 | .sattr = sattr |
559 | }; | 559 | }; |
560 | struct nfs3_diropres res = { | 560 | struct nfs3_diropres res = { |
561 | .dir_attr = &dir_attr, | 561 | .dir_attr = &dir_attr, |
562 | .fh = fhandle, | 562 | .fh = &fhandle, |
563 | .fattr = fattr | 563 | .fattr = &fattr |
564 | }; | 564 | }; |
565 | struct rpc_message msg = { | 565 | struct rpc_message msg = { |
566 | .rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK], | 566 | .rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK], |
@@ -571,11 +571,17 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, | |||
571 | 571 | ||
572 | if (path->len > NFS3_MAXPATHLEN) | 572 | if (path->len > NFS3_MAXPATHLEN) |
573 | return -ENAMETOOLONG; | 573 | return -ENAMETOOLONG; |
574 | dprintk("NFS call symlink %s -> %s\n", name->name, path->name); | 574 | |
575 | dprintk("NFS call symlink %s -> %s\n", dentry->d_name.name, | ||
576 | path->name); | ||
575 | nfs_fattr_init(&dir_attr); | 577 | nfs_fattr_init(&dir_attr); |
576 | nfs_fattr_init(fattr); | 578 | nfs_fattr_init(&fattr); |
577 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); | 579 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); |
578 | nfs_post_op_update_inode(dir, &dir_attr); | 580 | nfs_post_op_update_inode(dir, &dir_attr); |
581 | if (status != 0) | ||
582 | goto out; | ||
583 | status = nfs_instantiate(dentry, &fhandle, &fattr); | ||
584 | out: | ||
579 | dprintk("NFS reply symlink: %d\n", status); | 585 | dprintk("NFS reply symlink: %d\n", status); |
580 | return status; | 586 | return status; |
581 | } | 587 | } |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a825547e8214..2d18eac6bee5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -2084,24 +2084,24 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n | |||
2084 | return err; | 2084 | return err; |
2085 | } | 2085 | } |
2086 | 2086 | ||
2087 | static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name, | 2087 | static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, |
2088 | struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle, | 2088 | struct qstr *path, struct iattr *sattr) |
2089 | struct nfs_fattr *fattr) | ||
2090 | { | 2089 | { |
2091 | struct nfs_server *server = NFS_SERVER(dir); | 2090 | struct nfs_server *server = NFS_SERVER(dir); |
2092 | struct nfs_fattr dir_fattr; | 2091 | struct nfs_fh fhandle; |
2092 | struct nfs_fattr fattr, dir_fattr; | ||
2093 | struct nfs4_create_arg arg = { | 2093 | struct nfs4_create_arg arg = { |
2094 | .dir_fh = NFS_FH(dir), | 2094 | .dir_fh = NFS_FH(dir), |
2095 | .server = server, | 2095 | .server = server, |
2096 | .name = name, | 2096 | .name = &dentry->d_name, |
2097 | .attrs = sattr, | 2097 | .attrs = sattr, |
2098 | .ftype = NF4LNK, | 2098 | .ftype = NF4LNK, |
2099 | .bitmask = server->attr_bitmask, | 2099 | .bitmask = server->attr_bitmask, |
2100 | }; | 2100 | }; |
2101 | struct nfs4_create_res res = { | 2101 | struct nfs4_create_res res = { |
2102 | .server = server, | 2102 | .server = server, |
2103 | .fh = fhandle, | 2103 | .fh = &fhandle, |
2104 | .fattr = fattr, | 2104 | .fattr = &fattr, |
2105 | .dir_fattr = &dir_fattr, | 2105 | .dir_fattr = &dir_fattr, |
2106 | }; | 2106 | }; |
2107 | struct rpc_message msg = { | 2107 | struct rpc_message msg = { |
@@ -2113,27 +2113,28 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name, | |||
2113 | 2113 | ||
2114 | if (path->len > NFS4_MAXPATHLEN) | 2114 | if (path->len > NFS4_MAXPATHLEN) |
2115 | return -ENAMETOOLONG; | 2115 | return -ENAMETOOLONG; |
2116 | |||
2116 | arg.u.symlink = path; | 2117 | arg.u.symlink = path; |
2117 | nfs_fattr_init(fattr); | 2118 | nfs_fattr_init(&fattr); |
2118 | nfs_fattr_init(&dir_fattr); | 2119 | nfs_fattr_init(&dir_fattr); |
2119 | 2120 | ||
2120 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); | 2121 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); |
2121 | if (!status) | 2122 | if (!status) { |
2122 | update_changeattr(dir, &res.dir_cinfo); | 2123 | update_changeattr(dir, &res.dir_cinfo); |
2123 | nfs_post_op_update_inode(dir, res.dir_fattr); | 2124 | nfs_post_op_update_inode(dir, res.dir_fattr); |
2125 | status = nfs_instantiate(dentry, &fhandle, &fattr); | ||
2126 | } | ||
2124 | return status; | 2127 | return status; |
2125 | } | 2128 | } |
2126 | 2129 | ||
2127 | static int nfs4_proc_symlink(struct inode *dir, struct qstr *name, | 2130 | static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, |
2128 | struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle, | 2131 | struct qstr *path, struct iattr *sattr) |
2129 | struct nfs_fattr *fattr) | ||
2130 | { | 2132 | { |
2131 | struct nfs4_exception exception = { }; | 2133 | struct nfs4_exception exception = { }; |
2132 | int err; | 2134 | int err; |
2133 | do { | 2135 | do { |
2134 | err = nfs4_handle_exception(NFS_SERVER(dir), | 2136 | err = nfs4_handle_exception(NFS_SERVER(dir), |
2135 | _nfs4_proc_symlink(dir, name, path, sattr, | 2137 | _nfs4_proc_symlink(dir, dentry, path, sattr), |
2136 | fhandle, fattr), | ||
2137 | &exception); | 2138 | &exception); |
2138 | } while (exception.retry); | 2139 | } while (exception.retry); |
2139 | return err; | 2140 | return err; |
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 5a8b9407ee9a..0b507bf0f330 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c | |||
@@ -425,14 +425,15 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) | |||
425 | } | 425 | } |
426 | 426 | ||
427 | static int | 427 | static int |
428 | nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, | 428 | nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path, |
429 | struct iattr *sattr, struct nfs_fh *fhandle, | 429 | struct iattr *sattr) |
430 | struct nfs_fattr *fattr) | ||
431 | { | 430 | { |
431 | struct nfs_fh fhandle; | ||
432 | struct nfs_fattr fattr; | ||
432 | struct nfs_symlinkargs arg = { | 433 | struct nfs_symlinkargs arg = { |
433 | .fromfh = NFS_FH(dir), | 434 | .fromfh = NFS_FH(dir), |
434 | .fromname = name->name, | 435 | .fromname = dentry->d_name.name, |
435 | .fromlen = name->len, | 436 | .fromlen = dentry->d_name.len, |
436 | .topath = path->name, | 437 | .topath = path->name, |
437 | .tolen = path->len, | 438 | .tolen = path->len, |
438 | .sattr = sattr | 439 | .sattr = sattr |
@@ -445,11 +446,23 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, | |||
445 | 446 | ||
446 | if (path->len > NFS2_MAXPATHLEN) | 447 | if (path->len > NFS2_MAXPATHLEN) |
447 | return -ENAMETOOLONG; | 448 | return -ENAMETOOLONG; |
448 | dprintk("NFS call symlink %s -> %s\n", name->name, path->name); | 449 | |
449 | nfs_fattr_init(fattr); | 450 | dprintk("NFS call symlink %s -> %s\n", dentry->d_name.name, |
450 | fhandle->size = 0; | 451 | path->name); |
451 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); | 452 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); |
452 | nfs_mark_for_revalidate(dir); | 453 | nfs_mark_for_revalidate(dir); |
454 | |||
455 | /* | ||
456 | * V2 SYMLINK requests don't return any attributes. Setting the | ||
457 | * filehandle size to zero indicates to nfs_instantiate that it | ||
458 | * should fill in the data with a LOOKUP call on the wire. | ||
459 | */ | ||
460 | if (status == 0) { | ||
461 | nfs_fattr_init(&fattr); | ||
462 | fhandle.size = 0; | ||
463 | status = nfs_instantiate(dentry, &fhandle, &fattr); | ||
464 | } | ||
465 | |||
453 | dprintk("NFS reply symlink: %d\n", status); | 466 | dprintk("NFS reply symlink: %d\n", status); |
454 | return status; | 467 | return status; |
455 | } | 468 | } |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0f33e621892f..ddf5d75e97a2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -793,9 +793,8 @@ struct nfs_rpc_ops { | |||
793 | int (*rename) (struct inode *, struct qstr *, | 793 | int (*rename) (struct inode *, struct qstr *, |
794 | struct inode *, struct qstr *); | 794 | struct inode *, struct qstr *); |
795 | int (*link) (struct inode *, struct inode *, struct qstr *); | 795 | int (*link) (struct inode *, struct inode *, struct qstr *); |
796 | int (*symlink) (struct inode *, struct qstr *, struct qstr *, | 796 | int (*symlink) (struct inode *, struct dentry *, struct qstr *, |
797 | struct iattr *, struct nfs_fh *, | 797 | struct iattr *); |
798 | struct nfs_fattr *); | ||
799 | int (*mkdir) (struct inode *, struct dentry *, struct iattr *); | 798 | int (*mkdir) (struct inode *, struct dentry *, struct iattr *); |
800 | int (*rmdir) (struct inode *, struct qstr *); | 799 | int (*rmdir) (struct inode *, struct qstr *); |
801 | int (*readdir) (struct dentry *, struct rpc_cred *, | 800 | int (*readdir) (struct dentry *, struct rpc_cred *, |