diff options
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 74 |
1 files changed, 44 insertions, 30 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 6c68ffd6b4bb..99f835753596 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -55,6 +55,7 @@ | |||
55 | #include <linux/security.h> | 55 | #include <linux/security.h> |
56 | #endif /* CONFIG_NFSD_V4 */ | 56 | #endif /* CONFIG_NFSD_V4 */ |
57 | #include <linux/jhash.h> | 57 | #include <linux/jhash.h> |
58 | #include <linux/ima.h> | ||
58 | 59 | ||
59 | #include <asm/uaccess.h> | 60 | #include <asm/uaccess.h> |
60 | 61 | ||
@@ -100,36 +101,35 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, | |||
100 | { | 101 | { |
101 | struct svc_export *exp = *expp, *exp2 = NULL; | 102 | struct svc_export *exp = *expp, *exp2 = NULL; |
102 | struct dentry *dentry = *dpp; | 103 | struct dentry *dentry = *dpp; |
103 | struct vfsmount *mnt = mntget(exp->ex_path.mnt); | 104 | struct path path = {.mnt = mntget(exp->ex_path.mnt), |
104 | struct dentry *mounts = dget(dentry); | 105 | .dentry = dget(dentry)}; |
105 | int err = 0; | 106 | int err = 0; |
106 | 107 | ||
107 | while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); | 108 | while (d_mountpoint(path.dentry) && follow_down(&path)) |
109 | ; | ||
108 | 110 | ||
109 | exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); | 111 | exp2 = rqst_exp_get_by_name(rqstp, &path); |
110 | if (IS_ERR(exp2)) { | 112 | if (IS_ERR(exp2)) { |
111 | if (PTR_ERR(exp2) != -ENOENT) | 113 | if (PTR_ERR(exp2) != -ENOENT) |
112 | err = PTR_ERR(exp2); | 114 | err = PTR_ERR(exp2); |
113 | dput(mounts); | 115 | path_put(&path); |
114 | mntput(mnt); | ||
115 | goto out; | 116 | goto out; |
116 | } | 117 | } |
117 | if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { | 118 | if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { |
118 | /* successfully crossed mount point */ | 119 | /* successfully crossed mount point */ |
119 | /* | 120 | /* |
120 | * This is subtle: dentry is *not* under mnt at this point. | 121 | * This is subtle: path.dentry is *not* on path.mnt |
121 | * The only reason we are safe is that original mnt is pinned | 122 | * at this point. The only reason we are safe is that |
122 | * down by exp, so we should dput before putting exp. | 123 | * original mnt is pinned down by exp, so we should |
124 | * put path *before* putting exp | ||
123 | */ | 125 | */ |
124 | dput(dentry); | 126 | *dpp = path.dentry; |
125 | *dpp = mounts; | 127 | path.dentry = dentry; |
126 | exp_put(exp); | ||
127 | *expp = exp2; | 128 | *expp = exp2; |
128 | } else { | 129 | exp2 = exp; |
129 | exp_put(exp2); | ||
130 | dput(mounts); | ||
131 | } | 130 | } |
132 | mntput(mnt); | 131 | path_put(&path); |
132 | exp_put(exp2); | ||
133 | out: | 133 | out: |
134 | return err; | 134 | return err; |
135 | } | 135 | } |
@@ -168,28 +168,29 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
168 | /* checking mountpoint crossing is very different when stepping up */ | 168 | /* checking mountpoint crossing is very different when stepping up */ |
169 | struct svc_export *exp2 = NULL; | 169 | struct svc_export *exp2 = NULL; |
170 | struct dentry *dp; | 170 | struct dentry *dp; |
171 | struct vfsmount *mnt = mntget(exp->ex_path.mnt); | 171 | struct path path = {.mnt = mntget(exp->ex_path.mnt), |
172 | dentry = dget(dparent); | 172 | .dentry = dget(dparent)}; |
173 | while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) | 173 | |
174 | while (path.dentry == path.mnt->mnt_root && | ||
175 | follow_up(&path)) | ||
174 | ; | 176 | ; |
175 | dp = dget_parent(dentry); | 177 | dp = dget_parent(path.dentry); |
176 | dput(dentry); | 178 | dput(path.dentry); |
177 | dentry = dp; | 179 | path.dentry = dp; |
178 | 180 | ||
179 | exp2 = rqst_exp_parent(rqstp, mnt, dentry); | 181 | exp2 = rqst_exp_parent(rqstp, &path); |
180 | if (PTR_ERR(exp2) == -ENOENT) { | 182 | if (PTR_ERR(exp2) == -ENOENT) { |
181 | dput(dentry); | ||
182 | dentry = dget(dparent); | 183 | dentry = dget(dparent); |
183 | } else if (IS_ERR(exp2)) { | 184 | } else if (IS_ERR(exp2)) { |
184 | host_err = PTR_ERR(exp2); | 185 | host_err = PTR_ERR(exp2); |
185 | dput(dentry); | 186 | path_put(&path); |
186 | mntput(mnt); | ||
187 | goto out_nfserr; | 187 | goto out_nfserr; |
188 | } else { | 188 | } else { |
189 | dentry = dget(path.dentry); | ||
189 | exp_put(exp); | 190 | exp_put(exp); |
190 | exp = exp2; | 191 | exp = exp2; |
191 | } | 192 | } |
192 | mntput(mnt); | 193 | path_put(&path); |
193 | } | 194 | } |
194 | } else { | 195 | } else { |
195 | fh_lock(fhp); | 196 | fh_lock(fhp); |
@@ -735,6 +736,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | |||
735 | flags, cred); | 736 | flags, cred); |
736 | if (IS_ERR(*filp)) | 737 | if (IS_ERR(*filp)) |
737 | host_err = PTR_ERR(*filp); | 738 | host_err = PTR_ERR(*filp); |
739 | else | ||
740 | ima_counts_get(*filp); | ||
738 | out_nfserr: | 741 | out_nfserr: |
739 | err = nfserrno(host_err); | 742 | err = nfserrno(host_err); |
740 | out: | 743 | out: |
@@ -1015,6 +1018,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1015 | host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); | 1018 | host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); |
1016 | set_fs(oldfs); | 1019 | set_fs(oldfs); |
1017 | if (host_err >= 0) { | 1020 | if (host_err >= 0) { |
1021 | *cnt = host_err; | ||
1018 | nfsdstats.io_write += host_err; | 1022 | nfsdstats.io_write += host_err; |
1019 | fsnotify_modify(file->f_path.dentry); | 1023 | fsnotify_modify(file->f_path.dentry); |
1020 | } | 1024 | } |
@@ -1060,10 +1064,9 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1060 | } | 1064 | } |
1061 | 1065 | ||
1062 | dprintk("nfsd: write complete host_err=%d\n", host_err); | 1066 | dprintk("nfsd: write complete host_err=%d\n", host_err); |
1063 | if (host_err >= 0) { | 1067 | if (host_err >= 0) |
1064 | err = 0; | 1068 | err = 0; |
1065 | *cnt = host_err; | 1069 | else |
1066 | } else | ||
1067 | err = nfserrno(host_err); | 1070 | err = nfserrno(host_err); |
1068 | out: | 1071 | out: |
1069 | return err; | 1072 | return err; |
@@ -2024,6 +2027,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, | |||
2024 | struct dentry *dentry, int acc) | 2027 | struct dentry *dentry, int acc) |
2025 | { | 2028 | { |
2026 | struct inode *inode = dentry->d_inode; | 2029 | struct inode *inode = dentry->d_inode; |
2030 | struct path path; | ||
2027 | int err; | 2031 | int err; |
2028 | 2032 | ||
2029 | if (acc == NFSD_MAY_NOP) | 2033 | if (acc == NFSD_MAY_NOP) |
@@ -2096,7 +2100,17 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, | |||
2096 | if (err == -EACCES && S_ISREG(inode->i_mode) && | 2100 | if (err == -EACCES && S_ISREG(inode->i_mode) && |
2097 | acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) | 2101 | acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) |
2098 | err = inode_permission(inode, MAY_EXEC); | 2102 | err = inode_permission(inode, MAY_EXEC); |
2103 | if (err) | ||
2104 | goto nfsd_out; | ||
2099 | 2105 | ||
2106 | /* Do integrity (permission) checking now, but defer incrementing | ||
2107 | * IMA counts to the actual file open. | ||
2108 | */ | ||
2109 | path.mnt = exp->ex_path.mnt; | ||
2110 | path.dentry = dentry; | ||
2111 | err = ima_path_check(&path, acc & (MAY_READ | MAY_WRITE | MAY_EXEC), | ||
2112 | IMA_COUNT_LEAVE); | ||
2113 | nfsd_out: | ||
2100 | return err? nfserrno(err) : 0; | 2114 | return err? nfserrno(err) : 0; |
2101 | } | 2115 | } |
2102 | 2116 | ||