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