1 /* $NetBSD: nfs_subr.c,v 1.3 2015/01/21 21:47:44 joerg Exp $ */
2
3 /*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/amd/nfs_subr.c
39 *
40 */
41
42 #ifdef HAVE_CONFIG_H
43 # include <config.h>
44 #endif /* HAVE_CONFIG_H */
45 #include <am_defs.h>
46 #include <amd.h>
47
48 /*
49 * Convert from UN*X to NFS error code.
50 * Some systems like linux define their own (see
51 * conf/mount/mount_linux.h).
52 */
53 #ifndef nfs_error
54 # define nfs_error(e) ((nfsstat)(e))
55 #endif /* nfs_error */
56
57 /*
58 * File Handle structure
59 *
60 * This is interpreted by indexing the exported array
61 * by fhh_id (for old-style filehandles), or by retrieving
62 * the node name from fhh_path (for new-style filehandles).
63 *
64 * The whole structure is mapped onto a standard fhandle_t
65 * when transmitted.
66 */
67 struct am_fh {
68 u_int fhh_gen; /* generation number */
69 union {
70 struct {
71 int fhh_type; /* old or new am_fh */
72 pid_t fhh_pid; /* process id */
73 int fhh_id; /* map id */
74 } s;
75 char fhh_path[NFS_FHSIZE-sizeof(u_int)]; /* path to am_node */
76 } u;
77 };
78
79 struct am_fh3 {
80 u_int fhh_gen; /* generation number */
81 union {
82 struct {
83 int fhh_type; /* old or new am_fh */
84 pid_t fhh_pid; /* process id */
85 int fhh_id; /* map id */
86 } s;
87 char fhh_path[AM_FHSIZE3-sizeof(u_int)]; /* path to am_node */
88 } u;
89 };
90
91 /* forward declarations */
92 /* converting am-filehandles to mount-points */
93 static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop);
94 static am_node *fh_to_mp(am_nfs_fh *fhp);
95 static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
96
97
98 static char *
do_readlink(am_node * mp,int * error_return)99 do_readlink(am_node *mp, int *error_return)
100 {
101 char *ln;
102
103 /*
104 * If there is a readlink method then use it,
105 * otherwise if a link exists use that,
106 * otherwise use the mount point.
107 */
108 if (mp->am_al->al_mnt->mf_ops->readlink) {
109 int retry = 0;
110 mp = (*mp->am_al->al_mnt->mf_ops->readlink) (mp, &retry);
111 if (mp == NULL) {
112 *error_return = retry;
113 return 0;
114 }
115 /* reschedule_timeout_mp(); */
116 }
117
118 if (mp->am_link) {
119 ln = mp->am_link;
120 } else {
121 ln = mp->am_al->al_mnt->mf_mount;
122 }
123
124 return ln;
125 }
126
127
128 voidp
nfsproc_null_2_svc(voidp argp,struct svc_req * rqstp)129 nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
130 {
131 static char res;
132
133 return (voidp) &res;
134 }
135
136
137 nfsattrstat *
nfsproc_getattr_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)138 nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
139 {
140 static nfsattrstat res;
141 am_node *mp;
142 int retry = 0;
143 time_t now = clocktime(NULL);
144
145 if (amuDebug(D_TRACE))
146 plog(XLOG_DEBUG, "getattr:");
147
148 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
149 if (mp == NULL) {
150 if (amuDebug(D_TRACE))
151 plog(XLOG_DEBUG, "\tretry=%d", retry);
152
153 if (retry < 0) {
154 amd_stats.d_drops++;
155 return 0;
156 }
157 res.ns_status = nfs_error(retry);
158 return &res;
159 }
160
161 res = mp->am_attr;
162 if (amuDebug(D_TRACE))
163 plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld",
164 mp->am_path,
165 (int) res.ns_u.ns_attr_u.na_size,
166 (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds,
167 (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds);
168
169 /* Delay unmount of what was looked up */
170 if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
171 mp->am_timeo_w += gopt.am_timeo_w;
172 mp->am_ttl = now + mp->am_timeo_w;
173
174 mp->am_stats.s_getattr++;
175 return &res;
176 }
177
178
179 nfsattrstat *
nfsproc_setattr_2_svc(nfssattrargs * argp,struct svc_req * rqstp)180 nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
181 {
182 static nfsattrstat res;
183
184 if (!fh_to_mp(&argp->sag_fhandle))
185 res.ns_status = nfs_error(ESTALE);
186 else
187 res.ns_status = nfs_error(EROFS);
188
189 return &res;
190 }
191
192
193 voidp
nfsproc_root_2_svc(voidp argp,struct svc_req * rqstp)194 nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
195 {
196 static char res;
197
198 return (voidp) &res;
199 }
200
201
202 nfsdiropres *
nfsproc_lookup_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)203 nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
204 {
205 static nfsdiropres res;
206 am_node *mp;
207 int retry;
208 uid_t uid;
209 gid_t gid;
210
211 if (amuDebug(D_TRACE))
212 plog(XLOG_DEBUG, "lookup:");
213
214 /* finally, find the effective uid/gid from RPC request */
215 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
216 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
217 xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
218 xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
219
220 mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE);
221 if (mp == NULL) {
222 if (retry < 0) {
223 amd_stats.d_drops++;
224 return 0;
225 }
226 res.dr_status = nfs_error(retry);
227 } else {
228 int error;
229 am_node *ap;
230 if (amuDebug(D_TRACE))
231 plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name);
232 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE);
233 if (ap && error < 0)
234 ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error);
235 if (ap == 0) {
236 if (error < 0) {
237 amd_stats.d_drops++;
238 return 0;
239 }
240 res.dr_status = nfs_error(error);
241 } else {
242 /*
243 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This
244 * should reduce the chance for race condition between unmounting an
245 * entry synchronously, and re-mounting it asynchronously.
246 */
247 if (ap->am_ttl < mp->am_ttl)
248 ap->am_ttl = mp->am_ttl;
249 mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
250 res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
251 res.dr_status = NFS_OK;
252 }
253 mp->am_stats.s_lookup++;
254 /* reschedule_timeout_mp(); */
255 }
256
257 return &res;
258 }
259
260
261 void
nfs_quick_reply(am_node * mp,int error)262 nfs_quick_reply(am_node *mp, int error)
263 {
264 SVCXPRT *transp = mp->am_transp;
265 nfsdiropres res;
266 xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
267
268 /*
269 * If there's a transp structure then we can reply to the client's
270 * nfs lookup request.
271 */
272 if (transp) {
273 if (error == 0) {
274 /*
275 * Construct a valid reply to a lookup request. Same
276 * code as in nfsproc_lookup_2_svc() above.
277 */
278 mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
279 res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
280 res.dr_status = NFS_OK;
281 } else
282 /*
283 * Return the error that was passed to us.
284 */
285 res.dr_status = nfs_error(error);
286
287 /*
288 * Send off our reply
289 */
290 if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
291 svcerr_systemerr(transp);
292
293 /*
294 * Free up transp. It's only used for one reply.
295 */
296 XFREE(mp->am_transp);
297 dlog("Quick reply sent for %s", mp->am_al->al_mnt->mf_mount);
298 }
299 }
300
301
302 nfsreadlinkres *
nfsproc_readlink_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)303 nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
304 {
305 static nfsreadlinkres res;
306 am_node *mp;
307 int retry;
308
309 if (amuDebug(D_TRACE))
310 plog(XLOG_DEBUG, "readlink:");
311
312 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
313 if (mp == NULL) {
314 readlink_retry:
315 if (retry < 0) {
316 amd_stats.d_drops++;
317 return 0;
318 }
319 res.rlr_status = nfs_error(retry);
320 } else {
321 char *ln = do_readlink(mp, &retry);
322 if (ln == 0)
323 goto readlink_retry;
324 res.rlr_status = NFS_OK;
325 if (amuDebug(D_TRACE) && ln)
326 plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
327 res.rlr_u.rlr_data_u = ln;
328 mp->am_stats.s_readlink++;
329 }
330
331 return &res;
332 }
333
334
335 nfsreadres *
nfsproc_read_2_svc(nfsreadargs * argp,struct svc_req * rqstp)336 nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
337 {
338 static nfsreadres res;
339
340 memset((char *) &res, 0, sizeof(res));
341 res.rr_status = nfs_error(EACCES);
342
343 return &res;
344 }
345
346
347 voidp
nfsproc_writecache_2_svc(voidp argp,struct svc_req * rqstp)348 nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
349 {
350 static char res;
351
352 return (voidp) &res;
353 }
354
355
356 nfsattrstat *
nfsproc_write_2_svc(nfswriteargs * argp,struct svc_req * rqstp)357 nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
358 {
359 static nfsattrstat res;
360
361 if (!fh_to_mp(&argp->wra_fhandle))
362 res.ns_status = nfs_error(ESTALE);
363 else
364 res.ns_status = nfs_error(EROFS);
365
366 return &res;
367 }
368
369
370 nfsdiropres *
nfsproc_create_2_svc(nfscreateargs * argp,struct svc_req * rqstp)371 nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
372 {
373 static nfsdiropres res;
374
375 if (!fh_to_mp(&argp->ca_where.da_fhandle))
376 res.dr_status = nfs_error(ESTALE);
377 else
378 res.dr_status = nfs_error(EROFS);
379
380 return &res;
381 }
382
383
384 static nfsstat *
unlink_or_rmdir(nfsdiropargs * argp,struct svc_req * rqstp,int unlinkp)385 unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
386 {
387 static nfsstat res;
388 int retry;
389
390 am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
391 if (mp == NULL) {
392 if (retry < 0) {
393 amd_stats.d_drops++;
394 return 0;
395 }
396 res = nfs_error(retry);
397 goto out;
398 }
399
400 if (mp->am_fattr.na_type != NFDIR) {
401 res = nfs_error(ENOTDIR);
402 goto out;
403 }
404
405 if (amuDebug(D_TRACE))
406 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
407
408 mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE);
409 if (mp == NULL) {
410 /*
411 * Ignore retries...
412 */
413 if (retry < 0)
414 retry = 0;
415 /*
416 * Usual NFS workaround...
417 */
418 else if (retry == ENOENT)
419 retry = 0;
420 res = nfs_error(retry);
421 } else {
422 forcibly_timeout_mp(mp);
423 res = NFS_OK;
424 }
425
426 out:
427 return &res;
428 }
429
430
431 nfsstat *
nfsproc_remove_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)432 nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
433 {
434 return unlink_or_rmdir(argp, rqstp, TRUE);
435 }
436
437
438 nfsstat *
nfsproc_rename_2_svc(nfsrenameargs * argp,struct svc_req * rqstp)439 nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
440 {
441 static nfsstat res;
442
443 if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
444 res = nfs_error(ESTALE);
445 /*
446 * If the kernel is doing clever things with referenced files
447 * then let it pretend...
448 */
449 else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
450 res = NFS_OK;
451 /*
452 * otherwise a failure
453 */
454 else
455 res = nfs_error(EROFS);
456
457 return &res;
458 }
459
460
461 nfsstat *
nfsproc_link_2_svc(nfslinkargs * argp,struct svc_req * rqstp)462 nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
463 {
464 static nfsstat res;
465
466 if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
467 res = nfs_error(ESTALE);
468 else
469 res = nfs_error(EROFS);
470
471 return &res;
472 }
473
474
475 nfsstat *
nfsproc_symlink_2_svc(nfssymlinkargs * argp,struct svc_req * rqstp)476 nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
477 {
478 static nfsstat res;
479
480 if (!fh_to_mp(&argp->sla_from.da_fhandle))
481 res = nfs_error(ESTALE);
482 else
483 res = nfs_error(EROFS);
484
485 return &res;
486 }
487
488
489 nfsdiropres *
nfsproc_mkdir_2_svc(nfscreateargs * argp,struct svc_req * rqstp)490 nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
491 {
492 static nfsdiropres res;
493
494 if (!fh_to_mp(&argp->ca_where.da_fhandle))
495 res.dr_status = nfs_error(ESTALE);
496 else
497 res.dr_status = nfs_error(EROFS);
498
499 return &res;
500 }
501
502
503 nfsstat *
nfsproc_rmdir_2_svc(nfsdiropargs * argp,struct svc_req * rqstp)504 nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
505 {
506 return unlink_or_rmdir(argp, rqstp, FALSE);
507 }
508
509
510 nfsreaddirres *
nfsproc_readdir_2_svc(nfsreaddirargs * argp,struct svc_req * rqstp)511 nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
512 {
513 static nfsreaddirres res;
514 static nfsentry e_res[MAX_READDIR_ENTRIES];
515 am_node *mp;
516 int retry;
517
518 if (amuDebug(D_TRACE))
519 plog(XLOG_DEBUG, "readdir:");
520
521 mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE);
522 if (mp == NULL) {
523 if (retry < 0) {
524 amd_stats.d_drops++;
525 return 0;
526 }
527 res.rdr_status = nfs_error(retry);
528 } else {
529 if (amuDebug(D_TRACE))
530 plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
531 res.rdr_status = nfs_error((*mp->am_al->al_mnt->mf_ops->readdir)
532 (mp, argp->rda_cookie,
533 &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
534 mp->am_stats.s_readdir++;
535 }
536
537 return &res;
538 }
539
540
541 nfsstatfsres *
nfsproc_statfs_2_svc(am_nfs_fh * argp,struct svc_req * rqstp)542 nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
543 {
544 static nfsstatfsres res;
545 am_node *mp;
546 int retry;
547 mntent_t mnt;
548
549 if (amuDebug(D_TRACE))
550 plog(XLOG_DEBUG, "statfs:");
551
552 mp = fh_to_mp3(argp, &retry, VLOOK_CREATE);
553 if (mp == NULL) {
554 if (retry < 0) {
555 amd_stats.d_drops++;
556 return 0;
557 }
558 res.sfr_status = nfs_error(retry);
559 } else {
560 nfsstatfsokres *fp;
561 if (amuDebug(D_TRACE))
562 plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
563
564 /*
565 * just return faked up file system information
566 */
567 fp = &res.sfr_u.sfr_reply_u;
568
569 fp->sfrok_tsize = 1024;
570 fp->sfrok_bsize = 1024;
571
572 /* check if map is browsable and show_statfs_entries=yes */
573 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
574 mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
575 mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
576 if (amu_hasmntopt(&mnt, "browsable")) {
577 count_map_entries(mp,
578 &fp->sfrok_blocks,
579 &fp->sfrok_bfree,
580 &fp->sfrok_bavail);
581 }
582 } else {
583 fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
584 fp->sfrok_bfree = 0;
585 fp->sfrok_bavail = 0;
586 }
587
588 res.sfr_status = NFS_OK;
589 mp->am_stats.s_statfs++;
590 }
591
592 return &res;
593 }
594
595
596 /*
597 * count how many total entries there are in a map, and how many
598 * of them are in use.
599 */
600 static void
count_map_entries(const am_node * mp,u_int * out_blocks,u_int * out_bfree,u_int * out_bavail)601 count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
602 {
603 u_int blocks, bfree, bavail, i;
604 mntfs *mf;
605 mnt_map *mmp;
606 kv *k;
607
608 blocks = bfree = bavail = 0;
609 if (!mp)
610 goto out;
611 mf = mp->am_al->al_mnt;
612 if (!mf)
613 goto out;
614 mmp = (mnt_map *) mf->mf_private;
615 if (!mmp)
616 goto out;
617
618 /* iterate over keys */
619 for (i = 0; i < NKVHASH; i++) {
620 for (k = mmp->kvhash[i]; k ; k = k->next) {
621 if (!k->key)
622 continue;
623 blocks++;
624 /*
625 * XXX: Need to count how many are actively in use and recompute
626 * bfree and bavail based on it.
627 */
628 }
629 }
630
631 out:
632 *out_blocks = blocks;
633 *out_bfree = bfree;
634 *out_bavail = bavail;
635 }
636
637 static am_node *
validate_ap(am_node * node,int * rp,u_int fhh_gen)638 validate_ap(am_node *node, int *rp, u_int fhh_gen)
639 {
640 am_node *ap = node;
641 /*
642 * Check the generation number in the node
643 * matches the one from the kernel. If not
644 * then the old node has been timed out and
645 * a new one allocated.
646 */
647 if (node != NULL && node->am_gen != fhh_gen)
648 ap = NULL;
649
650 /*
651 * If it doesn't exists then drop the request
652 */
653 if (!ap)
654 goto drop;
655
656 #if 0
657 /*
658 * If the node is hung then locate a new node
659 * for it. This implements the replicated filesystem
660 * retries.
661 */
662 if (ap->am_al->al_mnt && FSRV_ISDOWN(ap->am_al->al_mnt->mf_server) && ap->am_parent) {
663 int error;
664 am_node *orig_ap = ap;
665
666 dlog("%s: %s (%s) is hung: lookup alternative file server", __func__,
667 orig_ap->am_path, orig_ap->am_al->al_mnt->mf_info);
668
669 /*
670 * Update modify time of parent node.
671 * With any luck the kernel will re-stat
672 * the child node and get new information.
673 */
674 clocktime(&orig_ap->am_fattr.na_mtime);
675
676 /*
677 * Call the parent's lookup routine for an object
678 * with the same name. This may return -1 in error
679 * if a mount is in progress. In any case, if no
680 * mount node is returned the error code is propagated
681 * to the caller.
682 */
683 if (vop == VLOOK_CREATE) {
684 ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop);
685 if (ap && error < 0)
686 ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->mount_child(ap, &error);
687 } else {
688 ap = NULL;
689 error = ESTALE;
690 }
691 if (ap == 0) {
692 if (error < 0 && amd_state == Finishing)
693 error = ENOENT;
694 *rp = error;
695 return 0;
696 }
697
698 /*
699 * Update last access to original node. This
700 * avoids timing it out and so sending ESTALE
701 * back to the kernel.
702 * XXX - Not sure we need this anymore (jsp, 90/10/6).
703 */
704 new_ttl(orig_ap);
705
706 }
707 #endif /* 0 */
708
709 /*
710 * Disallow references to objects being unmounted, unless
711 * they are automount points.
712 */
713 if (ap->am_al->al_mnt && (ap->am_al->al_mnt->mf_flags & MFF_UNMOUNTING) &&
714 !(ap->am_flags & AMF_ROOT)) {
715 if (amd_state == Finishing)
716 *rp = ENOENT;
717 else
718 *rp = -1;
719 return 0;
720 }
721 new_ttl(ap);
722
723 drop:
724 if (!ap || !ap->am_al->al_mnt) {
725 /*
726 * If we are shutting down then it is likely
727 * that this node has disappeared because of
728 * a fast timeout. To avoid things thrashing
729 * just pretend it doesn't exist at all. If
730 * ESTALE is returned, some NFS clients just
731 * keep retrying (stupid or what - if it's
732 * stale now, what's it going to be in 5 minutes?)
733 */
734 if (amd_state == Finishing)
735 *rp = ENOENT;
736 else {
737 *rp = ESTALE;
738 amd_stats.d_stale++;
739 }
740 }
741
742 return ap;
743 }
744
745 /*
746 * Convert from file handle to automount node.
747 */
748 static am_node *
fh_to_mp3(am_nfs_fh * fhp,int * rp,int vop)749 fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop)
750 {
751 struct am_fh *fp = (struct am_fh *) fhp;
752 am_node *ap = NULL;
753
754 if (fp->u.s.fhh_type != 0) {
755 /* New filehandle type */
756 int len = sizeof(*fhp) - sizeof(fp->fhh_gen);
757 char *path = xmalloc(len+1);
758 /*
759 * Because fhp is treated as a filehandle we use memcpy
760 * instead of xstrlcpy.
761 */
762 memcpy(path, (char *) fp->u.fhh_path, len);
763 path[len] = '\0';
764 dlog("%s: new filehandle: %s", __func__, path);
765
766 ap = path_to_exported_ap(path);
767 XFREE(path);
768 } else {
769 dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id);
770 /*
771 * Check process id matches
772 * If it doesn't then it is probably
773 * from an old kernel-cached filehandle
774 * which is now out of date.
775 */
776 if (fp->u.s.fhh_pid != get_server_pid()) {
777 dlog("%s: wrong pid %ld != my pid %ld", __func__,
778 (long) fp->u.s.fhh_pid, get_server_pid());
779 goto done;
780 }
781
782 /*
783 * Get hold of the supposed mount node
784 */
785 ap = get_exported_ap(fp->u.s.fhh_id);
786 }
787 done:
788 return validate_ap(ap, rp, fp->fhh_gen);
789 }
790
791 static am_node *
fh_to_mp(am_nfs_fh * fhp)792 fh_to_mp(am_nfs_fh *fhp)
793 {
794 int dummy;
795
796 return fh_to_mp3(fhp, &dummy, VLOOK_CREATE);
797 }
798
799 static am_node *
fh3_to_mp3(am_nfs_fh3 * fhp,int * rp,int vop)800 fh3_to_mp3(am_nfs_fh3 *fhp, int *rp, int vop)
801 {
802 struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data;
803 am_node *ap = NULL;
804
805 if (fp->u.s.fhh_type != 0) {
806 /* New filehandle type */
807 int len = sizeof(*fp) - sizeof(fp->fhh_gen);
808 char *path = xmalloc(len+1);
809 /*
810 * Because fhp is treated as a filehandle we use memcpy
811 * instead of xstrlcpy.
812 */
813 memcpy(path, (char *) fp->u.fhh_path, len);
814 path[len] = '\0';
815 dlog("%s: new filehandle: %s", __func__, path);
816
817 ap = path_to_exported_ap(path);
818 XFREE(path);
819 } else {
820 dlog("%s: old filehandle: %d", __func__, fp->u.s.fhh_id);
821 /*
822 * Check process id matches
823 * If it doesn't then it is probably
824 * from an old kernel-cached filehandle
825 * which is now out of date.
826 */
827 if (fp->u.s.fhh_pid != get_server_pid()) {
828 dlog("%s: wrong pid %ld != my pid %ld", __func__,
829 (long) fp->u.s.fhh_pid, get_server_pid());
830 goto done;
831 }
832
833 /*
834 * Get hold of the supposed mount node
835 */
836 ap = get_exported_ap(fp->u.s.fhh_id);
837 }
838 done:
839 return validate_ap(ap, rp, fp->fhh_gen);
840 }
841
842 static am_node *
fh3_to_mp(am_nfs_fh3 * fhp)843 fh3_to_mp(am_nfs_fh3 *fhp)
844 {
845 int dummy;
846
847 return fh3_to_mp3(fhp, &dummy, VLOOK_CREATE);
848 }
849
850 /*
851 * Convert from automount node to file handle.
852 */
853 void
mp_to_fh(am_node * mp,am_nfs_fh * fhp)854 mp_to_fh(am_node *mp, am_nfs_fh *fhp)
855 {
856 u_int pathlen;
857 struct am_fh *fp = (struct am_fh *) fhp;
858
859 memset((char *) fhp, 0, sizeof(am_nfs_fh));
860
861 /* Store the generation number */
862 fp->fhh_gen = mp->am_gen;
863
864 pathlen = strlen(mp->am_path);
865 if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) {
866 /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
867
868 /*
869 * Because fhp is treated as a filehandle we use memcpy instead of
870 * xstrlcpy.
871 */
872 memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
873 } else {
874 /*
875 * Take the process id
876 */
877 fp->u.s.fhh_pid = get_server_pid();
878
879 /*
880 * ... the map number
881 */
882 fp->u.s.fhh_id = mp->am_mapno;
883
884 /*
885 * ... and the generation number (previously stored)
886 * to make a "unique" triple that will never
887 * be reallocated except across reboots (which doesn't matter)
888 * or if we are unlucky enough to be given the same
889 * pid as a previous amd (very unlikely).
890 */
891 /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
892 }
893 }
894 void
mp_to_fh3(am_node * mp,am_nfs_fh3 * fhp)895 mp_to_fh3(am_node *mp, am_nfs_fh3 *fhp)
896 {
897 u_int pathlen;
898 struct am_fh3 *fp = (struct am_fh3 *) fhp->am_fh3_data;
899
900 memset((char *) fhp, 0, sizeof(am_nfs_fh3));
901 fhp->am_fh3_length = AM_FHSIZE3;
902
903 /* Store the generation number */
904 fp->fhh_gen = mp->am_gen;
905
906 pathlen = strlen(mp->am_path);
907 if (pathlen <= sizeof(*fp) - sizeof(fp->fhh_gen)) {
908 /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */
909
910 /*
911 * Because fhp is treated as a filehandle we use memcpy instead of
912 * xstrlcpy.
913 */
914 memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */
915 } else {
916 /*
917 * Take the process id
918 */
919 fp->u.s.fhh_pid = get_server_pid();
920
921 /*
922 * ... the map number
923 */
924 fp->u.s.fhh_id = mp->am_mapno;
925
926 /*
927 * ... and the generation number (previously stored)
928 * to make a "unique" triple that will never
929 * be reallocated except across reboots (which doesn't matter)
930 * or if we are unlucky enough to be given the same
931 * pid as a previous amd (very unlikely).
932 */
933 /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */
934 }
935 }
936
937 #ifdef HAVE_FS_NFS3
ftype_to_ftype3(nfsftype ftype)938 static am_ftype3 ftype_to_ftype3(nfsftype ftype)
939 {
940 if (ftype == NFFIFO)
941 return AM_NF3FIFO;
942 else
943 return ftype;
944 }
945
nfstime_to_am_nfstime3(nfstime * time,am_nfstime3 * time3)946 static void nfstime_to_am_nfstime3(nfstime *time, am_nfstime3 *time3)
947 {
948 time3->seconds = time->seconds;
949 time3->nseconds = time->useconds * 1000;
950 }
951
rdev_to_am_specdata3(u_int rdev,am_specdata3 * rdev3)952 static void rdev_to_am_specdata3(u_int rdev, am_specdata3 *rdev3)
953 {
954 /* No device node here */
955 rdev3->specdata1 = (u_int) -1;
956 rdev3->specdata2 = (u_int) -1;
957 }
958
fattr_to_fattr3(nfsfattr * fattr,am_fattr3 * fattr3)959 static void fattr_to_fattr3(nfsfattr *fattr, am_fattr3 *fattr3)
960 {
961 fattr3->type = ftype_to_ftype3(fattr->na_type);
962 fattr3->mode = (am_mode3) fattr->na_mode;
963 fattr3->nlink = fattr->na_nlink;
964 fattr3->uid = (am_uid3) fattr->na_uid;
965 fattr3->gid = (am_uid3) fattr->na_gid;
966 fattr3->size = (am_size3) fattr->na_size;
967 fattr3->used = (am_size3) fattr->na_size;
968 rdev_to_am_specdata3(fattr->na_rdev, &fattr3->rdev);
969 fattr3->fsid = (uint64) fattr->na_fsid;
970 fattr3->fileid = (uint64) fattr->na_fileid;
971 nfstime_to_am_nfstime3(&fattr->na_atime, &fattr3->atime);
972 nfstime_to_am_nfstime3(&fattr->na_mtime, &fattr3->mtime);
973 nfstime_to_am_nfstime3(&fattr->na_ctime, &fattr3->ctime);
974 }
975
fattr_to_wcc_attr(nfsfattr * fattr,am_wcc_attr * wcc_attr)976 static void fattr_to_wcc_attr(nfsfattr *fattr, am_wcc_attr *wcc_attr)
977 {
978 wcc_attr->size = (am_size3) fattr->na_size;
979 nfstime_to_am_nfstime3(&fattr->na_mtime, &wcc_attr->mtime);
980 nfstime_to_am_nfstime3(&fattr->na_ctime, &wcc_attr->ctime);
981 }
982
return_estale_or_rofs(am_nfs_fh3 * fh,am_pre_op_attr * pre_op,am_post_op_attr * post_op)983 static am_nfsstat3 return_estale_or_rofs(am_nfs_fh3 *fh,
984 am_pre_op_attr *pre_op,
985 am_post_op_attr *post_op)
986 {
987 am_node *mp;
988
989 mp = fh3_to_mp(fh);
990 if (!mp) {
991 pre_op->attributes_follow = 0;
992 post_op->attributes_follow = 0;
993 return nfs_error(ESTALE);
994 } else {
995 am_fattr3 *fattr3 = &post_op->am_post_op_attr_u.attributes;
996 am_wcc_attr *wcc_attr = &pre_op->am_pre_op_attr_u.attributes;
997 nfsfattr *fattr = &mp->am_fattr;
998 pre_op->attributes_follow = 1;
999 fattr_to_wcc_attr(fattr, wcc_attr);
1000 post_op->attributes_follow = 1;
1001 fattr_to_fattr3(fattr, fattr3);
1002 return nfs_error(EROFS);
1003 }
1004 }
1005
unlink3_or_rmdir3(am_diropargs3 * argp,am_wcc_data * wcc_data,int unlinkp)1006 static am_nfsstat3 unlink3_or_rmdir3(am_diropargs3 *argp,
1007 am_wcc_data *wcc_data, int unlinkp)
1008 {
1009 static am_nfsstat3 res;
1010 am_nfs_fh3 *dir = &argp->dir;
1011 am_filename3 name = argp->name;
1012 am_pre_op_attr *pre_op_dir = &wcc_data->before;
1013 am_post_op_attr *post_op_dir = &wcc_data->after;
1014 nfsfattr *fattr;
1015 am_wcc_attr *wcc_attr;
1016 am_node *mp, *ap;
1017 int retry;
1018
1019 post_op_dir->attributes_follow = 0;
1020
1021 mp = fh3_to_mp3(dir, &retry, VLOOK_DELETE);
1022 if (!mp) {
1023 pre_op_dir->attributes_follow = 0;
1024 if (retry < 0) {
1025 amd_stats.d_drops++;
1026 return 0;
1027 }
1028 res = nfs_error(retry);
1029 goto out;
1030 }
1031
1032 pre_op_dir->attributes_follow = 1;
1033 fattr = &mp->am_fattr;
1034 wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes;
1035 fattr_to_wcc_attr(fattr, wcc_attr);
1036
1037 if (mp->am_fattr.na_type != NFDIR) {
1038 res = nfs_error(ENOTDIR);
1039 goto out;
1040 }
1041
1042 if (amuDebug(D_TRACE))
1043 plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, name);
1044
1045 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &retry, VLOOK_DELETE);
1046 if (!ap) {
1047 /*
1048 * Ignore retries...
1049 */
1050 if (retry < 0)
1051 retry = 0;
1052 /*
1053 * Usual NFS workaround...
1054 */
1055 else if (retry == ENOENT)
1056 retry = 0;
1057 res = nfs_error(retry);
1058 } else {
1059 forcibly_timeout_mp(mp);
1060 res = AM_NFS3_OK;
1061 }
1062
1063 out:
1064 return res;
1065 }
1066
1067 voidp
am_nfs3_null_3_svc(voidp argp,struct svc_req * rqstp)1068 am_nfs3_null_3_svc(voidp argp, struct svc_req *rqstp)
1069 {
1070 static char * result;
1071
1072 return (voidp) &result;
1073 }
1074
1075 am_GETATTR3res *
am_nfs3_getattr_3_svc(am_GETATTR3args * argp,struct svc_req * rqstp)1076 am_nfs3_getattr_3_svc(am_GETATTR3args *argp, struct svc_req *rqstp)
1077 {
1078 static am_GETATTR3res result;
1079 am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object;
1080 am_fattr3 *fattr3;
1081 nfsfattr *fattr;
1082 am_node *mp;
1083 int retry = 0;
1084 time_t now = clocktime(NULL);
1085
1086 if (amuDebug(D_TRACE))
1087 plog(XLOG_DEBUG, "getattr_3:");
1088
1089 mp = fh3_to_mp3(fh, &retry, VLOOK_CREATE);
1090 if (!mp) {
1091 if (amuDebug(D_TRACE))
1092 plog(XLOG_DEBUG, "\tretry=%d", retry);
1093
1094 if (retry < 0) {
1095 amd_stats.d_drops++;
1096 return 0;
1097 }
1098 result.status = nfs_error(retry);
1099 return &result;
1100 }
1101
1102 fattr = &mp->am_fattr;
1103 fattr3 = (am_fattr3 *) &result.res_u.ok.obj_attributes;
1104 fattr_to_fattr3(fattr, fattr3);
1105
1106 result.status = AM_NFS3_OK;
1107
1108 if (amuDebug(D_TRACE))
1109 plog(XLOG_DEBUG, "\tstat(%s), size = %llu, mtime=%d.%d",
1110 mp->am_path,
1111 (unsigned long long) fattr3->size,
1112 (u_int) fattr3->mtime.seconds,
1113 (u_int) fattr3->mtime.nseconds);
1114
1115 /* Delay unmount of what was looked up */
1116 if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
1117 mp->am_timeo_w += gopt.am_timeo_w;
1118 mp->am_ttl = now + mp->am_timeo_w;
1119
1120 mp->am_stats.s_getattr++;
1121
1122 return &result;
1123 }
1124
1125 am_SETATTR3res *
am_nfs3_setattr_3_svc(am_SETATTR3args * argp,struct svc_req * rqstp)1126 am_nfs3_setattr_3_svc(am_SETATTR3args *argp, struct svc_req *rqstp)
1127 {
1128 static am_SETATTR3res result;
1129 am_nfs_fh3 *fh = (am_nfs_fh3 *) &argp->object;
1130 am_pre_op_attr *pre_op_obj = &result.res_u.fail.obj_wcc.before;
1131 am_post_op_attr *post_op_obj = &result.res_u.fail.obj_wcc.after;
1132
1133 if (amuDebug(D_TRACE))
1134 plog(XLOG_DEBUG, "setattr_3:");
1135
1136 result.status = return_estale_or_rofs(fh, pre_op_obj, post_op_obj);
1137
1138 return &result;
1139 }
1140
1141 am_LOOKUP3res *
am_nfs3_lookup_3_svc(am_LOOKUP3args * argp,struct svc_req * rqstp)1142 am_nfs3_lookup_3_svc(am_LOOKUP3args *argp, struct svc_req *rqstp)
1143 {
1144 static am_LOOKUP3res result;
1145 am_nfs_fh3 *dir = &argp->what.dir;
1146 am_post_op_attr *post_op_dir;
1147 am_post_op_attr *post_op_obj;
1148 am_node *mp;
1149 int retry;
1150 uid_t uid;
1151 gid_t gid;
1152
1153 if (amuDebug(D_TRACE))
1154 plog(XLOG_DEBUG, "lookup_3:");
1155
1156 /* finally, find the effective uid/gid from RPC request */
1157 if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0)
1158 plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials");
1159 xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid);
1160 xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid);
1161
1162 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1163 if (!mp) {
1164 post_op_dir = &result.res_u.fail.dir_attributes;
1165 post_op_dir->attributes_follow = 0;
1166 if (retry < 0) {
1167 amd_stats.d_drops++;
1168 return 0;
1169 }
1170 result.status = nfs_error(retry);
1171 } else {
1172 post_op_dir = &result.res_u.ok.dir_attributes;
1173 post_op_obj = &result.res_u.ok.obj_attributes;
1174 am_filename3 name;
1175 am_fattr3 *fattr3;
1176 nfsfattr *fattr;
1177 am_node *ap;
1178 int error;
1179
1180 /* dir attributes */
1181 post_op_dir->attributes_follow = 1;
1182 fattr = &mp->am_fattr;
1183 fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1184 fattr_to_fattr3(fattr, fattr3);
1185
1186 post_op_obj->attributes_follow = 0;
1187
1188 name = argp->what.name;
1189
1190 if (amuDebug(D_TRACE))
1191 plog(XLOG_DEBUG, "\tlookup_3(%s, %s)", mp->am_path, name);
1192
1193 ap = mp->am_al->al_mnt->mf_ops->lookup_child(mp, name, &error, VLOOK_CREATE);
1194 if (ap && error < 0)
1195 ap = mp->am_al->al_mnt->mf_ops->mount_child(ap, &error);
1196 if (ap == 0) {
1197 if (error < 0) {
1198 amd_stats.d_drops++;
1199 return 0;
1200 }
1201 result.status = nfs_error(error);
1202 } else {
1203 /*
1204 * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This
1205 * should reduce the chance for race condition between unmounting an
1206 * entry synchronously, and re-mounting it asynchronously.
1207 */
1208 if (ap->am_ttl < mp->am_ttl)
1209 ap->am_ttl = mp->am_ttl;
1210
1211 mp_to_fh3(ap, &result.res_u.ok.object);
1212
1213 /* mount attributes */
1214 post_op_obj->attributes_follow = 1;
1215 fattr = &ap->am_fattr;
1216 fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1217 fattr_to_fattr3(fattr, fattr3);
1218
1219 result.status = AM_NFS3_OK;
1220 }
1221 mp->am_stats.s_lookup++;
1222 }
1223 return &result;
1224 }
1225
1226 am_ACCESS3res *
am_nfs3_access_3_svc(am_ACCESS3args * argp,struct svc_req * rqstp)1227 am_nfs3_access_3_svc(am_ACCESS3args *argp, struct svc_req *rqstp)
1228 {
1229 static am_ACCESS3res result;
1230
1231 am_nfs_fh3 *obj = &argp->object;
1232 u_int accessbits = argp->access;
1233 u_int accessmask = AM_ACCESS3_LOOKUP|AM_ACCESS3_READ;
1234 am_post_op_attr *post_op_obj;
1235 am_node *mp;
1236
1237 if (amuDebug(D_TRACE))
1238 plog(XLOG_DEBUG, "access_3:");
1239
1240 mp = fh3_to_mp(obj);
1241 if (!mp) {
1242 post_op_obj = &result.res_u.fail.obj_attributes;
1243 post_op_obj->attributes_follow = 0;
1244 result.status = nfs_error(ENOENT);
1245 if (amuDebug(D_TRACE))
1246 plog(XLOG_DEBUG, "access_3: ENOENT");
1247 } else {
1248 nfsfattr *fattr = &mp->am_fattr;
1249 am_fattr3 *fattr3;
1250 post_op_obj = &result.res_u.ok.obj_attributes;
1251 fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1252 post_op_obj->attributes_follow = 1;
1253 fattr_to_fattr3(fattr, fattr3);
1254
1255 result.res_u.ok.access = accessbits & accessmask;
1256 if (amuDebug(D_TRACE))
1257 plog(XLOG_DEBUG, "access_3: b=%x m=%x", accessbits, accessmask);
1258
1259 result.status = AM_NFS3_OK;
1260 }
1261
1262 return &result;
1263 }
1264
1265 am_READLINK3res *
am_nfs3_readlink_3_svc(am_READLINK3args * argp,struct svc_req * rqstp)1266 am_nfs3_readlink_3_svc(am_READLINK3args *argp, struct svc_req *rqstp)
1267 {
1268 static am_READLINK3res result;
1269
1270 am_nfs_fh3 *symlink = (am_nfs_fh3 *) &argp->symlink;
1271 am_post_op_attr *post_op_sym;
1272 am_node *mp;
1273 int retry = 0;
1274
1275 if (amuDebug(D_TRACE))
1276 plog(XLOG_DEBUG, "readlink_3:");
1277
1278 mp = fh3_to_mp3(symlink, &retry, VLOOK_CREATE);
1279 if (!mp) {
1280 readlink_retry:
1281 if (retry < 0) {
1282 amd_stats.d_drops++;
1283 return 0;
1284 }
1285 post_op_sym = &result.res_u.fail.symlink_attributes;
1286 post_op_sym->attributes_follow = 0;
1287 result.status = nfs_error(retry);
1288 } else {
1289 nfsfattr *fattr;
1290 am_fattr3 *fattr3;
1291 char *ln;
1292
1293 ln = do_readlink(mp, &retry);
1294 if (!ln)
1295 goto readlink_retry;
1296
1297 if (amuDebug(D_TRACE) && ln)
1298 plog(XLOG_DEBUG, "\treadlink_3(%s) = %s", mp->am_path, ln);
1299
1300 result.res_u.ok.data = ln;
1301
1302 post_op_sym = &result.res_u.ok.symlink_attributes;
1303 post_op_sym->attributes_follow = 1;
1304 fattr = &mp->am_fattr;
1305 fattr3 = &post_op_sym->am_post_op_attr_u.attributes;
1306 fattr_to_fattr3(fattr, fattr3);
1307
1308 mp->am_stats.s_readlink++;
1309 result.status = AM_NFS3_OK;
1310 }
1311
1312 return &result;
1313 }
1314
1315 am_READ3res *
am_nfs3_read_3_svc(am_READ3args * argp,struct svc_req * rqstp)1316 am_nfs3_read_3_svc(am_READ3args *argp, struct svc_req *rqstp)
1317 {
1318 static am_READ3res result;
1319
1320 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1321 am_post_op_attr *post_op_file;
1322 am_node *mp;
1323
1324 if (amuDebug(D_TRACE))
1325 plog(XLOG_DEBUG, "read_3:");
1326
1327 post_op_file = &result.res_u.fail.file_attributes;
1328 result.status = nfs_error(EACCES);
1329
1330 mp = fh3_to_mp(file);
1331 if (!mp)
1332 post_op_file->attributes_follow = 0;
1333 else {
1334 nfsfattr *fattr = &mp->am_fattr;
1335 am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes;
1336 post_op_file->attributes_follow = 1;
1337 fattr_to_fattr3(fattr, fattr3);
1338 }
1339
1340 return &result;
1341 }
1342
1343 am_WRITE3res *
am_nfs3_write_3_svc(am_WRITE3args * argp,struct svc_req * rqstp)1344 am_nfs3_write_3_svc(am_WRITE3args *argp, struct svc_req *rqstp)
1345 {
1346 static am_WRITE3res result;
1347
1348 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1349 am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before;
1350 am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after;
1351
1352 if (amuDebug(D_TRACE))
1353 plog(XLOG_DEBUG, "write_3:");
1354
1355 result.status = return_estale_or_rofs(file, pre_op_file, post_op_file);
1356
1357 return &result;
1358 }
1359
1360 am_CREATE3res *
am_nfs3_create_3_svc(am_CREATE3args * argp,struct svc_req * rqstp)1361 am_nfs3_create_3_svc(am_CREATE3args *argp, struct svc_req *rqstp)
1362 {
1363 static am_CREATE3res result;
1364
1365 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1366 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1367 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1368
1369 if (amuDebug(D_TRACE))
1370 plog(XLOG_DEBUG, "create_3:");
1371
1372 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1373
1374 return &result;
1375 }
1376
1377 am_MKDIR3res *
am_nfs3_mkdir_3_svc(am_MKDIR3args * argp,struct svc_req * rqstp)1378 am_nfs3_mkdir_3_svc(am_MKDIR3args *argp, struct svc_req *rqstp)
1379 {
1380 static am_MKDIR3res result;
1381
1382 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1383 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1384 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1385
1386 if (amuDebug(D_TRACE))
1387 plog(XLOG_DEBUG, "mkdir_3:");
1388
1389 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1390
1391 return &result;
1392 }
1393
1394 am_SYMLINK3res *
am_nfs3_symlink_3_svc(am_SYMLINK3args * argp,struct svc_req * rqstp)1395 am_nfs3_symlink_3_svc(am_SYMLINK3args *argp, struct svc_req *rqstp)
1396 {
1397 static am_SYMLINK3res result;
1398
1399 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1400 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1401 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1402
1403 if (amuDebug(D_TRACE))
1404 plog(XLOG_DEBUG, "symlink_3:");
1405
1406 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1407
1408 return &result;
1409 }
1410
1411 am_MKNOD3res *
am_nfs3_mknod_3_svc(am_MKNOD3args * argp,struct svc_req * rqstp)1412 am_nfs3_mknod_3_svc(am_MKNOD3args *argp, struct svc_req *rqstp)
1413 {
1414 static am_MKNOD3res result;
1415
1416 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->where.dir;
1417 am_pre_op_attr *pre_op_dir = &result.res_u.fail.dir_wcc.before;
1418 am_post_op_attr *post_op_dir = &result.res_u.fail.dir_wcc.after;
1419
1420 if (amuDebug(D_TRACE))
1421 plog(XLOG_DEBUG, "mknod_3:");
1422
1423 result.status = return_estale_or_rofs(dir, pre_op_dir, post_op_dir);
1424 return &result;
1425 }
1426
1427 am_REMOVE3res *
am_nfs3_remove_3_svc(am_REMOVE3args * argp,struct svc_req * rqstp)1428 am_nfs3_remove_3_svc(am_REMOVE3args *argp, struct svc_req *rqstp)
1429 {
1430 static am_REMOVE3res result;
1431
1432 am_diropargs3 *obj = &argp->object;
1433 am_wcc_data dir_wcc;
1434
1435 if (amuDebug(D_TRACE))
1436 plog(XLOG_DEBUG, "remove_3:");
1437
1438 result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE);
1439
1440 result.res_u.ok.dir_wcc = dir_wcc;
1441
1442 return &result;
1443 }
1444
1445 am_RMDIR3res *
am_nfs3_rmdir_3_svc(am_RMDIR3args * argp,struct svc_req * rqstp)1446 am_nfs3_rmdir_3_svc(am_RMDIR3args *argp, struct svc_req *rqstp)
1447 {
1448 static am_RMDIR3res result;
1449
1450 am_diropargs3 *obj = &argp->object;
1451 am_wcc_data dir_wcc;
1452
1453 if (amuDebug(D_TRACE))
1454 plog(XLOG_DEBUG, "rmdir_3:");
1455
1456 result.status = unlink3_or_rmdir3(obj, &dir_wcc, TRUE);
1457
1458 result.res_u.ok.dir_wcc = dir_wcc;
1459
1460 return &result;
1461 }
1462
1463 am_RENAME3res *
am_nfs3_rename_3_svc(am_RENAME3args * argp,struct svc_req * rqstp)1464 am_nfs3_rename_3_svc(am_RENAME3args *argp, struct svc_req *rqstp)
1465 {
1466 static am_RENAME3res result;
1467
1468 am_nfs_fh3 *fromdir = (am_nfs_fh3 *) &argp->from.dir;
1469 am_nfs_fh3 *todir = (am_nfs_fh3 *) &argp->to.dir;
1470 am_filename3 name = argp->to.name;
1471 am_node *to_mp, *from_mp;
1472
1473 if (amuDebug(D_TRACE))
1474 plog(XLOG_DEBUG, "rename_3:");
1475
1476 if (!(from_mp = fh3_to_mp(fromdir)) || !(to_mp = fh3_to_mp(todir)))
1477 result.status = nfs_error(ESTALE);
1478 /*
1479 * If the kernel is doing clever things with referenced files
1480 * then let it pretend...
1481 */
1482 else {
1483 am_wcc_attr *wcc_attr;
1484 am_fattr3 *fattr3;
1485 am_wcc_data *to_wcc_data, *from_wcc_data;
1486 am_pre_op_attr *pre_op_to, *pre_op_from;
1487 am_post_op_attr *post_op_to, *post_op_from;
1488 nfsfattr *fattr;
1489
1490 to_wcc_data = &result.res_u.ok.todir_wcc;
1491
1492 pre_op_to = &to_wcc_data->before;
1493 post_op_to = &to_wcc_data->after;
1494
1495 pre_op_to->attributes_follow = 1;
1496 fattr = &to_mp->am_fattr;
1497 wcc_attr = &pre_op_to->am_pre_op_attr_u.attributes;
1498 fattr_to_wcc_attr(fattr, wcc_attr);
1499 post_op_to->attributes_follow = 1;
1500 fattr3 = &post_op_to->am_post_op_attr_u.attributes;
1501 fattr_to_fattr3(fattr, fattr3);
1502
1503 from_wcc_data = &result.res_u.ok.fromdir_wcc;
1504
1505 pre_op_from = &from_wcc_data->before;
1506 post_op_from = &from_wcc_data->after;
1507
1508 pre_op_from->attributes_follow = 1;
1509 fattr = &from_mp->am_fattr;
1510 wcc_attr = &pre_op_from->am_pre_op_attr_u.attributes;
1511 fattr_to_wcc_attr(fattr, wcc_attr);
1512 post_op_from->attributes_follow = 1;
1513 fattr3 = &post_op_from->am_post_op_attr_u.attributes;
1514 fattr_to_fattr3(fattr, fattr3);
1515
1516 if (NSTREQ(name, ".nfs", 4))
1517 result.status = AM_NFS3_OK;
1518 /*
1519 * otherwise a failure
1520 */
1521 else
1522 result.status = nfs_error(EROFS);
1523 }
1524
1525 return &result;
1526 }
1527
1528 am_LINK3res *
am_nfs3_link_3_svc(am_LINK3args * argp,struct svc_req * rqstp)1529 am_nfs3_link_3_svc(am_LINK3args *argp, struct svc_req *rqstp)
1530 {
1531 static am_LINK3res result;
1532
1533 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1534 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->link.dir;
1535 am_post_op_attr *post_op_file;
1536 am_pre_op_attr *pre_op_dir;
1537 am_post_op_attr *post_op_dir;
1538 am_node *mp_file, *mp_dir;
1539
1540 if (amuDebug(D_TRACE))
1541 plog(XLOG_DEBUG, "link_3:");
1542
1543 post_op_file = &result.res_u.fail.file_attributes;
1544 post_op_file->attributes_follow = 0;
1545
1546 mp_file = fh3_to_mp(file);
1547 if (mp_file) {
1548 nfsfattr *fattr = &mp_file->am_fattr;
1549 am_fattr3 *fattr3 = &post_op_file->am_post_op_attr_u.attributes;
1550 fattr_to_fattr3(fattr, fattr3);
1551 }
1552
1553 pre_op_dir = &result.res_u.fail.linkdir_wcc.before;
1554 pre_op_dir->attributes_follow = 0;
1555 post_op_dir = &result.res_u.fail.linkdir_wcc.after;
1556 post_op_dir->attributes_follow = 0;
1557
1558 mp_dir = fh3_to_mp(dir);
1559 if (mp_dir) {
1560 nfsfattr *fattr = &mp_dir->am_fattr;
1561 am_fattr3 *fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1562 am_wcc_attr *wcc_attr = &pre_op_dir->am_pre_op_attr_u.attributes;
1563
1564 pre_op_dir->attributes_follow = 1;
1565 fattr_to_wcc_attr(fattr, wcc_attr);
1566 post_op_dir->attributes_follow = 1;
1567 fattr_to_fattr3(fattr, fattr3);
1568 }
1569
1570 if (!mp_file || !mp_dir)
1571 result.status = nfs_error(ESTALE);
1572 else
1573 result.status = nfs_error(EROFS);
1574
1575 return &result;
1576 }
1577
1578 am_READDIR3res *
am_nfs3_readdir_3_svc(am_READDIR3args * argp,struct svc_req * rqstp)1579 am_nfs3_readdir_3_svc(am_READDIR3args *argp, struct svc_req *rqstp)
1580 {
1581 static am_READDIR3res result;
1582 static am_entry3 entries[MAX_READDIR_ENTRIES];
1583 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir;
1584 am_cookie3 cookie = argp->cookie;
1585 am_cookieverf3 cookieverf;
1586 am_count3 count = argp->count;
1587 am_post_op_attr *post_op_dir;
1588 am_node *mp;
1589 int retry;
1590
1591 if (amuDebug(D_TRACE))
1592 plog(XLOG_DEBUG, "readdir_3:");
1593
1594 memcpy(&cookieverf, &argp->cookieverf, sizeof(am_cookieverf3));
1595
1596 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1597 if (mp == NULL) {
1598 if (retry < 0) {
1599 amd_stats.d_drops++;
1600 return 0;
1601 }
1602 post_op_dir = &result.res_u.fail.dir_attributes;
1603 post_op_dir->attributes_follow = 0;
1604 result.status = nfs_error(retry);
1605 } else {
1606 am_dirlist3 *list = &result.res_u.ok.reply;
1607 am_nfsstat3 status;
1608
1609 if (amuDebug(D_TRACE))
1610 plog(XLOG_DEBUG, "\treaddir_3(%s)", mp->am_path);
1611
1612 status = mp->am_al->al_mnt->mf_ops->readdir(mp,
1613 (voidp)&cookie, list, entries, count);
1614 if (status == 0) {
1615 post_op_dir = &result.res_u.ok.dir_attributes;
1616 nfsfattr *fattr;
1617 am_fattr3 *fattr3;
1618
1619 fattr = &mp->am_fattr;
1620 fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1621 post_op_dir->attributes_follow = 1;
1622 fattr_to_fattr3(fattr, fattr3);
1623 result.status = AM_NFS3_OK;
1624 } else {
1625 post_op_dir = &result.res_u.fail.dir_attributes;
1626 post_op_dir->attributes_follow = 0;
1627 result.status = nfs_error(status);
1628 }
1629
1630 mp->am_stats.s_readdir++;
1631 }
1632
1633 return &result;
1634 }
1635
1636 am_READDIRPLUS3res *
am_nfs3_readdirplus_3_svc(am_READDIRPLUS3args * argp,struct svc_req * rqstp)1637 am_nfs3_readdirplus_3_svc(am_READDIRPLUS3args *argp, struct svc_req *rqstp)
1638 {
1639 static am_READDIRPLUS3res result;
1640 am_nfs_fh3 *dir = (am_nfs_fh3 *) &argp->dir;
1641 am_post_op_attr *post_op_dir;
1642 nfsfattr *fattr;
1643 am_fattr3 *fattr3;
1644 am_node *mp;
1645 int retry;
1646
1647 mp = fh3_to_mp3(dir, &retry, VLOOK_CREATE);
1648 if (mp == NULL) {
1649 if (retry < 0) {
1650 amd_stats.d_drops++;
1651 return 0;
1652 }
1653 post_op_dir = &result.res_u.fail.dir_attributes;
1654 post_op_dir->attributes_follow = 0;
1655 result.status = nfs_error(retry);
1656 } else {
1657 post_op_dir = &result.res_u.ok.dir_attributes;
1658 fattr = &mp->am_fattr;
1659 fattr3 = &post_op_dir->am_post_op_attr_u.attributes;
1660 post_op_dir->attributes_follow = 1;
1661 fattr_to_fattr3(fattr, fattr3);
1662 result.status = AM_NFS3ERR_NOTSUPP;
1663 }
1664
1665 return &result;
1666 }
1667
1668 am_FSSTAT3res *
am_nfs3_fsstat_3_svc(am_FSSTAT3args * argp,struct svc_req * rqstp)1669 am_nfs3_fsstat_3_svc(am_FSSTAT3args *argp, struct svc_req *rqstp)
1670 {
1671 static am_FSSTAT3res result;
1672
1673 am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot;
1674 am_post_op_attr *post_op_fsroot;
1675 am_node *mp;
1676 int retry;
1677
1678 if (amuDebug(D_TRACE))
1679 plog(XLOG_DEBUG, "fsstat_3:");
1680
1681 mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE);
1682 if (!mp) {
1683 if (retry < 0) {
1684 amd_stats.d_drops++;
1685 return 0;
1686 }
1687 post_op_fsroot = &result.res_u.fail.obj_attributes;
1688 post_op_fsroot->attributes_follow = 0;
1689 result.status = nfs_error(retry);
1690 } else {
1691 am_FSSTAT3resok *ok = &result.res_u.ok;
1692 u_int blocks, bfree, bavail;
1693 nfsfattr *fattr;
1694 am_fattr3 *fattr3;
1695 mntent_t mnt;
1696
1697 if (amuDebug(D_TRACE))
1698 plog(XLOG_DEBUG, "\tfsstat_3(%s)", mp->am_path);
1699
1700 fattr = &mp->am_fattr;
1701 post_op_fsroot = &ok->obj_attributes;
1702 post_op_fsroot->attributes_follow = 1;
1703 fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes;
1704 fattr_to_fattr3(fattr, fattr3);
1705
1706 /*
1707 * just return faked up file system information
1708 */
1709 ok->tbytes = 1024;
1710 ok->invarsec = 0;
1711
1712 /* check if map is browsable and show_statfs_entries=yes */
1713 if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
1714 mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
1715 mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
1716 blocks = 0;
1717 bfree = 0;
1718 bavail = 0;
1719 if (amu_hasmntopt(&mnt, "browsable")) {
1720 count_map_entries(mp, &blocks, &bfree, &bavail);
1721 }
1722 ok->fbytes = bfree;
1723 ok->abytes = bavail;
1724 ok->ffiles = bfree;
1725 ok->afiles = bavail;
1726 ok->tfiles = blocks;
1727 } else {
1728 ok->fbytes = 0;
1729 ok->abytes = 0;
1730 ok->ffiles = 0;
1731 ok->afiles = 0;
1732 ok->tfiles = 0; /* set to 1 if you don't want empty automounts */
1733 }
1734
1735 result.status = AM_NFS3_OK;
1736 mp->am_stats.s_statfs++;
1737 }
1738
1739 return &result;
1740 }
1741
1742 #define FSF3_HOMOGENEOUS 0x0008
1743
1744 am_FSINFO3res *
am_nfs3_fsinfo_3_svc(am_FSINFO3args * argp,struct svc_req * rqstp)1745 am_nfs3_fsinfo_3_svc(am_FSINFO3args *argp, struct svc_req *rqstp)
1746 {
1747 static am_FSINFO3res result;
1748
1749 am_nfs_fh3 *fsroot = (am_nfs_fh3 *) &argp->fsroot;
1750 am_post_op_attr *post_op_fsroot;
1751 am_node *mp;
1752 int retry;
1753
1754 if (amuDebug(D_TRACE))
1755 plog(XLOG_DEBUG, "fsinfo_3:");
1756
1757 mp = fh3_to_mp3(fsroot, &retry, VLOOK_CREATE);
1758 if (!mp) {
1759 if (retry < 0) {
1760 amd_stats.d_drops++;
1761 return 0;
1762 }
1763 post_op_fsroot = &result.res_u.fail.obj_attributes;
1764 post_op_fsroot->attributes_follow = 0;
1765 result.status = nfs_error(retry);
1766 } else {
1767 am_FSINFO3resok *ok = &result.res_u.ok;
1768 nfsfattr *fattr;
1769 am_fattr3 *fattr3;
1770
1771 if (amuDebug(D_TRACE))
1772 plog(XLOG_DEBUG, "\tfsinfo_3(%s)", mp->am_path);
1773
1774 fattr = &mp->am_fattr;
1775 post_op_fsroot = &ok->obj_attributes;
1776 post_op_fsroot->attributes_follow = 1;
1777 fattr3 = &post_op_fsroot->am_post_op_attr_u.attributes;
1778 fattr_to_fattr3(fattr, fattr3);
1779
1780 /*
1781 * just return faked up file system information
1782 */
1783 ok->rtmax = 0;
1784 ok->rtpref = 0;
1785 ok->rtmult = 0;
1786 ok->wtmax = 0;
1787 ok->wtpref = 0;
1788 ok->wtmult = 0;
1789 ok->dtpref = 1024;
1790 ok->maxfilesize = 0;
1791 ok->time_delta.seconds = 1;
1792 ok->time_delta.nseconds = 0;
1793 ok->properties = FSF3_HOMOGENEOUS;
1794
1795 result.status = AM_NFS3_OK;
1796 mp->am_stats.s_fsinfo++;
1797 }
1798
1799 return &result;
1800 }
1801
1802 am_PATHCONF3res *
am_nfs3_pathconf_3_svc(am_PATHCONF3args * argp,struct svc_req * rqstp)1803 am_nfs3_pathconf_3_svc(am_PATHCONF3args *argp, struct svc_req *rqstp)
1804 {
1805 static am_PATHCONF3res result;
1806
1807 am_nfs_fh3 *obj = (am_nfs_fh3 *) &argp->object;
1808 am_post_op_attr *post_op_obj;
1809 am_node *mp;
1810 int retry;
1811
1812 if (amuDebug(D_TRACE))
1813 plog(XLOG_DEBUG, "pathconf_3:");
1814
1815 mp = fh3_to_mp3(obj, &retry, VLOOK_CREATE);
1816 if (!mp) {
1817 if (retry < 0) {
1818 amd_stats.d_drops++;
1819 return 0;
1820 }
1821 post_op_obj = &result.res_u.fail.obj_attributes;
1822 post_op_obj->attributes_follow = 0;
1823 result.status = nfs_error(retry);
1824 } else {
1825 am_PATHCONF3resok *ok = &result.res_u.ok;
1826 nfsfattr *fattr;
1827 am_fattr3 *fattr3;
1828
1829 if (amuDebug(D_TRACE))
1830 plog(XLOG_DEBUG, "\tpathconf_3(%s)", mp->am_path);
1831
1832 fattr = &mp->am_fattr;
1833 post_op_obj = &ok->obj_attributes;
1834 post_op_obj->attributes_follow = 1;
1835 fattr3 = &post_op_obj->am_post_op_attr_u.attributes;
1836 fattr_to_fattr3(fattr, fattr3);
1837
1838 ok->linkmax = 0;
1839 ok->name_max = NAME_MAX;
1840 ok->no_trunc = 1;
1841 ok->chown_restricted = 1;
1842 ok->case_insensitive = 0;
1843 ok->case_preserving = 1;
1844
1845 result.status = AM_NFS3_OK;
1846 mp->am_stats.s_pathconf++;
1847 }
1848
1849 return &result;
1850 }
1851
1852 am_COMMIT3res *
am_nfs3_commit_3_svc(am_COMMIT3args * argp,struct svc_req * rqstp)1853 am_nfs3_commit_3_svc(am_COMMIT3args *argp, struct svc_req *rqstp)
1854 {
1855 static am_COMMIT3res result;
1856
1857 am_nfs_fh3 *file = (am_nfs_fh3 *) &argp->file;
1858 am_pre_op_attr *pre_op_file = &result.res_u.fail.file_wcc.before;
1859 am_post_op_attr *post_op_file = &result.res_u.fail.file_wcc.after;
1860
1861 if (amuDebug(D_TRACE))
1862 plog(XLOG_DEBUG, "commit_3:");
1863
1864 result.status = return_estale_or_rofs(file, pre_op_file, post_op_file);
1865
1866 return &result;
1867 }
1868 #endif /* HAVE_FS_NFS3 */
1869