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"
54 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
55 #define idmapd_check() do { \
56 if (access(IDMAPLCK, F_OK)) { \
57 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
58 " All uids will be mapped to the nobody uid.\n")); \
62 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
63 #define gssd_check() do { \
64 if (access(GSSDLCK, F_OK)) { \
65 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
77 { "krb5", RPC_AUTH_GSS_KRB5 },
78 { "krb5i", RPC_AUTH_GSS_KRB5I },
79 { "krb5p", RPC_AUTH_GSS_KRB5P },
80 { "lipkey", RPC_AUTH_GSS_LKEY },
81 { "lipkey-i", RPC_AUTH_GSS_LKEYI },
82 { "lipkey-p", RPC_AUTH_GSS_LKEYP },
83 { "spkm3", RPC_AUTH_GSS_SPKM },
84 { "spkm3i", RPC_AUTH_GSS_SPKMI },
85 { "spkm3p", RPC_AUTH_GSS_SPKMP },
86 { "unix", AUTH_UNIX },
88 { "null", AUTH_NULL },
89 { "none", AUTH_NONE },
92 #define FMAPSIZE (sizeof(flav_map)/sizeof(flav_map[0]))
93 #define MAX_USER_FLAVOUR 16
95 static int parse_sec(char *sec, int *pseudoflavour)
97 int i, num_flavour = 0;
99 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
100 if (num_flavour >= MAX_USER_FLAVOUR) {
102 _("mount: maximum number of security flavors "
106 for (i = 0; i < FMAPSIZE; i++) {
107 if (strcmp(sec, flav_map[i].flavour) == 0) {
108 pseudoflavour[num_flavour++] = flav_map[i].fnum;
114 _("mount: unknown security type %s\n"), sec);
120 _("mount: no security flavors passed to sec= option\n"));
124 static int parse_devname(char *hostdir, char **hostname, char **dirname)
128 if (!(s = strchr(hostdir, ':'))) {
131 "directory to mount not in host:dir format\n"));
137 /* Ignore all but first hostname in replicated mounts
138 until they can be fully supported. (mack@sgi.com) */
139 if ((s = strchr(hostdir, ','))) {
143 "multiple hostnames not supported\n"));
148 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
151 addr->sin_family = AF_INET;
153 if (inet_aton(hostname, &addr->sin_addr))
155 if ((hp = gethostbyname(hostname)) == NULL) {
156 fprintf(stderr, _("mount: can't get address for %s\n"),
160 if (hp->h_length > sizeof(struct in_addr)) {
162 _("mount: got bad hp->h_length\n"));
163 hp->h_length = sizeof(struct in_addr);
165 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
169 static int get_my_ipv4addr(char *ip_addr, int len)
172 struct sockaddr_in myaddr;
174 if (gethostname(myname, sizeof(myname))) {
175 fprintf(stderr, _("mount: can't determine client address\n"));
178 if (fill_ipv4_sockaddr(myname, &myaddr))
180 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
181 ip_addr[len-1] = '\0';
185 int nfs4mount(const char *spec, const char *node, int *flags,
186 char **extra_opts, char **mount_opts,
189 static struct nfs4_mount_data data;
190 static char hostdir[1024];
191 static char ip_addr[16] = "127.0.0.1";
192 static struct sockaddr_in server_addr, client_addr;
193 static int pseudoflavour[MAX_USER_FLAVOUR];
195 int ip_addr_in_opts = 0;
197 char *hostname, *dirname, *old_opts;
209 if (strlen(spec) >= sizeof(hostdir)) {
210 fprintf(stderr, _("mount: "
211 "excessively long host:dir argument\n"));
214 strcpy(hostdir, spec);
215 if (parse_devname(hostdir, &hostname, &dirname))
218 if (fill_ipv4_sockaddr(hostname, &server_addr))
220 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
223 /* add IP address to mtab options for use when unmounting */
224 s = inet_ntoa(server_addr.sin_addr);
225 old_opts = *extra_opts;
228 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
229 fprintf(stderr, _("mount: "
230 "excessively long option argument\n"));
233 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
234 old_opts, *old_opts ? "," : "", s);
235 *extra_opts = xstrdup(new_opts);
237 /* Set default options.
238 * rsize/wsize and timeo are left 0 in order to
239 * let the kernel decide.
241 memset(&data, 0, sizeof(data));
247 data.proto = IPPROTO_TCP;
251 intr = NFS4_MOUNT_INTR;
254 retry = 10000; /* 10000 minutes ~ 1 week */
257 * NFSv4 specifies that the default port should be 2049
259 server_addr.sin_port = htons(NFS_PORT);
263 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
264 if ((opteq = strchr(opt, '='))) {
265 val = atoi(opteq + 1);
267 if (!strcmp(opt, "rsize"))
269 else if (!strcmp(opt, "wsize"))
271 else if (!strcmp(opt, "timeo"))
273 else if (!strcmp(opt, "retrans"))
275 else if (!strcmp(opt, "acregmin"))
277 else if (!strcmp(opt, "acregmax"))
279 else if (!strcmp(opt, "acdirmin"))
281 else if (!strcmp(opt, "acdirmax"))
283 else if (!strcmp(opt, "actimeo")) {
289 else if (!strcmp(opt, "retry"))
291 else if (!strcmp(opt, "port"))
292 server_addr.sin_port = htons(val);
293 else if (!strcmp(opt, "proto")) {
294 if (!strncmp(opteq+1, "tcp", 3))
295 data.proto = IPPROTO_TCP;
296 else if (!strncmp(opteq+1, "udp", 3))
297 data.proto = IPPROTO_UDP;
299 printf(_("Warning: Unrecognized proto= option.\n"));
300 } else if (!strcmp(opt, "clientaddr")) {
301 if (strlen(opteq+1) >= sizeof(ip_addr))
302 printf(_("Invalid client address %s"),
304 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
305 ip_addr[sizeof(ip_addr)-1] = '\0';
307 } else if (!strcmp(opt, "sec")) {
308 num_flavour = parse_sec(opteq+1, pseudoflavour);
311 } else if (!strcmp(opt, "addr")) {
314 printf(_("unknown nfs mount parameter: "
315 "%s=%d\n"), opt, val);
320 if (!strncmp(opt, "no", 2)) {
324 if (!strcmp(opt, "bg"))
326 else if (!strcmp(opt, "fg"))
328 else if (!strcmp(opt, "soft"))
330 else if (!strcmp(opt, "hard"))
332 else if (!strcmp(opt, "intr"))
334 else if (!strcmp(opt, "cto"))
336 else if (!strcmp(opt, "ac"))
339 printf(_("unknown nfs mount option: "
340 "%s%s\n"), val ? "" : "no", opt);
346 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
347 | (intr ? NFS4_MOUNT_INTR : 0)
348 | (nocto ? NFS4_MOUNT_NOCTO : 0)
349 | (noac ? NFS4_MOUNT_NOAC : 0);
352 * Give a warning if the rpc.idmapd daemon is not running
356 if (num_flavour == 0)
357 pseudoflavour[num_flavour++] = AUTH_UNIX;
360 * ditto with rpc.gssd daemon
364 data.auth_flavourlen = num_flavour;
365 data.auth_flavours = pseudoflavour;
367 data.client_addr.data = ip_addr;
368 data.client_addr.len = strlen(ip_addr);
370 data.mnt_path.data = dirname;
371 data.mnt_path.len = strlen(dirname);
373 data.hostname.data = hostname;
374 data.hostname.len = strlen(hostname);
375 data.host_addr = (struct sockaddr *)&server_addr;
376 data.host_addrlen = sizeof(server_addr);
378 #ifdef NFS_MOUNT_DEBUG
379 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
380 data.rsize, data.wsize, data.timeo, data.retrans);
381 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
382 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
383 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
384 ntohs(server_addr.sin_port), bg, retry, data.flags);
385 printf("soft = %d, intr = %d, nocto = %d, noac = %d\n",
386 (data.flags & NFS4_MOUNT_SOFT) != 0,
387 (data.flags & NFS4_MOUNT_INTR) != 0,
388 (data.flags & NFS4_MOUNT_NOCTO) != 0,
389 (data.flags & NFS4_MOUNT_NOAC) != 0);
391 if (num_flavour > 0) {
395 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
396 for (i = 0; i < FMAPSIZE; i++) {
397 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
398 printf("%s", flav_map[i].flavour);
402 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
405 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
408 timeout = time(NULL) + 60 * retry;
409 data.version = NFS4_MOUNT_VERSION;
413 "mount: pinging: prog %d vers %d prot %s port %d\n",
414 NFS_PROGRAM, 4, data.proto == IPPROTO_UDP ? "udp" : "tcp",
415 ntohs(server_addr.sin_port));
417 client_addr.sin_family = 0;
418 client_addr.sin_addr.s_addr = 0;
419 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
420 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
421 if (!ip_addr_in_opts &&
422 client_addr.sin_family != 0 &&
423 client_addr.sin_addr.s_addr != 0) {
424 snprintf(ip_addr, sizeof(ip_addr), "%s",
425 inet_ntoa(client_addr.sin_addr));
426 data.client_addr.len = strlen(ip_addr);
431 switch(rpc_createerr.cf_stat){
434 case RPC_SYSTEMERROR:
435 if (errno == ETIMEDOUT)
438 mount_errors(hostname, 0, bg);
443 mount_errors(hostname, 0, bg);
446 mount_errors(hostname, 1, bg);
450 *mount_opts = (char *) &data;