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>
37 #ifdef HAVE_RPCSVC_NFS_PROT_H
38 #include <rpcsvc/nfs_prot.h>
40 #include <linux/nfs.h>
41 #define nfsstat nfs_stat
44 #include "pseudoflavors.h"
48 #include "mount_constants.h"
49 #include "nfs4_mount.h"
50 #include "nfs_mount.h"
54 #if defined(VAR_LOCK_DIR)
55 #define DEFAULT_DIR VAR_LOCK_DIR
57 #define DEFAULT_DIR "/var/lock/subsys"
60 extern char *progname;
64 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
65 #define idmapd_check() do { \
66 if (access(IDMAPLCK, F_OK)) { \
67 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
68 " All uids will be mapped to the nobody uid.\n")); \
72 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
73 #define gssd_check() do { \
74 if (access(GSSDLCK, F_OK)) { \
75 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
83 #define MAX_USER_FLAVOUR 16
85 static int parse_sec(char *sec, int *pseudoflavour)
87 int i, num_flavour = 0;
89 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
90 if (num_flavour >= MAX_USER_FLAVOUR) {
91 nfs_error(_("%s: maximum number of security flavors "
92 "exceeded"), progname);
95 for (i = 0; i < flav_map_size; i++) {
96 if (strcmp(sec, flav_map[i].flavour) == 0) {
97 pseudoflavour[num_flavour++] = flav_map[i].fnum;
101 if (i == flav_map_size) {
102 nfs_error(_("%s: unknown security type %s\n"),
108 nfs_error(_("%s: no security flavors passed to sec= option"),
113 static int parse_devname(char *hostdir, char **hostname, char **dirname)
117 if (!(s = strchr(hostdir, ':'))) {
118 nfs_error(_("%s: directory to mount not in host:dir format"),
125 /* Ignore all but first hostname in replicated mounts
126 until they can be fully supported. (mack@sgi.com) */
127 if ((s = strchr(hostdir, ','))) {
129 nfs_error(_("%s: warning: multiple hostnames not supported"),
135 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
138 addr->sin_family = AF_INET;
140 if (inet_aton(hostname, &addr->sin_addr))
142 if ((hp = gethostbyname(hostname)) == NULL) {
143 nfs_error(_("%s: can't get address for %s\n"),
147 if (hp->h_length > sizeof(struct in_addr)) {
148 nfs_error(_("%s: got bad hp->h_length"), progname);
149 hp->h_length = sizeof(struct in_addr);
151 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
155 static int get_my_ipv4addr(char *ip_addr, int len)
158 struct sockaddr_in myaddr;
160 if (gethostname(myname, sizeof(myname))) {
161 nfs_error(_("%s: can't determine client address\n"),
165 if (fill_ipv4_sockaddr(myname, &myaddr))
167 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
168 ip_addr[len-1] = '\0';
172 int nfs4mount(const char *spec, const char *node, int flags,
173 char **extra_opts, int fake, int running_bg)
175 static struct nfs4_mount_data data;
176 static char hostdir[1024];
177 static char ip_addr[16] = "127.0.0.1";
178 static struct sockaddr_in server_addr, client_addr;
179 static int pseudoflavour[MAX_USER_FLAVOUR];
181 int ip_addr_in_opts = 0;
183 char *hostname, *dirname, *old_opts;
189 int nocto, noac, unshared;
195 if (strlen(spec) >= sizeof(hostdir)) {
196 nfs_error(_("%s: excessively long host:dir argument\n"),
200 strcpy(hostdir, spec);
201 if (parse_devname(hostdir, &hostname, &dirname))
204 if (fill_ipv4_sockaddr(hostname, &server_addr))
206 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
209 /* add IP address to mtab options for use when unmounting */
210 s = inet_ntoa(server_addr.sin_addr);
211 old_opts = *extra_opts;
214 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
215 nfs_error(_("%s: excessively long option argument\n"),
219 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
220 old_opts, *old_opts ? "," : "", s);
221 *extra_opts = xstrdup(new_opts);
223 /* Set default options.
224 * rsize/wsize and timeo are left 0 in order to
225 * let the kernel decide.
227 memset(&data, 0, sizeof(data));
233 data.proto = IPPROTO_TCP;
237 intr = NFS4_MOUNT_INTR;
241 retry = 10000; /* 10000 minutes ~ 1 week */
244 * NFSv4 specifies that the default port should be 2049
246 server_addr.sin_port = htons(NFS_PORT);
250 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
251 if ((opteq = strchr(opt, '='))) {
252 val = atoi(opteq + 1);
254 if (!strcmp(opt, "rsize"))
256 else if (!strcmp(opt, "wsize"))
258 else if (!strcmp(opt, "timeo"))
260 else if (!strcmp(opt, "retrans"))
262 else if (!strcmp(opt, "acregmin"))
264 else if (!strcmp(opt, "acregmax"))
266 else if (!strcmp(opt, "acdirmin"))
268 else if (!strcmp(opt, "acdirmax"))
270 else if (!strcmp(opt, "actimeo")) {
276 else if (!strcmp(opt, "retry"))
278 else if (!strcmp(opt, "port"))
279 server_addr.sin_port = htons(val);
280 else if (!strcmp(opt, "proto")) {
281 if (!strncmp(opteq+1, "tcp", 3))
282 data.proto = IPPROTO_TCP;
283 else if (!strncmp(opteq+1, "udp", 3))
284 data.proto = IPPROTO_UDP;
286 printf(_("Warning: Unrecognized proto= option.\n"));
287 } else if (!strcmp(opt, "clientaddr")) {
288 if (strlen(opteq+1) >= sizeof(ip_addr))
289 printf(_("Invalid client address %s"),
291 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
292 ip_addr[sizeof(ip_addr)-1] = '\0';
294 } else if (!strcmp(opt, "sec")) {
295 num_flavour = parse_sec(opteq+1, pseudoflavour);
298 } else if (!strcmp(opt, "addr") || sloppy) {
301 printf(_("unknown nfs mount parameter: "
302 "%s=%d\n"), opt, val);
307 if (!strncmp(opt, "no", 2)) {
311 if (!strcmp(opt, "bg"))
313 else if (!strcmp(opt, "fg"))
315 else if (!strcmp(opt, "soft"))
317 else if (!strcmp(opt, "hard"))
319 else if (!strcmp(opt, "intr"))
321 else if (!strcmp(opt, "cto"))
323 else if (!strcmp(opt, "ac"))
325 else if (!strcmp(opt, "sharecache"))
328 printf(_("unknown nfs mount option: %s%s\n"),
329 val ? "" : "no", opt);
335 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
336 | (intr ? NFS4_MOUNT_INTR : 0)
337 | (nocto ? NFS4_MOUNT_NOCTO : 0)
338 | (noac ? NFS4_MOUNT_NOAC : 0)
339 | (unshared ? NFS4_MOUNT_UNSHARED : 0);
342 * Give a warning if the rpc.idmapd daemon is not running
345 /* We shouldn't have these checks as nothing in this package
346 * creates the files that are checked
350 if (num_flavour == 0)
351 pseudoflavour[num_flavour++] = AUTH_UNIX;
354 * ditto with rpc.gssd daemon
359 data.auth_flavourlen = num_flavour;
360 data.auth_flavours = pseudoflavour;
362 data.client_addr.data = ip_addr;
363 data.client_addr.len = strlen(ip_addr);
365 data.mnt_path.data = dirname;
366 data.mnt_path.len = strlen(dirname);
368 data.hostname.data = hostname;
369 data.hostname.len = strlen(hostname);
370 data.host_addr = (struct sockaddr *)&server_addr;
371 data.host_addrlen = sizeof(server_addr);
373 #ifdef NFS_MOUNT_DEBUG
374 printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
375 data.rsize, data.wsize, data.timeo, data.retrans);
376 printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
377 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
378 printf(_("port = %d, bg = %d, retry = %d, flags = %.8x\n"),
379 ntohs(server_addr.sin_port), bg, retry, data.flags);
380 printf(_("soft = %d, intr = %d, nocto = %d, noac = %d, "
381 "nosharecache = %d\n"),
382 (data.flags & NFS4_MOUNT_SOFT) != 0,
383 (data.flags & NFS4_MOUNT_INTR) != 0,
384 (data.flags & NFS4_MOUNT_NOCTO) != 0,
385 (data.flags & NFS4_MOUNT_NOAC) != 0,
386 (data.flags & NFS4_MOUNT_UNSHARED) != 0);
388 if (num_flavour > 0) {
392 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
393 for (i = 0; i < flav_map_size; i++) {
394 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
395 printf("%s", flav_map[i].flavour);
399 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
402 printf(_("proto = %s\n"), (data.proto == IPPROTO_TCP) ? _("tcp") : _("udp"));
405 timeout = time(NULL) + 60 * retry;
406 data.version = NFS4_MOUNT_VERSION;
409 printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
410 progname, NFS_PROGRAM, 4,
411 data.proto == IPPROTO_UDP ? "udp" : "tcp",
412 ntohs(server_addr.sin_port));
414 client_addr.sin_family = 0;
415 client_addr.sin_addr.s_addr = 0;
416 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
417 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
418 if (!ip_addr_in_opts &&
419 client_addr.sin_family != 0 &&
420 client_addr.sin_addr.s_addr != 0) {
421 snprintf(ip_addr, sizeof(ip_addr), "%s",
422 inet_ntoa(client_addr.sin_addr));
423 data.client_addr.len = strlen(ip_addr);
428 switch(rpc_createerr.cf_stat){
431 case RPC_SYSTEMERROR:
432 if (errno == ETIMEDOUT)
435 rpc_mount_errors(hostname, 0, bg);
440 rpc_mount_errors(hostname, 0, bg);
443 rpc_mount_errors(hostname, 1, bg);
448 if (mount(spec, node, "nfs4",
449 flags & ~(MS_USER|MS_USERS), &data)) {
450 mount_error(spec, node, errno);