~ubuntu-branches/ubuntu/hardy/nfs-utils/hardy-updates

« back to all changes in this revision

Viewing changes to utils/mount/stropts.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug
  • Date: 2007-11-14 04:52:46 UTC
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20071114045246-37yxn6na36segpuh
Tags: upstream-1.1.1
ImportĀ upstreamĀ versionĀ 1.1.1

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
#include <ctype.h>
 
25
#include <unistd.h>
 
26
#include <stdio.h>
 
27
#include <string.h>
 
28
#include <stdlib.h>
 
29
#include <errno.h>
 
30
#include <netdb.h>
 
31
#include <time.h>
 
32
#include <sys/socket.h>
 
33
#include <sys/mount.h>
 
34
 
 
35
#include "xcommon.h"
 
36
#include "mount.h"
 
37
#include "nls.h"
 
38
#include "nfs_mount.h"
 
39
#include "mount_constants.h"
 
40
#include "stropts.h"
 
41
#include "error.h"
 
42
#include "network.h"
 
43
#include "parse_opt.h"
 
44
 
 
45
#ifdef HAVE_RPCSVC_NFS_PROT_H
 
46
#include <rpcsvc/nfs_prot.h>
 
47
#else
 
48
#include <linux/nfs.h>
 
49
#define nfsstat nfs_stat
 
50
#endif
 
51
 
 
52
#ifndef NFS_PORT
 
53
#define NFS_PORT 2049
 
54
#endif
 
55
 
 
56
#ifndef NFS_MAXHOSTNAME
 
57
#define NFS_MAXHOSTNAME         (255)
 
58
#endif
 
59
 
 
60
#ifndef NFS_MAXPATHNAME
 
61
#define NFS_MAXPATHNAME         (1024)
 
62
#endif
 
63
 
 
64
extern int nfs_mount_data_version;
 
65
extern char *progname;
 
66
extern int verbose;
 
67
 
 
68
static int parse_devname(const char *spec, char **hostname)
 
69
{
 
70
        int ret = 0;
 
71
        char *dev, *pathname, *s;
 
72
 
 
73
        dev = xstrdup(spec);
 
74
 
 
75
        if (!(pathname = strchr(dev, ':'))) {
 
76
                nfs_error(_("%s: remote share not in 'host:dir' format"),
 
77
                                progname);
 
78
                goto out;
 
79
        }
 
80
        *pathname = '\0';
 
81
        pathname++;
 
82
 
 
83
        /*
 
84
         * We don't need a copy of the pathname, but let's
 
85
         * sanity check it anyway.
 
86
         */
 
87
        if (strlen(pathname) > NFS_MAXPATHNAME) {
 
88
                nfs_error(_("%s: export pathname is too long"),
 
89
                                progname);
 
90
                goto out;
 
91
        }
 
92
 
 
93
        /*
 
94
         * Ignore all but first hostname in replicated mounts
 
95
         * until they can be fully supported. (mack@sgi.com)
 
96
         */
 
97
        if ((s = strchr(dev, ','))) {
 
98
                *s = '\0';
 
99
                nfs_error(_("%s: warning: multiple hostnames not supported"),
 
100
                                progname);
 
101
                nfs_error(_("%s: ignoring hostnames that follow the first one"),
 
102
                                progname);
 
103
        }
 
104
        *hostname = xstrdup(dev);
 
105
        if (strlen(*hostname) > NFS_MAXHOSTNAME) {
 
106
                nfs_error(_("%s: server hostname is too long"),
 
107
                                progname);
 
108
                free(*hostname);
 
109
                goto out;
 
110
        }
 
111
 
 
112
        ret = 1;
 
113
 
 
114
out:
 
115
        free(dev);
 
116
        return ret;
 
117
}
 
118
 
 
119
static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
 
