~adam-stokes/ubuntu/quantal/nfs-utils/hang-on-kernel-expire-794112

« back to all changes in this revision

Viewing changes to .pc/13-preserve-explicit-port-2049.patch/utils/mount/stropts.c

  • Committer: Steve Langasek
  • Date: 2011-10-29 21:56:45 UTC
  • mfrom: (14.1.15 sid)
  • Revision ID: steve.langasek@canonical.com-20111029215645-ohwji14mw3tamz84
Merge Debian version 1:1.2.5-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * stropts.c -- NFS mount using C string to pass options to kernel
3
 
 *
4
 
 * Copyright (C) 2007 Oracle.  All rights reserved.
5
 
 * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or
8
 
 * modify it under the terms of the GNU General Public
9
 
 * License as published by the Free Software Foundation; either
10
 
 * version 2 of the License, or (at your option) any later version.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
 * General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public
18
 
 * License along with this program; if not, write to the
19
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
 
 * Boston, MA 021110-1307, USA.
21
 
 *
22
 
 */
23
 
 
24
 
#ifdef HAVE_CONFIG_H
25
 
#include <config.h>
26
 
#endif
27
 
 
28
 
#include <unistd.h>
29
 
#include <errno.h>
30
 
#include <netdb.h>
31
 
#include <time.h>
32
 
 
33
 
#include <sys/socket.h>
34
 
#include <sys/mount.h>
35
 
#include <netinet/in.h>
36
 
#include <arpa/inet.h>
37
 
 
38
 
#include "sockaddr.h"
39
 
#include "xcommon.h"
40
 
#include "mount.h"
41
 
#include "nls.h"
42
 
#include "nfsrpc.h"
43
 
#include "mount_constants.h"
44
 
#include "stropts.h"
45
 
#include "error.h"
46
 
#include "network.h"
47
 
#include "parse_opt.h"
48
 
#include "version.h"
49
 
#include "parse_dev.h"
50
 
#include "conffile.h"
51
 
 
52
 
#ifndef NFS_PROGRAM
53
 
#define NFS_PROGRAM     (100003)
54
 
#endif
55
 
 
56
 
#ifndef NFS_PORT
57
 
#define NFS_PORT        (2049)
58
 
#endif
59
 
 
60
 
#ifndef NFS_MAXHOSTNAME
61
 
#define NFS_MAXHOSTNAME         (255)
62
 
#endif
63
 
 
64
 
#ifndef NFS_MAXPATHNAME
65
 
#define NFS_MAXPATHNAME         (1024)
66
 
#endif
67
 
 
68
 
#ifndef NFS_DEF_FG_TIMEOUT_MINUTES
69
 
#define NFS_DEF_FG_TIMEOUT_MINUTES      (2u)
70
 
#endif
71
 
 
72
 
#ifndef NFS_DEF_BG_TIMEOUT_MINUTES
73
 
#define NFS_DEF_BG_TIMEOUT_MINUTES      (10000u)
74
 
#endif
75
 
 
76
 
extern int nfs_mount_data_version;
77
 
extern char *progname;
78
 
extern int verbose;
79
 
extern int sloppy;
80
 
 
81
 
struct nfsmount_info {
82
 
        const char              *spec,          /* server:/path */
83
 
                                *node,          /* mounted-on dir */
84
 
                                *type;          /* "nfs" or "nfs4" */
85
 
        char                    *hostname;      /* server's hostname */
86
 
        struct addrinfo         *address;       /* server's addresses */
87
 
 
88
 
        struct mount_options    *options;       /* parsed mount options */
89
 
        char                    **extra_opts;   /* string for /etc/mtab */
90
 
 
91
 
        unsigned long           version;        /* NFS version */
92
 
        int                     flags,          /* MS_ flags */
93
 
                                fake,           /* actually do the mount? */
94
 
                                child;          /* forked bg child? */
95
 
};
96
 
 
97
 
#ifdef MOUNT_CONFIG
98
 
static void nfs_default_version(struct nfsmount_info *mi);
99
 
 
100
 
static void nfs_default_version(struct nfsmount_info *mi)
101
 
{
102
 
        extern unsigned long config_default_vers;
103
 
        /*
104
 
         * Use the default value set in the config file when
105
 
         * the version has not been explicitly set.
106
 
         */
107
 
        if (mi->version == 0 && config_default_vers) {
108
 
                if (config_default_vers < 4)
109
 
                        mi->version = config_default_vers;
110
 
        }
111
 
}
112
 
#else
113
 
inline void nfs_default_version(__attribute__ ((unused)) struct nfsmount_info *mi) {}
114
 
#endif /* MOUNT_CONFIG */
115
 
 
116
 
/*
117
 
 * Obtain a retry timeout value based on the value of the "retry=" option.
118
 
 *
119
 
 * Returns a time_t timeout timestamp, in seconds.
120
 
 */
121
 
static time_t nfs_parse_retry_option(struct mount_options *options,
122
 
                                     const time_t default_timeout)
123
 
