2 * nfsmount.c -- Linux NFS mount
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
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)
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.
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.
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.
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.
26 * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
35 #include <rpc/pmap_prot.h>
36 #include <rpc/pmap_clnt.h>
37 #include <sys/socket.h>
39 #include <sys/utsname.h>
41 #include <arpa/inet.h>
46 #include <linux/nfs.h>
47 #include "mount_constants.h"
48 #include "nfs_mount3.h"
50 static char *nfs_strerror(int stat
);
52 #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
55 linux_version_code(void) {
56 struct utsname my_utsname
;
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
);
69 * nfs_mount_version according to the kernel sources seen at compile time.
71 static int nfs_mount_version
= KERNEL_NFS_MOUNT_VERSION
;
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.
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
85 find_kernel_nfs_mount_version(void) {
86 int kernel_version
= linux_version_code();
89 if (kernel_version
< MAKE_VERSION(2,1,32))
90 nfs_mount_version
= 1;
92 nfs_mount_version
= 3;
94 if (nfs_mount_version
> NFS_MOUNT_VERSION
)
95 nfs_mount_version
= NFS_MOUNT_VERSION
;
98 int nfsmount(const char *spec
, const char *node
, int *flags
,
99 char **extra_opts
, char **mount_opts
, int running_bg
)
101 static char *prev_bg_host
;
107 char *mounthost
=NULL
;
109 fhandle root_fhandle
;
110 struct timeval total_timeout
;
111 enum clnt_stat clnt_stat
;
112 static struct nfs_mount_data data
;
116 struct sockaddr_in server_addr
;
117 struct sockaddr_in mount_server_addr
;
119 struct timeval retry_timeout
;
120 struct fhstatus status
;
143 find_kernel_nfs_mount_version();
148 if (strlen(spec
) >= sizeof(hostdir
)) {
149 fprintf(stderr
, "mount: "
150 "excessively long host:dir argument\n");
153 strcpy(hostdir
, spec
);
154 if ((s
= strchr(hostdir
, ':'))) {
158 /* Ignore all but first hostname in replicated mounts
159 until they can be fully supported. (mack@sgi.com) */
160 if ((s
= strchr(hostdir
, ','))) {
162 fprintf(stderr
, "mount: warning: "
163 "multiple hostnames not supported\n");
166 fprintf(stderr
, "mount: "
167 "directory to mount not in host:dir format\n");
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
))
176 if ((hp
= gethostbyname(hostname
)) == NULL
) {
177 fprintf(stderr
, "mount: can't get address for %s\n",
181 if (hp
->h_length
> sizeof(struct in_addr
)) {
183 "mount: got bad hp->h_length\n");
184 hp
->h_length
= sizeof(struct in_addr
);
186 memcpy(&server_addr
.sin_addr
,
187 hp
->h_addr
, hp
->h_length
);
191 memcpy (&mount_server_addr
, &server_addr
, sizeof (mount_server_addr
));
193 /* add IP address to mtab options for use when unmounting */
195 s
= inet_ntoa(server_addr
.sin_addr
);
196 old_opts
= *extra_opts
;
199 if (strlen(old_opts
) + strlen(s
) + 10 >= sizeof(new_opts
)) {
200 fprintf(stderr
, "mount: "
201 "excessively long option argument\n");
204 sprintf(new_opts
, "%s%saddr=%s",
205 old_opts
, *old_opts
? "," : "", s
);
206 *extra_opts
= xstrdup(new_opts
);
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
));
218 #if NFS_MOUNT_VERSION >= 2
219 data
.namlen
= NAME_MAX
;
229 retry
= 10000; /* 10000 minutes ~ 1 week */
232 mountprog
= MOUNTPROG
;
233 mountvers
= MOUNTVERS
;
236 nfsprog
= NFS_PROGRAM
;
237 nfsvers
= NFS_VERSION
;
241 for (opt
= strtok(old_opts
, ","); opt
; opt
= strtok(NULL
, ",")) {
242 if ((opteq
= strchr(opt
, '='))) {
243 val
= atoi(opteq
+ 1);
245 if (!strcmp(opt
, "rsize"))
247 else if (!strcmp(opt
, "wsize"))
249 else if (!strcmp(opt
, "timeo"))
251 else if (!strcmp(opt
, "retrans"))
253 else if (!strcmp(opt
, "acregmin"))
255 else if (!strcmp(opt
, "acregmax"))
257 else if (!strcmp(opt
, "acdirmin"))
259 else if (!strcmp(opt
, "acdirmax"))
261 else if (!strcmp(opt
, "actimeo")) {
267 else if (!strcmp(opt
, "retry"))
269 else if (!strcmp(opt
, "port"))
271 else if (!strcmp(opt
, "mountport"))
273 else if (!strcmp(opt
, "mounthost"))
274 mounthost
=xstrndup(opteq
+1,
275 strcspn(opteq
+1," \t\n\r,"));
276 else if (!strcmp(opt
, "mountprog"))
278 else if (!strcmp(opt
, "mountvers"))
280 else if (!strcmp(opt
, "nfsprog"))
282 else if (!strcmp(opt
, "nfsvers") ||
283 !strcmp(opt
, "vers"))
285 else if (!strcmp(opt
, "proto")) {
286 if (!strncmp(opteq
+1, "tcp", 3))
288 else if (!strncmp(opteq
+1, "udp", 3))
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)
298 printf("Warning: Option namlen is not supported.\n");
299 } else if (!strcmp(opt
, "addr"))
302 printf("unknown nfs mount parameter: "
303 "%s=%d\n", opt
, val
);
309 if (!strncmp(opt
, "no", 2)) {
313 if (!strcmp(opt
, "bg"))
315 else if (!strcmp(opt
, "fg"))
317 else if (!strcmp(opt
, "soft"))
319 else if (!strcmp(opt
, "hard"))
321 else if (!strcmp(opt
, "intr"))
323 else if (!strcmp(opt
, "posix"))
325 else if (!strcmp(opt
, "cto"))
327 else if (!strcmp(opt
, "ac"))
329 else if (!strcmp(opt
, "tcp"))
331 else if (!strcmp(opt
, "udp"))
333 else if (!strcmp(opt
, "lock")) {
334 if (nfs_mount_version
>= 3)
337 printf("Warning: option nolock is not supported.\n");
340 printf("unknown nfs mount option: "
341 "%s%s\n", val
? "" : "no", opt
);
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);
356 #if NFS_MOUNT_VERSION >= 3
357 if (nfs_mount_version
>= 3)
358 data
.flags
|= (nolock
? NFS_MOUNT_NONLM
: 0);
361 /* Adjust options if none specified */
363 data
.timeo
= tcp
? 70 : 7;
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
382 (data
.flags
& NFS_MOUNT_TCP
) != 0);
386 data
.version
= nfs_mount_version
;
387 *mount_opts
= (char *) &data
;
389 if (*flags
& MS_REMOUNT
)
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.
397 if (bg
&& !running_bg
&&
398 prev_bg_host
&& strcmp(hostname
, prev_bg_host
) == 0) {
404 /* create mount deamon client */
405 /* See if the nfs host = mount host. */
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
);
411 if ((hp
= gethostbyname(mounthost
)) == NULL
) {
412 fprintf(stderr
, "mount: can't get address for %s\n",
416 if (hp
->h_length
> sizeof(struct in_addr
)) {
418 "mount: got bad hp->h_length?\n");
419 hp
->h_length
= sizeof(struct in_addr
);
421 mount_server_addr
.sin_family
= AF_INET
;
422 memcpy(&mount_server_addr
.sin_addr
,
423 hp
->h_addr
, hp
->h_length
);
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.
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.
439 * The "retry" count specified by the user is the number of
440 * minutes to retry before giving up.
442 * Only the first error message will be displayed.
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
;
453 if (bg
&& stat(node
, &statbuf
) == -1) {
455 sleep(val
); /* 1, 2, 4, 8, 16, 30, ... */
461 /* be careful not to use too many CPU cycles */
465 /* contact the mount daemon via TCP */
466 mount_server_addr
.sin_port
= htons(mountport
);
468 mclient
= clnttcp_create(&mount_server_addr
,
469 mountprog
, mountvers
,
472 /* if this fails, contact the mount daemon via UDP */
474 mount_server_addr
.sin_port
= htons(mountport
);
476 mclient
= clntudp_create(&mount_server_addr
,
477 mountprog
, mountvers
,
478 retry_timeout
, &msock
);
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
,
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 */
493 if (!running_bg
&& prevt
== 0)
494 clnt_perror(mclient
, "mount");
495 auth_destroy(mclient
->cl_auth
);
496 clnt_destroy(mclient
);
500 if (!running_bg
&& prevt
== 0)
501 clnt_pcreateerror("mount");
508 prev_bg_host
= xstrdup(hostname
);
518 if (status
.fhs_status
!= 0) {
520 "mount: %s:%s failed, reason given by server: %s\n",
521 hostname
, dirname
, nfs_strerror(status
.fhs_status
));
524 memcpy((char *) &root_fhandle
, (char *) status
.fhstatus_u
.fhs_fhandle
,
525 sizeof (root_fhandle
));
527 /* create nfs socket for kernel */
530 if (nfs_mount_version
< 3) {
531 printf("NFS over TCP is not supported.\n");
534 fsock
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
536 fsock
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
538 perror("nfs socket");
541 if (bindresvport(fsock
, 0) < 0) {
542 perror("nfs bindresvport");
546 server_addr
.sin_port
= PMAPPORT
;
547 port
= pmap_getport(&server_addr
, nfsprog
, nfsvers
,
548 tcp
? IPPROTO_TCP
: IPPROTO_UDP
);
551 #ifdef NFS_MOUNT_DEBUG
553 printf("used portmapper to find NFS port\n");
556 #ifdef NFS_MOUNT_DEBUG
557 printf("using port %d for nfs deamon\n", port
);
559 server_addr
.sin_port
= htons(port
);
561 * connect() the socket for kernels 1.3.10 and below only,
562 * to avoid problems with multihomed hosts.
565 if (linux_version_code() <= 66314
566 && connect(fsock
, (struct sockaddr
*) &server_addr
,
567 sizeof (server_addr
)) < 0) {
568 perror("nfs connect");
572 /* prepare data structure for kernel */
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
));
582 auth_destroy(mclient
->cl_auth
);
583 clnt_destroy(mclient
);
592 auth_destroy(mclient
->cl_auth
);
593 clnt_destroy(mclient
);
603 * We need to translate between nfs status return values and
604 * the local errno values which may not be the same.
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".
612 #define EDQUOT ENOSPC
620 { NFSERR_PERM
, EPERM
},
621 { NFSERR_NOENT
, ENOENT
},
623 { NFSERR_NXIO
, ENXIO
},
624 { NFSERR_ACCES
, EACCES
},
625 { NFSERR_EXIST
, EEXIST
},
626 { NFSERR_NODEV
, ENODEV
},
627 { NFSERR_NOTDIR
, ENOTDIR
},
628 { NFSERR_ISDIR
, EISDIR
},
630 { NFSERR_INVAL
, EINVAL
}, /* that Sun forgot */
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
},
640 { NFSERR_WFLUSH
, EWFLUSH
},
642 /* Throw in some NFSv3 values for even more fun (HP returns these) */
648 static char *nfs_strerror(int stat
)
651 static char buf
[256];
653 for (i
= 0; nfs_errtbl
[i
].stat
!= -1; i
++) {
654 if (nfs_errtbl
[i
].stat
== stat
)
655 return strerror(nfs_errtbl
[i
].errnum
);
657 sprintf(buf
, "unknown nfs status return value: %d", stat
);
663 my_getport(struct in_addr server
, struct timeval
*timeo
, ...)
665 struct sockaddr_in sin
;
668 int sock
= RPC_ANYSOCK
, port
;
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
) {