120
{
 
121
        struct hostent *hp;
 
122
        addr->sin_family = AF_INET;
 
123
 
 
124
        if (inet_aton(hostname, &addr->sin_addr))
 
125
                return 1;
 
126
        if ((hp = gethostbyname(hostname)) == NULL) {
 
127
                nfs_error(_("%s: can't get address for %s\n"),
 
128
                                progname, hostname);
 
129
                return 0;
 
130
        }
 
131
        if (hp->h_length > sizeof(struct in_addr)) {
 
132
                nfs_error(_("%s: got bad hp->h_length"), progname);
 
133
                hp->h_length = sizeof(struct in_addr);
 
134
        }
 
135
        memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
 
136
        return 1;
 
137
}
 
138
 
 
139
/*
 
140
 * Append the 'addr=' option to the options string to pass a resolved
 
141
 * server address to the kernel.  After a successful mount, this address
 
142
 * is also added to /etc/mtab for use when unmounting.
 
143
 *
 
144
 * If 'addr=' is already present, we strip it out.  This prevents users
 
145
 * from setting a bogus 'addr=' option themselves, and also allows bg
 
146
 * retries to recompute the server's address, in case it has changed.
 
147
 *
 
148
 * Returns 1 if 'addr=' option appended successfully;
 
149
 * otherwise zero.
 
150
 */
 
151
static int append_addr_option(struct sockaddr_in *saddr,
 
152
                           struct mount_options *options)
 
153
{
 
154
        char new_option[24];
 
155
 
 
156
        po_remove_all(options, "addr");
 
157
 
 
158
        snprintf(new_option, sizeof(new_option) - 1,
 
159
                        "addr=%s", inet_ntoa(saddr->sin_addr));
 
160
 
 
161
        if (po_append(options, new_option) == PO_SUCCEEDED)
 
162
                return 1;
 
163
        return 0;
 
164
}
 
165
 
 
166
/*
 
167
 * Called to discover our address and append an appropriate 'clientaddr='
 
168
 * option to the options string.
 
169
 *
 
170
 * Returns 1 if 'clientaddr=' option created successfully or if
 
171
 * 'clientaddr=' option is already present; otherwise zero.
 
172
 */
 
173
static int append_clientaddr_option(struct sockaddr_in *saddr,
 
174
                                    struct mount_options *options)
 
175
{
 
176
        struct sockaddr_in my_addr;
 
177
        char new_option[32];
 
178
 
 
179
        if (po_contains(options, "clientaddr") == PO_SUCCEEDED)
 
180
                return 1;
 
181
 
 
182
        if (!get_client_address(saddr, &my_addr))
 
183
                return 0;
 
184
 
 
185
        snprintf(new_option, sizeof(new_option) - 1,
 
186
                        "clientaddr=%s", inet_ntoa(my_addr.sin_addr));
 
187
 
 
188
        if (po_append(options, new_option) == PO_SUCCEEDED)
 
189
                return 1;
 
190
        return 0;
 
191
}
 
192
 
 
193
/*
 
194
 * Resolve the 'mounthost=' hostname and append a new option using
 
195
 * the resulting IPv4 address.
 
196
 */
 
197
static int fix_mounthost_option(struct mount_options *options)
 
198
{
 
199
        struct sockaddr_in maddr;
 
200
        char *mounthost, new_option[32];
 
201
 
 
202
        mounthost = po_get(options, "mounthost");
 
203
        if (!mounthost)
 
204
                return 1;
 
205
 
 
206
        if (!fill_ipv4_sockaddr(mounthost, &maddr))
 
207
                return 0;
 
208
 
 
209
        snprintf(new_option, sizeof(new_option) - 1,
 
210
                        "mountaddr=%s", inet_ntoa(maddr.sin_addr));
 
211
 
 
212
        if (po_append(options, new_option) == PO_SUCCEEDED)
 
213
                return 1;
 
214
        return 0;
 
215
}
 
216
 
 
217
/*
 
218
 * Set up mandatory mount options.
 
219
 *
 
220
 * Returns 1 if successful; otherwise zero.
 
221
 */
 