{
124
 
        time_t timeout_minutes;
125
 
        long tmp;
126
 
 
127
 
        timeout_minutes = default_timeout;
128
 
        switch (po_get_numeric(options, "retry", &tmp)) {
129
 
        case PO_NOT_FOUND:
130
 
                break;
131
 
        case PO_FOUND:
132
 
                if (tmp >= 0) {
133
 
                        timeout_minutes = tmp;
134
 
                        break;
135
 
                }
136
 
                /*FALLTHROUGH*/
137
 
        case PO_BAD_VALUE:
138
 
                if (verbose)
139
 
                        nfs_error(_("%s: invalid retry timeout was specified; "
140
 
                                        "using default timeout"), progname);
141
 
                break;
142
 
        }
143
 
 
144
 
        return time(NULL) + (timeout_minutes * 60);
145
 
}
146
 
 
147
 
/*
148
 
 * Convert the passed-in sockaddr-style address to presentation
149
 
 * format, then append an option of the form "keyword=address".
150
 
 *
151
 
 * Returns 1 if the option was appended successfully; otherwise zero.
152
 
 */
153
 
static int nfs_append_generic_address_option(const struct sockaddr *sap,
154
 
                                             const socklen_t salen,
155
 
                                             const char *keyword,
156
 
                                             struct mount_options *options)
157
 
{
158
 
        char address[NI_MAXHOST];
159
 
        char new_option[512];
160
 
        int len;
161
 
 
162
 
        if (!nfs_present_sockaddr(sap, salen, address, sizeof(address)))
163
 
                goto out_err;
164
 
 
165
 
        len = snprintf(new_option, sizeof(new_option), "%s=%s",
166
 
                                                keyword, address);
167
 
        if (len < 0 || (size_t)len >= sizeof(new_option))
168
 
                goto out_err;
169
 
 
170
 
        if (po_append(options, new_option) != PO_SUCCEEDED)
171
 
                goto out_err;
172
 
 
173
 
        return 1;
174
 
 
175
 
out_err:
176
 
        nfs_error(_("%s: failed to construct %s option"), progname, keyword);
177
 
        return 0;
178
 
}
179
 
 
180
 
/*
181
 
 * Append the 'addr=' option to the options string to pass a resolved
182
 
 * server address to the kernel.  After a successful mount, this address
183
 
 * is also added to /etc/mtab for use when unmounting.
184
 
 *
185
 
 * If 'addr=' is already present, we strip it out.  This prevents users
186
 
 * from setting a bogus 'addr=' option themselves, and also allows bg
187
 
 * retries to recompute the server's address, in case it has changed.
188
 
 *
189
 
 * Returns 1 if 'addr=' option appended successfully;
190
 
 * otherwise zero.
191
 
 */
192
 
static int nfs_append_addr_option(const struct sockaddr *sap,
193
 
                                  socklen_t salen,
194
 
                                  struct mount_options *options)
195
 
{
196
 
        po_remove_all(options, "addr");
197
 
        return nfs_append_generic_address_option(sap, salen, "addr", options);
198
 
}
199
 
 
200
 
/*
201
 
 * Called to discover our address and append an appropriate 'clientaddr='
202
 
 * option to the options string.
203
 
 *
204
 
 * Returns 1 if 'clientaddr=' option created successfully or if
205
 
 * 'clientaddr=' option is already present; otherwise zero.
206
 
 */
207
 
static int nfs_append_clientaddr_option(const struct sockaddr *sap,
208
 
                                        socklen_t salen,
209
 
                                        struct mount_options *options)
210
 
{
211
 
        union nfs_sockaddr address;
212
 
        struct sockaddr *my_addr = &address.sa;
213
 
        socklen_t my_len = sizeof(address);
214
 
 
215
 
        if (po_contains(options, "clientaddr") == PO_FOUND)
216
 
                return 1;
217
 
 
218
 
        nfs_callback_address(sap, salen, my_addr, &my_len);
219
 
 
220
 
        return nfs_append_generic_address_option(my_addr, my_len,
221
 
                                                        "clientaddr", options);
222
 
}
223
 
 
224
 
/*
225
 
 * Determine whether to append a 'mountaddr=' option.  The option is needed if:
226
 
 *
227
 
 *   1. "mounthost=" was specified, or
228
 
 *   2. The address families for proto= and mountproto= are different.
229
 
 */
230
 
static int nfs_fix_mounthost_option(struct mount_options *options,
231
 
                const char *nfs_hostname)
232
 
{
233
 
        union nfs_sockaddr address;
234
 
        struct sockaddr *sap = &address.sa;
235
 
        socklen_t salen = sizeof(address);
236
 
        sa_family_t nfs_family, mnt_family;
237
 
        char *mounthost;
238
 
 
239
 
        if (!nfs_nfs_proto_family(options, &nfs_family))
240
 
                return 0;
241
 
        if (!nfs_mount_proto_family(options, &mnt_family))
242
 
                return 0;
243
 
 
244
 
        mounthost = po_get(options, "mounthost");
245
 
        if (mounthost == NULL) {
246
 
                if (nfs_family == mnt_family)
247
 
                        return 1;
248
 
                mounthost = (char *)nfs_hostname;
249
 
        }
250
 
 
251
 
        if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
252
 
                nfs_error(_("%s: unable to determine mount server's address"),
253
 
                                progname);
254
 
                return 0;
255
 
        }
