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 <sys/mount.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
33 #ifdef HAVE_RPCSVC_NFS_PROT_H
34 #include <rpcsvc/nfs_prot.h>
36 #include <linux/nfs.h>
37 #define nfsstat nfs_stat
40 #include "pseudoflavors.h"
44 #include "mount_constants.h"
45 #include "nfs4_mount.h"
46 #include "nfs_mount.h"
50 #if defined(VAR_LOCK_DIR)
51 #define DEFAULT_DIR VAR_LOCK_DIR
53 #define DEFAULT_DIR "/var/lock/subsys"
56 extern char *progname;
60 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
61 #define idmapd_check() do { \
62 if (access(IDMAPLCK, F_OK)) { \
63 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
64 " All uids will be mapped to the nobody uid.\n")); \
68 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
69 #define gssd_check() do { \
70 if (access(GSSDLCK, F_OK)) { \
71 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
79 #define MAX_USER_FLAVOUR 16
81 static int parse_sec(char *sec, int *pseudoflavour)
83 int i, num_flavour = 0;
85 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
86 if (num_flavour >= MAX_USER_FLAVOUR) {
87 nfs_error(_("%s: maximum number of security flavors "
88 "exceeded"), progname);
91 for (i = 0; i < flav_map_size; i++) {
92 if (strcmp(sec, flav_map[i].flavour) == 0) {
93 pseudoflavour[num_flavour++] = flav_map[i].fnum;
97 if (i == flav_map_size) {
98 nfs_error(_("%s: unknown security type %s\n"),
104 nfs_error(_("%s: no security flavors passed to sec= option"),
109 static int parse_devname(char *hostdir, char **hostname, char **dirname)
113 if (!(s = strchr(hostdir, ':'))) {
114 nfs_error(_("%s: directory to mount not in host:dir format"),
121 /* Ignore all but first hostname in replicated mounts
122 until they can be fully supported. (mack@sgi.com) */
123 if ((s = strchr(hostdir, ','))) {
125 nfs_error(_("%s: warning: multiple hostnames not supported"),
131 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
134 addr->sin_family = AF_INET;
136 if (inet_aton(hostname, &addr->sin_addr))
138 if ((hp = gethostbyname(hostname)) == NULL) {
139 nfs_error(_("%s: can't get address for %s\n"),
143 if (hp->h_length > sizeof(struct in_addr)) {
144 nfs_error(_("%s: got bad hp->h_length"), progname);
145 hp->h_length = sizeof(struct in_addr);
147 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
151 static int get_my_ipv4addr(char *ip_addr, int len)
154 struct sockaddr_in myaddr;
156 if (gethostname(myname, sizeof(myname))) {
157 nfs_error(_("%s: can't determine client address\n"),
161 if (fill_ipv4_sockaddr(myname, &myaddr))
163 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
164 ip_addr[len-1] = '\0';
168 int nfs4mount(const char *spec, const char *node, int flags,
169 char **extra_opts, int fake)
171 static struct nfs4_mount_data data;
172 static char hostdir[1024];
173 static char ip_addr[16] = "127.0.0.1";
174 static struct sockaddr_in server_addr, client_addr;
175 static int pseudoflavour[MAX_USER_FLAVOUR];
177 int ip_addr_in_opts = 0;
179 char *hostname, *dirname, *old_opts;
185 int nocto, noac, unshared;
191 if (strlen(spec) >= sizeof(hostdir)) {
192 nfs_error(_("%s: excessively long host:dir argument\n"),
196 strcpy(hostdir, spec);
197 if (parse_devname(hostdir, &hostname, &dirname))
200 if (fill_ipv4_sockaddr(hostname, &server_addr))
202 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
205 /* add IP address to mtab options for use when unmounting */
206 s = inet_ntoa(server_addr.sin_addr);
207 old_opts = *extra_opts;
210 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
211 nfs_error(_("%s: excessively long option argument\n"),
215 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
216 old_opts, *old_opts ? "," : "", s);
217 *extra_opts = xstrdup(new_opts);
219 /* Set default options.
220 * rsize/wsize and timeo are left 0 in order to
221 * let the kernel decide.
223 memset(&data, 0, sizeof(data));
229 data.proto = IPPROTO_TCP;
233 intr = NFS4_MOUNT_INTR;
237 retry = 10000; /* 10000 minutes ~ 1 week */
240 * NFSv4 specifies that the default port should be 2049
242 server_addr.sin_port = htons(NFS_PORT);
246 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
247 if ((opteq = strchr(opt, '='))) {
248 val = atoi(opteq + 1);
250 if (!strcmp(opt, "rsize"))
252 else if (!strcmp(opt, "wsize"))
254 else if (!strcmp(opt, "timeo"))
256 else if (!strcmp(opt, "retrans"))
258 else if (!strcmp(opt, "acregmin"))
260 else if (!strcmp(opt, "acregmax"))
262 else if (!strcmp(opt, "acdirmin"))
264 else if (!strcmp(opt, "acdirmax"))
266 else if (!strcmp(opt, "actimeo")) {
272 else if (!strcmp(opt, "retry"))
274 else if (!strcmp(opt, "port"))
275 server_addr.sin_port = htons(val);
276 else if (!strcmp(opt, "proto")) {
277 if (!strncmp(opteq+1, "tcp", 3))
278 data.proto = IPPROTO_TCP;
279 else if (!strncmp(opteq+1, "udp", 3))
280 data.proto = IPPROTO_UDP;
282 printf(_("Warning: Unrecognized proto= option.\n"));
283 } else if (!strcmp(opt, "clientaddr")) {
284 if (strlen(opteq+1) >= sizeof(ip_addr))
285 printf(_("Invalid client address %s"),
287 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
288 ip_addr[sizeof(ip_addr)-1] = '\0';
290 } else if (!strcmp(opt, "sec")) {
291 num_flavour = parse_sec(opteq+1, pseudoflavour);
294 } else if (!strcmp(opt, "addr") || sloppy) {
297 printf(_("unknown nfs mount parameter: "
298 "%s=%d\n"), opt, val);
303 if (!strncmp(opt, "no", 2)) {
307 if (!strcmp(opt, "bg"))
309 else if (!strcmp(opt, "fg"))
311 else if (!strcmp(opt, "soft"))
313 else if (!strcmp(opt, "hard"))
315 else if (!strcmp(opt, "intr"))
317 else if (!strcmp(opt, "cto"))
319 else if (!strcmp(opt, "ac"))
321 else if (!strcmp(opt, "sharecache"))
324 printf(_("unknown nfs mount option: "
325 "%s%s\n"), val ? "" : "no", opt);
331 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
332 | (intr ? NFS4_MOUNT_INTR : 0)
333 | (nocto ? NFS4_MOUNT_NOCTO : 0)
334 | (noac ? NFS4_MOUNT_NOAC : 0)
335 | (unshared ? NFS4_MOUNT_UNSHARED : 0);
338 * Give a warning if the rpc.idmapd daemon is not running
341 /* We shouldn't have these checks as nothing in this package
342 * creates the files that are checked
346 if (num_flavour == 0)
347 pseudoflavour[num_flavour++] = AUTH_UNIX;
350 * ditto with rpc.gssd daemon
355 data.auth_flavourlen = num_flavour;
356 data.auth_flavours = pseudoflavour;
358 data.client_addr.data = ip_addr;
359 data.client_addr.len = strlen(ip_addr);
361 data.mnt_path.data = dirname;
362 data.mnt_path.len = strlen(dirname);
364 data.hostname.data = hostname;
365 data.hostname.len = strlen(hostname);
366 data.host_addr = (struct sockaddr *)&server_addr;
367 data.host_addrlen = sizeof(server_addr);
369 #ifdef NFS_MOUNT_DEBUG
370 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
371 data.rsize, data.wsize, data.timeo, data.retrans);
372 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
373 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
374 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
375 ntohs(server_addr.sin_port), bg, retry, data.flags);
376 printf("soft = %d, intr = %d, nocto = %d, noac = %d, "
377 "nosharecache = %d\n",
378 (data.flags & NFS4_MOUNT_SOFT) != 0,
379 (data.flags & NFS4_MOUNT_INTR) != 0,
380 (data.flags & NFS4_MOUNT_NOCTO) != 0,
381 (data.flags & NFS4_MOUNT_NOAC) != 0,
382 (data.flags & NFS4_MOUNT_UNSHARED) != 0);
384 if (num_flavour > 0) {
388 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
389 for (i = 0; i < flav_map_size; i++) {
390 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
391 printf("%s", flav_map[i].flavour);
395 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
398 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
401 timeout = time(NULL) + 60 * retry;
402 data.version = NFS4_MOUNT_VERSION;
405 printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
406 progname, NFS_PROGRAM, 4,
407 data.proto == IPPROTO_UDP ? "udp" : "tcp",
408 ntohs(server_addr.sin_port));
410 client_addr.sin_family = 0;
411 client_addr.sin_addr.s_addr = 0;
412 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
413 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
414 if (!ip_addr_in_opts &&
415 client_addr.sin_family != 0 &&
416 client_addr.sin_addr.s_addr != 0) {
417 snprintf(ip_addr, sizeof(ip_addr), "%s",
418 inet_ntoa(client_addr.sin_addr));
419 data.client_addr.len = strlen(ip_addr);
424 switch(rpc_createerr.cf_stat){
427 case RPC_SYSTEMERROR:
428 if (errno == ETIMEDOUT)
431 mount_errors(hostname, 0, bg);
436 mount_errors(hostname, 0, bg);
439 mount_errors(hostname, 1, bg);
444 if (mount(spec, node, "nfs4",
445 flags & ~(MS_USER|MS_USERS), &data)) {
446 mount_error(spec, node, errno);