222
static int set_mandatory_options(const char *type,
 
223
                                 struct sockaddr_in *saddr,
 
224
                                 struct mount_options *options)
 
225
{
 
226
        if (!append_addr_option(saddr, options))
 
227
                return 0;
 
228
 
 
229
        if (strncmp(type, "nfs4", 4) == 0) {
 
230
                if (!append_clientaddr_option(saddr, options))
 
231
                        return 0;
 
232
        } else {
 
233
                if (!fix_mounthost_option(options))
 
234
                        return 0;
 
235
        }
 
236
 
 
237
        return 1;
 
238
}
 
239
 
 
240
/*
 
241
 * Distinguish between permanent and temporary errors.
 
242
 *
 
243
 * Returns 0 if the passed-in error is temporary, thus the
 
244
 * mount system call should be retried; returns one if the
 
245
 * passed-in error is permanent, thus the mount system call
 
246
 * should not be retried.
 
247
 */
 
248
static int is_permanent_error(int error)
 
249
{
 
250
        switch (error) {
 
251
        case EACCES:
 
252
        case ESTALE:
 
253
        case ETIMEDOUT:
 
254
        case ECONNREFUSED:
 
255
                return 0;       /* temporary */
 
256
        default:
 
257
                return 1;       /* permanent */
 
258
        }
 
259
}
 
260
 
 
261
/*
 
262
 * Reconstruct the mount option string based on a portmapper probe
 
263
 * of the server.  Returns one if the server's portmapper returned
 
264
 * something we can use, otherwise zero.
 
265
 *
 
266
 * To handle version and transport protocol fallback properly, we
 
267
 * need to parse some of the mount options in order to set up a
 
268
 * portmap probe.  Mount options that rewrite_mount_options()
 
269
 * doesn't recognize are left alone.
 
270
 *
 
271
 * Returns a new group of mount options if successful; otherwise
 
272
 * NULL is returned if some failure occurred.
 
273
 */
 
274
static struct mount_options *rewrite_mount_options(char *str)
 