256
 
 
257
 
        return nfs_append_generic_address_option(sap, salen,
258
 
                                                        "mountaddr", options);
259
 
}
260
 
 
261
 
/*
262
 
 * Returns zero if the "lock" option is in effect, but statd
263
 
 * can't be started.  Otherwise, returns 1.
264
 
 */
265
 
static const char *nfs_lock_opttbl[] = {
266
 
        "nolock",
267
 
        "lock",
268
 
        NULL,
269
 
};
270
 
 
271
 
static int nfs_verify_lock_option(struct mount_options *options)
272
 
{
273
 
        if (po_rightmost(options, nfs_lock_opttbl) == 0)
274
 
                return 1;
275
 
 
276
 
        if (!start_statd()) {
277
 
                nfs_error(_("%s: rpc.statd is not running but is "
278
 
                            "required for remote locking."), progname);
279
 
                nfs_error(_("%s: Either use '-o nolock' to keep "
280
 
                            "locks local, or start statd."), progname);
281
 
                return 0;
282
 
        }
283
 
 
284
 
        return 1;
285
 
}
286
 
 
287
 
static int nfs_append_sloppy_option(struct mount_options *options)
288
 
{
289
 
        if (!sloppy || linux_version_code() < MAKE_VERSION(2, 6, 27))
290
 
                return 1;
291
 
 
292
 
        if (po_append(options, "sloppy") == PO_FAILED)
293
 
                return 0;
294
 
        return 1;
295
 
}
296
 
 
297
 
static int nfs_set_version(struct nfsmount_info *mi)
298
 
{
299
 
        if (!nfs_nfs_version(mi->options, &mi->version))
300
 
                return 0;
301
 
 
302
 
        if (strncmp(mi->type, "nfs4", 4) == 0)
303
 
                mi->version = 4;
304
 
 
305
 
        /*
306
 
         * Before 2.6.32, the kernel NFS client didn't
307
 
         * support "-t nfs vers=4" mounts, so NFS version
308
 
         * 4 cannot be included when autonegotiating
309
 
         * while running on those kernels.
310
 
         */
311
 
        if (mi->version == 0 &&
312
 
            linux_version_code() <= MAKE_VERSION(2, 6, 31))
313
 
                mi->version = 3;
314
 
 
315
 
        /*
316
 
         * If we still don't know, check for version-specific
317
 
         * mount options.
318
 
         */
319
 
        if (mi->version == 0) {
320
 
                if (po_contains(mi->options, "mounthost") ||
321
 
                    po_contains(mi->options, "mountaddr") ||
322
 
                    po_contains(mi->options, "mountvers") ||
323
 
                    po_contains(mi->options, "mountproto"))
324
 
                        mi->version = 3;
325
 
        }
326
 
 
327
 
        /*
328
 
         * If enabled, see if the default version was
329
 
         * set in the config file
330
 
         */
331
 
        nfs_default_version(mi);
332
 
        
333
 
        return 1;
334
 
}
335
 
 
336
 
/*
337
 
 * Set up mandatory non-version specific NFS mount options.
338
 
 *
339
 
 * Returns 1 if successful; otherwise zero.
340
 
 */
341
 
static int nfs_validate_options(struct nfsmount_info *mi)
342
 
{
343
 
        struct addrinfo hint = {
344
 
                .ai_protocol    = (int)IPPROTO_UDP,
345
 
        };
346
 
        sa_family_t family;
347
 
        int error;
348
 
 
349
 
        if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
350
 
                return 0;
351
 
 
352
 
        if (!nfs_nfs_proto_family(mi->options, &family))
353
 
                return 0;
354
 
 
355
 
        hint.ai_family = (int)family;
356
 
        error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address);
357
 
        if (error != 0) {
358
 
                nfs_error(_("%s: Failed to resolve server %s: %s"),
359
 
                        progname, mi->hostname, gai_strerror(error));
360
 
                mi->address = NULL;
361
 
                return 0;
362
 
        }
363
 
 
364
 
        if (!nfs_set_version(mi))
365
 
                return 0;
366
 
 
367
 
        if (!nfs_append_sloppy_option(mi->options))
368
 
                return 0;
369
 
 
370
 
        if (!nfs_append_addr_option(mi->address->ai_addr,
371
 
                                        mi->address->ai_addrlen, mi->options))
372
 
                return 0;
373
 
 
374
 
        return 1;
375
 
}
376
 
 
377
 
/*
378
 
 * Get NFS/mnt server addresses from mount options
379
 
 *
380
 
 * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
381
 
 * if all goes well; otherwise zero.
382
 
 */
383
 
