]> git.ipfire.org Git - thirdparty/util-linux.git/blob - mount/nfsmount.c
Imported from util-linux-2.9i tarball.
[thirdparty/util-linux.git] / mount / nfsmount.c
1 /*
2 * nfsmount.c -- Linux NFS mount
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
16 * numbers to be specified on the command line.
17 *
18 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
19 * Omit the call to connect() for Linux version 1.3.11 or later.
20 *
21 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
22 * Implemented the "bg", "fg" and "retry" mount options for NFS.
23 */
24
25 /*
26 * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
27 */
28
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <netdb.h>
34 #include <rpc/rpc.h>
35 #include <rpc/pmap_prot.h>
36 #include <rpc/pmap_clnt.h>
37 #include <sys/socket.h>
38 #include <sys/time.h>
39 #include <sys/utsname.h>
40 #include <sys/stat.h>
41 #include <arpa/inet.h>
42
43 #include "sundries.h"
44 #include "nfsmount.h"
45
46 #include <linux/nfs.h>
47 #include "mount_constants.h"
48 #include "nfs_mount3.h"
49
50 static char *nfs_strerror(int stat);
51
52 #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
53
54 static int
55 linux_version_code(void) {
56 struct utsname my_utsname;
57 int p, q, r;
58
59 if (uname(&my_utsname) == 0) {
60 p = atoi(strtok(my_utsname.release, "."));
61 q = atoi(strtok(NULL, "."));
62 r = atoi(strtok(NULL, "."));
63 return MAKE_VERSION(p,q,r);
64 }
65 return 0;
66 }
67
68 /*
69 * nfs_mount_version according to the kernel sources seen at compile time.
70 */
71 static int nfs_mount_version = KERNEL_NFS_MOUNT_VERSION;
72
73 /*
74 * Unfortunately, the kernel prints annoying console messages
75 * in case of an unexpected nfs mount version (instead of
76 * just returning some error). Therefore we'll have to try
77 * and figure out what version the kernel expects.
78 *
79 * Variables:
80 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
81 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
82 * nfs_mount_version: version this source and running kernel can handle
83 */
84 static void
85 find_kernel_nfs_mount_version(void) {
86 int kernel_version = linux_version_code();
87
88 if (kernel_version) {
89 if (kernel_version < MAKE_VERSION(2,1,32))
90 nfs_mount_version = 1;
91 else
92 nfs_mount_version = 3;
93 }
94 if (nfs_mount_version > NFS_MOUNT_VERSION)
95 nfs_mount_version = NFS_MOUNT_VERSION;
96 }
97
98 int nfsmount(const char *spec, const char *node, int *flags,
99 char **extra_opts, char **mount_opts, int running_bg)
100 {
101 static char *prev_bg_host;
102 char hostdir[1024];
103 CLIENT *mclient;
104 char *hostname;
105 char *dirname;
106 char *old_opts;
107 char *mounthost=NULL;
108 char new_opts[1024];
109 fhandle root_fhandle;
110 struct timeval total_timeout;
111 enum clnt_stat clnt_stat;
112 static struct nfs_mount_data data;
113 char *opt, *opteq;
114 int val;
115 struct hostent *hp;
116 struct sockaddr_in server_addr;
117 struct sockaddr_in mount_server_addr;
118 int msock, fsock;
119 struct timeval retry_timeout;
120 struct fhstatus status;
121 struct stat statbuf;
122 char *s;
123 int port;
124 int mountport;
125 int bg;
126 int soft;
127 int intr;
128 int posix;
129 int nocto;
130 int noac;
131 int nolock;
132 int retry;
133 int tcp;
134 int mountprog;
135 int mountvers;
136 int nfsprog;
137 int nfsvers;
138 int retval;
139 time_t t;
140 time_t prevt;
141 time_t timeout;
142
143 find_kernel_nfs_mount_version();
144
145 retval = EX_FAIL;
146 msock = fsock = -1;
147 mclient = NULL;
148 if (strlen(spec) >= sizeof(hostdir)) {
149 fprintf(stderr, "mount: "
150 "excessively long host:dir argument\n");
151 goto fail;
152 }
153 strcpy(hostdir, spec);
154 if ((s = strchr(hostdir, ':'))) {
155 hostname = hostdir;
156 dirname = s + 1;
157 *s = '\0';
158 /* Ignore all but first hostname in replicated mounts
159 until they can be fully supported. (mack@sgi.com) */
160 if ((s = strchr(hostdir, ','))) {
161 *s = '\0';
162 fprintf(stderr, "mount: warning: "
163 "multiple hostnames not supported\n");
164 }
165 } else {
166 fprintf(stderr, "mount: "
167 "directory to mount not in host:dir format\n");
168 goto fail;
169 }
170
171 server_addr.sin_family = AF_INET;
172 #if 1 /* old libc's do not have inet_aton() -- change 1 to 0 */
173 if (!inet_aton(hostname, &server_addr.sin_addr))
174 #endif
175 {
176 if ((hp = gethostbyname(hostname)) == NULL) {
177 fprintf(stderr, "mount: can't get address for %s\n",
178 hostname);
179 goto fail;
180 } else {
181 if (hp->h_length > sizeof(struct in_addr)) {
182 fprintf(stderr,
183 "mount: got bad hp->h_length\n");
184 hp->h_length = sizeof(struct in_addr);
185 }
186 memcpy(&server_addr.sin_addr,
187 hp->h_addr, hp->h_length);
188 }
189 }
190
191 memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
192
193 /* add IP address to mtab options for use when unmounting */
194
195 s = inet_ntoa(server_addr.sin_addr);
196 old_opts = *extra_opts;
197 if (!old_opts)
198 old_opts = "";
199 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
200 fprintf(stderr, "mount: "
201 "excessively long option argument\n");
202 goto fail;
203 }
204 sprintf(new_opts, "%s%saddr=%s",
205 old_opts, *old_opts ? "," : "", s);
206 *extra_opts = xstrdup(new_opts);
207
208 /* Set default options.
209 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
210 * let the kernel decide.
211 * timeo is filled in after we know whether it'll be TCP or UDP. */
212 memset(&data, 0, sizeof(data));
213 data.retrans = 3;
214 data.acregmin = 3;
215 data.acregmax = 60;
216 data.acdirmin = 30;
217 data.acdirmax = 60;
218 #if NFS_MOUNT_VERSION >= 2
219 data.namlen = NAME_MAX;
220 #endif
221
222 bg = 0;
223 soft = 0;
224 intr = 0;
225 posix = 0;
226 nocto = 0;
227 nolock = 0;
228 noac = 0;
229 retry = 10000; /* 10000 minutes ~ 1 week */
230 tcp = 0;
231
232 mountprog = MOUNTPROG;
233 mountvers = MOUNTVERS;
234 port = 0;
235 mountport = 0;
236 nfsprog = NFS_PROGRAM;
237 nfsvers = NFS_VERSION;
238
239 /* parse options */
240
241 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
242 if ((opteq = strchr(opt, '='))) {
243 val = atoi(opteq + 1);
244 *opteq = '\0';
245 if (!strcmp(opt, "rsize"))
246 data.rsize = val;
247 else if (!strcmp(opt, "wsize"))
248 data.wsize = val;
249 else if (!strcmp(opt, "timeo"))
250 data.timeo = val;
251 else if (!strcmp(opt, "retrans"))
252 data.retrans = val;
253 else if (!strcmp(opt, "acregmin"))
254 data.acregmin = val;
255 else if (!strcmp(opt, "acregmax"))
256 data.acregmax = val;
257 else if (!strcmp(opt, "acdirmin"))
258 data.acdirmin = val;
259 else if (!strcmp(opt, "acdirmax"))
260 data.acdirmax = val;
261 else if (!strcmp(opt, "actimeo")) {
262 data.acregmin = val;
263 data.acregmax = val;
264 data.acdirmin = val;
265 data.acdirmax = val;
266 }
267 else if (!strcmp(opt, "retry"))
268 retry = val;
269 else if (!strcmp(opt, "port"))
270 port = val;
271 else if (!strcmp(opt, "mountport"))
272 mountport = val;
273 else if (!strcmp(opt, "mounthost"))
274 mounthost=xstrndup(opteq+1,
275 strcspn(opteq+1," \t\n\r,"));
276 else if (!strcmp(opt, "mountprog"))
277 mountprog = val;
278 else if (!strcmp(opt, "mountvers"))
279 mountvers = val;
280 else if (!strcmp(opt, "nfsprog"))
281 nfsprog = val;
282 else if (!strcmp(opt, "nfsvers") ||
283 !strcmp(opt, "vers"))
284 nfsvers = val;
285 else if (!strcmp(opt, "proto")) {
286 if (!strncmp(opteq+1, "tcp", 3))
287 tcp = 1;
288 else if (!strncmp(opteq+1, "udp", 3))
289 tcp = 0;
290 else
291 printf("Warning: Unrecognized proto= option.\n");
292 } else if (!strcmp(opt, "namlen")) {
293 #if NFS_MOUNT_VERSION >= 2
294 if (nfs_mount_version >= 2)
295 data.namlen = val;
296 else
297 #endif
298 printf("Warning: Option namlen is not supported.\n");
299 } else if (!strcmp(opt, "addr"))
300 /* ignore */;
301 else {
302 printf("unknown nfs mount parameter: "
303 "%s=%d\n", opt, val);
304 goto fail;
305 }
306 }
307 else {
308 val = 1;
309 if (!strncmp(opt, "no", 2)) {
310 val = 0;
311 opt += 2;
312 }
313 if (!strcmp(opt, "bg"))
314 bg = val;
315 else if (!strcmp(opt, "fg"))
316 bg = !val;
317 else if (!strcmp(opt, "soft"))
318 soft = val;
319 else if (!strcmp(opt, "hard"))
320 soft = !val;
321 else if (!strcmp(opt, "intr"))
322 intr = val;
323 else if (!strcmp(opt, "posix"))
324 posix = val;
325 else if (!strcmp(opt, "cto"))
326 nocto = !val;
327 else if (!strcmp(opt, "ac"))
328 noac = !val;
329 else if (!strcmp(opt, "tcp"))
330 tcp = val;
331 else if (!strcmp(opt, "udp"))
332 tcp = !val;
333 else if (!strcmp(opt, "lock")) {
334 if (nfs_mount_version >= 3)
335 nolock = !val;
336 else
337 printf("Warning: option nolock is not supported.\n");
338 } else {
339 if (!sloppy) {
340 printf("unknown nfs mount option: "
341 "%s%s\n", val ? "" : "no", opt);
342 goto fail;
343 }
344 }
345 }
346 }
347 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
348 | (intr ? NFS_MOUNT_INTR : 0)
349 | (posix ? NFS_MOUNT_POSIX : 0)
350 | (nocto ? NFS_MOUNT_NOCTO : 0)
351 | (noac ? NFS_MOUNT_NOAC : 0);
352 #if NFS_MOUNT_VERSION >= 2
353 if (nfs_mount_version >= 2)
354 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
355 #endif
356 #if NFS_MOUNT_VERSION >= 3
357 if (nfs_mount_version >= 3)
358 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
359 #endif
360
361 /* Adjust options if none specified */
362 if (!data.timeo)
363 data.timeo = tcp ? 70 : 7;
364
365 #ifdef NFS_MOUNT_DEBUG
366 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
367 data.rsize, data.wsize, data.timeo, data.retrans);
368 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
369 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
370 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
371 port, bg, retry, data.flags);
372 printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
373 mountprog, mountvers, nfsprog, nfsvers);
374 printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
375 (data.flags & NFS_MOUNT_SOFT) != 0,
376 (data.flags & NFS_MOUNT_INTR) != 0,
377 (data.flags & NFS_MOUNT_POSIX) != 0,
378 (data.flags & NFS_MOUNT_NOCTO) != 0,
379 (data.flags & NFS_MOUNT_NOAC) != 0);
380 #if NFS_MOUNT_VERSION >= 2
381 printf("tcp = %d\n",
382 (data.flags & NFS_MOUNT_TCP) != 0);
383 #endif
384 #endif
385
386 data.version = nfs_mount_version;
387 *mount_opts = (char *) &data;
388
389 if (*flags & MS_REMOUNT)
390 return 0;
391
392 /*
393 * If the previous mount operation on the same host was
394 * backgrounded, and the "bg" for this mount is also set,
395 * give up immediately, to avoid the initial timeout.
396 */
397 if (bg && !running_bg &&
398 prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
399 if (retry > 0)
400 retval = EX_BG;
401 return retval;
402 }
403
404 /* create mount deamon client */
405 /* See if the nfs host = mount host. */
406 if (mounthost) {
407 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
408 mount_server_addr.sin_family = AF_INET;
409 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
410 } else {
411 if ((hp = gethostbyname(mounthost)) == NULL) {
412 fprintf(stderr, "mount: can't get address for %s\n",
413 hostname);
414 goto fail;
415 } else {
416 if (hp->h_length > sizeof(struct in_addr)) {
417 fprintf(stderr,
418 "mount: got bad hp->h_length?\n");
419 hp->h_length = sizeof(struct in_addr);
420 }
421 mount_server_addr.sin_family = AF_INET;
422 memcpy(&mount_server_addr.sin_addr,
423 hp->h_addr, hp->h_length);
424 }
425 }
426 }
427
428 /*
429 * The following loop implements the mount retries. On the first
430 * call, "running_bg" is 0. When the mount times out, and the
431 * "bg" option is set, the exit status EX_BG will be returned.
432 * For a backgrounded mount, there will be a second call by the
433 * child process with "running_bg" set to 1.
434 *
435 * The case where the mount point is not present and the "bg"
436 * option is set, is treated as a timeout. This is done to
437 * support nested mounts.
438 *
439 * The "retry" count specified by the user is the number of
440 * minutes to retry before giving up.
441 *
442 * Only the first error message will be displayed.
443 */
444 retry_timeout.tv_sec = 3;
445 retry_timeout.tv_usec = 0;
446 total_timeout.tv_sec = 20;
447 total_timeout.tv_usec = 0;
448 timeout = time(NULL) + 60 * retry;
449 prevt = 0;
450 t = 30;
451 val = 1;
452 for (;;) {
453 if (bg && stat(node, &statbuf) == -1) {
454 if (running_bg) {
455 sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
456 val *= 2;
457 if (val > 30)
458 val = 30;
459 }
460 } else {
461 /* be careful not to use too many CPU cycles */
462 if (t - prevt < 30)
463 sleep(30);
464
465 /* contact the mount daemon via TCP */
466 mount_server_addr.sin_port = htons(mountport);
467 msock = RPC_ANYSOCK;
468 mclient = clnttcp_create(&mount_server_addr,
469 mountprog, mountvers,
470 &msock, 0, 0);
471
472 /* if this fails, contact the mount daemon via UDP */
473 if (!mclient) {
474 mount_server_addr.sin_port = htons(mountport);
475 msock = RPC_ANYSOCK;
476 mclient = clntudp_create(&mount_server_addr,
477 mountprog, mountvers,
478 retry_timeout, &msock);
479 }
480 if (mclient) {
481 /* try to mount hostname:dirname */
482 mclient->cl_auth = authunix_create_default();
483 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
484 (xdrproc_t) xdr_dirpath, (caddr_t) &dirname,
485 (xdrproc_t) xdr_fhstatus, (caddr_t) &status,
486 total_timeout);
487 if (clnt_stat == RPC_SUCCESS)
488 break; /* we're done */
489 if (errno != ECONNREFUSED) {
490 clnt_perror(mclient, "mount");
491 goto fail; /* don't retry */
492 }
493 if (!running_bg && prevt == 0)
494 clnt_perror(mclient, "mount");
495 auth_destroy(mclient->cl_auth);
496 clnt_destroy(mclient);
497 mclient = 0;
498 close(msock);
499 } else {
500 if (!running_bg && prevt == 0)
501 clnt_pcreateerror("mount");
502 }
503 prevt = t;
504 }
505 if (!bg)
506 goto fail;
507 if (!running_bg) {
508 prev_bg_host = xstrdup(hostname);
509 if (retry > 0)
510 retval = EX_BG;
511 goto fail;
512 }
513 t = time(NULL);
514 if (t >= timeout)
515 goto fail;
516 }
517
518 if (status.fhs_status != 0) {
519 fprintf(stderr,
520 "mount: %s:%s failed, reason given by server: %s\n",
521 hostname, dirname, nfs_strerror(status.fhs_status));
522 goto fail;
523 }
524 memcpy((char *) &root_fhandle, (char *) status.fhstatus_u.fhs_fhandle,
525 sizeof (root_fhandle));
526
527 /* create nfs socket for kernel */
528
529 if (tcp) {
530 if (nfs_mount_version < 3) {
531 printf("NFS over TCP is not supported.\n");
532 goto fail;
533 }
534 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
535 } else
536 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
537 if (fsock < 0) {
538 perror("nfs socket");
539 goto fail;
540 }
541 if (bindresvport(fsock, 0) < 0) {
542 perror("nfs bindresvport");
543 goto fail;
544 }
545 if (port == 0) {
546 server_addr.sin_port = PMAPPORT;
547 port = pmap_getport(&server_addr, nfsprog, nfsvers,
548 tcp ? IPPROTO_TCP : IPPROTO_UDP);
549 if (port == 0)
550 port = NFS_PORT;
551 #ifdef NFS_MOUNT_DEBUG
552 else
553 printf("used portmapper to find NFS port\n");
554 #endif
555 }
556 #ifdef NFS_MOUNT_DEBUG
557 printf("using port %d for nfs deamon\n", port);
558 #endif
559 server_addr.sin_port = htons(port);
560 /*
561 * connect() the socket for kernels 1.3.10 and below only,
562 * to avoid problems with multihomed hosts.
563 * --Swen
564 */
565 if (linux_version_code() <= 66314
566 && connect(fsock, (struct sockaddr *) &server_addr,
567 sizeof (server_addr)) < 0) {
568 perror("nfs connect");
569 goto fail;
570 }
571
572 /* prepare data structure for kernel */
573
574 data.fd = fsock;
575 memcpy((char *) &data.root, (char *) &root_fhandle,
576 sizeof (root_fhandle));
577 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
578 strncpy(data.hostname, hostname, sizeof(data.hostname));
579
580 /* clean up */
581
582 auth_destroy(mclient->cl_auth);
583 clnt_destroy(mclient);
584 close(msock);
585 return 0;
586
587 /* abort */
588
589 fail:
590 if (msock != -1) {
591 if (mclient) {
592 auth_destroy(mclient->cl_auth);
593 clnt_destroy(mclient);
594 }
595 close(msock);
596 }
597 if (fsock != -1)
598 close(fsock);
599 return retval;
600 }
601
602 /*
603 * We need to translate between nfs status return values and
604 * the local errno values which may not be the same.
605 *
606 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
607 * "after #include <errno.h> the symbol errno is reserved for any use,
608 * it cannot even be used as a struct tag or field name".
609 */
610
611 #ifndef EDQUOT
612 #define EDQUOT ENOSPC
613 #endif
614
615 static struct {
616 enum nfs_stat stat;
617 int errnum;
618 } nfs_errtbl[] = {
619 { NFS_OK, 0 },
620 { NFSERR_PERM, EPERM },
621 { NFSERR_NOENT, ENOENT },
622 { NFSERR_IO, EIO },
623 { NFSERR_NXIO, ENXIO },
624 { NFSERR_ACCES, EACCES },
625 { NFSERR_EXIST, EEXIST },
626 { NFSERR_NODEV, ENODEV },
627 { NFSERR_NOTDIR, ENOTDIR },
628 { NFSERR_ISDIR, EISDIR },
629 #ifdef NFSERR_INVAL
630 { NFSERR_INVAL, EINVAL }, /* that Sun forgot */
631 #endif
632 { NFSERR_FBIG, EFBIG },
633 { NFSERR_NOSPC, ENOSPC },
634 { NFSERR_ROFS, EROFS },
635 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
636 { NFSERR_NOTEMPTY, ENOTEMPTY },
637 { NFSERR_DQUOT, EDQUOT },
638 { NFSERR_STALE, ESTALE },
639 #ifdef EWFLUSH
640 { NFSERR_WFLUSH, EWFLUSH },
641 #endif
642 /* Throw in some NFSv3 values for even more fun (HP returns these) */
643 { 71, EREMOTE },
644
645 { -1, EIO }
646 };
647
648 static char *nfs_strerror(int stat)
649 {
650 int i;
651 static char buf[256];
652
653 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
654 if (nfs_errtbl[i].stat == stat)
655 return strerror(nfs_errtbl[i].errnum);
656 }
657 sprintf(buf, "unknown nfs status return value: %d", stat);
658 return buf;
659 }
660
661 #if 0
662 int
663 my_getport(struct in_addr server, struct timeval *timeo, ...)
664 {
665 struct sockaddr_in sin;
666 struct pmap pmap;
667 CLIENT *clnt;
668 int sock = RPC_ANYSOCK, port;
669
670 pmap.pm_prog = prog;
671 pmap.pm_vers = vers;
672 pmap.pm_prot = prot;
673 pmap.pm_port = 0;
674 sin.sin_family = AF_INET;
675 sin.sin_addr = server;
676 sin.sin_port = htons(111);
677 clnt = clntudp_create(&sin, 100000, 2, *timeo, &sock);
678 status = clnt_call(clnt, PMAP_GETPORT,
679 &pmap, (xdrproc_t) xdr_pmap,
680 &port, (xdrproc_t) xdr_uint);
681 if (status != SUCCESS) {
682 /* natter */
683 port = 0;
684 }
685
686 clnt_destroy(clnt);
687 close(sock);
688 return port;
689 }
690 #endif