275
{
 
276
        struct mount_options *options;
 
277
        char *option, new_option[64];
 
278
        clnt_addr_t mnt_server = { };
 
279
        clnt_addr_t nfs_server = { };
 
280
        int p;
 
281
 
 
282
        options = po_split(str);
 
283
        if (!options)
 
284
                return NULL;
 
285
 
 
286
        option = po_get(options, "addr");
 
287
        if (option) {
 
288
                nfs_server.saddr.sin_family = AF_INET;
 
289
                if (!inet_aton((const char *)option, &nfs_server.saddr.sin_addr))
 
290
                        goto err;
 
291
        } else
 
292
                goto err;
 
293
 
 
294
        option = po_get(options, "mountaddr");
 
295
        if (option) {
 
296
                mnt_server.saddr.sin_family = AF_INET;
 
297
                if (!inet_aton((const char *)option, &mnt_server.saddr.sin_addr))
 
298
                        goto err;
 
299
        } else
 
300
                memcpy(&mnt_server.saddr, &nfs_server.saddr,
 
301
                                sizeof(mnt_server.saddr));
 
302
 
 
303
        option = po_get(options, "mountport");
 
304
        if (option)
 
305
                mnt_server.pmap.pm_port = atoi(option);
 
306
        mnt_server.pmap.pm_prog = MOUNTPROG;
 
307
        option = po_get(options, "mountprog");
 
308
        if (option)
 
309
                mnt_server.pmap.pm_prog = atoi(option);
 
310
        option = po_get(options, "mountvers");
 
311
        if (option)
 
312
                mnt_server.pmap.pm_vers = atoi(option);
 
313
 
 
314
        option = po_get(options, "port");
 
315
        if (option) {
 
316
                nfs_server.pmap.pm_port = atoi(option);
 
317
                po_remove_all(options, "port");
 
318
        }
 
319
        nfs_server.pmap.pm_prog = NFS_PROGRAM;
 
320
        option = po_get(options, "nfsprog");
 
321
        if (option)
 
322
                nfs_server.pmap.pm_prog = atoi(option);
 
323
 
 
324
        option = po_get(options, "nfsvers");
 
325
        if (option) {
 
326
                nfs_server.pmap.pm_vers = atoi(option);
 
327
                po_remove_all(options, "nfsvers");
 
328
        }
 
329
        option = po_get(options, "vers");
 
330
        if (option) {
 
331
                nfs_server.pmap.pm_vers = atoi(option);
 
332
                po_remove_all(options, "vers");
 
333
        }
 
334
        option = po_get(options, "proto");
 
335
        if (option) {
 
336
                if (strcmp(option, "tcp") == 0) {
 
337
                        nfs_server.pmap.pm_prot = IPPROTO_TCP;
 
338
                        po_remove_all(options, "proto");
 
339
                }
 
340
                if (strcmp(option, "udp") == 0) {
 
341
                        nfs_server.pmap.pm_prot = IPPROTO_UDP;
 
342
                        po_remove_all(options, "proto");
 
343
                }
 
344
        }
 
345
        p = po_rightmost(options, "tcp", "udp");
 
346
        switch (p) {
 
347
        case PO_KEY2_RIGHTMOST:
 
348
                nfs_server.pmap.pm_prot = IPPROTO_UDP;
 
349
                break;
 
350
        case PO_KEY1_RIGHTMOST:
 
351
                nfs_server.pmap.pm_prot = IPPROTO_TCP;
 
352
                break;
 
353
        }
 
354
        po_remove_all(options, "tcp");
 
355
        po_remove_all(options, "udp");
 
356
 
 
357
        if (!probe_bothports(&mnt_server, &nfs_server)) {
 
358
                rpc_mount_errors("rpcbind", 0, 0);
 
359
                goto err;
 
360
        }
 
361
 
 
362
        snprintf(new_option, sizeof(new_option) - 1,
 
363
                 "nfsvers=%lu", nfs_server.pmap.pm_vers);
 
364
        if (po_append(options, new_option) == PO_FAILED)
 
365
                goto err;
 
366
 
 
367
        if (nfs_server.pmap.pm_prot == IPPROTO_TCP)
 
368
                snprintf(new_option, sizeof(new_option) - 1,
 
369
                         "proto=tcp");
 
370
        else
 
371
                snprintf(new_option, sizeof(new_option) - 1,
 
372
                         "proto=udp");
 
373
        if (po_append(options, new_option) == PO_FAILED)
 
374
                goto err;
 
375
 
 
376
        if (nfs_server.pmap.pm_port != NFS_PORT) {
 
377
                snprintf(new_option, sizeof(new_option) - 1,
 
378
                         "port=%lu", nfs_server.pmap.pm_port);
 
379
                if (po_append(options, new_option) == PO_FAILED)
 
380
                        goto err;
 
381
 
 
382
        }
 
383
 
 
384
        return options;
 
385
 
 
386
err:
 
387
        po_destroy(options);
 
388
        return NULL;
 
389
}
 
390
 
 
391
/*
 
392
 * Retry an NFS mount that failed because the requested service isn't
 
393
 * available on the server.
 
394
 *
 
395
 * Returns 1 if successful.  Otherwise, returns zero.
 
396
 * "errno" is set to reflect the individual error.
 
397
 *
 
398
 * Side effect: If the retry is successful, both 'options' and
 
399
 * 'extra_opts' are updated to reflect the mount options that worked.
 
400
 * If the retry fails, 'options' and 'extra_opts' are left unchanged.
 
401
 */
 
402
static int retry_nfsmount(const char *spec, const char *node,
 
403
                        int flags, struct mount_options *options,
 
404
                        int fake, char **extra_opts)
 