static int nfs_extract_server_addresses(struct mount_options *options,
384
 
                                        struct sockaddr *nfs_saddr,
385
 
                                        socklen_t *nfs_salen,
386
 
                                        struct sockaddr *mnt_saddr,
387
 
                                        socklen_t *mnt_salen)
388
 
{
389
 
        char *option;
390
 
 
391
 
        option = po_get(options, "addr");
392
 
        if (option == NULL)
393
 
                return 0;
394
 
        if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen))
395
 
                return 0;
396
 
 
397
 
        option = po_get(options, "mountaddr");
398
 
        if (option == NULL) {
399
 
                memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
400
 
                *mnt_salen = *nfs_salen;
401
 
        } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen))
402
 
                return 0;
403
 
 
404
 
        return 1;
405
 
}
406
 
 
407
 
static int nfs_construct_new_options(struct mount_options *options,
408
 
                                     struct sockaddr *nfs_saddr,
409
 
                                     struct pmap *nfs_pmap,
410
 
                                     struct sockaddr *mnt_saddr,
411
 
                                     struct pmap *mnt_pmap)
412
 
{
413
 
        char new_option[64];
414
 
        char *netid;
415
 
 
416
 
        po_remove_all(options, "nfsprog");
417
 
        po_remove_all(options, "mountprog");
418
 
 
419
 
        po_remove_all(options, "v2");
420
 
        po_remove_all(options, "v3");
421
 
        po_remove_all(options, "vers");
422
 
        po_remove_all(options, "nfsvers");
423
 
        snprintf(new_option, sizeof(new_option) - 1,
424
 
                 "vers=%lu", nfs_pmap->pm_vers);
425
 
        if (po_append(options, new_option) == PO_FAILED)
426
 
                return 0;
427
 
 
428
 
        po_remove_all(options, "proto");
429
 
        po_remove_all(options, "udp");
430
 
        po_remove_all(options, "tcp");
431
 
        netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
432
 
        if (netid == NULL)
433
 
                return 0;
434
 
        snprintf(new_option, sizeof(new_option) - 1,
435
 
                         "proto=%s", netid);
436
 
        free(netid);
437
 
        if (po_append(options, new_option) == PO_FAILED)
438
 
                return 0;
439
 
 
440
 
        po_remove_all(options, "port");
441
 
        if (nfs_pmap->pm_port != NFS_PORT) {
442
 
                snprintf(new_option, sizeof(new_option) - 1,
443
 
                         "port=%lu", nfs_pmap->pm_port);
444
 
                if (po_append(options, new_option) == PO_FAILED)
445
 
                        return 0;
446
 
        }
447
 
 
448
 
        po_remove_all(options, "mountvers");
449
 
        snprintf(new_option, sizeof(new_option) - 1,
450
 
                 "mountvers=%lu", mnt_pmap->pm_vers);
451
 
        if (po_append(options, new_option) == PO_FAILED)
452
 
                return 0;
453
 
 
454
 
        po_remove_all(options, "mountproto");
455
 
        netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
456
 
        if (netid == NULL)
457
 
                return 0;
458
 
        snprintf(new_option, sizeof(new_option) - 1,
459
 
                         "mountproto=%s", netid);
460
 
        free(netid);
461
 
        if (po_append(options, new_option) == PO_FAILED)
462
 
                return 0;
463
 
 
464
 
        po_remove_all(options, "mountport");
465
 
        snprintf(new_option, sizeof(new_option) - 1,
466
 
                 "mountport=%lu", mnt_pmap->pm_port);
467
 
        if (po_append(options, new_option) == PO_FAILED)
468
 
                return 0;
469
 
 
470
 
        return 1;
471
 
}
472
 
 
473
 
/*
474
 
 * Reconstruct the mount option string based on a portmapper probe
475
 
 * of the server.  Returns one if the server's portmapper returned
476
 
 * something we can use, otherwise zero.
477
 
 *
478
 
 * To handle version and transport protocol fallback properly, we
479
 
 * need to parse some of the mount options in order to set up a
480
 
 * portmap probe.  Mount options that nfs_rewrite_pmap_mount_options()
481
 
 * doesn't recognize are left alone.
482
 
 *
483
 
 * Returns TRUE if rewriting was successful; otherwise
484
 
 * FALSE is returned if some failure occurred.
485
 
 */
486
 
static int
487
 
nfs_rewrite_pmap_mount_options(struct mount_options *options)
488
 
{
489
 
        union nfs_sockaddr nfs_address;
490
 
        struct sockaddr *nfs_saddr = &nfs_address.sa;
491
 
        socklen_t nfs_salen = sizeof(nfs_address);
492
 
        struct pmap nfs_pmap;
493
 
        union nfs_sockaddr mnt_address;
494
 
        struct sockaddr *mnt_saddr = &mnt_address.sa;
495
 
        socklen_t mnt_salen = sizeof(mnt_address);
496
 
        unsigned long protocol;
497
 
        struct pmap mnt_pmap;
498
 
 
499
 
        /*
500
 
         * Version and transport negotiation is not required
501
 
         * and does not work for RDMA mounts.
502
 
         */
503
 
        if (!nfs_nfs_protocol(options, &protocol)) {
504
 
                errno = EINVAL;
505
 
                return 0;
506
 
        }
507
 
        if (protocol == NFSPROTO_RDMA)
508
 
                goto out;
509
 
 
510
 
        /*
511
 
         * Extract just the options needed to contact server.
512
 
         * Bail now if any of these have bad values.
513
 
         */
514
 
        if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen,
515
 
                                                mnt_saddr, &mnt_salen)) {
516
 
                errno = EINVAL;
517
 
                return 0;
518
 
        }
