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