latest and greatest.
[oweals/busybox.git] / mount.c
1 /*
2    3/21/1999    Charles P. Wright <cpwright@cpwright.com>
3                 searches through fstab when -a is passed
4                 will try mounting stuff with all fses when passed -t auto
5
6         1999-04-17      Dave Cinege...Rewrote -t auto. Fixed ro mtab.
7 */
8
9 #include "internal.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <mntent.h>
16 #include <sys/mount.h>
17 #include <ctype.h>
18
19 const char      mount_usage[] = "mount\n"
20 "\t\tmount [flags] special-device directory\n"
21 "\n"
22 "Flags:\n"
23 "\t-a:\tMount all file systems in fstab.\n"
24 "\t-f:\t\"Fake\" mount. Add entry to mount table but don't mount it.\n"
25 "\t-n:\tDon't write a mount table entry.\n"
26 "\t-o option:\tOne of many filesystem options, listed below.\n"
27 "\t-r:\tMount the filesystem read-only.\n"
28 "\t-t filesystem-type:\tSpecify the filesystem type.\n"
29 "\t-w:\tMount for reading and writing (default).\n"
30 "\n"
31 "Options for use with the \"-o\" flag:\n"
32 "\tasync / sync:\tWrites are asynchronous / synchronous.\n"
33 "\tdev / nodev:\tAllow use of special device files / disallow them.\n"
34 "\texec / noexec:\tAllow use of executable files / disallow them.\n"
35 "\tsuid / nosuid:\tAllow set-user-id-root programs / disallow them.\n"
36 "\tremount: Re-mount a currently-mounted filesystem, changing its flags.\n"
37 "\tro / rw: Mount for read-only / read-write.\n"
38 "\t"
39 "There are EVEN MORE flags that are specific to each filesystem.\n"
40 "You'll have to see the written documentation for those.\n";
41
42 struct mount_options {
43         const char *    name;
44         unsigned long   and;
45         unsigned long   or;
46 };
47
48 static const struct mount_options       mount_options[] = {
49         {       "async",        ~MS_SYNCHRONOUS,0               },
50         {       "defaults",     ~0,             0               },
51         {       "dev",          ~MS_NODEV,      0               },
52         {       "exec",         ~MS_NOEXEC,     0               },
53         {       "nodev",        ~0,             MS_NODEV        },
54         {       "noexec",       ~0,             MS_NOEXEC       },
55         {       "nosuid",       ~0,             MS_NOSUID       },
56         {       "remount",      ~0,             MS_REMOUNT      },
57         {       "ro",           ~0,             MS_RDONLY       },
58         {       "rw",           ~MS_RDONLY,     0               },
59         {       "suid",         ~MS_NOSUID,     0               },
60         {       "sync",         ~0,             MS_SYNCHRONOUS  },
61         {       0,              0,              0               }
62 };
63
64 static void
65 show_flags(unsigned long flags, char * buffer)
66 {
67         const struct mount_options *    f = mount_options;
68         while ( f->name ) {
69                 if ( flags & f->and ) {
70                         int     length = strlen(f->name);
71                         memcpy(buffer, f->name, length);
72                         buffer += length;
73                         *buffer++ = ',';
74                         *buffer = '\0';
75                 }
76                 f++;
77         }
78 }
79
80 static void
81 one_option(
82  char *                 option
83 ,unsigned long *        flags
84 ,char *                 data)
85 {
86         const struct mount_options *    f = mount_options;
87
88         while ( f->name != 0 ) {
89                 if ( strcasecmp(f->name, option) == 0 ) {
90                         *flags &= f->and;
91                         *flags |= f->or;
92                         return;
93                 }
94                 f++;
95         }
96         if ( *data ) {
97                 data += strlen(data);
98                 *data++ = ',';
99         }
100         strcpy(data, option);
101 }
102
103 static void
104 parse_mount_options(
105  char *                 options
106 ,unsigned long *        flags
107 ,char *                 data)
108 {
109         while ( *options ) {
110                 char * comma = strchr(options, ',');
111                 if ( comma )
112                         *comma = '\0';
113                 one_option(options, flags, data);
114                 if ( comma ) {
115                         *comma = ',';
116                         options = ++comma;
117                 }
118                 else
119                         break;
120         }
121 }
122
123 int
124 mount_one(
125  char *         blockDevice
126 ,char *         directory
127 ,char *         filesystemType
128 ,unsigned long  flags
129 ,char *         string_flags
130 ,int            noMtab
131 ,int            fake)
132 {
133         int     error = 0;
134         int     status = 0;
135
136         char    buf[255];
137                 
138         if (!fake) {
139                 if (*filesystemType == 'a') {                   //Will fail on real FS starting with 'a'
140
141                         FILE    *f = fopen("/proc/filesystems", "r");
142
143                         if (f == NULL)  return 1;
144
145                         while (fgets(buf, sizeof(buf), f) != NULL) {
146                                 filesystemType = buf;
147                                 if (*filesystemType == '\t') {  // Not a nodev filesystem
148                                         
149                                         while (*filesystemType && *filesystemType != '\n')      filesystemType++;
150                                         *filesystemType = '\0';
151                                                 
152                                         filesystemType = buf;
153                                         filesystemType++;       //hop past tab
154                                         
155                                         status = mount(blockDevice, directory, filesystemType,
156                                                         flags|MS_MGC_VAL ,string_flags);
157                                         error = errno;
158
159                                         if (status == 0) break;
160                                 }
161                         }
162                         fclose(f);
163                 } else {        
164
165                         status = mount( blockDevice, directory, filesystemType,
166                                         flags|MS_MGC_VAL ,string_flags);
167                         error = errno;
168                 }
169         }
170
171         if ( status == 0 ) {
172                 char *  s = &string_flags[strlen(string_flags)];
173                 FILE *  mountTable;
174                 if ( s != string_flags ) {
175                         *s++ = ',';
176                         show_flags(flags, s);
177                 }
178                 if ( !noMtab && (mountTable = setmntent("/etc/mtab", "a+")) ) {
179                         int     length = strlen(directory);
180                         struct mntent   m;
181
182                         if ( length > 1 && directory[length - 1] == '/' )
183                                 directory[length - 1] = '\0';
184
185                         if ( filesystemType == 0 ) {
186                                 struct mntent * p
187                                  = findMountPoint(blockDevice, "/proc/mounts");
188
189                                 if ( p && p->mnt_type )
190                                         filesystemType = p->mnt_type;
191                         }
192                         m.mnt_fsname = blockDevice;
193                         m.mnt_dir = directory;
194                         m.mnt_type = filesystemType ? filesystemType : "default";
195                         
196                         if (*string_flags) {
197                                 m.mnt_opts = string_flags;
198                         } else {
199                                 if ( (flags | MS_RDONLY) == flags )
200                                         m.mnt_opts = "ro";
201                                 else    
202                                         m.mnt_opts = "rw";
203                         }
204                         
205                         m.mnt_freq = 0;
206                         m.mnt_passno = 0;
207                         addmntent(mountTable, &m);
208                         endmntent(mountTable);
209                 }
210                 return 0;
211         } else {
212                 fprintf(stderr, "Mount %s", blockDevice);
213                 if ( filesystemType && *filesystemType )
214                         fprintf(stderr, " (type %s)", filesystemType);
215
216                 fprintf(
217                  stderr
218                 ," on %s: "
219                 ,directory);
220
221                 switch ( error ) {
222                 case EPERM:
223                         if (geteuid() == 0)
224                                 fprintf(
225                                  stderr
226                                 ,"mount point %s is not a directory"
227                                 ,blockDevice);
228                         else
229                                 fprintf(
230                                  stderr
231                                 ,"must be superuser to use mount");
232                         break;
233                 case EBUSY:
234                         fprintf(
235                          stderr
236                         ,"%s already mounted or %s busy"
237                         ,blockDevice
238                         ,directory);
239                         break;
240                 case ENOENT:
241                 {
242                         struct stat statbuf;
243                         if ( stat(directory, &statbuf) != 0 )
244                                 fprintf(
245                                  stderr
246                                 ,"directory %s does not exist"
247                                 ,directory);
248                         else if ( stat(blockDevice, &statbuf) != 0 )
249                                 fprintf(
250                                  stderr
251                                 ,"block device %s does not exist"
252                                 ,blockDevice);
253                         else
254                                 fprintf(
255                                  stderr
256                                 ,"%s is not mounted on %s, but the mount table says it is."
257                                 ,blockDevice
258                                 ,directory);
259                         break;
260                 }
261                 case ENOTDIR:
262                         fprintf(
263                          stderr
264                         ,"%s is not a directory"
265                         ,directory);
266                         break;
267                 case EINVAL:
268                         fprintf(
269                          stderr
270                         ,"wrong filesystem type, or bad superblock on %s"
271                         ,blockDevice);
272                         break;
273                 case EMFILE:
274                         fprintf(stderr, "mount table full");
275                         break;
276                 case EIO:
277                         fprintf(
278                          stderr
279                         ,"I/O error reading %s"
280                         ,blockDevice);
281                         break;
282                 case ENODEV:
283                 {
284                         FILE *  f = fopen("/proc/filesystems", "r");
285
286                         fprintf(
287                          stderr
288                         ,"filesystem type %s not in kernel.\n"
289                         ,filesystemType);
290                         fprintf(stderr, "Do you need to load a module?\n");
291                         if ( f ) {
292                                 char    buf[100];
293
294                                 fprintf(
295                                  stderr
296                                 ,"Here are the filesystem types the kernel"
297                                  " can mount:\n");
298                                 while ( fgets(buf, sizeof(buf), f) != 0 )
299                                         fprintf(stderr, "\t%s", buf);
300                                 fclose(f);
301                         }
302                         break;
303                 }
304                 case ENOTBLK:
305                         fprintf(
306                          stderr
307                         ,"%s is not a block device"
308                         ,blockDevice);
309                         break;
310                 case ENXIO:
311                         fprintf(
312                          stderr
313                         ,"%s is not a valid block device"
314                         ,blockDevice);
315                         break;
316                 default:
317                         fputs(strerror(errno), stderr);
318                 }
319                 putc('\n', stderr);
320                 return -1;
321         }
322 }
323
324 extern int
325 mount_main(struct FileInfo * i, int argc, char * * argv)
326 {
327         char    string_flags[1024];
328         unsigned long   flags = 0;
329         char *  filesystemType = "auto";
330         int             fake = 0;
331         int             noMtab = 0;
332         int             all = 0;
333         
334         *string_flags = '\0';
335
336         if ( argc == 1 ) {
337                 FILE *  mountTable;
338                 if ( (mountTable = setmntent("/etc/mtab", "r")) ) {
339                         struct mntent * m;
340                         while ( (m = getmntent(mountTable)) != 0 ) {
341                                 printf(
342                                  "%s on %s type %s (%s)\n"
343                                 ,m->mnt_fsname
344                                 ,m->mnt_dir
345                                 ,m->mnt_type
346                                 ,m->mnt_opts);
347                         }
348                         endmntent(mountTable);
349                 }
350                 return 0;
351         }
352
353         while ( argc >= 2 && argv[1][0] == '-' ) {
354                 switch ( argv[1][1] ) {
355                 case 'f':
356                         fake = 1;
357                         break;
358                 case 'n':
359                         noMtab = 1;
360                         break;
361                 case 'o':
362                         if ( argc < 3 ) {
363                                 usage(mount_usage);
364                                 return 1;
365                         }
366                         parse_mount_options(argv[2], &flags, string_flags);
367                         argc--;
368                         argv++;
369                         break;
370                 case 'r':
371                         flags |= MS_RDONLY;
372                         break;
373                 case 't':
374                         if ( argc < 3 ) {
375                                 usage(mount_usage);
376                                 return 1;
377                         }
378                         filesystemType = argv[2];
379                         argc--;
380                         argv++;
381                         break;
382                 case 'v':
383                         break;
384                 case 'w':
385                         flags &= ~MS_RDONLY;
386                         break;
387                 case 'a':
388                         all = 1;
389                         break;
390                 default:
391                         usage(mount_usage);
392                         return 1;
393                 }
394                 argc--;
395                 argv++;
396         }
397
398         if (all == 1) {
399                 struct  mntent *m;
400                 FILE    *f = setmntent("/etc/fstab", "r");
401
402                 if (f == NULL)  {
403                         return 1;
404                 }
405
406                 // FIXME: Combine read routine (make new function) with unmount_all to save space.
407
408                 while ((m = getmntent(f)) != NULL) {    
409                         // If the file system isn't noauto, and isn't mounted on /, mount it
410                         if ((!strstr(m->mnt_opts, "noauto")) && (m->mnt_dir[1] != '\0') 
411                                 && !((m->mnt_type[0] == 's') && (m->mnt_type[1] == 'w')) 
412                                 && !((m->mnt_type[0] == 'n') && (m->mnt_type[1] == 'f'))) {
413                                 mount_one(m->mnt_fsname, m->mnt_dir, m->mnt_type, flags, m->mnt_opts, noMtab, fake);    
414                         }
415                 }
416
417                 endmntent(f);
418         } else {
419                 if ( argc >= 3 ) {
420                         if ( mount_one( argv[1], argv[2], filesystemType, flags, string_flags, noMtab, fake) == 0 )
421                                 return 0;
422                         else
423                                 return 1;
424                 } else {
425                         usage(mount_usage);
426                         return 1;
427                 }
428         }
429         return 0;
430 }