405
{
 
406
        struct mount_options *retry_options;
 
407
        char *retry_str = NULL;
 
408
 
 
409
        retry_options = rewrite_mount_options(*extra_opts);
 
410
        if (!retry_options) {
 
411
                errno = EIO;
 
412
                return 0;
 
413
        }
 
414
 
 
415
        if (po_join(retry_options, &retry_str) == PO_FAILED) {
 
416
                po_destroy(retry_options);
 
417
                errno = EIO;
 
418
                return 0;
 
419
        }
 
420
 
 
421
        if (verbose)
 
422
                printf(_("%s: text-based options (retry): '%s'\n"),
 
423
                        progname, retry_str);
 
424
 
 
425
        if (!mount(spec, node, "nfs",
 
426
                                flags & ~(MS_USER|MS_USERS), retry_str)) {
 
427
                free(*extra_opts);
 
428
                *extra_opts = retry_str;
 
429
                po_replace(options, retry_options);
 
430
                return 1;
 
431
        }
 
432
 
 
433
        po_destroy(retry_options);
 
434
        free(retry_str);
 
435
        return 0;
 
436
}
 
437
 
 
438
/*
 
439
 * Attempt an NFSv2/3 mount via a mount(2) system call.  If the kernel
 
440
 * claims the requested service isn't supported on the server, probe
 
441
 * the server to see what's supported, rewrite the mount options,
 
442
 * and retry the request.
 
443
 *
 
444
 * Returns 1 if successful.  Otherwise, returns zero.
 
445
 * "errno" is set to reflect the individual error.
 
446
 *
 
447
 * Side effect: If the retry is successful, both 'options' and
 
448
 * 'extra_opts' are updated to reflect the mount options that worked.
 
449
 * If the retry fails, 'options' and 'extra_opts' are left unchanged.
 
450
 */
 
451
static int try_nfs23mount(const char *spec, const char *node,
 
452
                          int flags, struct mount_options *options,
 
453
                          int fake, char **extra_opts)
 
454
{
 
455
        if (po_join(options, extra_opts) == PO_FAILED) {
 
456
                errno = EIO;
 
457
                return 0;
 
458
        }
 
459
 
 
460
        if (verbose)
 
461
                printf(_("%s: text-based options: '%s'\n"),
 
462
                        progname, *extra_opts);
 
463
 
 
464
        if (fake)
 
465
                return 1;
 
466
 
 
467
        if (!mount(spec, node, "nfs",
 
468
                                flags & ~(MS_USER|MS_USERS), *extra_opts))
 
469
                return 1;
 
470
 
 
471
        /*
 
472
         * The kernel returns EOPNOTSUPP if the RPC bind failed,
 
473
         * and EPROTONOSUPPORT if the version isn't supported.
 
474
         */
 
475
        if (errno != EOPNOTSUPP && errno != EPROTONOSUPPORT)
 
476
                return 0;
 
477
 
 
478
        return retry_nfsmount(spec, node, flags, options, fake, extra_opts);
 
479
}
 
480
 
 
481
/*
 
482
 * Attempt an NFS v4 mount via a mount(2) system call.
 
483
 *
 
484
 * Returns 1 if successful.  Otherwise, returns zero.
 
485
 * "errno" is set to reflect the individual error.
 
486
 */
 
487
static int try_nfs4mount(const char *spec, const char *node,
 
488
                         int flags, struct mount_options *options,
 
489
                         int fake, char **extra_opts)
 
490
{
 
491
        if (po_join(options, extra_opts) == PO_FAILED) {
 
492
                errno = EIO;
 
493
                return 0;
 
494
        }
 
495
 
 
496
        if (verbose)
 
497
                printf(_("%s: text-based options: '%s'\n"),
 
498
                        progname, *extra_opts);
 
499
 
 
500
        if (fake)
 
501
                return 1;
 
502
 
 
503
        if (!mount(spec, node, "nfs4",
 
504
                                flags & ~(MS_USER|MS_USERS), *extra_opts))
 
505
                return 1;
 
506
        return 0;
 
507
}
 