519
 
        if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) {
520
 
                errno = EINVAL;
521
 
                return 0;
522
 
        }
523
 
 
524
 
        /*
525
 
         * The kernel NFS client doesn't support changing the RPC
526
 
         * program number for these services, so force the value of
527
 
         * these fields before probing the server's ports.
528
 
         */
529
 
        nfs_pmap.pm_prog = NFS_PROGRAM;
530
 
        mnt_pmap.pm_prog = MOUNTPROG;
531
 
 
532
 
        /*
533
 
         * If the server's rpcbind service isn't available, we can't
534
 
         * negotiate.  Bail now if we can't contact it.
535
 
         */
536
 
        if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
537
 
                                 nfs_saddr, nfs_salen, &nfs_pmap)) {
538
 
                errno = ESPIPE;
539
 
                if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
540
 
                        errno = EOPNOTSUPP;
541
 
                else if (rpc_createerr.cf_error.re_errno != 0)
542
 
                        errno = rpc_createerr.cf_error.re_errno;
543
 
                return 0;
544
 
        }
545
 
 
546
 
        if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
547
 
                                        mnt_saddr, &mnt_pmap)) {
548
 
                if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO)
549
 
                        errno = EPROTONOSUPPORT;
550
 
                else
551
 
                        errno = EINVAL;
552
 
                return 0;
553
 
        }
554
 
 
555
 
out:
556
 
        errno = 0;
557
 
        return 1;
558
 
}
559
 
 
560
 
/*
561
 
 * Do the mount(2) system call.
562
 
 *
563
 
 * Returns TRUE if successful, otherwise FALSE.
564
 
 * "errno" is set to reflect the individual error.
565
 
 */
566
 
static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
567
 
{
568
 
        char *options = NULL;
569
 
        int result;
570
 
 
571
 
        if (mi->fake)
572
 
                return 1;
573
 
 
574
 
        if (po_join(opts, &options) == PO_FAILED) {
575
 
                errno = EIO;
576
 
                return 0;
577
 
        }
578
 
 
579
 
        result = mount(mi->spec, mi->node, mi->type,
580
 
                        mi->flags & ~(MS_USER|MS_USERS), options);
581
 
        free(options);
582
 
 
583
 
        if (verbose && result) {
584
 
                int save = errno;
585
 
                nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
586
 
                errno = save;
587
 
        }
588
 
        return !result;
589
 
}
590
 
 
591
 
static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
592
 
                struct sockaddr *sap, socklen_t salen)
593
 
{
594
 
        struct mount_options *options = po_dup(mi->options);
595
 
        int result = 0;
596
 
 
597
 
        if (!options) {
598
 
                errno = ENOMEM;
599
 
                return result;
600
 
        }
601
 
        errno = 0;
602
 
        if (!nfs_append_addr_option(sap, salen, options)) {
603
 
                if (errno == 0)
604
 
                        errno = EINVAL;
605
 
                goto out_fail;
606
 
        }
607
 
 
608
 
        if (!nfs_fix_mounthost_option(options, mi->hostname)) {
609
 
                if (errno == 0)
610
 
                        errno = EINVAL;
611
 
                goto out_fail;
612
 
        }
613
 
        if (!mi->fake && !nfs_verify_lock_option(options)) {
614
 
                if (errno == 0)
615
 
                        errno = EINVAL;
616
 
                goto out_fail;
617
 
        }
618
 
 
619
 
        /*
620
 
         * Options we negotiate below may be stale by the time this
621
 
         * file system is unmounted.  In order to force umount.nfs
622
 
         * to renegotiate with the server, only write the user-
623
 
         * specified options, and not negotiated options, to /etc/mtab.
624
 
         */
625
 
        if (po_join(options, mi->extra_opts) == PO_FAILED) {
626
 
                errno = ENOMEM;
627
 
                goto out_fail;
628
 
        }
629
 
 
630
 
        if (verbose)
631
 
                printf(_("%s: trying text-based options '%s'\n"),
632
 
                        progname, *mi->extra_opts);
633
 
 
634
 
        if (!nfs_rewrite_pmap_mount_options(options))
635
 
                goto out_fail;
636
 
 
637
 
        result = nfs_sys_mount(mi, options);
638
 
 
639
 
out_fail:
640
 
        po_destroy(options);
641
 
        return result;
642
 
}
643
 
 
644
 
