2 * nfs4mount.c -- Linux NFS mount
3 * Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
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 * Note: this file based on the original nfsmount.c
17 * 2006-06-06 Amit Gud <agud@redhat.com>
18 * - Moved to nfs-utils/utils/mount from util-linux/mount.
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
32 #ifdef HAVE_RPCSVC_NFS_PROT_H
33 #include <rpcsvc/nfs_prot.h>
35 #include <linux/nfs.h>
36 #define nfsstat nfs_stat
43 #include "nfs4_mount.h"
44 #include "nfs_mount.h"
46 #if defined(VAR_LOCK_DIR)
47 #define DEFAULT_DIR VAR_LOCK_DIR
49 #define DEFAULT_DIR "/var/lock/subsys"
55 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
56 #define idmapd_check() do { \
57 if (access(IDMAPLCK, F_OK)) { \
58 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
59 " All uids will be mapped to the nobody uid.\n")); \
63 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
64 #define gssd_check() do { \
65 if (access(GSSDLCK, F_OK)) { \
66 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
78 { "krb5", RPC_AUTH_GSS_KRB5 },
79 { "krb5i", RPC_AUTH_GSS_KRB5I },
80 { "krb5p", RPC_AUTH_GSS_KRB5P },
81 { "lipkey", RPC_AUTH_GSS_LKEY },
82 { "lipkey-i", RPC_AUTH_GSS_LKEYI },
83 { "lipkey-p", RPC_AUTH_GSS_LKEYP },
84 { "spkm3", RPC_AUTH_GSS_SPKM },
85 { "spkm3i", RPC_AUTH_GSS_SPKMI },
86 { "spkm3p", RPC_AUTH_GSS_SPKMP },
87 { "unix", AUTH_UNIX },
89 { "null", AUTH_NULL },
90 { "none", AUTH_NONE },
93 #define FMAPSIZE (sizeof(flav_map)/sizeof(flav_map[0]))
94 #define MAX_USER_FLAVOUR 16
96 static int parse_sec(char *sec, int *pseudoflavour)
98 int i, num_flavour = 0;
100 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
101 if (num_flavour >= MAX_USER_FLAVOUR) {
103 _("mount: maximum number of security flavors "
107 for (i = 0; i < FMAPSIZE; i++) {
108 if (strcmp(sec, flav_map[i].flavour) == 0) {
109 pseudoflavour[num_flavour++] = flav_map[i].fnum;
115 _("mount: unknown security type %s\n"), sec);
121 _("mount: no security flavors passed to sec= option\n"));
125 static int parse_devname(char *hostdir, char **hostname, char **dirname)
129 if (!(s = strchr(hostdir, ':'))) {
132 "directory to mount not in host:dir format\n"));
138 /* Ignore all but first hostname in replicated mounts
139 until they can be fully supported. (mack@sgi.com) */
140 if ((s = strchr(hostdir, ','))) {
144 "multiple hostnames not supported\n"));
149 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
152 addr->sin_family = AF_INET;
154 if (inet_aton(hostname, &addr->sin_addr))
156 if ((hp = gethostbyname(hostname)) == NULL) {
157 fprintf(stderr, _("mount: can't get address for %s\n"),
161 if (hp->h_length > sizeof(struct in_addr)) {
163 _("mount: got bad hp->h_length\n"));
164 hp->h_length = sizeof(struct in_addr);
166 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
170 static int get_my_ipv4addr(char *ip_addr, int len)
173 struct sockaddr_in myaddr;
175 if (gethostname(myname, sizeof(myname))) {
176 fprintf(stderr, _("mount: can't determine client address\n"));
179 if (fill_ipv4_sockaddr(myname, &myaddr))
181 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
182 ip_addr[len-1] = '\0';
186 int nfs4mount(const char *spec, const char *node, int *flags,
187 char **extra_opts, char **mount_opts,
190 static struct nfs4_mount_data data;
191 static char hostdir[1024];
192 static char ip_addr[16] = "127.0.0.1";
193 static struct sockaddr_in server_addr, client_addr;
194 static int pseudoflavour[MAX_USER_FLAVOUR];
196 int ip_addr_in_opts = 0;
198 char *hostname, *dirname, *old_opts;
210 if (strlen(spec) >= sizeof(hostdir)) {
211 fprintf(stderr, _("mount: "
212 "excessively long host:dir argument\n"));
215 strcpy(hostdir, spec);
216 if (parse_devname(hostdir, &hostname, &dirname))
219 if (fill_ipv4_sockaddr(hostname, &server_addr))
221 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
224 /* add IP address to mtab options for use when unmounting */
225 s = inet_ntoa(server_addr.sin_addr);
226 old_opts = *extra_opts;
229 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
230 fprintf(stderr, _("mount: "
231 "excessively long option argument\n"));
234 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
235 old_opts, *old_opts ? "," : "", s);
236 *extra_opts = xstrdup(new_opts);
238 /* Set default options.
239 * rsize/wsize and timeo are left 0 in order to
240 * let the kernel decide.
242 memset(&data, 0, sizeof(data));
248 data.proto = IPPROTO_TCP;
252 intr = NFS4_MOUNT_INTR;
255 retry = 10000; /* 10000 minutes ~ 1 week */
258 * NFSv4 specifies that the default port should be 2049
260 server_addr.sin_port = htons(NFS_PORT);
264 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
265 if ((opteq = strchr(opt, '='))) {
266 val = atoi(opteq + 1);
268 if (!strcmp(opt, "rsize"))
270 else if (!strcmp(opt, "wsize"))
272 else if (!strcmp(opt, "timeo"))
274 else if (!strcmp(opt, "retrans"))
276 else if (!strcmp(opt, "acregmin"))
278 else if (!strcmp(opt, "acregmax"))
280 else if (!strcmp(opt, "acdirmin"))
282 else if (!strcmp(opt, "acdirmax"))
284 else if (!strcmp(opt, "actimeo")) {
290 else if (!strcmp(opt, "retry"))
292 else if (!strcmp(opt, "port"))
293 server_addr.sin_port = htons(val);
294 else if (!strcmp(opt, "proto")) {
295 if (!strncmp(opteq+1, "tcp", 3))
296 data.proto = IPPROTO_TCP;
297 else if (!strncmp(opteq+1, "udp", 3))
298 data.proto = IPPROTO_UDP;
300 printf(_("Warning: Unrecognized proto= option.\n"));
301 } else if (!strcmp(opt, "clientaddr")) {
302 if (strlen(opteq+1) >= sizeof(ip_addr))
303 printf(_("Invalid client address %s"),
305 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
306 ip_addr[sizeof(ip_addr)-1] = '\0';
308 } else if (!strcmp(opt, "sec")) {
309 num_flavour = parse_sec(opteq+1, pseudoflavour);
312 } else if (!strcmp(opt, "addr") || sloppy) {
315 printf(_("unknown nfs mount parameter: "
316 "%s=%d\n"), opt, val);
321 if (!strncmp(opt, "no", 2)) {
325 if (!strcmp(opt, "bg"))
327 else if (!strcmp(opt, "fg"))
329 else if (!strcmp(opt, "soft"))
331 else if (!strcmp(opt, "hard"))
333 else if (!strcmp(opt, "intr"))
335 else if (!strcmp(opt, "cto"))
337 else if (!strcmp(opt, "ac"))
340 printf(_("unknown nfs mount option: "
341 "%s%s\n"), val ? "" : "no", opt);
347 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
348 | (intr ? NFS4_MOUNT_INTR : 0)
349 | (nocto ? NFS4_MOUNT_NOCTO : 0)
350 | (noac ? NFS4_MOUNT_NOAC : 0);
353 * Give a warning if the rpc.idmapd daemon is not running
357 if (num_flavour == 0)
358 pseudoflavour[num_flavour++] = AUTH_UNIX;
361 * ditto with rpc.gssd daemon
365 data.auth_flavourlen = num_flavour;
366 data.auth_flavours = pseudoflavour;
368 data.client_addr.data = ip_addr;
369 data.client_addr.len = strlen(ip_addr);
371 data.mnt_path.data = dirname;
372 data.mnt_path.len = strlen(dirname);
374 data.hostname.data = hostname;
375 data.hostname.len = strlen(hostname);
376 data.host_addr = (struct sockaddr *)&server_addr;
377 data.host_addrlen = sizeof(server_addr);
379 #ifdef NFS_MOUNT_DEBUG
380 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
381 data.rsize, data.wsize, data.timeo, data.retrans);
382 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
383 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
384 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
385 ntohs(server_addr.sin_port), bg, retry, data.flags);
386 printf("soft = %d, intr = %d, nocto = %d, noac = %d\n",
387 (data.flags & NFS4_MOUNT_SOFT) != 0,
388 (data.flags & NFS4_MOUNT_INTR) != 0,
389 (data.flags & NFS4_MOUNT_NOCTO) != 0,
390 (data.flags & NFS4_MOUNT_NOAC) != 0);
392 if (num_flavour > 0) {
396 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
397 for (i = 0; i < FMAPSIZE; i++) {
398 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
399 printf("%s", flav_map[i].flavour);
403 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
406 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
409 timeout = time(NULL) + 60 * retry;
410 data.version = NFS4_MOUNT_VERSION;
414 "mount: pinging: prog %d vers %d prot %s port %d\n",
415 NFS_PROGRAM, 4, data.proto == IPPROTO_UDP ? "udp" : "tcp",
416 ntohs(server_addr.sin_port));
418 client_addr.sin_family = 0;
419 client_addr.sin_addr.s_addr = 0;
420 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
421 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
422 if (!ip_addr_in_opts &&
423 client_addr.sin_family != 0 &&
424 client_addr.sin_addr.s_addr != 0) {
425 snprintf(ip_addr, sizeof(ip_addr), "%s",
426 inet_ntoa(client_addr.sin_addr));
427 data.client_addr.len = strlen(ip_addr);
432 switch(rpc_createerr.cf_stat){
435 case RPC_SYSTEMERROR:
436 if (errno == ETIMEDOUT)
439 mount_errors(hostname, 0, bg);
444 mount_errors(hostname, 0, bg);
447 mount_errors(hostname, 1, bg);
451 *mount_opts = (char *) &data;