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.
32 #include <sys/mount.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
38 #ifdef HAVE_RPCSVC_NFS_PROT_H
39 #include <rpcsvc/nfs_prot.h>
41 #include <linux/nfs.h>
42 #define nfsstat nfs_stat
45 #include "pseudoflavors.h"
50 #include "mount_constants.h"
51 #include "nfs4_mount.h"
52 #include "nfs_mount.h"
56 #if defined(VAR_LOCK_DIR)
57 #define DEFAULT_DIR VAR_LOCK_DIR
59 #define DEFAULT_DIR "/var/lock/subsys"
62 extern char *progname;
66 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
67 #define idmapd_check() do { \
68 if (access(IDMAPLCK, F_OK)) { \
69 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
70 " All uids will be mapped to the nobody uid.\n")); \
74 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
75 #define gssd_check() do { \
76 if (access(GSSDLCK, F_OK)) { \
77 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
85 #define MAX_USER_FLAVOUR 16
87 static int parse_sec(char *sec, int *pseudoflavour)
89 int i, num_flavour = 0;
91 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
92 if (num_flavour >= MAX_USER_FLAVOUR) {
93 nfs_error(_("%s: maximum number of security flavors "
94 "exceeded"), progname);
97 for (i = 0; i < flav_map_size; i++) {
98 if (strcmp(sec, flav_map[i].flavour) == 0) {
99 pseudoflavour[num_flavour++] = flav_map[i].fnum;
103 if (i == flav_map_size) {
104 nfs_error(_("%s: unknown security type %s\n"),
110 nfs_error(_("%s: no security flavors passed to sec= option"),
115 static int parse_devname(char *hostdir, char **hostname, char **dirname)
119 if (!(s = strchr(hostdir, ':'))) {
120 nfs_error(_("%s: directory to mount not in host:dir format"),
127 /* Ignore all but first hostname in replicated mounts
128 until they can be fully supported. (mack@sgi.com) */
129 if ((s = strchr(hostdir, ','))) {
131 nfs_error(_("%s: warning: multiple hostnames not supported"),
137 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
140 addr->sin_family = AF_INET;
142 if (inet_aton(hostname, &addr->sin_addr))
144 if ((hp = gethostbyname(hostname)) == NULL) {
145 nfs_error(_("%s: can't get address for %s\n"),
149 if (hp->h_length > (int)sizeof(struct in_addr)) {
150 nfs_error(_("%s: got bad hp->h_length"), progname);
151 hp->h_length = sizeof(struct in_addr);
153 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
157 static int get_my_ipv4addr(char *ip_addr, int len)
160 struct sockaddr_in myaddr;
162 if (gethostname(myname, sizeof(myname))) {
163 nfs_error(_("%s: can't determine client address\n"),
167 if (fill_ipv4_sockaddr(myname, &myaddr))
169 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
170 ip_addr[len-1] = '\0';
174 int nfs4mount(const char *spec, const char *node, int flags,
175 char **extra_opts, int fake, int running_bg)
177 static struct nfs4_mount_data data;
178 static char hostdir[1024];
179 static char ip_addr[16] = "127.0.0.1";
180 static struct sockaddr_in server_addr, client_addr;
181 static int pseudoflavour[MAX_USER_FLAVOUR];
183 int ip_addr_in_opts = 0;
185 char *hostname, *dirname, *old_opts;
191 int nocto, noac, unshared;
193 int retval = EX_FAIL;
196 if (strlen(spec) >= sizeof(hostdir)) {
197 nfs_error(_("%s: excessively long host:dir argument\n"),
201 strcpy(hostdir, spec);
202 if (parse_devname(hostdir, &hostname, &dirname))
205 if (fill_ipv4_sockaddr(hostname, &server_addr))
207 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
210 /* add IP address to mtab options for use when unmounting */
211 s = inet_ntoa(server_addr.sin_addr);
212 old_opts = *extra_opts;
215 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
216 nfs_error(_("%s: excessively long option argument\n"),
221 strncpy(new_opts, old_opts, sizeof(new_opts));
223 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
224 old_opts, *old_opts ? "," : "", s);
225 *extra_opts = xstrdup(new_opts);
227 /* Set default options.
228 * rsize/wsize and timeo are left 0 in order to
229 * let the kernel decide.
231 memset(&data, 0, sizeof(data));
237 data.proto = IPPROTO_TCP;
241 intr = NFS4_MOUNT_INTR;
248 * NFSv4 specifies that the default port should be 2049
250 server_addr.sin_port = htons(NFS_PORT);
254 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
255 if ((opteq = strchr(opt, '='))) {
256 val = atoi(opteq + 1);
258 if (!strcmp(opt, "rsize"))
260 else if (!strcmp(opt, "wsize"))
262 else if (!strcmp(opt, "timeo"))
264 else if (!strcmp(opt, "retrans"))
266 else if (!strcmp(opt, "acregmin"))
268 else if (!strcmp(opt, "acregmax"))
270 else if (!strcmp(opt, "acdirmin"))
272 else if (!strcmp(opt, "acdirmax"))
274 else if (!strcmp(opt, "actimeo")) {
280 else if (!strcmp(opt, "retry"))
282 else if (!strcmp(opt, "port"))
283 server_addr.sin_port = htons(val);
284 else if (!strcmp(opt, "proto")) {
285 if (!strncmp(opteq+1, "tcp", 3))
286 data.proto = IPPROTO_TCP;
287 else if (!strncmp(opteq+1, "udp", 3))
288 data.proto = IPPROTO_UDP;
290 printf(_("Warning: Unrecognized proto= option.\n"));
291 } else if (!strcmp(opt, "clientaddr")) {
292 if (strlen(opteq+1) >= sizeof(ip_addr))
293 printf(_("Invalid client address %s"),
295 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
296 ip_addr[sizeof(ip_addr)-1] = '\0';
298 } else if (!strcmp(opt, "sec")) {
299 num_flavour = parse_sec(opteq+1, pseudoflavour);
302 } else if (!strcmp(opt, "addr") || sloppy) {
305 printf(_("unknown nfs mount parameter: "
306 "%s=%d\n"), opt, val);
311 if (!strncmp(opt, "no", 2)) {
315 if (!strcmp(opt, "bg"))
317 else if (!strcmp(opt, "fg"))
319 else if (!strcmp(opt, "soft"))
321 else if (!strcmp(opt, "hard"))
323 else if (!strcmp(opt, "intr"))
325 else if (!strcmp(opt, "cto"))
327 else if (!strcmp(opt, "ac"))
329 else if (!strcmp(opt, "sharecache"))
332 printf(_("unknown nfs mount option: %s%s\n"),
333 val ? "" : "no", opt);
339 /* if retry is still -1, then it wasn't set via an option */
342 retry = 10000; /* 10000 mins == ~1 week */
344 retry = 2; /* 2 min default on fg mounts */
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)
351 | (unshared ? NFS4_MOUNT_UNSHARED : 0);
354 * Give a warning if the rpc.idmapd daemon is not running
357 /* We shouldn't have these checks as nothing in this package
358 * creates the files that are checked
362 if (num_flavour == 0)
363 pseudoflavour[num_flavour++] = AUTH_UNIX;
366 * ditto with rpc.gssd daemon
371 data.auth_flavourlen = num_flavour;
372 data.auth_flavours = pseudoflavour;
374 data.client_addr.data = ip_addr;
375 data.client_addr.len = strlen(ip_addr);
377 data.mnt_path.data = dirname;
378 data.mnt_path.len = strlen(dirname);
380 data.hostname.data = hostname;
381 data.hostname.len = strlen(hostname);
382 data.host_addr = (struct sockaddr *)&server_addr;
383 data.host_addrlen = sizeof(server_addr);
385 #ifdef NFS_MOUNT_DEBUG
386 printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
387 data.rsize, data.wsize, data.timeo, data.retrans);
388 printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
389 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
390 printf(_("port = %d, bg = %d, retry = %d, flags = %.8x\n"),
391 ntohs(server_addr.sin_port), bg, retry, data.flags);
392 printf(_("soft = %d, intr = %d, nocto = %d, noac = %d, "
393 "nosharecache = %d\n"),
394 (data.flags & NFS4_MOUNT_SOFT) != 0,
395 (data.flags & NFS4_MOUNT_INTR) != 0,
396 (data.flags & NFS4_MOUNT_NOCTO) != 0,
397 (data.flags & NFS4_MOUNT_NOAC) != 0,
398 (data.flags & NFS4_MOUNT_UNSHARED) != 0);
400 if (num_flavour > 0) {
404 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
405 for (i = 0; i < flav_map_size; i++) {
406 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
407 printf("%s", flav_map[i].flavour);
411 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
414 printf(_("proto = %s\n"), (data.proto == IPPROTO_TCP) ? _("tcp") : _("udp"));
417 timeout = time(NULL) + 60 * retry;
418 data.version = NFS4_MOUNT_VERSION;
421 printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
422 progname, NFS_PROGRAM, 4,
423 data.proto == IPPROTO_UDP ? "udp" : "tcp",
424 ntohs(server_addr.sin_port));
426 client_addr.sin_family = 0;
427 client_addr.sin_addr.s_addr = 0;
428 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
429 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
430 if (!ip_addr_in_opts &&
431 client_addr.sin_family != 0 &&
432 client_addr.sin_addr.s_addr != 0) {
433 snprintf(ip_addr, sizeof(ip_addr), "%s",
434 inet_ntoa(client_addr.sin_addr));
435 data.client_addr.len = strlen(ip_addr);
441 switch(rpc_createerr.cf_stat) {
444 case RPC_SYSTEMERROR:
445 if (errno == ETIMEDOUT)
448 rpc_mount_errors(hostname, 0, bg);
453 if (bg && !running_bg) {
461 rpc_mount_errors(hostname, 0, bg);
464 rpc_mount_errors(hostname, 1, bg);
469 if (mount(spec, node, "nfs4",
470 flags & ~(MS_USER|MS_USERS), &data)) {
471 mount_error(spec, node, errno);