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"
45 #include "mount_constants.h"
46 #include "nfs4_mount.h"
47 #include "nfs_mount.h"
51 #if defined(VAR_LOCK_DIR)
52 #define DEFAULT_DIR VAR_LOCK_DIR
54 #define DEFAULT_DIR "/var/lock/subsys"
57 extern char *progname;
61 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
62 #define idmapd_check() do { \
63 if (access(IDMAPLCK, F_OK)) { \
64 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
65 " All uids will be mapped to the nobody uid.\n")); \
69 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
70 #define gssd_check() do { \
71 if (access(GSSDLCK, F_OK)) { \
72 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
80 #define MAX_USER_FLAVOUR 16
82 static int parse_sec(char *sec, int *pseudoflavour)
84 int i, num_flavour = 0;
86 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
87 if (num_flavour >= MAX_USER_FLAVOUR) {
88 nfs_error(_("%s: maximum number of security flavors "
89 "exceeded"), progname);
92 for (i = 0; i < flav_map_size; i++) {
93 if (strcmp(sec, flav_map[i].flavour) == 0) {
94 pseudoflavour[num_flavour++] = flav_map[i].fnum;
98 if (i == flav_map_size) {
99 nfs_error(_("%s: unknown security type %s\n"),
105 nfs_error(_("%s: no security flavors passed to sec= option"),
110 static int parse_devname(char *hostdir, char **hostname, char **dirname)
114 if (!(s = strchr(hostdir, ':'))) {
115 nfs_error(_("%s: directory to mount not in host:dir format"),
122 /* Ignore all but first hostname in replicated mounts
123 until they can be fully supported. (mack@sgi.com) */
124 if ((s = strchr(hostdir, ','))) {
126 nfs_error(_("%s: warning: multiple hostnames not supported"),
132 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
135 addr->sin_family = AF_INET;
137 if (inet_aton(hostname, &addr->sin_addr))
139 if ((hp = gethostbyname(hostname)) == NULL) {
140 nfs_error(_("%s: can't get address for %s\n"),
144 if (hp->h_length > sizeof(struct in_addr)) {
145 nfs_error(_("%s: got bad hp->h_length"), progname);
146 hp->h_length = sizeof(struct in_addr);
148 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
152 static int get_my_ipv4addr(char *ip_addr, int len)
155 struct sockaddr_in myaddr;
157 if (gethostname(myname, sizeof(myname))) {
158 nfs_error(_("%s: can't determine client address\n"),
162 if (fill_ipv4_sockaddr(myname, &myaddr))
164 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
165 ip_addr[len-1] = '\0';
169 int nfs4mount(const char *spec, const char *node, int flags,
170 char **extra_opts, int fake)
172 static struct nfs4_mount_data data;
173 static char hostdir[1024];
174 static char ip_addr[16] = "127.0.0.1";
175 static struct sockaddr_in server_addr, client_addr;
176 static int pseudoflavour[MAX_USER_FLAVOUR];
178 int ip_addr_in_opts = 0;
180 char *hostname, *dirname, *old_opts;
186 int nocto, noac, unshared;
192 if (strlen(spec) >= sizeof(hostdir)) {
193 nfs_error(_("%s: excessively long host:dir argument\n"),
197 strcpy(hostdir, spec);
198 if (parse_devname(hostdir, &hostname, &dirname))
201 if (fill_ipv4_sockaddr(hostname, &server_addr))
203 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
206 /* add IP address to mtab options for use when unmounting */
207 s = inet_ntoa(server_addr.sin_addr);
208 old_opts = *extra_opts;
211 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
212 nfs_error(_("%s: excessively long option argument\n"),
216 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
217 old_opts, *old_opts ? "," : "", s);
218 *extra_opts = xstrdup(new_opts);
220 /* Set default options.
221 * rsize/wsize and timeo are left 0 in order to
222 * let the kernel decide.
224 memset(&data, 0, sizeof(data));
230 data.proto = IPPROTO_TCP;
234 intr = NFS4_MOUNT_INTR;
238 retry = 10000; /* 10000 minutes ~ 1 week */
241 * NFSv4 specifies that the default port should be 2049
243 server_addr.sin_port = htons(NFS_PORT);
247 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
248 if ((opteq = strchr(opt, '='))) {
249 val = atoi(opteq + 1);
251 if (!strcmp(opt, "rsize"))
253 else if (!strcmp(opt, "wsize"))
255 else if (!strcmp(opt, "timeo"))
257 else if (!strcmp(opt, "retrans"))
259 else if (!strcmp(opt, "acregmin"))
261 else if (!strcmp(opt, "acregmax"))
263 else if (!strcmp(opt, "acdirmin"))
265 else if (!strcmp(opt, "acdirmax"))
267 else if (!strcmp(opt, "actimeo")) {
273 else if (!strcmp(opt, "retry"))
275 else if (!strcmp(opt, "port"))
276 server_addr.sin_port = htons(val);
277 else if (!strcmp(opt, "proto")) {
278 if (!strncmp(opteq+1, "tcp", 3))
279 data.proto = IPPROTO_TCP;
280 else if (!strncmp(opteq+1, "udp", 3))
281 data.proto = IPPROTO_UDP;
283 printf(_("Warning: Unrecognized proto= option.\n"));
284 } else if (!strcmp(opt, "clientaddr")) {
285 if (strlen(opteq+1) >= sizeof(ip_addr))
286 printf(_("Invalid client address %s"),
288 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
289 ip_addr[sizeof(ip_addr)-1] = '\0';
291 } else if (!strcmp(opt, "sec")) {
292 num_flavour = parse_sec(opteq+1, pseudoflavour);
295 } else if (!strcmp(opt, "addr") || sloppy) {
298 printf(_("unknown nfs mount parameter: "
299 "%s=%d\n"), opt, val);
304 if (!strncmp(opt, "no", 2)) {
308 if (!strcmp(opt, "bg"))
310 else if (!strcmp(opt, "fg"))
312 else if (!strcmp(opt, "soft"))
314 else if (!strcmp(opt, "hard"))
316 else if (!strcmp(opt, "intr"))
318 else if (!strcmp(opt, "cto"))
320 else if (!strcmp(opt, "ac"))
322 else if (!strcmp(opt, "sharecache"))
325 printf(_("unknown nfs mount option: "
326 "%s%s\n"), val ? "" : "no", opt);
332 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
333 | (intr ? NFS4_MOUNT_INTR : 0)
334 | (nocto ? NFS4_MOUNT_NOCTO : 0)
335 | (noac ? NFS4_MOUNT_NOAC : 0)
336 | (unshared ? NFS4_MOUNT_UNSHARED : 0);
339 * Give a warning if the rpc.idmapd daemon is not running
342 /* We shouldn't have these checks as nothing in this package
343 * creates the files that are checked
347 if (num_flavour == 0)
348 pseudoflavour[num_flavour++] = AUTH_UNIX;
351 * ditto with rpc.gssd daemon
356 data.auth_flavourlen = num_flavour;
357 data.auth_flavours = pseudoflavour;
359 data.client_addr.data = ip_addr;
360 data.client_addr.len = strlen(ip_addr);
362 data.mnt_path.data = dirname;
363 data.mnt_path.len = strlen(dirname);
365 data.hostname.data = hostname;
366 data.hostname.len = strlen(hostname);
367 data.host_addr = (struct sockaddr *)&server_addr;
368 data.host_addrlen = sizeof(server_addr);
370 #ifdef NFS_MOUNT_DEBUG
371 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
372 data.rsize, data.wsize, data.timeo, data.retrans);
373 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
374 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
375 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
376 ntohs(server_addr.sin_port), bg, retry, data.flags);
377 printf("soft = %d, intr = %d, nocto = %d, noac = %d, "
378 "nosharecache = %d\n",
379 (data.flags & NFS4_MOUNT_SOFT) != 0,
380 (data.flags & NFS4_MOUNT_INTR) != 0,
381 (data.flags & NFS4_MOUNT_NOCTO) != 0,
382 (data.flags & NFS4_MOUNT_NOAC) != 0,
383 (data.flags & NFS4_MOUNT_UNSHARED) != 0);
385 if (num_flavour > 0) {
389 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
390 for (i = 0; i < flav_map_size; i++) {
391 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
392 printf("%s", flav_map[i].flavour);
396 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
399 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
402 timeout = time(NULL) + 60 * retry;
403 data.version = NFS4_MOUNT_VERSION;
406 printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
407 progname, NFS_PROGRAM, 4,
408 data.proto == IPPROTO_UDP ? "udp" : "tcp",
409 ntohs(server_addr.sin_port));
411 client_addr.sin_family = 0;
412 client_addr.sin_addr.s_addr = 0;
413 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
414 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
415 if (!ip_addr_in_opts &&
416 client_addr.sin_family != 0 &&
417 client_addr.sin_addr.s_addr != 0) {
418 snprintf(ip_addr, sizeof(ip_addr), "%s",
419 inet_ntoa(client_addr.sin_addr));
420 data.client_addr.len = strlen(ip_addr);
425 switch(rpc_createerr.cf_stat){
428 case RPC_SYSTEMERROR:
429 if (errno == ETIMEDOUT)
432 mount_errors(hostname, 0, bg);
437 mount_errors(hostname, 0, bg);
440 mount_errors(hostname, 1, bg);
445 if (mount(spec, node, "nfs4",
446 flags & ~(MS_USER|MS_USERS), &data)) {
447 mount_error(spec, node, errno);