/*
645
 
 * Attempt a "-t nfs vers=2" or "-t nfs vers=3" mount.
646
 
 *
647
 
 * Returns TRUE if successful, otherwise FALSE.
648
 
 * "errno" is set to reflect the individual error.
649
 
 */
650
 
static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
651
 
{
652
 
        struct addrinfo *ai;
653
 
        int ret = 0;
654
 
 
655
 
        for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
656
 
                ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen);
657
 
                if (ret != 0)
658
 
                        return ret;
659
 
 
660
 
                switch (errno) {
661
 
                case ECONNREFUSED:
662
 
                case EOPNOTSUPP:
663
 
                case EHOSTUNREACH:
664
 
                        continue;
665
 
                default:
666
 
                        break;
667
 
                }
668
 
        }
669
 
        return ret;
670
 
}
671
 
 
672
 
static int nfs_do_mount_v4(struct nfsmount_info *mi,
673
 
                struct sockaddr *sap, socklen_t salen)
674
 
{
675
 
        struct mount_options *options = po_dup(mi->options);
676
 
        int result = 0;
677
 
 
678
 
        if (!options) {
679
 
                errno = ENOMEM;
680
 
                return result;
681
 
        }
682
 
 
683
 
        if (mi->version == 0) {
684
 
                if (po_contains(options, "mounthost") ||
685
 
                        po_contains(options, "mountaddr") ||
686
 
                        po_contains(options, "mountvers") ||
687
 
                        po_contains(options, "mountproto")) {
688
 
                /*
689
 
                 * Since these mountd options are set assume version 3
690
 
                 * is wanted so error out with EPROTONOSUPPORT so the
691
 
                 * protocol negation starts with v3.
692
 
                 */
693
 
                        errno = EPROTONOSUPPORT;
694
 
                        goto out_fail;
695
 
                }
696
 
                if (po_append(options, "vers=4") == PO_FAILED) {
697
 
                        errno = EINVAL;
698
 
                        goto out_fail;
699
 
                }
700
 
        }
701
 
 
702
 
        if (!nfs_append_addr_option(sap, salen, options)) {
703
 
                errno = EINVAL;
704
 
                goto out_fail;
705
 
        }
706
 
 
707
 
        if (!nfs_append_clientaddr_option(sap, salen, options)) {
708
 
                errno = EINVAL;
709
 
                goto out_fail;
710
 
        }
711
 
 
712
 
        /*
713
 
         * Update option string to be recorded in /etc/mtab.
714
 
         */
715
 
        if (po_join(options, mi->extra_opts) == PO_FAILED) {
716
 
                errno = ENOMEM;
717
 
                goto out_fail;
718
 
        }
719
 
 
720
 
        if (verbose)
721
 
                printf(_("%s: trying text-based options '%s'\n"),
722
 
                        progname, *mi->extra_opts);
723
 
 
724
 
        result = nfs_sys_mount(mi, options);
725
 
 
726
 
out_fail:
727
 
        po_destroy(options);
728
 
        return result;
729
 
}
730
 
 
731
 
/*
732
 
 * Attempt a "-t nfs -o vers=4" or "-t nfs4" mount.
733
 
 *
734
 
 * Returns TRUE if successful, otherwise FALSE.
735
 
 * "errno" is set to reflect the individual error.
736
 
 */
737
 
static int nfs_try_mount_v4(struct nfsmount_info *mi)
738
 
{
739
 
        struct addrinfo *ai;
740
 
        int ret = 0;
741
 
 
742
 
        for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
743
 
                ret = nfs_do_mount_v4(mi, ai->ai_addr, ai->ai_addrlen);
744
 
                if (ret != 0)
745
 
                        return ret;
746
 
 
747
 
                switch (errno) {
748
 
                case ECONNREFUSED:
749
 
                case EHOSTUNREACH:
750
 
                        continue;
751
 
                default:
752
 
                        break;
753
 
                }
754
 
        }
755
 
        return ret;
756
 
}
757
 
 
758
 
/*
759
 
 * Handle NFS version and transport protocol
760
 
 * autonegotiation.
761
 
 *
762
 
 * When no version or protocol is specified on the
763
 
 * command line, mount.nfs negotiates with the server
764
 
 * to determine appropriate settings for the new
765
 
 * mount point.
766
 
 *
767
 
 * Returns TRUE if successful, otherwise FALSE.
768
 
 * "errno" is set to reflect the individual error.
769
 
 */
770
 
static int nfs_autonegotiate(struct nfsmount_info *mi)
771
 
{
772
 
        int result;
773
 
 
774
 
        result = nfs_try_mount_v4(mi);
775
 
        if (result)
776
 
                return result;
777
 
                
778
 
        switch (errno) {
779
 
        case EPROTONOSUPPORT:
780
 
                /* A clear indication that the server or our
781
 
                 * client does not support NFS version 4. */
782
 
                goto fall_back;
783
 
        case ENOENT:
784
 
                /* Legacy Linux servers don't export an NFS
785
 
                 * version 4 pseudoroot. */
786
 
                goto fall_back;
787
 
        case EPERM:
788
 
                /* Linux servers prior to 2.6.25 may return
789
 
                 * EPERM when NFS version 4 is not supported. */
790
 
                goto fall_back;
791
 
        default:
792
 
                return result;
793
 
        }
794
 
 
795
 
fall_back:
796
 
        return nfs_try_mount_v3v2(mi);
797
 
}
798
 
 
799
 