508
 
 
509
/*
 
510
 * Try the mount(2) system call.
 
511
 *
 
512
 * Returns 1 if successful.  Otherwise, returns zero.
 
513
 * "errno" is set to reflect the individual error.
 
514
 */
 
515
static int try_mount(const char *spec, const char *node, const char *type,
 
516
                     int flags, struct mount_options *options, int fake,
 
517
                     char **extra_opts)
 
518
{
 
519
        if (strncmp(type, "nfs4", 4) == 0)
 
520
                return try_nfs4mount(spec, node, flags,
 
521
                                        options, fake, extra_opts);
 
522
        else
 
523
                return try_nfs23mount(spec, node, flags,
 
524
                                        options, fake, extra_opts);
 
525
}
 
526
 
 
527
/*
 
528
 * Handle "foreground" NFS mounts.
 
529
 *
 
530
 * Retry the mount request for as long as the 'retry=' option says.
 
531
 *
 
532
 * Returns a valid mount command exit code.
 
533
 */
 
534
static int nfsmount_fg(const char *spec, const char *node,
 
535
                       const char *type, int flags,
 
536
                       struct mount_options *options, int fake,
 
537
                       char **extra_opts)
 
538
{
 
539
        unsigned int secs = 1;
 
540
        time_t timeout = time(NULL);
 
541
        char *retry;
 
542
 
 
543
        timeout += 60 * 2;              /* default: 2 minutes */
 
544
        retry = po_get(options, "retry");
 
545
        if (retry)
 
546
                timeout += 60 * atoi(retry);
 
547
 
 
548
        if (verbose)
 
549
                printf(_("%s: timeout set for %s"),
 
550
                        progname, ctime(&timeout));
 
551
 
 
552
        for (;;) {
 
553
                if (try_mount(spec, node, type, flags,
 
554
                                        options, fake, extra_opts))
 
555
                        return EX_SUCCESS;
 
556
 
 
557
                if (is_permanent_error(errno))
 
558
                        break;
 
559
 
 
560
                if (time(NULL) > timeout) {
 
561
                        errno = ETIMEDOUT;
 
562
                        break;
 
563
                }
 
564
 
 
565
                if (errno != ETIMEDOUT) {
 
566
                        if (sleep(secs))
 
567
                                break;
 
568
                        secs <<= 1;
 
569
                        if (secs > 10)
 
570
                                secs = 10;
 
571
                }
 
572
        };
 
573
 
 
574
        mount_error(spec, node, errno);
 
575
        return EX_FAIL;
 
576
}
 
577
 
 
578
/*
 
579
 * Handle "background" NFS mount [first try]
 
580
 *
 
581
 * Returns a valid mount command exit code.
 
582
 *
 
583
 * EX_BG should cause the caller to fork and invoke nfsmount_child.
 
584
 */
 
585
static int nfsmount_parent(const char *spec, const char *node,
 
586
                           const char *type, char *hostname, int flags,
 
587
                           struct mount_options *options,
 
588
                           int fake, char **extra_opts)
 
589
{
 
590
        if (try_mount(spec, node, type, flags, options,
 
591
                                        fake, extra_opts))
 
592
                return EX_SUCCESS;
 
593
 
 
594
        if (is_permanent_error(errno)) {
 
595
                mount_error(spec, node, errno);
 
596
                return EX_FAIL;
 
597
        }
 
598
 
 
599
        sys_mount_errors(hostname, errno, 1, 1);
 
600
        return EX_BG;
 
601
}
 
602
 
 
603
/*
 
604
 * Handle "background" NFS mount [retry daemon]
 
605
 *
 
606
 * Returns a valid mount command exit code: EX_SUCCESS if successful,
 
607
 * EX_FAIL if a failure occurred.  There's nothing to catch the
 
608
 * error return, though, so we use sys_mount_errors to log the
 
609
 * failure.
 
610
 */
 
611
static int nfsmount_child(const char *spec, const char *node,
 
612
                          const char *type, char *hostname, int flags,
 
613
                          struct mount_options *options,
 
614
                          int fake, char **extra_opts)
 
