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"
55 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
56 #define idmapd_check() do { \
57 if (access(IDMAPLCK, F_OK)) { \
58 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
59 " All uids will be mapped to the nobody uid.\n")); \
63 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
64 #define gssd_check() do { \
65 if (access(GSSDLCK, F_OK)) { \
66 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
78 { "krb5", RPC_AUTH_GSS_KRB5 },
79 { "krb5i", RPC_AUTH_GSS_KRB5I },
80 { "krb5p", RPC_AUTH_GSS_KRB5P },
81 { "lipkey", RPC_AUTH_GSS_LKEY },
82 { "lipkey-i", RPC_AUTH_GSS_LKEYI },
83 { "lipkey-p", RPC_AUTH_GSS_LKEYP },
84 { "spkm3", RPC_AUTH_GSS_SPKM },
85 { "spkm3i", RPC_AUTH_GSS_SPKMI },
86 { "spkm3p", RPC_AUTH_GSS_SPKMP },
87 { "unix", AUTH_UNIX },
89 { "null", AUTH_NULL },
90 { "none", AUTH_NONE },
93 #define FMAPSIZE (sizeof(flav_map)/sizeof(flav_map[0]))
94 #define MAX_USER_FLAVOUR 16
96 static int parse_sec(char *sec, int *pseudoflavour)
98 int i, num_flavour = 0;
100 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
101 if (num_flavour >= MAX_USER_FLAVOUR) {
103 _("mount: maximum number of security flavors "
107 for (i = 0; i < FMAPSIZE; i++) {
108 if (strcmp(sec, flav_map[i].flavour) == 0) {
109 pseudoflavour[num_flavour++] = flav_map[i].fnum;
115 _("mount: unknown security type %s\n"), sec);
121 _("mount: no security flavors passed to sec= option\n"));
125 static int parse_devname(char *hostdir, char **hostname, char **dirname)
129 if (!(s = strchr(hostdir, ':'))) {
132 "directory to mount not in host:dir format\n"));
138 /* Ignore all but first hostname in replicated mounts
139 until they can be fully supported. (mack@sgi.com) */
140 if ((s = strchr(hostdir, ','))) {
144 "multiple hostnames not supported\n"));
149 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
152 addr->sin_family = AF_INET;
154 if (inet_aton(hostname, &addr->sin_addr))
156 if ((hp = gethostbyname(hostname)) == NULL) {
157 fprintf(stderr, _("mount: can't get address for %s\n"),
161 if (hp->h_length > sizeof(struct in_addr)) {
163 _("mount: got bad hp->h_length\n"));
164 hp->h_length = sizeof(struct in_addr);
166 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
170 static int get_my_ipv4addr(char *ip_addr, int len)
173 struct sockaddr_in myaddr;
175 if (gethostname(myname, sizeof(myname))) {
176 fprintf(stderr, _("mount: can't determine client address\n"));
179 if (fill_ipv4_sockaddr(myname, &myaddr))
181 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
182 ip_addr[len-1] = '\0';
186 int nfs4mount(const char *spec, const char *node, int *flags,
187 char **extra_opts, char **mount_opts,
190 static struct nfs4_mount_data data;
191 static char hostdir[1024];
192 static char ip_addr[16] = "127.0.0.1";
193 static struct sockaddr_in server_addr, client_addr;
194 static int pseudoflavour[MAX_USER_FLAVOUR];
196 int ip_addr_in_opts = 0;
198 char *hostname, *dirname, *old_opts;
210 if (strlen(spec) >= sizeof(hostdir)) {
211 fprintf(stderr, _("mount: "
212 "excessively long host:dir argument\n"));
215 strcpy(hostdir, spec);
216 if (parse_devname(hostdir, &hostname, &dirname))
219 if (fill_ipv4_sockaddr(hostname, &server_addr))
221 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
224 /* add IP address to mtab options for use when unmounting */
225 s = inet_ntoa(server_addr.sin_addr);
226 old_opts = *extra_opts;
229 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
230 fprintf(stderr, _("mount: "
231 "excessively long option argument\n"));
234 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
235 old_opts, *old_opts ? "," : "", s);
236 *extra_opts = xstrdup(new_opts);
238 /* Set default options.
239 * rsize/wsize and timeo are left 0 in order to
240 * let the kernel decide.
242 memset(&data, 0, sizeof(data));
248 data.proto = IPPROTO_TCP;
252 intr = NFS4_MOUNT_INTR;
255 retry = 10000; /* 10000 minutes ~ 1 week */
258 * NFSv4 specifies that the default port should be 2049
260 server_addr.sin_port = htons(NFS_PORT);
264 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
265 if ((opteq = strchr(opt, '='))) {
266 val = atoi(opteq + 1);
268 if (!strcmp(opt, "rsize"))
270 else if (!strcmp(opt, "wsize"))
272 else if (!strcmp(opt, "timeo"))
274 else if (!strcmp(opt, "retrans"))
276 else if (!strcmp(opt, "acregmin"))
278 else if (!strcmp(opt, "acregmax"))
280 else if (!strcmp(opt, "acdirmin"))
282 else if (!strcmp(opt, "acdirmax"))
284 else if (!strcmp(opt, "actimeo")) {
290 else if (!strcmp(opt, "retry"))
292 else if (!strcmp(opt, "port"))
293 server_addr.sin_port = htons(val);
294 else if (!strcmp(opt, "proto")) {
295 if (!strncmp(opteq+1, "tcp", 3))
296 data.proto = IPPROTO_TCP;
297 else if (!strncmp(opteq+1, "udp", 3))
298 data.proto = IPPROTO_UDP;
300 printf(_("Warning: Unrecognized proto= option.\n"));
301 } else if (!strcmp(opt, "clientaddr")) {
302 if (strlen(opteq+1) >= sizeof(ip_addr))
303 printf(_("Invalid client address %s"),
305 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
306 ip_addr[sizeof(ip_addr)-1] = '\0';
308 } else if (!strcmp(opt, "sec")) {
309 num_flavour = parse_sec(opteq+1, pseudoflavour);
312 } else if (!strcmp(opt, "addr") || sloppy) {
315 printf(_("unknown nfs mount parameter: "
316 "%s=%d\n"), opt, val);
321 if (!strncmp(opt, "no", 2)) {
325 if (!strcmp(opt, "bg"))
327 else if (!strcmp(opt, "fg"))
329 else if (!strcmp(opt, "soft"))
331 else if (!strcmp(opt, "hard"))
333 else if (!strcmp(opt, "intr"))
335 else if (!strcmp(opt, "cto"))
337 else if (!strcmp(opt, "ac"))
340 printf(_("unknown nfs mount option: "
341 "%s%s\n"), val ? "" : "no", opt);
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);
353 * Give a warning if the rpc.idmapd daemon is not running
356 /* We shouldn't have these checks as nothing in this package
357 * creates the files that are checked
361 if (num_flavour == 0)
362 pseudoflavour[num_flavour++] = AUTH_UNIX;
365 * ditto with rpc.gssd daemon
370 data.auth_flavourlen = num_flavour;
371 data.auth_flavours = pseudoflavour;
373 data.client_addr.data = ip_addr;
374 data.client_addr.len = strlen(ip_addr);
376 data.mnt_path.data = dirname;
377 data.mnt_path.len = strlen(dirname);
379 data.hostname.data = hostname;
380 data.hostname.len = strlen(hostname);
381 data.host_addr = (struct sockaddr *)&server_addr;
382 data.host_addrlen = sizeof(server_addr);
384 #ifdef NFS_MOUNT_DEBUG
385 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
386 data.rsize, data.wsize, data.timeo, data.retrans);
387 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
388 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
389 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
390 ntohs(server_addr.sin_port), bg, retry, data.flags);
391 printf("soft = %d, intr = %d, nocto = %d, noac = %d\n",
392 (data.flags & NFS4_MOUNT_SOFT) != 0,
393 (data.flags & NFS4_MOUNT_INTR) != 0,
394 (data.flags & NFS4_MOUNT_NOCTO) != 0,
395 (data.flags & NFS4_MOUNT_NOAC) != 0);
397 if (num_flavour > 0) {
401 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
402 for (i = 0; i < FMAPSIZE; i++) {
403 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
404 printf("%s", flav_map[i].flavour);
408 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
411 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
414 timeout = time(NULL) + 60 * retry;
415 data.version = NFS4_MOUNT_VERSION;
419 "mount: pinging: prog %d vers %d prot %s port %d\n",
420 NFS_PROGRAM, 4, data.proto == IPPROTO_UDP ? "udp" : "tcp",
421 ntohs(server_addr.sin_port));
423 client_addr.sin_family = 0;
424 client_addr.sin_addr.s_addr = 0;
425 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
426 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
427 if (!ip_addr_in_opts &&
428 client_addr.sin_family != 0 &&
429 client_addr.sin_addr.s_addr != 0) {
430 snprintf(ip_addr, sizeof(ip_addr), "%s",
431 inet_ntoa(client_addr.sin_addr));
432 data.client_addr.len = strlen(ip_addr);
437 switch(rpc_createerr.cf_stat){
440 case RPC_SYSTEMERROR:
441 if (errno == ETIMEDOUT)
444 mount_errors(hostname, 0, bg);
449 mount_errors(hostname, 0, bg);
452 mount_errors(hostname, 1, bg);
456 *mount_opts = (char *) &data;