/*
800
 
 * This is a single pass through the fg/bg loop.
801
 
 *
802
 
 * Returns TRUE if successful, otherwise FALSE.
803
 
 * "errno" is set to reflect the individual error.
804
 
 */
805
 
static int nfs_try_mount(struct nfsmount_info *mi)
806
 
{
807
 
        int result = 0;
808
 
 
809
 
        switch (mi->version) {
810
 
        case 0:
811
 
                result = nfs_autonegotiate(mi);
812
 
                break;
813
 
        case 2:
814
 
        case 3:
815
 
                result = nfs_try_mount_v3v2(mi);
816
 
                break;
817
 
        case 4:
818
 
                result = nfs_try_mount_v4(mi);
819
 
                break;
820
 
        default:
821
 
                errno = EIO;
822
 
        }
823
 
 
824
 
        return result;
825
 
}
826
 
 
827
 
/*
828
 
 * Distinguish between permanent and temporary errors.
829
 
 *
830
 
 * Basically, we retry if communication with the server has
831
 
 * failed so far, but fail immediately if there is a local
832
 
 * error (like a bad mount option).
833
 
 *
834
 
 * ESTALE is also a temporary error because some servers
835
 
 * return ESTALE when a share is temporarily offline.
836
 
 *
837
 
 * Returns 1 if we should fail immediately, or 0 if we
838
 
 * should retry.
839
 
 */
840
 
static int nfs_is_permanent_error(int error)
841
 
{
842
 
        switch (error) {
843
 
        case ESTALE:
844
 
        case ETIMEDOUT:
845
 
        case ECONNREFUSED:
846
 
        case EHOSTUNREACH:
847
 
                return 0;       /* temporary */
848
 
        default:
849
 
                return 1;       /* permanent */
850
 
        }
851
 
}
852
 
 
853
 
/*
854
 
 * Handle "foreground" NFS mounts.
855
 
 *
856
 
 * Retry the mount request for as long as the 'retry=' option says.
857
 
 *
858
 
 * Returns a valid mount command exit code.
859
 
 */
860
 
static int nfsmount_fg(struct nfsmount_info *mi)
861
 
{
862
 
        unsigned int secs = 1;
863
 
        time_t timeout;
864
 
 
865
 
        timeout = nfs_parse_retry_option(mi->options,
866
 
                                         NFS_DEF_FG_TIMEOUT_MINUTES);
867
 
        if (verbose)
868
 
                printf(_("%s: timeout set for %s"),
869
 
                        progname, ctime(&timeout));
870
 
 
871
 
        for (;;) {
872
 
                if (nfs_try_mount(mi))
873
 
                        return EX_SUCCESS;
874
 
 
875
 
                if (nfs_is_permanent_error(errno))
876
 
                        break;
877
 
 
878
 
                if (time(NULL) > timeout) {
879
 
                        errno = ETIMEDOUT;
880
 
                        break;
881
 
                }
882
 
 
883
 
                if (errno != ETIMEDOUT) {
884
 
                        if (sleep(secs))
885
 
                                break;
886
 
                        secs <<= 1;
887
 
                        if (secs > 10)
888
 
                                secs = 10;
889
 
                }
890
 
        };
891
 
 
892
 
        mount_error(mi->spec, mi->node, errno);
893
 
        return EX_FAIL;
894
 
}
895
 
 
896
 
/*
897
 
 * Handle "background" NFS mount [first try]
898
 
 *
899
 
 * Returns a valid mount command exit code.
900
 
 *
901
 
 * EX_BG should cause the caller to fork and invoke nfsmount_child.
902
 
 */
903
 
static int nfsmount_parent(struct nfsmount_info *mi)
904
 
{
905
 
        if (nfs_try_mount(mi))
906
 
                return EX_SUCCESS;
907
 
 
908
 
        if (nfs_is_permanent_error(errno)) {
909
 
                mount_error(mi->spec, mi->node, errno);
910
 
                return EX_FAIL;
911
 
        }
912
 
 
913
 
        sys_mount_errors(mi->hostname, errno, 1, 1);
914
 
        return EX_BG;
915
 
}
916
 
 
917
 
/*
918
 
 * Handle "background" NFS mount [retry daemon]
919
 
 *
920
 
 * Returns a valid mount command exit code: EX_SUCCESS if successful,
921
 
 * EX_FAIL if a failure occurred.  There's nothing to catch the
922
 
 * error return, though, so we use sys_mount_errors to log the
923
 
 * failure.
924
 
 */
925
 
static int nfsmount_child(struct nfsmount_info *mi)
926
 
