diff options
-rw-r--r-- | fs/nfsd/nfsfh.c | 228 |
1 files changed, 123 insertions, 105 deletions
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 3e6b3f41ee1f..100ae5641162 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c | |||
@@ -113,6 +113,124 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, | |||
113 | } | 113 | } |
114 | 114 | ||
115 | /* | 115 | /* |
116 | * Use the given filehandle to look up the corresponding export and | ||
117 | * dentry. On success, the results are used to set fh_export and | ||
118 | * fh_dentry. | ||
119 | */ | ||
120 | static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) | ||
121 | { | ||
122 | struct knfsd_fh *fh = &fhp->fh_handle; | ||
123 | struct fid *fid = NULL, sfid; | ||
124 | struct svc_export *exp; | ||
125 | struct dentry *dentry; | ||
126 | int fileid_type; | ||
127 | int data_left = fh->fh_size/4; | ||
128 | __be32 error; | ||
129 | |||
130 | error = nfserr_stale; | ||
131 | if (rqstp->rq_vers > 2) | ||
132 | error = nfserr_badhandle; | ||
133 | if (rqstp->rq_vers == 4 && fh->fh_size == 0) | ||
134 | return nfserr_nofilehandle; | ||
135 | |||
136 | if (fh->fh_version == 1) { | ||
137 | int len; | ||
138 | |||
139 | if (--data_left < 0) | ||
140 | return error; | ||
141 | if (fh->fh_auth_type != 0) | ||
142 | return error; | ||
143 | len = key_len(fh->fh_fsid_type) / 4; | ||
144 | if (len == 0) | ||
145 | return error; | ||
146 | if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { | ||
147 | /* deprecated, convert to type 3 */ | ||
148 | len = key_len(FSID_ENCODE_DEV)/4; | ||
149 | fh->fh_fsid_type = FSID_ENCODE_DEV; | ||
150 | fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); | ||
151 | fh->fh_fsid[1] = fh->fh_fsid[2]; | ||
152 | } | ||
153 | data_left -= len; | ||
154 | if (data_left < 0) | ||
155 | return error; | ||
156 | exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth); | ||
157 | fid = (struct fid *)(fh->fh_auth + len); | ||
158 | } else { | ||
159 | __u32 tfh[2]; | ||
160 | dev_t xdev; | ||
161 | ino_t xino; | ||
162 | |||
163 | if (fh->fh_size != NFS_FHSIZE) | ||
164 | return error; | ||
165 | /* assume old filehandle format */ | ||
166 | xdev = old_decode_dev(fh->ofh_xdev); | ||
167 | xino = u32_to_ino_t(fh->ofh_xino); | ||
168 | mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); | ||
169 | exp = rqst_exp_find(rqstp, FSID_DEV, tfh); | ||
170 | } | ||
171 | |||
172 | error = nfserr_stale; | ||
173 | if (PTR_ERR(exp) == -ENOENT) | ||
174 | return error; | ||
175 | |||
176 | if (IS_ERR(exp)) | ||
177 | return nfserrno(PTR_ERR(exp)); | ||
178 | |||
179 | error = nfsd_setuser_and_check_port(rqstp, exp); | ||
180 | if (error) | ||
181 | goto out; | ||
182 | |||
183 | /* | ||
184 | * Look up the dentry using the NFS file handle. | ||
185 | */ | ||
186 | error = nfserr_stale; | ||
187 | if (rqstp->rq_vers > 2) | ||
188 | error = nfserr_badhandle; | ||
189 | |||
190 | if (fh->fh_version != 1) { | ||
191 | sfid.i32.ino = fh->ofh_ino; | ||
192 | sfid.i32.gen = fh->ofh_generation; | ||
193 | sfid.i32.parent_ino = fh->ofh_dirino; | ||
194 | fid = &sfid; | ||
195 | data_left = 3; | ||
196 | if (fh->ofh_dirino == 0) | ||
197 | fileid_type = FILEID_INO32_GEN; | ||
198 | else | ||
199 | fileid_type = FILEID_INO32_GEN_PARENT; | ||
200 | } else | ||
201 | fileid_type = fh->fh_fileid_type; | ||
202 | |||
203 | if (fileid_type == FILEID_ROOT) | ||
204 | dentry = dget(exp->ex_path.dentry); | ||
205 | else { | ||
206 | dentry = exportfs_decode_fh(exp->ex_path.mnt, fid, | ||
207 | data_left, fileid_type, | ||
208 | nfsd_acceptable, exp); | ||
209 | } | ||
210 | if (dentry == NULL) | ||
211 | goto out; | ||
212 | if (IS_ERR(dentry)) { | ||
213 | if (PTR_ERR(dentry) != -EINVAL) | ||
214 | error = nfserrno(PTR_ERR(dentry)); | ||
215 | goto out; | ||
216 | } | ||
217 | |||
218 | if (S_ISDIR(dentry->d_inode->i_mode) && | ||
219 | (dentry->d_flags & DCACHE_DISCONNECTED)) { | ||
220 | printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", | ||
221 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
222 | } | ||
223 | |||
224 | fhp->fh_dentry = dentry; | ||
225 | fhp->fh_export = exp; | ||
226 | nfsd_nr_verified++; | ||
227 | return 0; | ||
228 | out: | ||
229 | exp_put(exp); | ||
230 | return error; | ||
231 | } | ||
232 | |||
233 | /* | ||
116 | * Perform sanity checks on the dentry in a client's file handle. | 234 | * Perform sanity checks on the dentry in a client's file handle. |
117 | * | 235 | * |
118 | * Note that the file handle dentry may need to be freed even after | 236 | * Note that the file handle dentry may need to be freed even after |
@@ -124,115 +242,18 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, | |||
124 | __be32 | 242 | __be32 |
125 | fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) | 243 | fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) |
126 | { | 244 | { |
127 | struct knfsd_fh *fh = &fhp->fh_handle; | 245 | struct svc_export *exp; |
128 | struct svc_export *exp = NULL; | ||
129 | struct dentry *dentry; | 246 | struct dentry *dentry; |
130 | __be32 error = 0; | 247 | __be32 error; |
131 | 248 | ||
132 | dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); | 249 | dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); |
133 | 250 | ||
134 | if (!fhp->fh_dentry) { | 251 | if (!fhp->fh_dentry) { |
135 | struct fid *fid = NULL, sfid; | 252 | error = nfsd_set_fh_dentry(rqstp, fhp); |
136 | int fileid_type; | ||
137 | int data_left = fh->fh_size/4; | ||
138 | |||
139 | error = nfserr_stale; | ||
140 | if (rqstp->rq_vers > 2) | ||
141 | error = nfserr_badhandle; | ||
142 | if (rqstp->rq_vers == 4 && fh->fh_size == 0) | ||
143 | return nfserr_nofilehandle; | ||
144 | |||
145 | if (fh->fh_version == 1) { | ||
146 | int len; | ||
147 | if (--data_left<0) goto out; | ||
148 | switch (fh->fh_auth_type) { | ||
149 | case 0: break; | ||
150 | default: goto out; | ||
151 | } | ||
152 | len = key_len(fh->fh_fsid_type) / 4; | ||
153 | if (len == 0) goto out; | ||
154 | if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { | ||
155 | /* deprecated, convert to type 3 */ | ||
156 | len = key_len(FSID_ENCODE_DEV)/4; | ||
157 | fh->fh_fsid_type = FSID_ENCODE_DEV; | ||
158 | fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); | ||
159 | fh->fh_fsid[1] = fh->fh_fsid[2]; | ||
160 | } | ||
161 | if ((data_left -= len)<0) goto out; | ||
162 | exp = rqst_exp_find(rqstp, fh->fh_fsid_type, | ||
163 | fh->fh_auth); | ||
164 | fid = (struct fid *)(fh->fh_auth + len); | ||
165 | } else { | ||
166 | __u32 tfh[2]; | ||
167 | dev_t xdev; | ||
168 | ino_t xino; | ||
169 | if (fh->fh_size != NFS_FHSIZE) | ||
170 | goto out; | ||
171 | /* assume old filehandle format */ | ||
172 | xdev = old_decode_dev(fh->ofh_xdev); | ||
173 | xino = u32_to_ino_t(fh->ofh_xino); | ||
174 | mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); | ||
175 | exp = rqst_exp_find(rqstp, FSID_DEV, tfh); | ||
176 | } | ||
177 | |||
178 | error = nfserr_stale; | ||
179 | if (PTR_ERR(exp) == -ENOENT) | ||
180 | goto out; | ||
181 | |||
182 | if (IS_ERR(exp)) { | ||
183 | error = nfserrno(PTR_ERR(exp)); | ||
184 | goto out; | ||
185 | } | ||
186 | |||
187 | error = nfsd_setuser_and_check_port(rqstp, exp); | ||
188 | if (error) | 253 | if (error) |
189 | goto out; | 254 | goto out; |
190 | 255 | dentry = fhp->fh_dentry; | |
191 | /* | 256 | exp = fhp->fh_export; |
192 | * Look up the dentry using the NFS file handle. | ||
193 | */ | ||
194 | error = nfserr_stale; | ||
195 | if (rqstp->rq_vers > 2) | ||
196 | error = nfserr_badhandle; | ||
197 | |||
198 | if (fh->fh_version != 1) { | ||
199 | sfid.i32.ino = fh->ofh_ino; | ||
200 | sfid.i32.gen = fh->ofh_generation; | ||
201 | sfid.i32.parent_ino = fh->ofh_dirino; | ||
202 | fid = &sfid; | ||
203 | data_left = 3; | ||
204 | if (fh->ofh_dirino == 0) | ||
205 | fileid_type = FILEID_INO32_GEN; | ||
206 | else | ||
207 | fileid_type = FILEID_INO32_GEN_PARENT; | ||
208 | } else | ||
209 | fileid_type = fh->fh_fileid_type; | ||
210 | |||
211 | if (fileid_type == FILEID_ROOT) | ||
212 | dentry = dget(exp->ex_path.dentry); | ||
213 | else { | ||
214 | dentry = exportfs_decode_fh(exp->ex_path.mnt, fid, | ||
215 | data_left, fileid_type, | ||
216 | nfsd_acceptable, exp); | ||
217 | } | ||
218 | if (dentry == NULL) | ||
219 | goto out; | ||
220 | if (IS_ERR(dentry)) { | ||
221 | if (PTR_ERR(dentry) != -EINVAL) | ||
222 | error = nfserrno(PTR_ERR(dentry)); | ||
223 | goto out; | ||
224 | } | ||
225 | |||
226 | if (S_ISDIR(dentry->d_inode->i_mode) && | ||
227 | (dentry->d_flags & DCACHE_DISCONNECTED)) { | ||
228 | printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", | ||
229 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
230 | } | ||
231 | |||
232 | fhp->fh_dentry = dentry; | ||
233 | fhp->fh_export = exp; | ||
234 | nfsd_nr_verified++; | ||
235 | cache_get(&exp->h); | ||
236 | } else { | 257 | } else { |
237 | /* | 258 | /* |
238 | * just rechecking permissions | 259 | * just rechecking permissions |
@@ -242,7 +263,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) | |||
242 | dprintk("nfsd: fh_verify - just checking\n"); | 263 | dprintk("nfsd: fh_verify - just checking\n"); |
243 | dentry = fhp->fh_dentry; | 264 | dentry = fhp->fh_dentry; |
244 | exp = fhp->fh_export; | 265 | exp = fhp->fh_export; |
245 | cache_get(&exp->h); | ||
246 | /* | 266 | /* |
247 | * Set user creds for this exportpoint; necessary even | 267 | * Set user creds for this exportpoint; necessary even |
248 | * in the "just checking" case because this may be a | 268 | * in the "just checking" case because this may be a |
@@ -281,8 +301,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) | |||
281 | access, ntohl(error)); | 301 | access, ntohl(error)); |
282 | } | 302 | } |
283 | out: | 303 | out: |
284 | if (exp && !IS_ERR(exp)) | ||
285 | exp_put(exp); | ||
286 | if (error == nfserr_stale) | 304 | if (error == nfserr_stale) |
287 | nfsdstats.fh_stale++; | 305 | nfsdstats.fh_stale++; |
288 | return error; | 306 | return error; |