1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185#include <mntent.h>
186#include <syslog.h>
187#include <sys/mount.h>
188
189#ifndef MS_DIRSYNC
190# define MS_DIRSYNC (1 << 7)
191#endif
192#ifndef MS_UNION
193# define MS_UNION (1 << 8)
194#endif
195#ifndef MS_BIND
196# define MS_BIND (1 << 12)
197#endif
198#ifndef MS_MOVE
199# define MS_MOVE (1 << 13)
200#endif
201#ifndef MS_RECURSIVE
202# define MS_RECURSIVE (1 << 14)
203#endif
204#ifndef MS_SILENT
205# define MS_SILENT (1 << 15)
206#endif
207
208#ifndef MS_UNBINDABLE
209# define MS_UNBINDABLE (1 << 17)
210#endif
211#ifndef MS_PRIVATE
212# define MS_PRIVATE (1 << 18)
213#endif
214#ifndef MS_SLAVE
215# define MS_SLAVE (1 << 19)
216#endif
217#ifndef MS_SHARED
218# define MS_SHARED (1 << 20)
219#endif
220#ifndef MS_RELATIME
221# define MS_RELATIME (1 << 21)
222#endif
223#ifndef MS_STRICTATIME
224# define MS_STRICTATIME (1 << 24)
225#endif
226
227
228#define BB_MS_INVERTED_VALUE (1u << 31)
229
230#include "libbb.h"
231#include "common_bufsiz.h"
232#if ENABLE_FEATURE_MOUNT_LABEL
233# include "volume_id.h"
234#else
235# define resolve_mount_spec(fsname) ((void)0)
236#endif
237
238
239#include <sys/utsname.h>
240#undef TRUE
241#undef FALSE
242#if ENABLE_FEATURE_MOUNT_NFS
243
244
245# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
246# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
247
248
249
250
251
252# endif
253# include <rpc/rpc.h>
254# include <rpc/pmap_prot.h>
255# include <rpc/pmap_clnt.h>
256#endif
257
258
259#if defined(__dietlibc__)
260
261
262static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
263 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
264{
265 struct mntent* ment = getmntent(stream);
266 return memcpy(result, ment, sizeof(*ment));
267}
268#endif
269
270
271
272enum {
273 MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
274 MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
275 MOUNT_NOAUTO = (1 << 29),
276 MOUNT_SWAP = (1 << 30),
277 MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
278};
279
280
281#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
282enum {
283 OPT_o = (1 << 0),
284 OPT_t = (1 << 1),
285 OPT_r = (1 << 2),
286 OPT_w = (1 << 3),
287 OPT_a = (1 << 4),
288 OPT_n = (1 << 5),
289 OPT_f = (1 << 6),
290 OPT_v = (1 << 7),
291 OPT_s = (1 << 8),
292 OPT_i = (1 << 9),
293 OPT_O = (1 << 10),
294 OPT_T = (1 << 11),
295};
296
297#if ENABLE_FEATURE_MTAB_SUPPORT
298#define USE_MTAB (!(option_mask32 & OPT_n))
299#else
300#define USE_MTAB 0
301#endif
302
303#if ENABLE_FEATURE_MOUNT_FAKE
304#define FAKE_IT (option_mask32 & OPT_f)
305#else
306#define FAKE_IT 0
307#endif
308
309#if ENABLE_FEATURE_MOUNT_HELPERS
310#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
311#else
312#define HELPERS_ALLOWED 0
313#endif
314
315
316
317
318
319
320
321
322
323
324
325
326
327static const int32_t mount_options[] = {
328
329
330 IF_FEATURE_MOUNT_LOOP(
331 0,
332 )
333
334 IF_FEATURE_MOUNT_FSTAB(
335 0,
336
337 MOUNT_NOAUTO,
338 MOUNT_SWAP,
339 MOUNT_SWAP,
340 IF_DESKTOP( MOUNT_USERS,)
341 IF_DESKTOP( MOUNT_USERS,)
342 IF_DESKTOP( MOUNT_NOFAIL,)
343 0,
344 IF_DESKTOP( 0,)
345 )
346
347 IF_FEATURE_MOUNT_FLAGS(
348
349 MS_NOSUID,
350 ~MS_NOSUID,
351 ~MS_NODEV,
352 MS_NODEV,
353 ~MS_NOEXEC,
354 MS_NOEXEC,
355 MS_SYNCHRONOUS,
356 MS_DIRSYNC,
357 ~MS_SYNCHRONOUS,
358 ~MS_NOATIME,
359 MS_NOATIME,
360 ~MS_NODIRATIME,
361 MS_NODIRATIME,
362 MS_MANDLOCK,
363 ~MS_MANDLOCK,
364 MS_RELATIME,
365 ~MS_RELATIME,
366 MS_STRICTATIME,
367 ~MS_SILENT,
368 MS_BIND|MS_RECURSIVE,
369
370
371 MS_UNION,
372 MS_BIND,
373 MS_MOVE,
374 MS_SHARED,
375 MS_SLAVE,
376 MS_PRIVATE,
377 MS_UNBINDABLE,
378 MS_SHARED|MS_RECURSIVE,
379 MS_SLAVE|MS_RECURSIVE,
380 MS_PRIVATE|MS_RECURSIVE,
381 MS_UNBINDABLE|MS_RECURSIVE,
382 )
383
384
385 MS_RDONLY,
386 ~MS_RDONLY,
387 MS_REMOUNT
388};
389
390static const char mount_option_str[] ALIGN1 =
391 IF_FEATURE_MOUNT_LOOP(
392 "loop\0"
393 )
394 IF_FEATURE_MOUNT_FSTAB(
395 "defaults\0"
396
397 "noauto\0"
398 "sw\0"
399 "swap\0"
400 IF_DESKTOP("user\0")
401 IF_DESKTOP("users\0")
402 IF_DESKTOP("nofail\0")
403 "_netdev\0"
404 IF_DESKTOP("comment=\0")
405 )
406 IF_FEATURE_MOUNT_FLAGS(
407
408 "nosuid\0"
409 "suid\0"
410 "dev\0"
411 "nodev\0"
412 "exec\0"
413 "noexec\0"
414 "sync\0"
415 "dirsync\0"
416 "async\0"
417 "atime\0"
418 "noatime\0"
419 "diratime\0"
420 "nodiratime\0"
421 "mand\0"
422 "nomand\0"
423 "relatime\0"
424 "norelatime\0"
425 "strictatime\0"
426 "loud\0"
427 "rbind\0"
428
429
430 "union\0"
431 "bind\0"
432 "move\0"
433 "make-shared\0"
434 "make-slave\0"
435 "make-private\0"
436 "make-unbindable\0"
437 "make-rshared\0"
438 "make-rslave\0"
439 "make-rprivate\0"
440 "make-runbindable\0"
441 )
442
443
444 "ro\0"
445 "rw\0"
446 "remount\0"
447;
448
449
450struct globals {
451#if ENABLE_FEATURE_MOUNT_NFS
452 smalluint nfs_mount_version;
453#endif
454#if ENABLE_FEATURE_MOUNT_VERBOSE
455 unsigned verbose;
456#endif
457 llist_t *fslist;
458 char getmntent_buf[1];
459} FIX_ALIASING;
460enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
461#define G (*(struct globals*)bb_common_bufsiz1)
462#define nfs_mount_version (G.nfs_mount_version)
463#if ENABLE_FEATURE_MOUNT_VERBOSE
464#define verbose (G.verbose )
465#else
466#define verbose 0
467#endif
468#define fslist (G.fslist )
469#define getmntent_buf (G.getmntent_buf )
470#define INIT_G() do { setup_common_bufsiz(); } while (0)
471
472#if ENABLE_FEATURE_MTAB_SUPPORT
473
474
475
476
477
478static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
479{
480 struct mntent *entries, *m;
481 int i, count;
482 FILE *mountTable;
483
484 mountTable = setmntent(bb_path_mtab_file, "r");
485 if (!mountTable) {
486 bb_perror_msg(bb_path_mtab_file);
487 return;
488 }
489
490 entries = NULL;
491 count = 0;
492 while ((m = getmntent(mountTable)) != NULL) {
493 entries = xrealloc_vector(entries, 3, count);
494 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
495 entries[count].mnt_dir = xstrdup(m->mnt_dir);
496 entries[count].mnt_type = xstrdup(m->mnt_type);
497 entries[count].mnt_opts = xstrdup(m->mnt_opts);
498 entries[count].mnt_freq = m->mnt_freq;
499 entries[count].mnt_passno = m->mnt_passno;
500 count++;
501 }
502 endmntent(mountTable);
503
504 mountTable = setmntent(bb_path_mtab_file, "w");
505 if (mountTable) {
506 for (i = 0; i < count; i++) {
507 if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
508 addmntent(mountTable, &entries[i]);
509 else
510 addmntent(mountTable, mp);
511 }
512 endmntent(mountTable);
513 } else if (errno != EROFS)
514 bb_perror_msg(bb_path_mtab_file);
515
516 if (ENABLE_FEATURE_CLEAN_UP) {
517 for (i = 0; i < count; i++) {
518 free(entries[i].mnt_fsname);
519 free(entries[i].mnt_dir);
520 free(entries[i].mnt_type);
521 free(entries[i].mnt_opts);
522 }
523 free(entries);
524 }
525}
526#endif
527
528#if ENABLE_FEATURE_MOUNT_VERBOSE
529static int verbose_mount(const char *source, const char *target,
530 const char *filesystemtype,
531 unsigned long mountflags, const void *data)
532{
533 int rc;
534
535 errno = 0;
536 rc = mount(source, target, filesystemtype, mountflags, data);
537 if (verbose >= 2)
538 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
539 source, target, filesystemtype,
540 mountflags, (char*)data, rc);
541 return rc;
542}
543#else
544#define verbose_mount(...) mount(__VA_ARGS__)
545#endif
546
547
548static void append_mount_options(char **oldopts, const char *newopts)
549{
550 if (*oldopts && **oldopts) {
551
552 while (newopts[0]) {
553 char *p;
554 int len = strlen(newopts);
555 p = strchr(newopts, ',');
556 if (p) len = p - newopts;
557 p = *oldopts;
558 while (1) {
559 if (!strncmp(p, newopts, len)
560 && (p[len] == ',' || p[len] == '\0'))
561 goto skip;
562 p = strchr(p,',');
563 if (!p) break;
564 p++;
565 }
566 p = xasprintf("%s,%.*s", *oldopts, len, newopts);
567 free(*oldopts);
568 *oldopts = p;
569 skip:
570 newopts += len;
571 while (newopts[0] == ',') newopts++;
572 }
573 } else {
574 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
575 *oldopts = xstrdup(newopts);
576 }
577}
578
579
580
581static unsigned long parse_mount_options(char *options, char **unrecognized)
582{
583 unsigned long flags = MS_SILENT;
584
585
586 for (;;) {
587 unsigned i;
588 char *comma = strchr(options, ',');
589 const char *option_str = mount_option_str;
590
591 if (comma) *comma = '\0';
592
593
594
595 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
596 unsigned opt_len = strlen(option_str);
597
598 if (strncasecmp(option_str, options, opt_len) == 0
599 && (options[opt_len] == '\0'
600
601 IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
602 )
603 ) {
604 unsigned long fl = mount_options[i];
605 if (fl & BB_MS_INVERTED_VALUE)
606 flags &= fl;
607 else
608 flags |= fl;
609 goto found;
610 }
611 option_str += opt_len + 1;
612 }
613
614
615
616
617
618
619
620
621 if (options[0] && unrecognized) {
622
623 char *p = *unrecognized;
624 unsigned len = p ? strlen(p) : 0;
625 *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
626
627
628 if (len) p[len++] = ',';
629 strcpy(p + len, options);
630 }
631 found:
632 if (!comma)
633 break;
634
635 *comma = ',';
636 options = ++comma;
637 }
638
639 return flags;
640}
641
642
643static llist_t *get_block_backed_filesystems(void)
644{
645 static const char filesystems[2][sizeof("/proc/filesystems")] = {
646 "/etc/filesystems",
647 "/proc/filesystems",
648 };
649 char *fs, *buf;
650 llist_t *list = NULL;
651 int i;
652 FILE *f;
653
654 for (i = 0; i < 2; i++) {
655 f = fopen_for_read(filesystems[i]);
656 if (!f) continue;
657
658 while ((buf = xmalloc_fgetline(f)) != NULL) {
659 if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
660 goto next;
661 fs = skip_whitespace(buf);
662 if (*fs == '#' || *fs == '*' || !*fs)
663 goto next;
664
665 llist_add_to_end(&list, xstrdup(fs));
666 next:
667 free(buf);
668 }
669 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
670 }
671
672 return list;
673}
674
675#if ENABLE_FEATURE_CLEAN_UP
676static void delete_block_backed_filesystems(void)
677{
678 llist_free(fslist, free);
679}
680#else
681void delete_block_backed_filesystems(void);
682#endif
683
684
685
686static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
687{
688 int rc = 0;
689
690 vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
691
692 if (FAKE_IT) {
693 if (verbose >= 2)
694 bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
695 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
696 vfsflags, filteropts);
697 goto mtab;
698 }
699
700
701 for (;;) {
702 errno = 0;
703 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
704 vfsflags, filteropts);
705
706
707
708 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
709 char *args[8];
710 int errno_save = errno;
711 args[0] = xasprintf("mount.%s", mp->mnt_type);
712 rc = 1;
713 if (FAKE_IT)
714 args[rc++] = (char *)"-f";
715 if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
716 args[rc++] = (char *)"-n";
717 args[rc++] = mp->mnt_fsname;
718 args[rc++] = mp->mnt_dir;
719 if (filteropts) {
720 args[rc++] = (char *)"-o";
721 args[rc++] = filteropts;
722 }
723 args[rc] = NULL;
724 rc = spawn_and_wait(args);
725 free(args[0]);
726 if (!rc)
727 break;
728 errno = errno_save;
729 }
730
731 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
732 break;
733 if (!(vfsflags & MS_SILENT))
734 bb_error_msg("%s is write-protected, mounting read-only",
735 mp->mnt_fsname);
736 vfsflags |= MS_RDONLY;
737 }
738
739
740
741 if (rc && errno == EPERM)
742 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
743
744
745
746 mtab:
747 if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
748 char *fsname;
749 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
750 const char *option_str = mount_option_str;
751 int i;
752
753 if (!mountTable) {
754 bb_perror_msg(bb_path_mtab_file);
755 goto ret;
756 }
757
758
759 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
760 if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
761 append_mount_options(&(mp->mnt_opts), option_str);
762 option_str += strlen(option_str) + 1;
763 }
764
765
766 i = strlen(mp->mnt_dir) - 1;
767 while (i > 0 && mp->mnt_dir[i] == '/')
768 mp->mnt_dir[i--] = '\0';
769
770
771 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
772 fsname = NULL;
773 if (!mp->mnt_type || !*mp->mnt_type) {
774 mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
775 mp->mnt_type = (char*)"bind";
776 }
777 mp->mnt_freq = mp->mnt_passno = 0;
778
779
780#if ENABLE_FEATURE_MTAB_SUPPORT
781 if (vfsflags & MS_MOVE)
782 update_mtab_entry_on_move(mp);
783 else
784#endif
785 addmntent(mountTable, mp);
786 endmntent(mountTable);
787
788 if (ENABLE_FEATURE_CLEAN_UP) {
789 free(mp->mnt_dir);
790 free(fsname);
791 }
792 }
793 ret:
794 return rc;
795}
796
797#if ENABLE_FEATURE_MOUNT_NFS
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821#define MOUNTPORT 635
822#define MNTPATHLEN 1024
823#define MNTNAMLEN 255
824#define FHSIZE 32
825#define FHSIZE3 64
826
827typedef char fhandle[FHSIZE];
828
829typedef struct {
830 unsigned int fhandle3_len;
831 char *fhandle3_val;
832} fhandle3;
833
834enum mountstat3 {
835 MNT_OK = 0,
836 MNT3ERR_PERM = 1,
837 MNT3ERR_NOENT = 2,
838 MNT3ERR_IO = 5,
839 MNT3ERR_ACCES = 13,
840 MNT3ERR_NOTDIR = 20,
841 MNT3ERR_INVAL = 22,
842 MNT3ERR_NAMETOOLONG = 63,
843 MNT3ERR_NOTSUPP = 10004,
844 MNT3ERR_SERVERFAULT = 10006,
845};
846typedef enum mountstat3 mountstat3;
847
848struct fhstatus {
849 unsigned int fhs_status;
850 union {
851 fhandle fhs_fhandle;
852 } fhstatus_u;
853};
854typedef struct fhstatus fhstatus;
855
856struct mountres3_ok {
857 fhandle3 fhandle;
858 struct {
859 unsigned int auth_flavours_len;
860 char *auth_flavours_val;
861 } auth_flavours;
862};
863typedef struct mountres3_ok mountres3_ok;
864
865struct mountres3 {
866 mountstat3 fhs_status;
867 union {
868 mountres3_ok mountinfo;
869 } mountres3_u;
870};
871typedef struct mountres3 mountres3;
872
873typedef char *dirpath;
874
875typedef char *name;
876
877typedef struct mountbody *mountlist;
878
879struct mountbody {
880 name ml_hostname;
881 dirpath ml_directory;
882 mountlist ml_next;
883};
884typedef struct mountbody mountbody;
885
886typedef struct groupnode *groups;
887
888struct groupnode {
889 name gr_name;
890 groups gr_next;
891};
892typedef struct groupnode groupnode;
893
894typedef struct exportnode *exports;
895
896struct exportnode {
897 dirpath ex_dir;
898 groups ex_groups;
899 exports ex_next;
900};
901typedef struct exportnode exportnode;
902
903struct ppathcnf {
904 int pc_link_max;
905 short pc_max_canon;
906 short pc_max_input;
907 short pc_name_max;
908 short pc_path_max;
909 short pc_pipe_buf;
910 uint8_t pc_vdisable;
911 char pc_xxx;
912 short pc_mask[2];
913};
914typedef struct ppathcnf ppathcnf;
915
916#define MOUNTPROG 100005
917#define MOUNTVERS 1
918
919#define MOUNTPROC_NULL 0
920#define MOUNTPROC_MNT 1
921#define MOUNTPROC_DUMP 2
922#define MOUNTPROC_UMNT 3
923#define MOUNTPROC_UMNTALL 4
924#define MOUNTPROC_EXPORT 5
925#define MOUNTPROC_EXPORTALL 6
926
927#define MOUNTVERS_POSIX 2
928
929#define MOUNTPROC_PATHCONF 7
930
931#define MOUNT_V3 3
932
933#define MOUNTPROC3_NULL 0
934#define MOUNTPROC3_MNT 1
935#define MOUNTPROC3_DUMP 2
936#define MOUNTPROC3_UMNT 3
937#define MOUNTPROC3_UMNTALL 4
938#define MOUNTPROC3_EXPORT 5
939
940enum {
941#ifndef NFS_FHSIZE
942 NFS_FHSIZE = 32,
943#endif
944#ifndef NFS_PORT
945 NFS_PORT = 2049
946#endif
947};
948
949
950
951
952
953
954
955
956
957
958
959struct nfs2_fh {
960 char data[32];
961};
962struct nfs3_fh {
963 unsigned short size;
964 unsigned char data[64];
965};
966
967struct nfs_mount_data {
968 int version;
969 int fd;
970 struct nfs2_fh old_root;
971 int flags;
972 int rsize;
973 int wsize;
974 int timeo;
975 int retrans;
976 int acregmin;
977 int acregmax;
978 int acdirmin;
979 int acdirmax;
980 struct sockaddr_in addr;
981 char hostname[256];
982 int namlen;
983 unsigned int bsize;
984 struct nfs3_fh root;
985};
986
987
988enum {
989 NFS_MOUNT_SOFT = 0x0001,
990 NFS_MOUNT_INTR = 0x0002,
991 NFS_MOUNT_SECURE = 0x0004,
992 NFS_MOUNT_POSIX = 0x0008,
993 NFS_MOUNT_NOCTO = 0x0010,
994 NFS_MOUNT_NOAC = 0x0020,
995 NFS_MOUNT_TCP = 0x0040,
996 NFS_MOUNT_VER3 = 0x0080,
997 NFS_MOUNT_KERBEROS = 0x0100,
998 NFS_MOUNT_NONLM = 0x0200,
999 NFS_MOUNT_NOACL = 0x0800,
1000 NFS_MOUNT_NORDIRPLUS = 0x4000
1001};
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012#ifndef EDQUOT
1013# define EDQUOT ENOSPC
1014#endif
1015
1016static const uint8_t nfs_err_stat[] ALIGN1 = {
1017 1, 2, 5, 6, 13, 17,
1018 19, 20, 21, 22, 27, 28,
1019 30, 63, 66, 69, 70, 71
1020};
1021#if ( \
1022 EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \
1023 ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \
1024 EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
1025typedef uint8_t nfs_err_type;
1026#else
1027typedef uint16_t nfs_err_type;
1028#endif
1029static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
1030 EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
1031 ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
1032 EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
1033};
1034static char *nfs_strerror(int status)
1035{
1036 int i;
1037
1038 for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
1039 if (nfs_err_stat[i] == status)
1040 return strerror(nfs_err_errnum[i]);
1041 }
1042 return xasprintf("unknown nfs status return value: %d", status);
1043}
1044
1045static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
1046{
1047 return xdr_opaque(xdrs, objp, FHSIZE);
1048}
1049
1050static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
1051{
1052 if (!xdr_u_int(xdrs, &objp->fhs_status))
1053 return FALSE;
1054 if (objp->fhs_status == 0)
1055 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
1056 return TRUE;
1057}
1058
1059static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
1060{
1061 return xdr_string(xdrs, objp, MNTPATHLEN);
1062}
1063
1064static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
1065{
1066 return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
1067 (unsigned int *) &objp->fhandle3_len,
1068 FHSIZE3);
1069}
1070
1071static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
1072{
1073 if (!xdr_fhandle3(xdrs, &objp->fhandle))
1074 return FALSE;
1075 return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
1076 &(objp->auth_flavours.auth_flavours_len),
1077 ~0,
1078 sizeof(int),
1079 (xdrproc_t) xdr_int);
1080}
1081
1082static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
1083{
1084 return xdr_enum(xdrs, (enum_t *) objp);
1085}
1086
1087static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
1088{
1089 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
1090 return FALSE;
1091 if (objp->fhs_status == MNT_OK)
1092 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
1093 return TRUE;
1094}
1095
1096#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109static void
1110find_kernel_nfs_mount_version(void)
1111{
1112 int kernel_version;
1113
1114 if (nfs_mount_version)
1115 return;
1116
1117 nfs_mount_version = 4;
1118
1119 kernel_version = get_linux_version_code();
1120 if (kernel_version) {
1121 if (kernel_version < KERNEL_VERSION(2,2,18))
1122 nfs_mount_version = 3;
1123
1124 }
1125}
1126
1127static void
1128get_mountport(struct pmap *pm_mnt,
1129 struct sockaddr_in *server_addr,
1130 long unsigned prog,
1131 long unsigned version,
1132 long unsigned proto,
1133 long unsigned port)
1134{
1135 struct pmaplist *pmap;
1136
1137 server_addr->sin_port = PMAPPORT;
1138
1139
1140 pmap = pmap_getmaps(server_addr);
1141
1142 if (version > MAX_NFSPROT)
1143 version = MAX_NFSPROT;
1144 if (!prog)
1145 prog = MOUNTPROG;
1146 pm_mnt->pm_prog = prog;
1147 pm_mnt->pm_vers = version;
1148 pm_mnt->pm_prot = proto;
1149 pm_mnt->pm_port = port;
1150
1151 while (pmap) {
1152 if (pmap->pml_map.pm_prog != prog)
1153 goto next;
1154 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1155 goto next;
1156 if (version > 2 && pmap->pml_map.pm_vers != version)
1157 goto next;
1158 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1159 goto next;
1160 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1161 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1162 || (port && pmap->pml_map.pm_port != port)
1163 ) {
1164 goto next;
1165 }
1166 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1167 next:
1168 pmap = pmap->pml_next;
1169 }
1170 if (!pm_mnt->pm_vers)
1171 pm_mnt->pm_vers = MOUNTVERS;
1172 if (!pm_mnt->pm_port)
1173 pm_mnt->pm_port = MOUNTPORT;
1174 if (!pm_mnt->pm_prot)
1175 pm_mnt->pm_prot = IPPROTO_TCP;
1176}
1177
1178#if BB_MMU
1179static int daemonize(void)
1180{
1181 int pid = fork();
1182 if (pid < 0)
1183 return -errno;
1184 if (pid > 0)
1185 return 0;
1186
1187 close(0);
1188 xopen(bb_dev_null, O_RDWR);
1189 xdup2(0, 1);
1190 xdup2(0, 2);
1191 setsid();
1192 openlog(applet_name, LOG_PID, LOG_DAEMON);
1193 logmode = LOGMODE_SYSLOG;
1194 return 1;
1195}
1196#else
1197static inline int daemonize(void) { return -ENOSYS; }
1198#endif
1199
1200
1201static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1202{
1203 return 0;
1204}
1205
1206
1207
1208
1209
1210static void error_msg_rpc(const char *msg)
1211{
1212 int len;
1213 while (msg[0] == ' ' || msg[0] == ':') msg++;
1214 len = strlen(msg);
1215 while (len && msg[len-1] == '\n') len--;
1216 bb_error_msg("%.*s", len, msg);
1217}
1218
1219
1220static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1221{
1222 CLIENT *mclient;
1223 char *hostname;
1224 char *pathname;
1225 char *mounthost;
1226
1227
1228
1229 struct nfs_mount_data data;
1230 char *opt;
1231 struct hostent *hp;
1232 struct sockaddr_in server_addr;
1233 struct sockaddr_in mount_server_addr;
1234 int msock, fsock;
1235 union {
1236 struct fhstatus nfsv2;
1237 struct mountres3 nfsv3;
1238 } status;
1239 int daemonized;
1240 char *s;
1241 int port;
1242 int mountport;
1243 int proto;
1244#if BB_MMU
1245 smallint bg = 0;
1246#else
1247 enum { bg = 0 };
1248#endif
1249 int retry;
1250 int mountprog;
1251 int mountvers;
1252 int nfsprog;
1253 int nfsvers;
1254 int retval;
1255
1256 smallint tcp;
1257 smallint soft;
1258 int intr;
1259 int posix;
1260 int nocto;
1261 int noac;
1262 int nordirplus;
1263 int nolock;
1264 int noacl;
1265
1266 find_kernel_nfs_mount_version();
1267
1268 daemonized = 0;
1269 mounthost = NULL;
1270 retval = ETIMEDOUT;
1271 msock = fsock = -1;
1272 mclient = NULL;
1273
1274
1275
1276 filteropts = xstrdup(filteropts);
1277
1278 hostname = xstrdup(mp->mnt_fsname);
1279
1280 s = strchr(hostname, ':');
1281 pathname = s + 1;
1282 *s = '\0';
1283
1284
1285 s = strchr(hostname, ',');
1286 if (s) {
1287 *s = '\0';
1288 bb_error_msg("warning: multiple hostnames not supported");
1289 }
1290
1291 server_addr.sin_family = AF_INET;
1292 if (!inet_aton(hostname, &server_addr.sin_addr)) {
1293 hp = gethostbyname(hostname);
1294 if (hp == NULL) {
1295 bb_herror_msg("%s", hostname);
1296 goto fail;
1297 }
1298 if (hp->h_length != (int)sizeof(struct in_addr)) {
1299 bb_error_msg_and_die("only IPv4 is supported");
1300 }
1301 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1302 }
1303
1304 memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1305
1306
1307
1308 if (!mp->mnt_opts) {
1309 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1310 } else {
1311 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1312 mp->mnt_opts[0] ? "," : "",
1313 inet_ntoa(server_addr.sin_addr));
1314 free(mp->mnt_opts);
1315 mp->mnt_opts = tmp;
1316 }
1317
1318
1319
1320
1321
1322 memset(&data, 0, sizeof(data));
1323 data.retrans = 3;
1324 data.acregmin = 3;
1325 data.acregmax = 60;
1326 data.acdirmin = 30;
1327 data.acdirmax = 60;
1328 data.namlen = NAME_MAX;
1329
1330 soft = 0;
1331 intr = 0;
1332 posix = 0;
1333 nocto = 0;
1334 nolock = 0;
1335 noac = 0;
1336 nordirplus = 0;
1337 noacl = 0;
1338 retry = 10000;
1339 tcp = 1;
1340
1341 mountprog = MOUNTPROG;
1342 mountvers = 0;
1343 port = 0;
1344 mountport = 0;
1345 nfsprog = 100003;
1346 nfsvers = 0;
1347
1348
1349 if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1350 char *opteq = strchr(opt, '=');
1351 if (opteq) {
1352 int val, idx;
1353 static const char options[] ALIGN1 =
1354 "rsize\0"
1355 "wsize\0"
1356 "timeo\0"
1357 "retrans\0"
1358 "acregmin\0"
1359 "acregmax\0"
1360 "acdirmin\0"
1361 "acdirmax\0"
1362 "actimeo\0"
1363 "retry\0"
1364 "port\0"
1365 "mountport\0"
1366 "mounthost\0"
1367 "mountprog\0"
1368 "mountvers\0"
1369 "nfsprog\0"
1370 "nfsvers\0"
1371 "vers\0"
1372 "proto\0"
1373 "namlen\0"
1374 "addr\0";
1375
1376 *opteq++ = '\0';
1377 idx = index_in_strings(options, opt);
1378 switch (idx) {
1379 case 12:
1380 mounthost = xstrndup(opteq,
1381 strcspn(opteq, " \t\n\r,"));
1382 continue;
1383 case 18:
1384 if (is_prefixed_with(opteq, "tcp"))
1385 tcp = 1;
1386 else if (is_prefixed_with(opteq, "udp"))
1387 tcp = 0;
1388 else
1389 bb_error_msg("warning: unrecognized proto= option");
1390 continue;
1391 case 20:
1392 continue;
1393 case -1:
1394 if (vfsflags & MS_REMOUNT)
1395 continue;
1396 }
1397
1398 val = xatoi_positive(opteq);
1399 switch (idx) {
1400 case 0:
1401 data.rsize = val;
1402 continue;
1403 case 1:
1404 data.wsize = val;
1405 continue;
1406 case 2:
1407 data.timeo = val;
1408 continue;
1409 case 3:
1410 data.retrans = val;
1411 continue;
1412 case 4:
1413 data.acregmin = val;
1414 continue;
1415 case 5:
1416 data.acregmax = val;
1417 continue;
1418 case 6:
1419 data.acdirmin = val;
1420 continue;
1421 case 7:
1422 data.acdirmax = val;
1423 continue;
1424 case 8:
1425 data.acregmin = val;
1426 data.acregmax = val;
1427 data.acdirmin = val;
1428 data.acdirmax = val;
1429 continue;
1430 case 9:
1431 retry = val;
1432 continue;
1433 case 10:
1434 port = val;
1435 continue;
1436 case 11:
1437 mountport = val;
1438 continue;
1439 case 13:
1440 mountprog = val;
1441 continue;
1442 case 14:
1443 mountvers = val;
1444 continue;
1445 case 15:
1446 nfsprog = val;
1447 continue;
1448 case 16:
1449 case 17:
1450 nfsvers = val;
1451 continue;
1452 case 19:
1453
1454 data.namlen = val;
1455
1456
1457 continue;
1458 default:
1459 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1460 goto fail;
1461 }
1462 }
1463 else {
1464 static const char options[] ALIGN1 =
1465 "bg\0"
1466 "fg\0"
1467 "soft\0"
1468 "hard\0"
1469 "intr\0"
1470 "posix\0"
1471 "cto\0"
1472 "ac\0"
1473 "tcp\0"
1474 "udp\0"
1475 "lock\0"
1476 "rdirplus\0"
1477 "acl\0";
1478 int val = 1;
1479 if (is_prefixed_with(opt, "no")) {
1480 val = 0;
1481 opt += 2;
1482 }
1483 switch (index_in_strings(options, opt)) {
1484 case 0:
1485#if BB_MMU
1486 bg = val;
1487#endif
1488 break;
1489 case 1:
1490#if BB_MMU
1491 bg = !val;
1492#endif
1493 break;
1494 case 2:
1495 soft = val;
1496 break;
1497 case 3:
1498 soft = !val;
1499 break;
1500 case 4:
1501 intr = val;
1502 break;
1503 case 5:
1504 posix = val;
1505 break;
1506 case 6:
1507 nocto = !val;
1508 break;
1509 case 7:
1510 noac = !val;
1511 break;
1512 case 8:
1513 tcp = val;
1514 break;
1515 case 9:
1516 tcp = !val;
1517 break;
1518 case 10:
1519 if (nfs_mount_version >= 3)
1520 nolock = !val;
1521 else
1522 bb_error_msg("warning: option nolock is not supported");
1523 break;
1524 case 11:
1525 nordirplus = !val;
1526 break;
1527 case 12:
1528 noacl = !val;
1529 break;
1530 default:
1531 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1532 goto fail;
1533 }
1534 }
1535 }
1536 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1537
1538 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1539 | (intr ? NFS_MOUNT_INTR : 0)
1540 | (posix ? NFS_MOUNT_POSIX : 0)
1541 | (nocto ? NFS_MOUNT_NOCTO : 0)
1542 | (noac ? NFS_MOUNT_NOAC : 0)
1543 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1544 | (noacl ? NFS_MOUNT_NOACL : 0);
1545 if (nfs_mount_version >= 2)
1546 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1547 if (nfs_mount_version >= 3)
1548 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1549 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1550 bb_error_msg("NFSv%d not supported", nfsvers);
1551 goto fail;
1552 }
1553 if (nfsvers && !mountvers)
1554 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1555 if (nfsvers && nfsvers < mountvers) {
1556 mountvers = nfsvers;
1557 }
1558
1559
1560 if (!data.timeo)
1561 data.timeo = tcp ? 70 : 7;
1562
1563 data.version = nfs_mount_version;
1564
1565 if (vfsflags & MS_REMOUNT)
1566 goto do_mount;
1567
1568
1569
1570
1571
1572
1573 if (bg && we_saw_this_host_before(hostname)) {
1574 daemonized = daemonize();
1575 if (daemonized <= 0) {
1576 retval = -daemonized;
1577 goto ret;
1578 }
1579 }
1580
1581
1582
1583 if (mounthost) {
1584 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1585 mount_server_addr.sin_family = AF_INET;
1586 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1587 } else {
1588 hp = gethostbyname(mounthost);
1589 if (hp == NULL) {
1590 bb_herror_msg("%s", mounthost);
1591 goto fail;
1592 }
1593 if (hp->h_length != (int)sizeof(struct in_addr)) {
1594 bb_error_msg_and_die("only IPv4 is supported");
1595 }
1596 mount_server_addr.sin_family = AF_INET;
1597 memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1598 }
1599 }
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613 {
1614 struct timeval total_timeout;
1615 struct timeval retry_timeout;
1616 struct pmap pm_mnt;
1617 time_t t;
1618 time_t prevt;
1619 time_t timeout;
1620
1621 retry_timeout.tv_sec = 3;
1622 retry_timeout.tv_usec = 0;
1623 total_timeout.tv_sec = 20;
1624 total_timeout.tv_usec = 0;
1625
1626 timeout = time(NULL) + 60 * retry;
1627 prevt = 0;
1628 t = 30;
1629 retry:
1630
1631 if (t - prevt < 30)
1632 sleep(30);
1633
1634 get_mountport(&pm_mnt, &mount_server_addr,
1635 mountprog,
1636 mountvers,
1637 proto,
1638 mountport);
1639 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1640
1641
1642 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1643 msock = RPC_ANYSOCK;
1644
1645 switch (pm_mnt.pm_prot) {
1646 case IPPROTO_UDP:
1647 mclient = clntudp_create(&mount_server_addr,
1648 pm_mnt.pm_prog,
1649 pm_mnt.pm_vers,
1650 retry_timeout,
1651 &msock);
1652 if (mclient)
1653 break;
1654 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1655 msock = RPC_ANYSOCK;
1656 case IPPROTO_TCP:
1657 mclient = clnttcp_create(&mount_server_addr,
1658 pm_mnt.pm_prog,
1659 pm_mnt.pm_vers,
1660 &msock, 0, 0);
1661 break;
1662 default:
1663 mclient = NULL;
1664 }
1665 if (!mclient) {
1666 if (!daemonized && prevt == 0)
1667 error_msg_rpc(clnt_spcreateerror(" "));
1668 } else {
1669 enum clnt_stat clnt_stat;
1670
1671
1672 mclient->cl_auth = authunix_create_default();
1673
1674
1675
1676
1677 memset(&status, 0, sizeof(status));
1678
1679 if (pm_mnt.pm_vers == 3)
1680 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1681 (xdrproc_t) xdr_dirpath,
1682 (caddr_t) &pathname,
1683 (xdrproc_t) xdr_mountres3,
1684 (caddr_t) &status,
1685 total_timeout);
1686 else
1687 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1688 (xdrproc_t) xdr_dirpath,
1689 (caddr_t) &pathname,
1690 (xdrproc_t) xdr_fhstatus,
1691 (caddr_t) &status,
1692 total_timeout);
1693
1694 if (clnt_stat == RPC_SUCCESS)
1695 goto prepare_kernel_data;
1696 if (errno != ECONNREFUSED) {
1697 error_msg_rpc(clnt_sperror(mclient, " "));
1698 goto fail;
1699 }
1700
1701 if (!daemonized && prevt == 0)
1702 error_msg_rpc(clnt_sperror(mclient, " "));
1703 auth_destroy(mclient->cl_auth);
1704 clnt_destroy(mclient);
1705 mclient = NULL;
1706 close(msock);
1707 msock = -1;
1708 }
1709
1710
1711 if (!bg)
1712 goto fail;
1713 if (!daemonized) {
1714 daemonized = daemonize();
1715 if (daemonized <= 0) {
1716 retval = -daemonized;
1717 goto ret;
1718 }
1719 }
1720 prevt = t;
1721 t = time(NULL);
1722 if (t >= timeout)
1723
1724 goto fail;
1725
1726 goto retry;
1727 }
1728
1729 prepare_kernel_data:
1730
1731 if (nfsvers == 2) {
1732 if (status.nfsv2.fhs_status != 0) {
1733 bb_error_msg("%s:%s failed, reason given by server: %s",
1734 hostname, pathname,
1735 nfs_strerror(status.nfsv2.fhs_status));
1736 goto fail;
1737 }
1738 memcpy(data.root.data,
1739 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1740 NFS_FHSIZE);
1741 data.root.size = NFS_FHSIZE;
1742 memcpy(data.old_root.data,
1743 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1744 NFS_FHSIZE);
1745 } else {
1746 fhandle3 *my_fhandle;
1747 if (status.nfsv3.fhs_status != 0) {
1748 bb_error_msg("%s:%s failed, reason given by server: %s",
1749 hostname, pathname,
1750 nfs_strerror(status.nfsv3.fhs_status));
1751 goto fail;
1752 }
1753 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1754 memset(data.old_root.data, 0, NFS_FHSIZE);
1755 memset(&data.root, 0, sizeof(data.root));
1756 data.root.size = my_fhandle->fhandle3_len;
1757 memcpy(data.root.data,
1758 (char *) my_fhandle->fhandle3_val,
1759 my_fhandle->fhandle3_len);
1760
1761 data.flags |= NFS_MOUNT_VER3;
1762 }
1763
1764
1765 if (tcp) {
1766 if (nfs_mount_version < 3) {
1767 bb_error_msg("NFS over TCP is not supported");
1768 goto fail;
1769 }
1770 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1771 } else
1772 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1773 if (fsock < 0) {
1774 bb_perror_msg("nfs socket");
1775 goto fail;
1776 }
1777 if (bindresvport(fsock, 0) < 0) {
1778 bb_perror_msg("nfs bindresvport");
1779 goto fail;
1780 }
1781 if (port == 0) {
1782 server_addr.sin_port = PMAPPORT;
1783 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1784 tcp ? IPPROTO_TCP : IPPROTO_UDP);
1785 if (port == 0)
1786 port = NFS_PORT;
1787 }
1788 server_addr.sin_port = htons(port);
1789
1790
1791 data.fd = fsock;
1792 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1793 strncpy(data.hostname, hostname, sizeof(data.hostname));
1794
1795
1796 auth_destroy(mclient->cl_auth);
1797 clnt_destroy(mclient);
1798 close(msock);
1799 msock = -1;
1800
1801 if (bg) {
1802
1803 struct stat statbuf;
1804 int delay = 1;
1805 while (stat(mp->mnt_dir, &statbuf) == -1) {
1806 if (!daemonized) {
1807 daemonized = daemonize();
1808 if (daemonized <= 0) {
1809
1810 retval = -daemonized;
1811 goto ret;
1812 }
1813 }
1814 sleep(delay);
1815 delay *= 2;
1816 if (delay > 30)
1817 delay = 30;
1818 }
1819 }
1820
1821
1822 do_mount:
1823 retval = mount_it_now(mp, vfsflags, (char*)&data);
1824 goto ret;
1825
1826
1827 fail:
1828 if (msock >= 0) {
1829 if (mclient) {
1830 auth_destroy(mclient->cl_auth);
1831 clnt_destroy(mclient);
1832 }
1833 close(msock);
1834 }
1835 if (fsock >= 0)
1836 close(fsock);
1837
1838 ret:
1839 free(hostname);
1840 free(mounthost);
1841 free(filteropts);
1842 return retval;
1843}
1844
1845#else
1846
1847
1848
1849
1850
1851static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1852{
1853 len_and_sockaddr *lsa;
1854 char *opts;
1855 char *end;
1856 char *dotted;
1857 int ret;
1858
1859# if ENABLE_FEATURE_IPV6
1860 end = strchr(mp->mnt_fsname, ']');
1861 if (end && end[1] == ':')
1862 end++;
1863 else
1864# endif
1865
1866 end = strchr(mp->mnt_fsname, ':');
1867
1868 *end = '\0';
1869 lsa = xhost2sockaddr(mp->mnt_fsname, 0);
1870 *end = ':';
1871 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1872 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1873 opts = xasprintf("%s%saddr=%s",
1874 filteropts ? filteropts : "",
1875 filteropts ? "," : "",
1876 dotted
1877 );
1878 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1879 ret = mount_it_now(mp, vfsflags, opts);
1880 if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1881
1882 return ret;
1883}
1884
1885#endif
1886
1887
1888
1889
1890static int singlemount(struct mntent *mp, int ignore_busy)
1891{
1892 int loopfd = -1;
1893 int rc = -1;
1894 unsigned long vfsflags;
1895 char *loopFile = NULL, *filteropts = NULL;
1896 llist_t *fl = NULL;
1897 struct stat st;
1898
1899 errno = 0;
1900
1901 vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1902
1903
1904 if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1905 mp->mnt_type = NULL;
1906
1907
1908 if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1909 char *args[35];
1910 char *s;
1911 int n;
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921 s = mp->mnt_fsname;
1922 n = 0;
1923 args[n++] = s;
1924 while (*s && n < 35 - 2) {
1925 if (*s++ == '#' && *s != '#') {
1926 s[-1] = '\0';
1927 args[n++] = s;
1928 }
1929 }
1930 args[n++] = mp->mnt_dir;
1931 args[n] = NULL;
1932 rc = spawn_and_wait(args);
1933 goto report_error;
1934 }
1935
1936
1937 if (ENABLE_FEATURE_MOUNT_CIFS
1938 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1939 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1940 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1941 ) {
1942 int len;
1943 char c;
1944 char *hostname, *share;
1945 len_and_sockaddr *lsa;
1946
1947
1948
1949 hostname = mp->mnt_fsname + 2;
1950 len = strcspn(hostname, "/\\");
1951 share = hostname + len + 1;
1952 if (len == 0
1953 || share[-1] == '\0'
1954 || share[0] == '\0'
1955 ) {
1956 goto report_error;
1957 }
1958 c = share[-1];
1959 share[-1] = '\0';
1960 len = strcspn(share, "/\\");
1961
1962
1963
1964
1965
1966 {
1967 char *unc = xasprintf(
1968 share[len] != '\0'
1969 ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1970 : "unc=\\\\%s\\%.*s",
1971 hostname,
1972 len, share,
1973 share + len + 1
1974 );
1975 parse_mount_options(unc, &filteropts);
1976 if (ENABLE_FEATURE_CLEAN_UP) free(unc);
1977 }
1978
1979 lsa = host2sockaddr(hostname, 0);
1980 share[-1] = c;
1981 if (!lsa)
1982 goto report_error;
1983
1984
1985 if (!is_prefixed_with(filteropts, ",ip="+1)
1986 && !strstr(filteropts, ",ip=")
1987 ) {
1988 char *dotted, *ip;
1989
1990 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1991 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1992 ip = xasprintf("ip=%s", dotted);
1993 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1994
1995
1996
1997
1998
1999
2000
2001 parse_mount_options(ip, &filteropts);
2002 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2003 }
2004
2005 mp->mnt_type = (char*)"cifs";
2006 rc = mount_it_now(mp, vfsflags, filteropts);
2007
2008 goto report_error;
2009 }
2010
2011
2012 if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2013 && strchr(mp->mnt_fsname, ':') != NULL
2014 ) {
2015 if (!mp->mnt_type)
2016 mp->mnt_type = (char*)"nfs";
2017 rc = nfsmount(mp, vfsflags, filteropts);
2018 goto report_error;
2019 }
2020
2021
2022
2023
2024
2025 if (!stat(mp->mnt_fsname, &st)
2026 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2027 ) {
2028
2029 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2030 loopFile = bb_simplify_path(mp->mnt_fsname);
2031 mp->mnt_fsname = NULL;
2032
2033
2034
2035
2036
2037
2038
2039 loopfd = set_loop(&mp->mnt_fsname,
2040 loopFile,
2041 0,
2042 ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2043 | BB_LO_FLAGS_AUTOCLEAR
2044 );
2045 if (loopfd < 0) {
2046 if (errno == EPERM || errno == EACCES)
2047 bb_error_msg(bb_msg_perm_denied_are_you_root);
2048 else
2049 bb_perror_msg("can't setup loop device");
2050 return errno;
2051 }
2052
2053
2054 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2055 vfsflags |= MS_BIND;
2056 }
2057
2058
2059
2060 if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2061 char *next;
2062 for (;;) {
2063 next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2064 if (next)
2065 *next = '\0';
2066 rc = mount_it_now(mp, vfsflags, filteropts);
2067 if (rc == 0 || !next)
2068 break;
2069 mp->mnt_type = next + 1;
2070 }
2071 } else {
2072
2073
2074
2075
2076
2077
2078 if (!fslist) {
2079 fslist = get_block_backed_filesystems();
2080 if (ENABLE_FEATURE_CLEAN_UP && fslist)
2081 atexit(delete_block_backed_filesystems);
2082 }
2083
2084 for (fl = fslist; fl; fl = fl->link) {
2085 mp->mnt_type = fl->data;
2086 rc = mount_it_now(mp, vfsflags, filteropts);
2087 if (rc == 0)
2088 break;
2089 }
2090 }
2091
2092
2093
2094
2095 if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2096 del_loop(mp->mnt_fsname);
2097 if (ENABLE_FEATURE_CLEAN_UP) {
2098 free(loopFile);
2099
2100 }
2101 }
2102
2103 report_error:
2104 if (ENABLE_FEATURE_CLEAN_UP)
2105 free(filteropts);
2106
2107 if (loopfd >= 0)
2108 close(loopfd);
2109
2110 if (errno == EBUSY && ignore_busy)
2111 return 0;
2112 if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2113 return 0;
2114 if (rc != 0)
2115 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2116 return rc;
2117}
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131static int match_opt(const char *fs_opt_in, const char *O_opt)
2132{
2133 if (!O_opt)
2134 return 1;
2135
2136 while (*O_opt) {
2137 const char *fs_opt = fs_opt_in;
2138 int O_len;
2139 int match;
2140
2141
2142
2143 match = 0;
2144 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2145 match = 1;
2146 O_opt += 2;
2147 }
2148
2149 O_len = strchrnul(O_opt, ',') - O_opt;
2150
2151 while (1) {
2152 if (strncmp(fs_opt, O_opt, O_len) == 0
2153 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2154 ) {
2155 if (match)
2156 return 0;
2157 match = 1;
2158 break;
2159 }
2160 fs_opt = strchr(fs_opt, ',');
2161 if (!fs_opt)
2162 break;
2163 fs_opt++;
2164 }
2165 if (match == 0)
2166 return 0;
2167 if (O_opt[O_len] == '\0')
2168 break;
2169
2170 O_opt += O_len + 1;
2171 }
2172
2173 return 1;
2174}
2175
2176
2177
2178int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2179int mount_main(int argc UNUSED_PARAM, char **argv)
2180{
2181 char *cmdopts = xzalloc(1);
2182 char *fstype = NULL;
2183 char *O_optmatch = NULL;
2184 char *storage_path;
2185 llist_t *lst_o = NULL;
2186 const char *fstabname = "/etc/fstab";
2187 FILE *fstab;
2188 int i, j;
2189 int rc = EXIT_SUCCESS;
2190 unsigned long cmdopt_flags;
2191 unsigned opt;
2192 struct mntent mtpair[2], *mtcur = mtpair;
2193 IF_NOT_DESKTOP(const int nonroot = 0;)
2194
2195 IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2196
2197 INIT_G();
2198
2199
2200
2201 for (i = j = 1; argv[i]; i++) {
2202 if (argv[i][0] == '-' && argv[i][1] == '-')
2203 append_mount_options(&cmdopts, argv[i] + 2);
2204 else
2205 argv[j++] = argv[i];
2206 }
2207 argv[j] = NULL;
2208
2209
2210
2211 opt = getopt32(argv, "^"
2212 OPTION_STR
2213 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2214 &lst_o, &fstype, &O_optmatch
2215 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2216 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2217 );
2218
2219 while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o));
2220 if (opt & OPT_r) append_mount_options(&cmdopts, "ro");
2221 if (opt & OPT_w) append_mount_options(&cmdopts, "rw");
2222 argv += optind;
2223
2224
2225 if (!argv[0]) {
2226 if (!(opt & OPT_a)) {
2227 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2228
2229 if (!mountTable)
2230 bb_error_msg_and_die("no %s", bb_path_mtab_file);
2231
2232 while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2233 GETMNTENT_BUFSIZE))
2234 {
2235
2236
2237
2238
2239 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2240 printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2241 mtpair->mnt_dir, mtpair->mnt_type,
2242 mtpair->mnt_opts);
2243 }
2244 if (ENABLE_FEATURE_CLEAN_UP)
2245 endmntent(mountTable);
2246 return EXIT_SUCCESS;
2247 }
2248 storage_path = NULL;
2249 } else {
2250
2251
2252
2253 if (argv[1]) {
2254 if (nonroot)
2255 bb_error_msg_and_die(bb_msg_you_must_be_root);
2256 mtpair->mnt_fsname = argv[0];
2257 mtpair->mnt_dir = argv[1];
2258 mtpair->mnt_type = fstype;
2259 mtpair->mnt_opts = cmdopts;
2260 resolve_mount_spec(&mtpair->mnt_fsname);
2261 rc = singlemount(mtpair, 0);
2262 return rc;
2263 }
2264 storage_path = bb_simplify_path(argv[0]);
2265 }
2266
2267
2268
2269
2270 cmdopt_flags = parse_mount_options(cmdopts, NULL);
2271 if (nonroot && (cmdopt_flags & ~MS_SILENT))
2272 bb_error_msg_and_die(bb_msg_you_must_be_root);
2273
2274
2275 if (ENABLE_FEATURE_MOUNT_FLAGS
2276 && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2277 ) {
2278
2279 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2280 if (rc)
2281 bb_simple_perror_msg_and_die(argv[0]);
2282 return rc;
2283 }
2284
2285
2286 if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2287 fstabname = "/etc/fstab";
2288
2289 if (cmdopt_flags & MS_REMOUNT) {
2290
2291
2292
2293 fstabname = bb_path_mtab_file;
2294 }
2295 fstab = setmntent(fstabname, "r");
2296 if (!fstab)
2297 bb_perror_msg_and_die("can't read '%s'", fstabname);
2298
2299
2300 memset(mtpair, 0, sizeof(mtpair));
2301 for (;;) {
2302 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2303
2304
2305 if (!getmntent_r(fstab, mtcur, getmntent_buf
2306 + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2307 GETMNTENT_BUFSIZE/2)
2308 ) {
2309 mtcur = mtother;
2310 break;
2311 }
2312
2313
2314
2315
2316 if (argv[0]) {
2317
2318
2319 if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2320 && strcmp(storage_path, mtcur->mnt_fsname) != 0
2321 && strcmp(argv[0], mtcur->mnt_dir) != 0
2322 && strcmp(storage_path, mtcur->mnt_dir) != 0
2323 ) {
2324 continue;
2325 }
2326
2327
2328
2329 mtcur = mtother;
2330
2331
2332 } else {
2333 struct mntent *mp;
2334
2335
2336 if (nonroot)
2337 bb_error_msg_and_die(bb_msg_you_must_be_root);
2338
2339
2340 if (!fstype_matches(mtcur->mnt_type, fstype))
2341 continue;
2342
2343
2344 if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2345
2346 || strcasecmp(mtcur->mnt_type, "swap") == 0
2347 ) {
2348 continue;
2349 }
2350
2351
2352
2353 if (!match_opt(mtcur->mnt_opts, O_optmatch))
2354 continue;
2355
2356 resolve_mount_spec(&mtcur->mnt_fsname);
2357
2358
2359 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2360
2361
2362
2363 mp = find_mount_point(mtcur->mnt_dir, 0);
2364
2365
2366
2367 if (mp) {
2368 if (verbose) {
2369 bb_error_msg("according to %s, "
2370 "%s is already mounted on %s",
2371 bb_path_mtab_file,
2372 mp->mnt_fsname, mp->mnt_dir);
2373 }
2374 } else {
2375
2376 if (singlemount(mtcur, 1)) {
2377
2378 rc++;
2379 }
2380 }
2381 free(mtcur->mnt_opts);
2382 }
2383 }
2384
2385
2386
2387 if (argv[0]) {
2388 unsigned long l;
2389
2390
2391 if (!mtcur->mnt_fsname)
2392 bb_error_msg_and_die("can't find %s in %s",
2393 argv[0], fstabname);
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403#if 0
2404
2405 l = parse_mount_options(mtcur->mnt_opts, NULL);
2406 if ((l & MOUNT_SWAP)
2407
2408 || strcasecmp(mtcur->mnt_type, "swap") == 0
2409 ) {
2410 goto ret;
2411 }
2412#endif
2413 if (nonroot) {
2414
2415 l = parse_mount_options(mtcur->mnt_opts, NULL);
2416 if (!(l & MOUNT_USERS))
2417 bb_error_msg_and_die(bb_msg_you_must_be_root);
2418 }
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431 mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2432 append_mount_options(&(mtcur->mnt_opts), cmdopts);
2433 resolve_mount_spec(&mtpair->mnt_fsname);
2434 rc = singlemount(mtcur, 0);
2435 if (ENABLE_FEATURE_CLEAN_UP)
2436 free(mtcur->mnt_opts);
2437
2438 }
2439
2440
2441 if (ENABLE_FEATURE_CLEAN_UP)
2442 endmntent(fstab);
2443 if (ENABLE_FEATURE_CLEAN_UP) {
2444 free(storage_path);
2445 free(cmdopts);
2446 }
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457 return rc;
2458}
2459