{
927
 
        unsigned int secs = 1;
928
 
        time_t timeout;
929
 
 
930
 
        timeout = nfs_parse_retry_option(mi->options,
931
 
                                         NFS_DEF_BG_TIMEOUT_MINUTES);
932
 
 
933
 
        for (;;) {
934
 
                if (sleep(secs))
935
 
                        break;
936
 
                secs <<= 1;
937
 
                if (secs > 120)
938
 
                        secs = 120;
939
 
 
940
 
                if (nfs_try_mount(mi))
941
 
                        return EX_SUCCESS;
942
 
 
943
 
                if (nfs_is_permanent_error(errno))
944
 
                        break;
945
 
 
946
 
                if (time(NULL) > timeout)
947
 
                        break;
948
 
 
949
 
                sys_mount_errors(mi->hostname, errno, 1, 1);
950
 
        };
951
 
 
952
 
        sys_mount_errors(mi->hostname, errno, 1, 0);
953
 
        return EX_FAIL;
954
 
}
955
 
 
956
 
/*
957
 
 * Handle "background" NFS mount
958
 
 *
959
 
 * Returns a valid mount command exit code.
960
 
 */
961
 
static int nfsmount_bg(struct nfsmount_info *mi)
962
 
{
963
 
        if (!mi->child)
964
 
                return nfsmount_parent(mi);
965
 
        else
966
 
                return nfsmount_child(mi);
967
 
}
968
 
 
969
 
/*
970
 
 * Usually all that is needed for an NFS remount is to change
971
 
 * generic mount options like "sync" or "ro".  These generic
972
 
 * options are controlled by mi->flags, not by text-based
973
 
 * options, and no contact with the server is needed.
974
 
 *
975
 
 * Take care with the /etc/mtab entry for this mount; just
976
 
 * calling update_mtab() will change an "-t nfs -o vers=4"
977
 
 * mount to an "-t nfs -o remount" mount, and that will
978
 
 * confuse umount.nfs.
979
 
 *
980
 
 * Returns a valid mount command exit code.
981
 
 */
982
 
static int nfs_remount(struct nfsmount_info *mi)
983
 
{
984
 
        if (nfs_sys_mount(mi, mi->options))
985
 
                return EX_SUCCESS;
986
 
        return EX_FAIL;
987
 
}
988
 
 
989
 
/*
990
 
 * Process mount options and try a mount system call.
991
 
 *
992
 
 * Returns a valid mount command exit code.
993
 
 */
994
 
static const char *nfs_background_opttbl[] = {
995
 
        "bg",
996
 
        "fg",
997
 
        NULL,
998
 
};
999
 
 
1000
 
static int nfsmount_start(struct nfsmount_info *mi)
1001
 
{
1002
 
        if (!nfs_validate_options(mi))
1003
 
                return EX_FAIL;
1004
 
 
1005
 
        /*
1006
 
         * Avoid retry and negotiation logic when remounting
1007
 
         */
1008
 
        if (mi->flags & MS_REMOUNT)
1009
 
                return nfs_remount(mi);
1010
 
 
1011
 
        if (po_rightmost(mi->options, nfs_background_opttbl) == 0)
1012
 
                return nfsmount_bg(mi);
1013
 
        else
1014
 
                return nfsmount_fg(mi);
1015
 
}
1016
 
 
1017
 
/**
1018
 
 * nfsmount_string - Mount an NFS file system using C string options
1019
 
 * @spec: C string specifying remote share to mount ("hostname:path")
1020
 
 * @node: C string pathname of local mounted-on directory
1021
 
 * @type: C string that represents file system type ("nfs" or "nfs4")
1022
 
 * @flags: MS_ style mount flags
1023
 
 * @extra_opts: pointer to C string containing fs-specific mount options
1024
 
 *              (input and output argument)
1025
 
 * @fake: flag indicating whether to carry out the whole operation
1026
 
 * @child: one if this is a mount daemon (bg)
1027
 
 *
1028
 
 * Returns a valid mount command exit code.
1029
 
 */
1030
 
int nfsmount_string(const char *spec, const char *node, const char *type,
1031
 
                    int flags, char **extra_opts, int fake, int child)
1032
 
{
1033
 
        struct nfsmount_info mi = {
1034
 
                .spec           = spec,
1035
 
                .node           = node,
1036
 
                .address        = NULL,
1037
 
                .type           = type,
1038
 
                .extra_opts     = extra_opts,
1039
 
                .flags          = flags,
1040
 
                .fake           = fake,
1041
 
                .child          = child,
1042
 
        };
1043
 
        int retval = EX_FAIL;
1044
 
 
1045
 
        mi.options = po_split(*extra_opts);
1046
 
        if (mi.options) {
1047
 
                retval = nfsmount_start(&mi);
1048
 
                po_destroy(mi.options);
1049
 
        } else
1050
 
                nfs_error(_("%s: internal option parsing error"), progname);
1051
 
 
1052
 
        freeaddrinfo(mi.address);
1053
 
        free(mi.hostname);
1054
 
        return retval;
1055
 
}