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"
50 #if defined(VAR_LOCK_DIR)
51 #define DEFAULT_DIR VAR_LOCK_DIR
53 #define DEFAULT_DIR "/var/lock/subsys"
59 char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
60 #define idmapd_check() do { \
61 if (access(IDMAPLCK, F_OK)) { \
62 printf(_("Warning: rpc.idmapd appears not to be running.\n" \
63 " All uids will be mapped to the nobody uid.\n")); \
67 char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
68 #define gssd_check() do { \
69 if (access(GSSDLCK, F_OK)) { \
70 printf(_("Warning: rpc.gssd appears not to be running.\n")); \
78 #define MAX_USER_FLAVOUR 16
80 static int parse_sec(char *sec, int *pseudoflavour)
82 int i, num_flavour = 0;
84 for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
85 if (num_flavour >= MAX_USER_FLAVOUR) {
87 _("mount: maximum number of security flavors "
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) {
99 _("mount: unknown security type %s\n"), sec);
105 _("mount: no security flavors passed to sec= option\n"));
109 static int parse_devname(char *hostdir, char **hostname, char **dirname)
113 if (!(s = strchr(hostdir, ':'))) {
116 "directory to mount not in host:dir format\n"));
122 /* Ignore all but first hostname in replicated mounts
123 until they can be fully supported. (mack@sgi.com) */
124 if ((s = strchr(hostdir, ','))) {
128 "multiple hostnames not supported\n"));
133 static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
136 addr->sin_family = AF_INET;
138 if (inet_aton(hostname, &addr->sin_addr))
140 if ((hp = gethostbyname(hostname)) == NULL) {
141 fprintf(stderr, _("mount: can't get address for %s\n"),
145 if (hp->h_length > sizeof(struct in_addr)) {
147 _("mount: got bad hp->h_length\n"));
148 hp->h_length = sizeof(struct in_addr);
150 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
154 static int get_my_ipv4addr(char *ip_addr, int len)
157 struct sockaddr_in myaddr;
159 if (gethostname(myname, sizeof(myname))) {
160 fprintf(stderr, _("mount: can't determine client address\n"));
163 if (fill_ipv4_sockaddr(myname, &myaddr))
165 snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
166 ip_addr[len-1] = '\0';
170 int nfs4mount(const char *spec, const char *node, int flags,
171 char **extra_opts, int fake)
173 static struct nfs4_mount_data data;
174 static char hostdir[1024];
175 static char ip_addr[16] = "127.0.0.1";
176 static struct sockaddr_in server_addr, client_addr;
177 static int pseudoflavour[MAX_USER_FLAVOUR];
179 int ip_addr_in_opts = 0;
181 char *hostname, *dirname, *old_opts;
187 int nocto, noac, unshared;
193 if (strlen(spec) >= sizeof(hostdir)) {
194 fprintf(stderr, _("mount: "
195 "excessively long host:dir argument\n"));
198 strcpy(hostdir, spec);
199 if (parse_devname(hostdir, &hostname, &dirname))
202 if (fill_ipv4_sockaddr(hostname, &server_addr))
204 if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
207 /* add IP address to mtab options for use when unmounting */
208 s = inet_ntoa(server_addr.sin_addr);
209 old_opts = *extra_opts;
212 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
213 fprintf(stderr, _("mount: "
214 "excessively long option argument\n"));
217 snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
218 old_opts, *old_opts ? "," : "", s);
219 *extra_opts = xstrdup(new_opts);
221 /* Set default options.
222 * rsize/wsize and timeo are left 0 in order to
223 * let the kernel decide.
225 memset(&data, 0, sizeof(data));
231 data.proto = IPPROTO_TCP;
235 intr = NFS4_MOUNT_INTR;
239 retry = 10000; /* 10000 minutes ~ 1 week */
242 * NFSv4 specifies that the default port should be 2049
244 server_addr.sin_port = htons(NFS_PORT);
248 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
249 if ((opteq = strchr(opt, '='))) {
250 val = atoi(opteq + 1);
252 if (!strcmp(opt, "rsize"))
254 else if (!strcmp(opt, "wsize"))
256 else if (!strcmp(opt, "timeo"))
258 else if (!strcmp(opt, "retrans"))
260 else if (!strcmp(opt, "acregmin"))
262 else if (!strcmp(opt, "acregmax"))
264 else if (!strcmp(opt, "acdirmin"))
266 else if (!strcmp(opt, "acdirmax"))
268 else if (!strcmp(opt, "actimeo")) {
274 else if (!strcmp(opt, "retry"))
276 else if (!strcmp(opt, "port"))
277 server_addr.sin_port = htons(val);
278 else if (!strcmp(opt, "proto")) {
279 if (!strncmp(opteq+1, "tcp", 3))
280 data.proto = IPPROTO_TCP;
281 else if (!strncmp(opteq+1, "udp", 3))
282 data.proto = IPPROTO_UDP;
284 printf(_("Warning: Unrecognized proto= option.\n"));
285 } else if (!strcmp(opt, "clientaddr")) {
286 if (strlen(opteq+1) >= sizeof(ip_addr))
287 printf(_("Invalid client address %s"),
289 strncpy(ip_addr,opteq+1, sizeof(ip_addr));
290 ip_addr[sizeof(ip_addr)-1] = '\0';
292 } else if (!strcmp(opt, "sec")) {
293 num_flavour = parse_sec(opteq+1, pseudoflavour);
296 } else if (!strcmp(opt, "addr") || sloppy) {
299 printf(_("unknown nfs mount parameter: "
300 "%s=%d\n"), opt, val);
305 if (!strncmp(opt, "no", 2)) {
309 if (!strcmp(opt, "bg"))
311 else if (!strcmp(opt, "fg"))
313 else if (!strcmp(opt, "soft"))
315 else if (!strcmp(opt, "hard"))
317 else if (!strcmp(opt, "intr"))
319 else if (!strcmp(opt, "cto"))
321 else if (!strcmp(opt, "ac"))
323 else if (!strcmp(opt, "sharecache"))
326 printf(_("unknown nfs mount option: "
327 "%s%s\n"), val ? "" : "no", opt);
333 data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
334 | (intr ? NFS4_MOUNT_INTR : 0)
335 | (nocto ? NFS4_MOUNT_NOCTO : 0)
336 | (noac ? NFS4_MOUNT_NOAC : 0)
337 | (unshared ? NFS4_MOUNT_UNSHARED : 0);
340 * Give a warning if the rpc.idmapd daemon is not running
343 /* We shouldn't have these checks as nothing in this package
344 * creates the files that are checked
348 if (num_flavour == 0)
349 pseudoflavour[num_flavour++] = AUTH_UNIX;
352 * ditto with rpc.gssd daemon
357 data.auth_flavourlen = num_flavour;
358 data.auth_flavours = pseudoflavour;
360 data.client_addr.data = ip_addr;
361 data.client_addr.len = strlen(ip_addr);
363 data.mnt_path.data = dirname;
364 data.mnt_path.len = strlen(dirname);
366 data.hostname.data = hostname;
367 data.hostname.len = strlen(hostname);
368 data.host_addr = (struct sockaddr *)&server_addr;
369 data.host_addrlen = sizeof(server_addr);
371 #ifdef NFS_MOUNT_DEBUG
372 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
373 data.rsize, data.wsize, data.timeo, data.retrans);
374 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
375 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
376 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
377 ntohs(server_addr.sin_port), bg, retry, data.flags);
378 printf("soft = %d, intr = %d, nocto = %d, noac = %d, "
379 "nosharecache = %d\n",
380 (data.flags & NFS4_MOUNT_SOFT) != 0,
381 (data.flags & NFS4_MOUNT_INTR) != 0,
382 (data.flags & NFS4_MOUNT_NOCTO) != 0,
383 (data.flags & NFS4_MOUNT_NOAC) != 0,
384 (data.flags & NFS4_MOUNT_UNSHARED) != 0);
386 if (num_flavour > 0) {
390 for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
391 for (i = 0; i < flav_map_size; i++) {
392 if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
393 printf("%s", flav_map[i].flavour);
397 printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
400 printf("proto = %s\n", (data.proto == IPPROTO_TCP) ? "tcp" : "udp");
403 timeout = time(NULL) + 60 * retry;
404 data.version = NFS4_MOUNT_VERSION;
408 "mount: pinging: prog %d vers %d prot %s port %d\n",
409 NFS_PROGRAM, 4, data.proto == IPPROTO_UDP ? "udp" : "tcp",
410 ntohs(server_addr.sin_port));
412 client_addr.sin_family = 0;
413 client_addr.sin_addr.s_addr = 0;
414 clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
415 if (rpc_createerr.cf_stat == RPC_SUCCESS) {
416 if (!ip_addr_in_opts &&
417 client_addr.sin_family != 0 &&
418 client_addr.sin_addr.s_addr != 0) {
419 snprintf(ip_addr, sizeof(ip_addr), "%s",
420 inet_ntoa(client_addr.sin_addr));
421 data.client_addr.len = strlen(ip_addr);
426 switch(rpc_createerr.cf_stat){
429 case RPC_SYSTEMERROR:
430 if (errno == ETIMEDOUT)
433 mount_errors(hostname, 0, bg);
438 mount_errors(hostname, 0, bg);
441 mount_errors(hostname, 1, bg);
446 if (mount(spec, node, "nfs4",
447 flags & ~(MS_USER|MS_USERS), &data)) {
448 mount_error(spec, node, errno);