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.
24 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
25 * - added Native Language Support
27 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
32 * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
41 #include <rpc/pmap_prot.h>
42 #include <rpc/pmap_clnt.h>
43 #include <sys/socket.h>
45 #include <sys/utsname.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
53 #include <linux/nfs.h>
54 #include "mount_constants.h"
55 #include "nfs_mount4.h"
59 #include "../defines.h" /* for HAVE_inet_aton */
68 static char *nfs_strerror(int stat
);
70 #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
72 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
75 linux_version_code(void) {
76 struct utsname my_utsname
;
79 if (uname(&my_utsname
) == 0) {
80 p
= atoi(strtok(my_utsname
.release
, "."));
81 q
= atoi(strtok(NULL
, "."));
82 r
= atoi(strtok(NULL
, "."));
83 return MAKE_VERSION(p
,q
,r
);
89 * nfs_mount_version according to the sources seen at compile time.
91 int nfs_mount_version
= NFS_MOUNT_VERSION
;
94 * Unfortunately, the kernel prints annoying console messages
95 * in case of an unexpected nfs mount version (instead of
96 * just returning some error). Therefore we'll have to try
97 * and figure out what version the kernel expects.
100 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
101 * nfs_mount_version: version this source and running kernel can handle
104 find_kernel_nfs_mount_version(void) {
105 static int kernel_version
= 0;
110 kernel_version
= linux_version_code();
112 if (kernel_version
) {
113 if (kernel_version
< MAKE_VERSION(2,1,32))
114 nfs_mount_version
= 1;
115 else if (kernel_version
< MAKE_VERSION(2,2,18))
116 nfs_mount_version
= 3;
117 else if (kernel_version
< MAKE_VERSION(2,3,0))
118 nfs_mount_version
= 4; /* since 2.2.18pre9 */
119 else if (kernel_version
< MAKE_VERSION(2,3,99))
120 nfs_mount_version
= 3;
122 nfs_mount_version
= 4; /* since 2.3.99pre4 */
124 if (nfs_mount_version
> NFS_MOUNT_VERSION
)
125 nfs_mount_version
= NFS_MOUNT_VERSION
;
129 get_mountport(struct sockaddr_in
*server_addr
,
131 long unsigned version
,
135 struct pmaplist
*pmap
;
136 static struct pmap p
= {0, 0, 0, 0};
138 server_addr
->sin_port
= PMAPPORT
;
139 pmap
= pmap_getmaps(server_addr
);
141 if (version
> MAX_NFSPROT
)
142 version
= MAX_NFSPROT
;
151 if (pmap
->pml_map
.pm_prog
!= prog
)
153 if (!version
&& p
.pm_vers
> pmap
->pml_map
.pm_vers
)
155 if (version
> 2 && pmap
->pml_map
.pm_vers
!= version
)
157 if (version
&& version
<= 2 && pmap
->pml_map
.pm_vers
> 2)
159 if (pmap
->pml_map
.pm_vers
> MAX_NFSPROT
||
160 (proto
&& p
.pm_prot
&& pmap
->pml_map
.pm_prot
!= proto
) ||
161 (port
&& pmap
->pml_map
.pm_port
!= port
))
163 memcpy(&p
, &pmap
->pml_map
, sizeof(p
));
165 pmap
= pmap
->pml_next
;
168 p
.pm_vers
= MOUNTVERS
;
170 p
.pm_port
= MOUNTPORT
;
172 p
.pm_prot
= IPPROTO_TCP
;
176 int nfsmount(const char *spec
, const char *node
, int *flags
,
177 char **extra_opts
, char **mount_opts
, int running_bg
)
179 static char *prev_bg_host
;
185 char *mounthost
=NULL
;
187 struct timeval total_timeout
;
188 enum clnt_stat clnt_stat
;
189 static struct nfs_mount_data data
;
193 struct sockaddr_in server_addr
;
194 struct sockaddr_in mount_server_addr
;
197 struct timeval retry_timeout
;
199 struct fhstatus nfsv2
;
200 struct mountres3 nfsv3
;
226 find_kernel_nfs_mount_version();
231 if (strlen(spec
) >= sizeof(hostdir
)) {
232 fprintf(stderr
, _("mount: "
233 "excessively long host:dir argument\n"));
236 strcpy(hostdir
, spec
);
237 if ((s
= strchr(hostdir
, ':'))) {
241 /* Ignore all but first hostname in replicated mounts
242 until they can be fully supported. (mack@sgi.com) */
243 if ((s
= strchr(hostdir
, ','))) {
245 fprintf(stderr
, _("mount: warning: "
246 "multiple hostnames not supported\n"));
249 fprintf(stderr
, _("mount: "
250 "directory to mount not in host:dir format\n"));
254 server_addr
.sin_family
= AF_INET
;
255 #ifdef HAVE_inet_aton
256 if (!inet_aton(hostname
, &server_addr
.sin_addr
))
259 if ((hp
= gethostbyname(hostname
)) == NULL
) {
260 fprintf(stderr
, _("mount: can't get address for %s\n"),
264 if (hp
->h_length
> sizeof(struct in_addr
)) {
266 _("mount: got bad hp->h_length\n"));
267 hp
->h_length
= sizeof(struct in_addr
);
269 memcpy(&server_addr
.sin_addr
,
270 hp
->h_addr
, hp
->h_length
);
274 memcpy (&mount_server_addr
, &server_addr
, sizeof (mount_server_addr
));
276 /* add IP address to mtab options for use when unmounting */
278 s
= inet_ntoa(server_addr
.sin_addr
);
279 old_opts
= *extra_opts
;
282 if (strlen(old_opts
) + strlen(s
) + 10 >= sizeof(new_opts
)) {
283 fprintf(stderr
, _("mount: "
284 "excessively long option argument\n"));
287 sprintf(new_opts
, "%s%saddr=%s",
288 old_opts
, *old_opts
? "," : "", s
);
289 *extra_opts
= xstrdup(new_opts
);
291 /* Set default options.
292 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
293 * let the kernel decide.
294 * timeo is filled in after we know whether it'll be TCP or UDP. */
295 memset(&data
, 0, sizeof(data
));
301 #if NFS_MOUNT_VERSION >= 2
302 data
.namlen
= NAME_MAX
;
313 retry
= 10000; /* 10000 minutes ~ 1 week */
316 mountprog
= MOUNTPROG
;
320 nfsprog
= NFS_PROGRAM
;
325 for (opt
= strtok(old_opts
, ","); opt
; opt
= strtok(NULL
, ",")) {
326 if ((opteq
= strchr(opt
, '='))) {
327 val
= atoi(opteq
+ 1);
329 if (!strcmp(opt
, "rsize"))
331 else if (!strcmp(opt
, "wsize"))
333 else if (!strcmp(opt
, "timeo"))
335 else if (!strcmp(opt
, "retrans"))
337 else if (!strcmp(opt
, "acregmin"))
339 else if (!strcmp(opt
, "acregmax"))
341 else if (!strcmp(opt
, "acdirmin"))
343 else if (!strcmp(opt
, "acdirmax"))
345 else if (!strcmp(opt
, "actimeo")) {
351 else if (!strcmp(opt
, "retry"))
353 else if (!strcmp(opt
, "port"))
355 else if (!strcmp(opt
, "mountport"))
357 else if (!strcmp(opt
, "mounthost"))
358 mounthost
=xstrndup(opteq
+1,
359 strcspn(opteq
+1," \t\n\r,"));
360 else if (!strcmp(opt
, "mountprog"))
362 else if (!strcmp(opt
, "mountvers"))
364 else if (!strcmp(opt
, "nfsprog"))
366 else if (!strcmp(opt
, "nfsvers") ||
367 !strcmp(opt
, "vers"))
369 else if (!strcmp(opt
, "proto")) {
370 if (!strncmp(opteq
+1, "tcp", 3))
372 else if (!strncmp(opteq
+1, "udp", 3))
375 printf(_("Warning: Unrecognized proto= option.\n"));
376 } else if (!strcmp(opt
, "namlen")) {
377 #if NFS_MOUNT_VERSION >= 2
378 if (nfs_mount_version
>= 2)
382 printf(_("Warning: Option namlen is not supported.\n"));
383 } else if (!strcmp(opt
, "addr"))
386 printf(_("unknown nfs mount parameter: "
387 "%s=%d\n"), opt
, val
);
393 if (!strncmp(opt
, "no", 2)) {
397 if (!strcmp(opt
, "bg"))
399 else if (!strcmp(opt
, "fg"))
401 else if (!strcmp(opt
, "soft"))
403 else if (!strcmp(opt
, "hard"))
405 else if (!strcmp(opt
, "intr"))
407 else if (!strcmp(opt
, "posix"))
409 else if (!strcmp(opt
, "cto"))
411 else if (!strcmp(opt
, "ac"))
413 else if (!strcmp(opt
, "tcp"))
415 else if (!strcmp(opt
, "udp"))
417 else if (!strcmp(opt
, "lock")) {
418 if (nfs_mount_version
>= 3)
421 printf(_("Warning: option nolock is not supported.\n"));
422 } else if (!strcmp(opt
, "broken_suid")) {
426 printf(_("unknown nfs mount option: "
427 "%s%s\n"), val
? "" : "no", opt
);
433 proto
= (tcp
) ? IPPROTO_TCP
: IPPROTO_UDP
;
435 data
.flags
= (soft
? NFS_MOUNT_SOFT
: 0)
436 | (intr
? NFS_MOUNT_INTR
: 0)
437 | (posix
? NFS_MOUNT_POSIX
: 0)
438 | (nocto
? NFS_MOUNT_NOCTO
: 0)
439 | (noac
? NFS_MOUNT_NOAC
: 0);
440 #if NFS_MOUNT_VERSION >= 2
441 if (nfs_mount_version
>= 2)
442 data
.flags
|= (tcp
? NFS_MOUNT_TCP
: 0);
444 #if NFS_MOUNT_VERSION >= 3
445 if (nfs_mount_version
>= 3)
446 data
.flags
|= (nolock
? NFS_MOUNT_NONLM
: 0);
448 #if NFS_MOUNT_VERSION >= 4
449 if (nfs_mount_version
>= 4)
450 data
.flags
|= (broken_suid
? NFS_MOUNT_BROKEN_SUID
: 0);
452 if (nfsvers
> MAX_NFSPROT
) {
453 fprintf(stderr
, "NFSv%d not supported!\n", nfsvers
);
456 if (mountvers
> MAX_NFSPROT
) {
457 fprintf(stderr
, "NFSv%d not supported!\n", nfsvers
);
460 if (nfsvers
&& !mountvers
)
461 mountvers
= (nfsvers
< 3) ? 1 : nfsvers
;
462 if (nfsvers
&& nfsvers
< mountvers
) {
466 /* Adjust options if none specified */
468 data
.timeo
= tcp
? 70 : 7;
470 #ifdef NFS_MOUNT_DEBUG
471 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
472 data
.rsize
, data
.wsize
, data
.timeo
, data
.retrans
);
473 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
474 data
.acregmin
, data
.acregmax
, data
.acdirmin
, data
.acdirmax
);
475 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
476 port
, bg
, retry
, data
.flags
);
477 printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
478 mountprog
, mountvers
, nfsprog
, nfsvers
);
479 printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
480 (data
.flags
& NFS_MOUNT_SOFT
) != 0,
481 (data
.flags
& NFS_MOUNT_INTR
) != 0,
482 (data
.flags
& NFS_MOUNT_POSIX
) != 0,
483 (data
.flags
& NFS_MOUNT_NOCTO
) != 0,
484 (data
.flags
& NFS_MOUNT_NOAC
) != 0);
485 #if NFS_MOUNT_VERSION >= 2
487 (data
.flags
& NFS_MOUNT_TCP
) != 0);
491 data
.version
= nfs_mount_version
;
492 *mount_opts
= (char *) &data
;
494 if (*flags
& MS_REMOUNT
)
498 * If the previous mount operation on the same host was
499 * backgrounded, and the "bg" for this mount is also set,
500 * give up immediately, to avoid the initial timeout.
502 if (bg
&& !running_bg
&&
503 prev_bg_host
&& strcmp(hostname
, prev_bg_host
) == 0) {
509 /* create mount deamon client */
510 /* See if the nfs host = mount host. */
512 if (mounthost
[0] >= '0' && mounthost
[0] <= '9') {
513 mount_server_addr
.sin_family
= AF_INET
;
514 mount_server_addr
.sin_addr
.s_addr
= inet_addr(hostname
);
516 if ((hp
= gethostbyname(mounthost
)) == NULL
) {
517 fprintf(stderr
, _("mount: can't get address for %s\n"),
521 if (hp
->h_length
> sizeof(struct in_addr
)) {
523 _("mount: got bad hp->h_length?\n"));
524 hp
->h_length
= sizeof(struct in_addr
);
526 mount_server_addr
.sin_family
= AF_INET
;
527 memcpy(&mount_server_addr
.sin_addr
,
528 hp
->h_addr
, hp
->h_length
);
534 * The following loop implements the mount retries. On the first
535 * call, "running_bg" is 0. When the mount times out, and the
536 * "bg" option is set, the exit status EX_BG will be returned.
537 * For a backgrounded mount, there will be a second call by the
538 * child process with "running_bg" set to 1.
540 * The case where the mount point is not present and the "bg"
541 * option is set, is treated as a timeout. This is done to
542 * support nested mounts.
544 * The "retry" count specified by the user is the number of
545 * minutes to retry before giving up.
547 * Only the first error message will be displayed.
549 retry_timeout
.tv_sec
= 3;
550 retry_timeout
.tv_usec
= 0;
551 total_timeout
.tv_sec
= 20;
552 total_timeout
.tv_usec
= 0;
553 timeout
= time(NULL
) + 60 * retry
;
558 if (bg
&& stat(node
, &statbuf
) == -1) {
560 sleep(val
); /* 1, 2, 4, 8, 16, 30, ... */
566 /* be careful not to use too many CPU cycles */
570 pm_mnt
= get_mountport(&mount_server_addr
,
576 /* contact the mount daemon via TCP */
577 mount_server_addr
.sin_port
= htons(pm_mnt
->pm_port
);
580 switch (pm_mnt
->pm_prot
) {
582 mclient
= clntudp_create(&mount_server_addr
,
589 mount_server_addr
.sin_port
= htons(pm_mnt
->pm_port
);
592 mclient
= clnttcp_create(&mount_server_addr
,
601 /* try to mount hostname:dirname */
602 mclient
->cl_auth
= authunix_create_default();
604 /* make pointers in xdr_mountres3 NULL so
605 * that xdr_array allocates memory for us
607 memset(&status
, 0, sizeof(status
));
609 if (pm_mnt
->pm_vers
== 3)
610 clnt_stat
= clnt_call(mclient
, MOUNTPROC3_MNT
,
611 (xdrproc_t
) xdr_dirpath
,
613 (xdrproc_t
) xdr_mountres3
,
617 clnt_stat
= clnt_call(mclient
, MOUNTPROC_MNT
,
618 (xdrproc_t
) xdr_dirpath
,
620 (xdrproc_t
) xdr_fhstatus
,
624 if (clnt_stat
== RPC_SUCCESS
)
625 break; /* we're done */
626 if (errno
!= ECONNREFUSED
) {
627 clnt_perror(mclient
, "mount");
628 goto fail
; /* don't retry */
630 if (!running_bg
&& prevt
== 0)
631 clnt_perror(mclient
, "mount");
632 auth_destroy(mclient
->cl_auth
);
633 clnt_destroy(mclient
);
637 if (!running_bg
&& prevt
== 0)
638 clnt_pcreateerror("mount");
645 prev_bg_host
= xstrdup(hostname
);
654 nfsvers
= (pm_mnt
->pm_vers
< 2) ? 2 : pm_mnt
->pm_vers
;
657 if (status
.nfsv2
.fhs_status
!= 0) {
659 "mount: %s:%s failed, reason given by server: %s\n",
661 nfs_strerror(status
.nfsv2
.fhs_status
));
664 memcpy(data
.root
.data
,
665 (char *) status
.nfsv2
.fhstatus_u
.fhs_fhandle
,
667 #if NFS_MOUNT_VERSION >= 4
668 data
.root
.size
= NFS_FHSIZE
;
669 memcpy(data
.old_root
.data
,
670 (char *) status
.nfsv2
.fhstatus_u
.fhs_fhandle
,
674 #if NFS_MOUNT_VERSION >= 4
676 if (status
.nfsv3
.fhs_status
!= 0) {
678 "mount: %s:%s failed, reason given by server: %s\n",
680 nfs_strerror(status
.nfsv3
.fhs_status
));
683 fhandle
= &status
.nfsv3
.mountres3_u
.mountinfo
.fhandle
;
684 memset(data
.old_root
.data
, 0, NFS_FHSIZE
);
685 memset(&data
.root
, 0, sizeof(data
.root
));
686 data
.root
.size
= fhandle
->fhandle3_len
;
687 memcpy(data
.root
.data
,
688 (char *) fhandle
->fhandle3_val
,
689 fhandle
->fhandle3_len
);
691 data
.flags
|= NFS_MOUNT_VER3
;
695 /* create nfs socket for kernel */
698 if (nfs_mount_version
< 3) {
699 printf(_("NFS over TCP is not supported.\n"));
702 fsock
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
704 fsock
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
706 perror(_("nfs socket"));
709 if (bindresvport(fsock
, 0) < 0) {
710 perror(_("nfs bindresvport"));
714 server_addr
.sin_port
= PMAPPORT
;
715 port
= pmap_getport(&server_addr
, nfsprog
, nfsvers
,
716 tcp
? IPPROTO_TCP
: IPPROTO_UDP
);
719 #ifdef NFS_MOUNT_DEBUG
721 printf(_("used portmapper to find NFS port\n"));
724 #ifdef NFS_MOUNT_DEBUG
725 printf(_("using port %d for nfs deamon\n"), port
);
727 server_addr
.sin_port
= htons(port
);
729 * connect() the socket for kernels 1.3.10 and below only,
730 * to avoid problems with multihomed hosts.
733 if (linux_version_code() <= 66314
734 && connect(fsock
, (struct sockaddr
*) &server_addr
,
735 sizeof (server_addr
)) < 0) {
736 perror(_("nfs connect"));
740 /* prepare data structure for kernel */
743 memcpy((char *) &data
.addr
, (char *) &server_addr
, sizeof(data
.addr
));
744 strncpy(data
.hostname
, hostname
, sizeof(data
.hostname
));
748 auth_destroy(mclient
->cl_auth
);
749 clnt_destroy(mclient
);
758 auth_destroy(mclient
->cl_auth
);
759 clnt_destroy(mclient
);
769 * We need to translate between nfs status return values and
770 * the local errno values which may not be the same.
772 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
773 * "after #include <errno.h> the symbol errno is reserved for any use,
774 * it cannot even be used as a struct tag or field name".
778 #define EDQUOT ENOSPC
786 { NFSERR_PERM
, EPERM
},
787 { NFSERR_NOENT
, ENOENT
},
789 { NFSERR_NXIO
, ENXIO
},
790 { NFSERR_ACCES
, EACCES
},
791 { NFSERR_EXIST
, EEXIST
},
792 { NFSERR_NODEV
, ENODEV
},
793 { NFSERR_NOTDIR
, ENOTDIR
},
794 { NFSERR_ISDIR
, EISDIR
},
796 { NFSERR_INVAL
, EINVAL
}, /* that Sun forgot */
798 { NFSERR_FBIG
, EFBIG
},
799 { NFSERR_NOSPC
, ENOSPC
},
800 { NFSERR_ROFS
, EROFS
},
801 { NFSERR_NAMETOOLONG
, ENAMETOOLONG
},
802 { NFSERR_NOTEMPTY
, ENOTEMPTY
},
803 { NFSERR_DQUOT
, EDQUOT
},
804 { NFSERR_STALE
, ESTALE
},
806 { NFSERR_WFLUSH
, EWFLUSH
},
808 /* Throw in some NFSv3 values for even more fun (HP returns these) */
814 static char *nfs_strerror(int stat
)
817 static char buf
[256];
819 for (i
= 0; nfs_errtbl
[i
].stat
!= -1; i
++) {
820 if (nfs_errtbl
[i
].stat
== stat
)
821 return strerror(nfs_errtbl
[i
].errnum
);
823 sprintf(buf
, _("unknown nfs status return value: %d"), stat
);
829 my_getport(struct in_addr server
, struct timeval
*timeo
, ...)
831 struct sockaddr_in sin
;
834 int sock
= RPC_ANYSOCK
, port
;
840 sin
.sin_family
= AF_INET
;
841 sin
.sin_addr
= server
;
842 sin
.sin_port
= htons(111);
843 clnt
= clntudp_create(&sin
, 100000, 2, *timeo
, &sock
);
844 status
= clnt_call(clnt
, PMAP_GETPORT
,
845 &pmap
, (xdrproc_t
) xdr_pmap
,
846 &port
, (xdrproc_t
) xdr_uint
);
847 if (status
!= SUCCESS
) {