|
1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License, Version 1.0 only |
|
6 * (the "License"). You may not use this file except in compliance |
|
7 * with the License. |
|
8 * |
|
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
10 * or http://www.opensolaris.org/os/licensing. |
|
11 * See the License for the specific language governing permissions |
|
12 * and limitations under the License. |
|
13 * |
|
14 * When distributing Covered Code, include this CDDL HEADER in each |
|
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
16 * If applicable, add the following below this CDDL HEADER, with the |
|
17 * fields enclosed by brackets "[]" replaced with your own identifying |
|
18 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
19 * |
|
20 * CDDL HEADER END |
|
21 * |
|
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. |
|
23 * Use is subject to license terms. |
|
24 */ |
|
25 |
|
26 #pragma ident "%Z%%M% %I% %E% SMI" |
|
27 |
|
28 #include <sys/systm.h> |
|
29 |
|
30 #include <nfs/nfs.h> |
|
31 #include <nfs/export.h> |
|
32 #include <sys/cmn_err.h> |
|
33 |
|
34 /* |
|
35 * A version of VOP_FID that deals with a remote VOP_FID for nfs. |
|
36 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid() |
|
37 * returns the filehandle of vp as its fid. When nfs uses fid to set the |
|
38 * exportinfo filehandle template, a remote nfs filehandle would be too big for |
|
39 * the fid of the exported directory. This routine remaps the value of the |
|
40 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit. |
|
41 * |
|
42 * We need this fid mainly for setting up NFSv4 server namespace where an |
|
43 * nfs filesystem is also part of it. Thus, need to be able to setup a pseudo |
|
44 * exportinfo for an nfs node. |
|
45 * |
|
46 * e.g. mount an ufs filesystem on an nfs filesystem, then share the ufs |
|
47 * filesystem. (like exporting a local disk from a "diskless" client) |
|
48 */ |
|
49 int |
|
50 vop_fid_pseudo(vnode_t *vp, fid_t *fidp) |
|
51 { |
|
52 struct vattr va; |
|
53 int error; |
|
54 |
|
55 error = VOP_FID(vp, fidp); |
|
56 |
|
57 /* |
|
58 * XXX nfs4_fid() does nothing and returns EREMOTE. |
|
59 * XXX nfs3_fid()/nfs_fid() returns nfs filehandle as its fid |
|
60 * which has a bigger length than local fid. |
|
61 * NFS_FHMAXDATA is the size of fhandle_t.fh_xdata[NFS_FHMAXDATA]. |
|
62 */ |
|
63 if (error == EREMOTE || (error == 0 && fidp->fid_len > NFS_FHMAXDATA)) { |
|
64 |
|
65 va.va_mask = AT_NODEID; |
|
66 error = VOP_GETATTR(vp, &va, 0, CRED()); |
|
67 if (error) |
|
68 return (error); |
|
69 |
|
70 fidp->fid_len = sizeof (va.va_nodeid); |
|
71 bcopy(&va.va_nodeid, fidp->fid_data, fidp->fid_len); |
|
72 return (0); |
|
73 } |
|
74 |
|
75 return (error); |
|
76 } |
|
77 |
|
78 /* |
|
79 * Get an nfsv4 vnode of the given fid from the visible list of an |
|
80 * nfs filesystem or get the exi_vp if it is the root node. |
|
81 */ |
|
82 int |
|
83 nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp) |
|
84 { |
|
85 fid_t exp_fid; |
|
86 struct exp_visible *visp; |
|
87 int error; |
|
88 |
|
89 /* check if the given fid is in the visible list */ |
|
90 |
|
91 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { |
|
92 if (EQFID(fidp, &visp->vis_fid)) { |
|
93 VN_HOLD(visp->vis_vp); |
|
94 *vpp = visp->vis_vp; |
|
95 return (0); |
|
96 } |
|
97 } |
|
98 |
|
99 /* check if the given fid is the same as the exported node */ |
|
100 |
|
101 bzero(&exp_fid, sizeof (exp_fid)); |
|
102 exp_fid.fid_len = MAXFIDSZ; |
|
103 error = vop_fid_pseudo(exi->exi_vp, &exp_fid); |
|
104 if (error) |
|
105 return (error); |
|
106 |
|
107 if (EQFID(fidp, &exp_fid)) { |
|
108 VN_HOLD(exi->exi_vp); |
|
109 *vpp = exi->exi_vp; |
|
110 return (0); |
|
111 } |
|
112 |
|
113 return (ENOENT); |
|
114 } |
|
115 |
|
116 /* |
|
117 * Create a pseudo export entry |
|
118 * |
|
119 * This is an export entry that's created as the |
|
120 * side-effect of a "real" export. As a part of |
|
121 * a real export, the pathname to the export is |
|
122 * checked to see if all the directory components |
|
123 * are accessible via an NFSv4 client, i.e. are |
|
124 * exported. If tree_climb() finds an unexported |
|
125 * mountpoint along the path, then it calls this |
|
126 * function to export it. |
|
127 * |
|
128 * This pseudo export differs from a real export |
|
129 * in restriction on simple. read-only access, |
|
130 * and the addition of a "visible" list of directories. |
|
131 * A real export may have a visible list if it is a root of |
|
132 * a file system and at least one of its subtree resides in |
|
133 * a different file system is shared. |
|
134 * |
|
135 * A visible list is per file system. It resides in the exportinfo |
|
136 * for the pseudo node (VROOT) and it could reside in a real export |
|
137 * of a VROOT node. |
|
138 */ |
|
139 int |
|
140 pseudo_exportfs(vnode_t *vp, struct exp_visible *vis_head, |
|
141 struct exportdata *exdata) |
|
142 { |
|
143 struct exportinfo *exi; |
|
144 struct exportdata *kex; |
|
145 fid_t fid; |
|
146 fsid_t fsid; |
|
147 int error; |
|
148 char *pseudo; |
|
149 |
|
150 ASSERT(RW_WRITE_HELD(&exported_lock)); |
|
151 |
|
152 /* |
|
153 * Get the vfs id |
|
154 */ |
|
155 bzero(&fid, sizeof (fid)); |
|
156 fid.fid_len = MAXFIDSZ; |
|
157 error = vop_fid_pseudo(vp, &fid); |
|
158 if (error) { |
|
159 /* |
|
160 * If VOP_FID returns ENOSPC then the fid supplied |
|
161 * is too small. For now we simply return EREMOTE. |
|
162 */ |
|
163 if (error == ENOSPC) |
|
164 error = EREMOTE; |
|
165 return (error); |
|
166 } |
|
167 |
|
168 fsid = vp->v_vfsp->vfs_fsid; |
|
169 exi = kmem_zalloc(sizeof (*exi), KM_SLEEP); |
|
170 exi->exi_fsid = fsid; |
|
171 exi->exi_fid = fid; |
|
172 exi->exi_vp = vp; |
|
173 VN_HOLD(exi->exi_vp); |
|
174 exi->exi_visible = vis_head; |
|
175 exi->exi_count = 1; |
|
176 exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag & |
|
177 VSW_VOLATILEDEV) ? 1 : 0; |
|
178 mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL); |
|
179 |
|
180 /* |
|
181 * Build up the template fhandle |
|
182 */ |
|
183 exi->exi_fh.fh_fsid = fsid; |
|
184 ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata)); |
|
185 exi->exi_fh.fh_xlen = exi->exi_fid.fid_len; |
|
186 bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata, |
|
187 exi->exi_fid.fid_len); |
|
188 exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data); |
|
189 |
|
190 kex = &exi->exi_export; |
|
191 kex->ex_flags = EX_PSEUDO; |
|
192 |
|
193 /* Set up a generic pathname */ |
|
194 |
|
195 pseudo = "(pseudo)"; |
|
196 kex->ex_pathlen = strlen(pseudo); |
|
197 kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP); |
|
198 (void) strcpy(kex->ex_path, pseudo); |
|
199 |
|
200 /* Transfer the secinfo data from exdata to this new pseudo node */ |
|
201 if (exdata) |
|
202 srv_secinfo_exp2pseu(&exi->exi_export, exdata); |
|
203 |
|
204 /* |
|
205 * Initialize auth cache lock |
|
206 */ |
|
207 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL); |
|
208 |
|
209 /* |
|
210 * Insert the new entry at the front of the export list |
|
211 */ |
|
212 export_link(exi); |
|
213 |
|
214 return (0); |
|
215 } |
|
216 |
|
217 /* |
|
218 * Free a list of visible directories |
|
219 */ |
|
220 void |
|
221 free_visible(struct exp_visible *head) |
|
222 { |
|
223 struct exp_visible *visp, *next; |
|
224 |
|
225 for (visp = head; visp; visp = next) { |
|
226 if (visp->vis_vp != NULL) |
|
227 VN_RELE(visp->vis_vp); |
|
228 next = visp->vis_next; |
|
229 kmem_free(visp, sizeof (*visp)); |
|
230 } |
|
231 } |
|
232 |
|
233 /* |
|
234 * Add a list of visible directories to a pseudo exportfs. |
|
235 * |
|
236 * When we export a new directory we need to add a new |
|
237 * path segment through the pseudofs to reach the new |
|
238 * directory. This new path is reflected in a list of |
|
239 * directories added to the "visible" list. |
|
240 * |
|
241 * Here there are two lists of visible fids: one hanging off the |
|
242 * pseudo exportinfo, and the one we want to add. It's possible |
|
243 * that the two lists share a common path segment |
|
244 * and have some common directories. We need to combine |
|
245 * the lists so there's no duplicate entries. Where a common |
|
246 * path component is found, the vis_count field is bumped. |
|
247 * |
|
248 * When the addition is complete, the supplied list is freed. |
|
249 */ |
|
250 |
|
251 static void |
|
252 more_visible(struct exportinfo *exi, struct exp_visible *vis_head) |
|
253 { |
|
254 struct exp_visible *vp1, *vp2; |
|
255 struct exp_visible *tail, *new; |
|
256 int found; |
|
257 |
|
258 /* |
|
259 * If exportinfo doesn't already have a visible |
|
260 * list just assign the entire supplied list. |
|
261 */ |
|
262 if (exi->exi_visible == NULL) { |
|
263 exi->exi_visible = vis_head; |
|
264 return; |
|
265 } |
|
266 |
|
267 /* |
|
268 * The outer loop traverses the supplied list. |
|
269 */ |
|
270 for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) { |
|
271 |
|
272 /* |
|
273 * Given an element from the list to be added, |
|
274 * search the exportinfo visible list looking for a match. |
|
275 * If a match is found, increment the reference count. |
|
276 */ |
|
277 found = 0; |
|
278 |
|
279 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) { |
|
280 |
|
281 tail = vp2; |
|
282 |
|
283 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { |
|
284 found = 1; |
|
285 vp2->vis_count++; |
|
286 VN_RELE(vp1->vis_vp); |
|
287 vp1->vis_vp = NULL; |
|
288 |
|
289 /* |
|
290 * If the visible struct we want to add |
|
291 * (vp1) has vis_exported set to 1, then |
|
292 * the matching visible struct we just found |
|
293 * must also have it's vis_exported field |
|
294 * set to 1. |
|
295 * |
|
296 * For example, if /export/home was shared |
|
297 * (and a UFS mountpoint), then "export" and |
|
298 * "home" would each have visible structs in |
|
299 * the root pseudo exportinfo. The vis_exported |
|
300 * for home would be 1, and vis_exported for |
|
301 * export would be 0. Now, if /export was |
|
302 * also shared, more_visible would find the |
|
303 * existing visible struct for export, and |
|
304 * see that vis_exported was 0. The code |
|
305 * below will set it to 1. |
|
306 * |
|
307 * vp1 is from vis list passed in (vis_head) |
|
308 * vp2 is from vis list on pseudo exportinfo |
|
309 */ |
|
310 if (vp1->vis_exported && !vp2->vis_exported) |
|
311 vp2->vis_exported = 1; |
|
312 break; |
|
313 } |
|
314 } |
|
315 |
|
316 /* If not found - add to the end of the list */ |
|
317 if (! found) { |
|
318 new = kmem_zalloc(sizeof (*new), KM_SLEEP); |
|
319 *new = *vp1; |
|
320 tail->vis_next = new; |
|
321 new->vis_next = NULL; |
|
322 vp1->vis_vp = NULL; |
|
323 } |
|
324 } |
|
325 |
|
326 /* |
|
327 * Throw away the path list. vis_vp pointers in vis_head list |
|
328 * are either VN_RELEed or reassigned, and are set to NULL. |
|
329 * There is no need to VN_RELE in free_visible for this vis_head. |
|
330 */ |
|
331 free_visible(vis_head); |
|
332 } |
|
333 |
|
334 /* |
|
335 * Remove a list of visible directories from the pseudo exportfs. |
|
336 * |
|
337 * When we unexport a directory, we have to remove path |
|
338 * components from the visible list in the pseudo exportfs |
|
339 * entry. The supplied visible list contains the fids of the path |
|
340 * to the unexported directory. The visible list of the export |
|
341 * is checked against this list any matching fids have their |
|
342 * reference count decremented. If a reference count drops to |
|
343 * zero, then it means no paths now use this directory, so its |
|
344 * fid can be removed from the visible list. |
|
345 * |
|
346 * When the last path is removed, the visible list will be null. |
|
347 */ |
|
348 static void |
|
349 less_visible(struct exportinfo *exi, struct exp_visible *vis_head) |
|
350 { |
|
351 struct exp_visible *vp1, *vp2; |
|
352 struct exp_visible *prev, *next; |
|
353 |
|
354 /* |
|
355 * The outer loop traverses the supplied list. |
|
356 */ |
|
357 for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) { |
|
358 |
|
359 /* |
|
360 * Given an element from the list to be removed, |
|
361 * search the exportinfo list looking for a match. |
|
362 * If a match is found, decrement the reference |
|
363 * count and drop the element if the count drops |
|
364 * to zero. |
|
365 */ |
|
366 for (vp2 = exi->exi_visible, prev = NULL; vp2; vp2 = next) { |
|
367 |
|
368 next = vp2->vis_next; |
|
369 |
|
370 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { |
|
371 |
|
372 /* |
|
373 * Decrement the ref count. |
|
374 * Remove the entry if it's zero. |
|
375 */ |
|
376 if (--vp2->vis_count <= 0) { |
|
377 if (prev == NULL) |
|
378 exi->exi_visible = next; |
|
379 else |
|
380 prev->vis_next = next; |
|
381 |
|
382 VN_RELE(vp2->vis_vp); |
|
383 kmem_free(vp2, sizeof (*vp1)); |
|
384 } else { |
|
385 /* |
|
386 * If we're here, then the vp2 will |
|
387 * remain in the vis list. If the |
|
388 * vis entry corresponds to the object |
|
389 * being unshared, then vis_exported |
|
390 * needs to be set to 0. |
|
391 * |
|
392 * vp1 is a node from caller's list |
|
393 * vp2 is node from exportinfo's list |
|
394 * |
|
395 * Only 1 node in the caller's list |
|
396 * will have vis_exported set to 1, |
|
397 * and it corresponds to the obj being |
|
398 * unshared. It should always be the |
|
399 * last element of the caller's list. |
|
400 */ |
|
401 if (vp1->vis_exported && |
|
402 vp2->vis_exported) { |
|
403 vp2->vis_exported = 0; |
|
404 } |
|
405 } |
|
406 |
|
407 break; |
|
408 } |
|
409 |
|
410 prev = vp2; |
|
411 } |
|
412 } |
|
413 |
|
414 free_visible(vis_head); |
|
415 } |
|
416 |
|
417 /* |
|
418 * This function checks the path to a new export to |
|
419 * check whether all the pathname components are |
|
420 * exported. It works by climbing the file tree one |
|
421 * component at a time via "..", crossing mountpoints |
|
422 * if necessary until an export entry is found, or the |
|
423 * system root is reached. |
|
424 * |
|
425 * If an unexported mountpoint is found, then |
|
426 * a new pseudo export is added and the pathname from |
|
427 * the mountpoint down to the export is added to the |
|
428 * visible list for the new pseudo export. If an existing |
|
429 * pseudo export is found, then the pathname is added |
|
430 * to its visible list. |
|
431 * |
|
432 * Note that there's some tests for exportdir. |
|
433 * The exportinfo entry that's passed as a parameter |
|
434 * is that of the real export and exportdir is set |
|
435 * for this case. |
|
436 * |
|
437 * Here is an example of a possible setup: |
|
438 * |
|
439 * () - a new fs; fs mount point |
|
440 * EXPORT - a real exported node |
|
441 * PSEUDO - a pseudo node |
|
442 * vis - visible list |
|
443 * f# - security flavor# |
|
444 * (f#) - security flavor# propagated from its decendents |
|
445 * "" - covered vnode |
|
446 * |
|
447 * |
|
448 * / |
|
449 * | |
|
450 * (a) PSEUDO (f1,f2) |
|
451 * | vis: b,b,"c","n" |
|
452 * | |
|
453 * b |
|
454 * ---------|------------------ |
|
455 * | | |
|
456 * (c) EXPORT,f1(f2) (n) PSEUDO (f1,f2) |
|
457 * | vis: "e","d" | vis: m,m,,p,q,"o" |
|
458 * | | |
|
459 * ------------------ ------------------- |
|
460 * | | | | | |
|
461 * (d) (e) f m EXPORT,f1(f2) p |
|
462 * EXPORT EXPORT | | |
|
463 * f1 f2 | | |
|
464 * | | | |
|
465 * j (o) EXPORT,f2 q EXPORT f2 |
|
466 * |
|
467 */ |
|
468 int |
|
469 treeclimb_export(struct exportinfo *exip) |
|
470 { |
|
471 vnode_t *dvp, *vp; |
|
472 fid_t fid; |
|
473 int error; |
|
474 int exportdir; |
|
475 struct exportinfo *exi = NULL; |
|
476 struct exp_visible *visp; |
|
477 struct exp_visible *vis_head = NULL; |
|
478 struct vattr va; |
|
479 |
|
480 ASSERT(RW_WRITE_HELD(&exported_lock)); |
|
481 |
|
482 vp = exip->exi_vp; |
|
483 VN_HOLD(vp); |
|
484 exportdir = 1; |
|
485 |
|
486 for (;;) { |
|
487 |
|
488 bzero(&fid, sizeof (fid)); |
|
489 fid.fid_len = MAXFIDSZ; |
|
490 error = vop_fid_pseudo(vp, &fid); |
|
491 if (error) |
|
492 break; |
|
493 |
|
494 if (! exportdir) { |
|
495 /* |
|
496 * Check if this exportroot is a VROOT dir. If so, |
|
497 * then attach the pseudonodes. If not, then |
|
498 * continue .. traversal until we hit a VROOT |
|
499 * export (pseudo or real). |
|
500 */ |
|
501 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); |
|
502 if (exi != NULL && vp->v_flag & VROOT) { |
|
503 /* |
|
504 * Found an export info |
|
505 * |
|
506 * Extend the list of visible |
|
507 * directories whether it's a pseudo |
|
508 * or a real export. |
|
509 */ |
|
510 more_visible(exi, vis_head); |
|
511 vis_head = NULL; |
|
512 break; /* and climb no further */ |
|
513 } |
|
514 } |
|
515 |
|
516 /* |
|
517 * If at the root of the filesystem, need |
|
518 * to traverse across the mountpoint |
|
519 * and continue the climb on the mounted-on |
|
520 * filesystem. |
|
521 */ |
|
522 if (vp->v_flag & VROOT) { |
|
523 |
|
524 if (! exportdir) { |
|
525 /* |
|
526 * Found the root directory of a filesystem |
|
527 * that isn't exported. Need to export |
|
528 * this as a pseudo export so that an NFS v4 |
|
529 * client can do lookups in it. |
|
530 */ |
|
531 error = pseudo_exportfs(vp, vis_head, NULL); |
|
532 if (error) |
|
533 break; |
|
534 vis_head = NULL; |
|
535 } |
|
536 |
|
537 if (VN_CMP(vp, rootdir)) { |
|
538 /* at system root */ |
|
539 break; |
|
540 } |
|
541 |
|
542 vp = untraverse(vp); |
|
543 exportdir = 0; |
|
544 continue; |
|
545 } |
|
546 |
|
547 /* |
|
548 * Do a getattr to obtain the nodeid (inode num) |
|
549 * for this vnode. |
|
550 */ |
|
551 va.va_mask = AT_NODEID; |
|
552 error = VOP_GETATTR(vp, &va, 0, CRED()); |
|
553 if (error) |
|
554 break; |
|
555 |
|
556 /* |
|
557 * Add this directory fid to visible list |
|
558 */ |
|
559 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); |
|
560 VN_HOLD(vp); |
|
561 visp->vis_vp = vp; |
|
562 visp->vis_fid = fid; /* structure copy */ |
|
563 visp->vis_ino = va.va_nodeid; |
|
564 visp->vis_count = 1; |
|
565 visp->vis_exported = exportdir; |
|
566 visp->vis_next = vis_head; |
|
567 vis_head = visp; |
|
568 |
|
569 /* |
|
570 * Now, do a ".." to find parent dir of vp. |
|
571 */ |
|
572 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); |
|
573 |
|
574 if (error == ENOTDIR && exportdir) { |
|
575 dvp = exip->exi_dvp; |
|
576 ASSERT(dvp != NULL); |
|
577 VN_HOLD(dvp); |
|
578 error = 0; |
|
579 } |
|
580 |
|
581 if (error) |
|
582 break; |
|
583 |
|
584 exportdir = 0; |
|
585 VN_RELE(vp); |
|
586 vp = dvp; |
|
587 } |
|
588 |
|
589 VN_RELE(vp); |
|
590 return (error); |
|
591 } |
|
592 |
|
593 /* |
|
594 * Walk up the tree looking for pseudo export entries. |
|
595 * |
|
596 * If a pseudo export is found, remove the path we've |
|
597 * climbed from its visible list. If the visible list |
|
598 * still has entries after the removal, then we can stop. |
|
599 * If it becomes null, then remove the pseudo export entry |
|
600 * and carry on up the tree to see if there's any more. |
|
601 */ |
|
602 int |
|
603 treeclimb_unexport(struct exportinfo *exip) |
|
604 { |
|
605 vnode_t *dvp, *vp; |
|
606 fid_t fid; |
|
607 int error = 0; |
|
608 int exportdir; |
|
609 struct exportinfo *exi = NULL; |
|
610 struct exp_visible *vis_head = NULL, *visp; |
|
611 |
|
612 ASSERT(RW_WRITE_HELD(&exported_lock)); |
|
613 |
|
614 exportdir = 1; |
|
615 vp = exip->exi_vp; |
|
616 VN_HOLD(vp); |
|
617 |
|
618 for (;;) { |
|
619 |
|
620 bzero(&fid, sizeof (fid)); |
|
621 fid.fid_len = MAXFIDSZ; |
|
622 error = vop_fid_pseudo(vp, &fid); |
|
623 if (error) |
|
624 break; |
|
625 |
|
626 if (! exportdir) { |
|
627 |
|
628 /* |
|
629 * We need to use checkexport4() here because it |
|
630 * doesn't acquire exported_lock and it doesn't |
|
631 * manipulate exi_count. |
|
632 * |
|
633 * Remove directories from the visible |
|
634 * list that are unique to the path |
|
635 * for this export. (Only VROOT exportinfos |
|
636 * have can have visible entries). |
|
637 */ |
|
638 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); |
|
639 if (exi != NULL && (vp->v_flag & VROOT)) { |
|
640 |
|
641 less_visible(exi, vis_head); |
|
642 vis_head = NULL; |
|
643 |
|
644 /* |
|
645 * If the visible list has entries |
|
646 * or if it's a real export, then |
|
647 * there's no need to keep climbing. |
|
648 */ |
|
649 if (exi->exi_visible || ! PSEUDO(exi)) |
|
650 break; |
|
651 |
|
652 /* |
|
653 * Otherwise, we have a pseudo export |
|
654 * with an empty list (no exports below |
|
655 * it) so we must remove and continue |
|
656 * the climb to remove its name from |
|
657 * the parent export. |
|
658 */ |
|
659 error = export_unlink(&vp->v_vfsp->vfs_fsid, |
|
660 &fid, vp, NULL); |
|
661 if (error) |
|
662 break; |
|
663 |
|
664 exi_rele(exi); |
|
665 } |
|
666 } |
|
667 |
|
668 /* |
|
669 * If at the root of the filesystem, need |
|
670 * to traverse across the mountpoint |
|
671 * and continue the climb on the mounted-on |
|
672 * filesystem. |
|
673 */ |
|
674 if (vp->v_flag & VROOT) { |
|
675 if (VN_CMP(vp, rootdir)) { |
|
676 /* at system root */ |
|
677 break; |
|
678 } |
|
679 vp = untraverse(vp); |
|
680 exportdir = 0; |
|
681 continue; |
|
682 } |
|
683 |
|
684 /* |
|
685 * Add this directory fid to path list |
|
686 */ |
|
687 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); |
|
688 VN_HOLD(vp); |
|
689 visp->vis_vp = vp; |
|
690 visp->vis_fid = fid; /* structure copy */ |
|
691 visp->vis_ino = 0; |
|
692 visp->vis_count = 1; |
|
693 visp->vis_exported = exportdir; |
|
694 visp->vis_next = vis_head; |
|
695 vis_head = visp; |
|
696 |
|
697 /* |
|
698 * Do a ".." to find parent dir of vp. |
|
699 */ |
|
700 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); |
|
701 |
|
702 if (error == ENOTDIR && exportdir) { |
|
703 dvp = exip->exi_dvp; |
|
704 ASSERT(dvp != NULL); |
|
705 VN_HOLD(dvp); |
|
706 error = 0; |
|
707 } |
|
708 if (error) |
|
709 break; |
|
710 |
|
711 exportdir = 0; |
|
712 VN_RELE(vp); |
|
713 vp = dvp; |
|
714 } |
|
715 |
|
716 VN_RELE(vp); |
|
717 return (error); |
|
718 } |
|
719 |
|
720 |
|
721 /* |
|
722 * Traverse backward across mountpoint from the |
|
723 * root vnode of a filesystem to its mounted-on |
|
724 * vnode. |
|
725 */ |
|
726 vnode_t * |
|
727 untraverse(vnode_t *vp) |
|
728 { |
|
729 vnode_t *tvp, *nextvp; |
|
730 |
|
731 tvp = vp; |
|
732 for (;;) { |
|
733 if (! (tvp->v_flag & VROOT)) |
|
734 break; |
|
735 |
|
736 /* lock vfs to prevent unmount of this vfs */ |
|
737 vfs_lock_wait(tvp->v_vfsp); |
|
738 |
|
739 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) { |
|
740 vfs_unlock(tvp->v_vfsp); |
|
741 break; |
|
742 } |
|
743 |
|
744 /* |
|
745 * Hold nextvp to prevent unmount. After unlock vfs and |
|
746 * rele tvp, any number of overlays could be unmounted. |
|
747 * Putting a hold on vfs_vnodecovered will only allow |
|
748 * tvp's vfs to be unmounted. Of course if caller placed |
|
749 * extra hold on vp before calling untraverse, the following |
|
750 * hold would not be needed. Since prev actions of caller |
|
751 * are unknown, we need to hold here just to be safe. |
|
752 */ |
|
753 VN_HOLD(nextvp); |
|
754 vfs_unlock(tvp->v_vfsp); |
|
755 VN_RELE(tvp); |
|
756 tvp = nextvp; |
|
757 } |
|
758 |
|
759 return (tvp); |
|
760 } |
|
761 |
|
762 /* |
|
763 * Given an exportinfo, climb up to find the exportinfo for the VROOT |
|
764 * of the filesystem. |
|
765 * |
|
766 * e.g. / |
|
767 * | |
|
768 * a (VROOT) pseudo-exportinfo |
|
769 * | |
|
770 * b |
|
771 * | |
|
772 * c #share /a/b/c |
|
773 * | |
|
774 * d |
|
775 * |
|
776 * where c is in the same filesystem as a. |
|
777 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a |
|
778 * |
|
779 * If d is shared, then c will be put into a's visible list. |
|
780 * Note: visible list is per filesystem and is attached to the |
|
781 * VROOT exportinfo. |
|
782 */ |
|
783 struct exportinfo * |
|
784 get_root_export(struct exportinfo *exip) |
|
785 { |
|
786 vnode_t *dvp, *vp; |
|
787 fid_t fid; |
|
788 struct exportinfo *exi = exip; |
|
789 int error; |
|
790 |
|
791 vp = exi->exi_vp; |
|
792 VN_HOLD(vp); |
|
793 |
|
794 for (;;) { |
|
795 |
|
796 if (vp->v_flag & VROOT) { |
|
797 ASSERT(exi != NULL); |
|
798 break; |
|
799 } |
|
800 |
|
801 /* |
|
802 * Now, do a ".." to find parent dir of vp. |
|
803 */ |
|
804 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); |
|
805 |
|
806 if (error) { |
|
807 exi = NULL; |
|
808 break; |
|
809 } |
|
810 |
|
811 VN_RELE(vp); |
|
812 vp = dvp; |
|
813 |
|
814 bzero(&fid, sizeof (fid)); |
|
815 fid.fid_len = MAXFIDSZ; |
|
816 error = vop_fid_pseudo(vp, &fid); |
|
817 if (error) { |
|
818 exi = NULL; |
|
819 break; |
|
820 } |
|
821 |
|
822 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); |
|
823 } |
|
824 |
|
825 VN_RELE(vp); |
|
826 return (exi); |
|
827 } |
|
828 |
|
829 /* |
|
830 * Return true if the supplied vnode has a sub-directory exported. |
|
831 */ |
|
832 int |
|
833 has_visible(struct exportinfo *exi, vnode_t *vp) |
|
834 { |
|
835 struct exp_visible *visp; |
|
836 fid_t fid; |
|
837 bool_t vp_is_exported; |
|
838 |
|
839 vp_is_exported = VN_CMP(vp, exi->exi_vp); |
|
840 |
|
841 /* |
|
842 * An exported root vnode has a sub-dir shared if it has a visible list. |
|
843 * i.e. if it does not have a visible list, then there is no node in |
|
844 * this filesystem leads to any other shared node. |
|
845 */ |
|
846 if (vp_is_exported && (vp->v_flag & VROOT)) |
|
847 return (exi->exi_visible ? 1 : 0); |
|
848 |
|
849 /* |
|
850 * Only the exportinfo of a fs root node may have a visible list. |
|
851 * Either it is a pseudo root node, or a real exported root node. |
|
852 */ |
|
853 if ((exi = get_root_export(exi)) == NULL) { |
|
854 return (0); |
|
855 } |
|
856 |
|
857 if (!exi->exi_visible) |
|
858 return (0); |
|
859 |
|
860 /* Get the fid of the vnode */ |
|
861 bzero(&fid, sizeof (fid)); |
|
862 fid.fid_len = MAXFIDSZ; |
|
863 if (vop_fid_pseudo(vp, &fid) != 0) { |
|
864 return (0); |
|
865 } |
|
866 |
|
867 /* |
|
868 * See if vp is in the visible list of the root node exportinfo. |
|
869 */ |
|
870 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { |
|
871 if (EQFID(&fid, &visp->vis_fid)) { |
|
872 /* |
|
873 * If vp is an exported non-root node with only 1 path |
|
874 * count (for itself), it indicates no sub-dir shared |
|
875 * using this vp as a path. |
|
876 */ |
|
877 if (vp_is_exported && visp->vis_count < 2) |
|
878 break; |
|
879 |
|
880 return (1); |
|
881 } |
|
882 } |
|
883 |
|
884 return (0); |
|
885 } |
|
886 |
|
887 /* |
|
888 * Returns true if the supplied vnode is visible |
|
889 * in this export. If vnode is visible, return |
|
890 * vis_exported in expseudo. |
|
891 */ |
|
892 int |
|
893 nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo) |
|
894 { |
|
895 struct exp_visible *visp; |
|
896 fid_t fid; |
|
897 |
|
898 /* |
|
899 * First check to see if vp is export root. |
|
900 * |
|
901 * A pseudo export root can never be exported |
|
902 * (it would be a real export then); however, |
|
903 * it is always visible. If a pseudo root object |
|
904 * was exported by server admin, then the entire |
|
905 * pseudo exportinfo (and all visible entries) would |
|
906 * be destroyed. A pseudo exportinfo only exists |
|
907 * to provide access to real (descendant) export(s). |
|
908 * |
|
909 * Previously, rootdir was special cased here; however, |
|
910 * the export root special case handles the rootdir |
|
911 * case also. |
|
912 */ |
|
913 if (VN_CMP(vp, exi->exi_vp)) { |
|
914 *expseudo = 0; |
|
915 return (1); |
|
916 } |
|
917 |
|
918 /* |
|
919 * Only a PSEUDO node has a visible list or an exported VROOT |
|
920 * node may have a visible list. |
|
921 */ |
|
922 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { |
|
923 *expseudo = 0; |
|
924 return (0); |
|
925 } |
|
926 |
|
927 /* Get the fid of the vnode */ |
|
928 |
|
929 bzero(&fid, sizeof (fid)); |
|
930 fid.fid_len = MAXFIDSZ; |
|
931 if (vop_fid_pseudo(vp, &fid) != 0) { |
|
932 *expseudo = 0; |
|
933 return (0); |
|
934 } |
|
935 |
|
936 /* |
|
937 * We can't trust VN_CMP() above because of LOFS. |
|
938 * Even though VOP_CMP will do the right thing for LOFS |
|
939 * objects, VN_CMP will short circuit out early when the |
|
940 * vnode ops ptrs are different. Just in case we're dealing |
|
941 * with LOFS, compare exi_fid/fsid here. |
|
942 * |
|
943 * expseudo is not set because this is not an export |
|
944 */ |
|
945 if (EQFID(&exi->exi_fid, &fid) && |
|
946 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) { |
|
947 *expseudo = 0; |
|
948 return (1); |
|
949 } |
|
950 |
|
951 |
|
952 /* See if it matches any fid in the visible list */ |
|
953 |
|
954 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { |
|
955 if (EQFID(&fid, &visp->vis_fid)) { |
|
956 *expseudo = visp->vis_exported; |
|
957 return (1); |
|
958 } |
|
959 } |
|
960 |
|
961 *expseudo = 0; |
|
962 |
|
963 return (0); |
|
964 } |
|
965 |
|
966 /* |
|
967 * Returns true if the supplied vnode is the |
|
968 * directory of an export point. |
|
969 */ |
|
970 int |
|
971 nfs_exported(struct exportinfo *exi, vnode_t *vp) |
|
972 { |
|
973 struct exp_visible *visp; |
|
974 fid_t fid; |
|
975 |
|
976 /* |
|
977 * First check to see if vp is the export root |
|
978 * This check required for the case of lookup .. |
|
979 * where .. is a V_ROOT vnode and a pseudo exportroot. |
|
980 * Pseudo export root objects do not have an entry |
|
981 * in the visible list even though every V_ROOT |
|
982 * pseudonode is visible. It is safe to compare |
|
983 * vp here because pseudo_exportfs put a hold on |
|
984 * it when exi_vp was initialized. |
|
985 * |
|
986 * Note: VN_CMP() won't match for LOFS shares, but they're |
|
987 * handled below w/EQFID/EQFSID. |
|
988 */ |
|
989 if (VN_CMP(vp, exi->exi_vp)) |
|
990 return (1); |
|
991 |
|
992 /* Get the fid of the vnode */ |
|
993 |
|
994 bzero(&fid, sizeof (fid)); |
|
995 fid.fid_len = MAXFIDSZ; |
|
996 if (vop_fid_pseudo(vp, &fid) != 0) |
|
997 return (0); |
|
998 |
|
999 if (EQFID(&fid, &exi->exi_fid) && |
|
1000 EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) { |
|
1001 return (1); |
|
1002 } |
|
1003 |
|
1004 /* See if it matches any fid in the visible list */ |
|
1005 |
|
1006 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { |
|
1007 if (EQFID(&fid, &visp->vis_fid)) |
|
1008 return (visp->vis_exported); |
|
1009 } |
|
1010 |
|
1011 return (0); |
|
1012 } |
|
1013 |
|
1014 /* |
|
1015 * Returns true if the supplied inode is visible |
|
1016 * in this export. This function is used by |
|
1017 * readdir which uses inode numbers from the |
|
1018 * directory. |
|
1019 * |
|
1020 * NOTE: this code does not match inode number for ".", |
|
1021 * but it isn't required because NFS4 server rddir |
|
1022 * skips . and .. entries. |
|
1023 */ |
|
1024 int |
|
1025 nfs_visible_inode(struct exportinfo *exi, ino64_t ino, int *expseudo) |
|
1026 { |
|
1027 struct exp_visible *visp; |
|
1028 |
|
1029 /* |
|
1030 * Only a PSEUDO node has a visible list or an exported VROOT |
|
1031 * node may have a visible list. |
|
1032 */ |
|
1033 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { |
|
1034 *expseudo = 0; |
|
1035 return (0); |
|
1036 } |
|
1037 |
|
1038 for (visp = exi->exi_visible; visp; visp = visp->vis_next) |
|
1039 if ((u_longlong_t)ino == visp->vis_ino) { |
|
1040 *expseudo = visp->vis_exported; |
|
1041 return (1); |
|
1042 } |
|
1043 |
|
1044 *expseudo = 0; |
|
1045 return (0); |
|
1046 } |