1 /*
2 * Copyright (c) 1997-2006 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *
40 * File: am-utils/amd/nfs_subr.c
41 *
42 */
43
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif /* HAVE_CONFIG_H */
47 #include <am_defs.h>
48 #include <amd.h>
49
50 /*
51 * Convert from UN*X to NFS error code.
52 * Some systems like linux define their own (see
53 * conf/mount/mount_linux.h).
54 */
55 #ifndef nfs_error
56 # define nfs_error(e) ((nfsstat)(e))
57 #endif /* nfs_error */
58
59 /*
60 * File Handle structure
61 *
62 * This is interpreted by indexing the exported array
63 * by fhh_id (for old-style filehandles), or by retrieving
64 * the node name from fhh_path (for new-style filehandles).
65 *
66 * The whole structure is mapped onto a standard fhandle_t
67 * when transmitted.
68 */
69 struct am_fh {
70 u_int fhh_gen; /* generation number */
71 union {
72 struct {
73 int fhh_type; /* old or new am_fh */
74 pid_t fhh_pid; /* process id */
75 int fhh_id; /* map id */
76 } s;
77 char fhh_path[NFS_FHSIZE-sizeof(u_int)]; /* path to am_node */
78 } u;
79 };
80
81
82 /* forward declarations */
83 /* converting am-filehandles to mount-points */
84 static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop);
85 static am_node *fh_to_mp(am_nfs_fh *fhp);
86 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
87
88
89 static char *
do_readlink(am_node * mp,int * error_return)90 do_readlink(am_node *mp, int *error_return)
91 {
92 char *ln;
93
94 /*
95 * If there is a readlink method then use it,
96 * otherwise if a link exists use that,
97 * otherwise use the mount point.
98 */
99 if (mp->am_mnt->mf_ops->readlink) {
100 int retry = 0;
101 mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry);
102 if (mp == 0) {
103 *error_return = retry;
104 return 0;
105 }
106 /* reschedule_timeout_mp(); */
107 }
108
109 if (mp->am_link) {
110 ln = mp->am_link;
111 } else {
112 ln = mp->am_mnt->mf_mount;
113 }
114
115 return ln;
116 }
117
118
119 voidp
nfsproc_null_2_svc(voidp argp,struct svc_req * rqstp)120 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
121 {
122 static char res;
123
124 return (voidp) &res;
125 }
126
127
128 nfsattrstat *
nfsproc_getattr_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)129 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
130 {
131 static nfsattrstat res;
132 am_node *mp;
133 int retry;
134 time_t now = clocktime(NULL);
135
136 if (amuDebug(D_TRACE))
137 plog(XLOG_DEBUG, "getattr:");
138
139 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
140 if (mp == 0) {
141 if (amuDebug(D_TRACE))
142 plog(XLOG_DEBUG, "\tretry=%d", retry);
143
144 if (retry < 0) {
145 amd_stats.d_drops++;
146 return 0;
147 }
148 res.ns_status = nfs_error(retry);
149 return &res;
150 }
151
152 res = mp->am_attr;
153 if (amuDebug(D_TRACE))
154 plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld",
155 mp->am_path,
156 (int) res.ns_u.ns_attr_u.na_size,
157 (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds,
158 (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds);
159
160 /* Delay unmount of what was looked up */
161 if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
162 mp->am_timeo_w += gopt.am_timeo_w;
163 mp->am_ttl = now + mp->am_timeo_w;
164
165 mp->am_stats.s_getattr++;
166 return &res;
167 }
168
169
170 nfsattrstat *
nfsproc_setattr_2_svc(nfssattrargs * argp,struct svc_req * rqstp)171 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
172 {
173 static nfsattrstat res;
174
175 if (!fh_to_mp(&argp->sag_fhandle))
176 res.ns_status = nfs_error(ESTALE);
177 else
178 res.ns_status = nfs_error(EROFS);
179
180 return &res;
181 }
182
183
184 voidp
nfsproc_root_2_svc(voidp argp,struct svc_req * rqstp)185 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
186 {
187 static char res;
188
189 return (voidp) &res;
190 }
191
192
193 nfsdiropres *
nfsproc_lookup_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)194 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
195 {
196 static nfsdiropres res;
197 am_node *mp;
198 int retry;
199 uid_t uid;
200 gid_t gid;
201
202 if (amuDebug(D_TRACE))
203 plog(XLOG_DEBUG, "lookup:");
204
205 /* finally, find the effective uid/gid from RPC request */
206 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
207 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
208 xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
209 xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
210
211 mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE);
212 if (mp == 0) {
213 if (retry < 0) {
214 amd_stats.d_drops++;
215 return 0;
216 }
217 res.dr_status = nfs_error(retry);
218 } else {
219 int error;
220 am_node *ap;
221 if (amuDebug(D_TRACE))
222 plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name);
223 ap = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE);
224 if (ap && error < 0)
225 ap = mp->am_mnt->mf_ops->mount_child(ap, &error);
226 if (ap == 0) {
227 if (error < 0) {
228 amd_stats.d_drops++;
229 return 0;
230 }
231 res.dr_status = nfs_error(error);
232 } else {
233 /*
234 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This
235 * should reduce the chance for race condition between unmounting an
236 * entry synchronously, and re-mounting it asynchronously.
237 */
238 if (ap->am_ttl < mp->am_ttl)
239 ap->am_ttl = mp->am_ttl;
240 mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
241 res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
242 res.dr_status = NFS_OK;
243 }
244 mp->am_stats.s_lookup++;
245 /* reschedule_timeout_mp(); */
246 }
247
248 return &res;
249 }
250
251
252 void
nfs_quick_reply(am_node * mp,int error)253 nfs_quick_reply(am_node *mp, int error)
254 {
255 SVCXPRT *transp = mp->am_transp;
256 nfsdiropres res;
257 xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
258
259 /*
260 * If there's a transp structure then we can reply to the client's
261 * nfs lookup request.
262 */
263 if (transp) {
264 if (error == 0) {
265 /*
266 * Construct a valid reply to a lookup request. Same
267 * code as in nfsproc_lookup_2_svc() above.
268 */
269 mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
270 res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
271 res.dr_status = NFS_OK;
272 } else
273 /*
274 * Return the error that was passed to us.
275 */
276 res.dr_status = nfs_error(error);
277
278 /*
279 * Send off our reply
280 */
281 if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
282 svcerr_systemerr(transp);
283
284 /*
285 * Free up transp. It's only used for one reply.
286 */
287 XFREE(mp->am_transp);
288 dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
289 }
290 }
291
292
293 nfsreadlinkres *
nfsproc_readlink_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)294 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
295 {
296 static nfsreadlinkres res;
297 am_node *mp;
298 int retry;
299
300 if (amuDebug(D_TRACE))
301 plog(XLOG_DEBUG, "readlink:");
302
303 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
304 if (mp == 0) {
305 readlink_retry:
306 if (retry < 0) {
307 amd_stats.d_drops++;
308 return 0;
309 }
310 res.rlr_status = nfs_error(retry);
311 } else {
312 char *ln = do_readlink(mp, &retry);
313 if (ln == 0)
314 goto readlink_retry;
315 res.rlr_status = NFS_OK;
316 if (amuDebug(D_TRACE) && ln)
317 plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
318 res.rlr_u.rlr_data_u = ln;
319 mp->am_stats.s_readlink++;
320 }
321
322 return &res;
323 }
324
325
326 nfsreadres *
nfsproc_read_2_svc(nfsreadargs * argp,struct svc_req * rqstp)327 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
328 {
329 static nfsreadres res;
330
331 memset((char *) &res, 0, sizeof(res));
332 res.rr_status = nfs_error(EACCES);
333
334 return &res;
335 }
336
337
338 voidp
nfsproc_writecache_2_svc(voidp argp,struct svc_req * rqstp)339 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
340 {
341 static char res;
342
343 return (voidp) &res;
344 }
345
346
347 nfsattrstat *
nfsproc_write_2_svc(nfswriteargs * argp,struct svc_req * rqstp)348 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
349 {
350 static nfsattrstat res;
351
352 if (!fh_to_mp(&argp->wra_fhandle))
353 res.ns_status = nfs_error(ESTALE);
354 else
355 res.ns_status = nfs_error(EROFS);
356
357 return &res;
358 }
359
360
361 nfsdiropres *
nfsproc_create_2_svc(nfscreateargs * argp,struct svc_req * rqstp)362 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
363 {
364 static nfsdiropres res;
365
366 if (!fh_to_mp(&argp->ca_where.da_fhandle))
367 res.dr_status = nfs_error(ESTALE);
368 else
369 res.dr_status = nfs_error(EROFS);
370
371 return &res;
372 }
373
374
375 static nfsstat *
unlink_or_rmdir(nfsdiropargs * argp,struct svc_req * rqstp,int unlinkp)376 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
377 {
378 static nfsstat res;
379 int retry;
380
381 am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
382 if (mp == 0) {
383 if (retry < 0) {
384 amd_stats.d_drops++;
385 return 0;
386 }
387 res = nfs_error(retry);
388 goto out;
389 }
390
391 if (mp->am_fattr.na_type != NFDIR) {
392 res = nfs_error(ENOTDIR);
393 goto out;
394 }
395
396 if (amuDebug(D_TRACE))
397 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
398
399 mp = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE);
400 if (mp == 0) {
401 /*
402 * Ignore retries...
403 */
404 if (retry < 0)
405 retry = 0;
406 /*
407 * Usual NFS workaround...
408 */
409 else if (retry == ENOENT)
410 retry = 0;
411 res = nfs_error(retry);
412 } else {
413 forcibly_timeout_mp(mp);
414 res = NFS_OK;
415 }
416
417 out:
418 return &res;
419 }
420
421
422 nfsstat *
nfsproc_remove_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)423 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
424 {
425 return unlink_or_rmdir(argp, rqstp, TRUE);
426 }
427
428
429 nfsstat *
nfsproc_rename_2_svc(nfsrenameargs * argp,struct svc_req * rqstp)430 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
431 {
432 static nfsstat res;
433
434 if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
435 res = nfs_error(ESTALE);
436 /*
437 * If the kernel is doing clever things with referenced files
438 * then let it pretend...
439 */
440 else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
441 res = NFS_OK;
442 /*
443 * otherwise a failure
444 */
445 else
446 res = nfs_error(EROFS);
447
448 return &res;
449 }
450
451
452 nfsstat *
nfsproc_link_2_svc(nfslinkargs * argp,struct svc_req * rqstp)453 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
454 {
455 static nfsstat res;
456
457 if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
458 res = nfs_error(ESTALE);
459 else
460 res = nfs_error(EROFS);
461
462 return &res;
463 }
464
465
466 nfsstat *
nfsproc_symlink_2_svc(nfssymlinkargs * argp,struct svc_req * rqstp)467 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
468 {
469 static nfsstat res;
470
471 if (!fh_to_mp(&argp->sla_from.da_fhandle))
472 res = nfs_error(ESTALE);
473 else
474 res = nfs_error(EROFS);
475
476 return &res;
477 }
478
479
480 nfsdiropres *
nfsproc_mkdir_2_svc(nfscreateargs * argp,struct svc_req * rqstp)481 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
482 {
483 static nfsdiropres res;
484
485 if (!fh_to_mp(&argp->ca_where.da_fhandle))
486 res.dr_status = nfs_error(ESTALE);
487 else
488 res.dr_status = nfs_error(EROFS);
489
490 return &res;
491 }
492
493
494 nfsstat *
nfsproc_rmdir_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)495 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
496 {
497 return unlink_or_rmdir(argp, rqstp, FALSE);
498 }
499
500
501 nfsreaddirres *
nfsproc_readdir_2_svc(nfsreaddirargs * argp,struct svc_req * rqstp)502 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
503 {
504 static nfsreaddirres res;
505 static nfsentry e_res[MAX_READDIR_ENTRIES];
506 am_node *mp;
507 int retry;
508
509 if (amuDebug(D_TRACE))
510 plog(XLOG_DEBUG, "readdir:");
511
512 mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE);
513 if (mp == 0) {
514 if (retry < 0) {
515 amd_stats.d_drops++;
516 return 0;
517 }
518 res.rdr_status = nfs_error(retry);
519 } else {
520 if (amuDebug(D_TRACE))
521 plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
522 res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
523 (mp, argp->rda_cookie,
524 &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
525 mp->am_stats.s_readdir++;
526 }
527
528 return &res;
529 }
530
531
532 nfsstatfsres *
nfsproc_statfs_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)533 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
534 {
535 static nfsstatfsres res;
536 am_node *mp;
537 int retry;
538 mntent_t mnt;
539
540 if (amuDebug(D_TRACE))
541 plog(XLOG_DEBUG, "statfs:");
542
543 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
544 if (mp == 0) {
545 if (retry < 0) {
546 amd_stats.d_drops++;
547 return 0;
548 }
549 res.sfr_status = nfs_error(retry);
550 } else {
551 nfsstatfsokres *fp;
552 if (amuDebug(D_TRACE))
553 plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
554
555 /*
556 * just return faked up file system information
557 */
558 fp = &res.sfr_u.sfr_reply_u;
559
560 fp->sfrok_tsize = 1024;
561 fp->sfrok_bsize = 1024;
562
563 /* check if map is browsable and show_statfs_entries=yes */
564 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
565 mp->am_mnt && mp->am_mnt->mf_mopts) {
566 mnt.mnt_opts = mp->am_mnt->mf_mopts;
567 if (amu_hasmntopt(&mnt, "browsable")) {
568 count_map_entries(mp,
569 &fp->sfrok_blocks,
570 &fp->sfrok_bfree,
571 &fp->sfrok_bavail);
572 }
573 } else {
574 fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
575 fp->sfrok_bfree = 0;
576 fp->sfrok_bavail = 0;
577 }
578
579 res.sfr_status = NFS_OK;
580 mp->am_stats.s_statfs++;
581 }
582
583 return &res;
584 }
585
586
587 /*
588 * count how many total entries there are in a map, and how many
589 * of them are in use.
590 */
591 static void
count_map_entries(const am_node * mp,u_int * out_blocks,u_int * out_bfree,u_int * out_bavail)592 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
593 {
594 u_int blocks, bfree, bavail, i;
595 mntfs *mf;
596 mnt_map *mmp;
597 kv *k;
598
599 blocks = bfree = bavail = 0;
600 if (!mp)
601 goto out;
602 mf = mp->am_mnt;
603 if (!mf)
604 goto out;
605 mmp = (mnt_map *) mf->mf_private;
606 if (!mmp)
607 goto out;
608
609 /* iterate over keys */
610 for (i = 0; i < NKVHASH; i++) {
611 for (k = mmp->kvhash[i]; k ; k = k->next) {
612 if (!k->key)
613 continue;
614 blocks++;
615 /*
616 * XXX: Need to count how many are actively in use and recompute
617 * bfree and bavail based on it.
618 */
619 }
620 }
621
622 out:
623 *out_blocks = blocks;
624 *out_bfree = bfree;
625 *out_bavail = bavail;
626 }
627
628
629 /*
630 * Convert from file handle to automount node.
631 */
632 static am_node *
fh_to_mp3(am_nfs_fh * fhp,int * rp,int vop)633 fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop)
634 {
635 struct am_fh *fp = (struct am_fh *) fhp;
636 am_node *ap = 0;
637
638 if (fp->u.s.fhh_type != 0) {
639 /* New filehandle type */
640 int len = sizeof(*fhp) - sizeof(fp->fhh_gen);
641 char *path = xmalloc(len+1);
642 /*
643 * Because fhp is treated as a filehandle we use memcpy
644 * instead of xstrlcpy.
645 */
646 memcpy(path, (char *) fp->u.fhh_path, len);
647 path[len] = '\0';
648 /* dlog("fh_to_mp3: new filehandle: %s", path); */
649
650 ap = path_to_exported_ap(path);
651 XFREE(path);
652 } else {
653 /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */
654 /*
655 * Check process id matches
656 * If it doesn't then it is probably
657 * from an old kernel-cached filehandle
658 * which is now out of date.
659 */
660 if (fp->u.s.fhh_pid != get_server_pid()) {
661 dlog("fh_to_mp3: wrong pid %ld != my pid %ld",
662 (long) fp->u.s.fhh_pid, get_server_pid());
663 goto drop;
664 }
665
666 /*
667 * Get hold of the supposed mount node
668 */
669 ap = get_exported_ap(fp->u.s.fhh_id);
670 }
671
672 /*
673 * Check the generation number in the node
674 * matches the one from the kernel. If not
675 * then the old node has been timed out and
676 * a new one allocated.
677 */
678 if (ap != NULL && ap->am_gen != fp->fhh_gen)
679 ap = 0;
680
681 /*
682 * If it doesn't exists then drop the request
683 */
684 if (!ap)
685 goto drop;
686
687 #if 0
688 /*
689 * If the node is hung then locate a new node
690 * for it. This implements the replicated filesystem
691 * retries.
692 */
693 if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
694 int error;
695 am_node *orig_ap = ap;
696
697 dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server",
698 orig_ap->am_path, orig_ap->am_mnt->mf_info);
699
700 /*
701 * Update modify time of parent node.
702 * With any luck the kernel will re-stat
703 * the child node and get new information.
704 */
705 clocktime(&orig_ap->am_fattr.na_mtime);
706
707 /*
708 * Call the parent's lookup routine for an object
709 * with the same name. This may return -1 in error
710 * if a mount is in progress. In any case, if no
711 * mount node is returned the error code is propagated
712 * to the caller.
713 */
714 if (vop == VLOOK_CREATE) {
715 ap = orig_ap->am_parent->am_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop);
716 if (ap && error < 0)
717 ap = orig_ap->am_parent->am_mnt->mf_ops->mount_child(ap, &error);
718 } else {
719 ap = 0;
720 error = ESTALE;
721 }
722 if (ap == 0) {
723 if (error < 0 && amd_state == Finishing)
724 error = ENOENT;
725 *rp = error;
726 return 0;
727 }
728
729 /*
730 * Update last access to original node. This
731 * avoids timing it out and so sending ESTALE
732 * back to the kernel.
733 * XXX - Not sure we need this anymore (jsp, 90/10/6).
734 */
735 new_ttl(orig_ap);
736
737 }
738 #endif
739
740 /*
741 * Disallow references to objects being unmounted, unless
742 * they are automount points.
743 */
744 if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
745 !(ap->am_flags & AMF_ROOT)) {
746 if (amd_state == Finishing)
747 *rp = ENOENT;
748 else
749 *rp = -1;
750 return 0;
751 }
752 new_ttl(ap);
753
754 drop:
755 if (!ap || !ap->am_mnt) {
756 /*
757 * If we are shutting down then it is likely
758 * that this node has disappeared because of
759 * a fast timeout. To avoid things thrashing
760 * just pretend it doesn't exist at all. If
761 * ESTALE is returned, some NFS clients just
762 * keep retrying (stupid or what - if it's
763 * stale now, what's it going to be in 5 minutes?)
764 */
765 if (amd_state == Finishing)
766 *rp = ENOENT;
767 else
768 *rp = ESTALE;
769 amd_stats.d_stale++;
770 }
771
772 return ap;
773 }
774
775
776 static am_node *
fh_to_mp(am_nfs_fh * fhp)777 fh_to_mp(am_nfs_fh *fhp)
778 {
779 int dummy;
780
781 return fh_to_mp3(fhp, &dummy, VLOOK_CREATE);
782 }
783
784
785 /*
786 * Convert from automount node to file handle.
787 */
788 void
mp_to_fh(am_node * mp,am_nfs_fh * fhp)789 mp_to_fh(am_node *mp, am_nfs_fh *fhp)
790 {
791 u_int pathlen;
792 struct am_fh *fp = (struct am_fh *) fhp;
793
794 memset((char *) fhp, 0, sizeof(am_nfs_fh));
795
796 /* Store the generation number */
797 fp->fhh_gen = mp->am_gen;
798
799 pathlen = strlen(mp->am_path);
800 if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) {
801 /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
802
803 /*
804 * Because fhp is treated as a filehandle we use memcpy instead of
805 * xstrlcpy.
806 */
807 memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
808 } else {
809 /*
810 * Take the process id
811 */
812 fp->u.s.fhh_pid = get_server_pid();
813
814 /*
815 * ... the map number
816 */
817 fp->u.s.fhh_id = mp->am_mapno;
818
819 /*
820 * ... and the generation number (previously stored)
821 * to make a "unique" triple that will never
822 * be reallocated except across reboots (which doesn't matter)
823 * or if we are unlucky enough to be given the same
824 * pid as a previous amd (very unlikely).
825 */
826 /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
827 }
828 }
829