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