615
{
 
616
        unsigned int secs = 1;
 
617
        time_t timeout = time(NULL);
 
618
        char *retry;
 
619
 
 
620
        timeout += 60 * 10000;          /* default: 10,000 minutes */
 
621
        retry = po_get(options, "retry");
 
622
        if (retry)
 
623
                timeout += 60 * atoi(retry);
 
624
 
 
625
        for (;;) {
 
626
                if (sleep(secs))
 
627
                        break;
 
628
                secs <<= 1;
 
629
                if (secs > 120)
 
630
                        secs = 120;
 
631
 
 
632
                if (try_mount(spec, node, type, flags, options,
 
633
                                                        fake, extra_opts))
 
634
                        return EX_SUCCESS;
 
635
 
 
636
                if (is_permanent_error(errno))
 
637
                        break;
 
638
 
 
639
                if (time(NULL) > timeout)
 
640
                        break;
 
641
 
 
642
                sys_mount_errors(hostname, errno, 1, 1);
 
643
        };
 
644
 
 
645
        sys_mount_errors(hostname, errno, 1, 0);
 
646
        return EX_FAIL;
 
647
}
 
648
 
 
649
/*
 
650
 * Handle "background" NFS mount
 
651
 *
 
652
 * Returns a valid mount command exit code.
 
653
 */
 
654
static int nfsmount_bg(const char *spec, const char *node,
 
655
                          const char *type, char *hostname, int flags,
 
656
                          struct mount_options *options,
 
657
                          int fake, int child, char **extra_opts)
 
658
{
 
659
        if (!child)
 
660
                return nfsmount_parent(spec, node, type, hostname, flags,
 
661
                                        options, fake, extra_opts);
 
662
        else
 
663
                return nfsmount_child(spec, node, type, hostname, flags,
 
664
                                        options, fake, extra_opts);
 
665
}
 
666
 
 
667
/**
 
668
 * nfsmount_string - Mount an NFS file system using C string options
 
669
 * @spec: C string specifying remote share to mount ("hostname:path")
 
670
 * @node: C string pathname of local mounted-on directory
 
671
 * @type: C string that represents file system type ("nfs" or "nfs4")
 
672
 * @flags: MS_ style mount flags
 
673
 * @extra_opts: pointer to C string containing fs-specific mount options
 
674
 *              (input and output argument)
 
675
 * @fake: flag indicating whether to carry out the whole operation
 
676
 * @child: one if this is a mount daemon (bg)
 
677
 */
 
678
int nfsmount_string(const char *spec, const char *node, const char *type,
 
679
                    int flags, char **extra_opts, int fake, int child)
 
680
{
 
681
        struct mount_options *options = NULL;
 
682
        struct sockaddr_in saddr;
 
683
        char *hostname;
 
684
        int retval = EX_FAIL;
 
685
 
 
686
        if (!parse_devname(spec, &hostname))
 
687
                return retval;
 
688
        if (!fill_ipv4_sockaddr(hostname, &saddr))
 
689
                goto fail;
 
690
 
 
691
        options = po_split(*extra_opts);
 
692
        if (!options) {
 
693
                nfs_error(_("%s: internal option parsing error"), progname);
 
694
                goto fail;
 
695
        }
 
696
 
 
697
        if (!set_mandatory_options(type, &saddr, options))
 
698
                goto out;
 
699
 
 
700
        if (po_rightmost(options, "bg", "fg") == PO_KEY1_RIGHTMOST)
 
701
                retval = nfsmount_bg(spec, node, type, hostname, flags,
 
702
                                        options, fake, child, extra_opts);
 
703
        else
 
704
                retval = nfsmount_fg(spec, node, type, flags, options,
 
705
                                                        fake, extra_opts);
 
706
 
 
707
out:
 
708
        po_destroy(options);
 
709
fail:
 
710
        free(hostname);
 
711
        return retval;
 
712
}