]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
1 | /* |
2 | * A mount(8) for Linux 0.99. | |
3 | * mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp | |
4 | * | |
726f69e2 | 5 | * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changes from Adam |
6dbe3af9 KZ |
6 | * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used |
7 | * if no -t option is given. I modified his patches so that, if | |
8 | * /proc/filesystems is not available, the behavior of mount is the same as | |
9 | * it was previously. | |
10 | * | |
11 | * Wed Sep 14 22:43:00 1994: Mitchum DSouza | |
12 | * (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting | |
13 | * the "loop" device. | |
14 | * | |
15 | * Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl) | |
16 | * added support for remounting readonly file systems readonly. | |
17 | * | |
18 | * Wed Feb 8 09:23:18 1995: Mike Grupenhoff <kashmir@umiacs.UMD.EDU> added | |
19 | * a probe of the superblock for the type before /proc/filesystems is | |
20 | * checked. | |
21 | * | |
22 | * Wed Feb 8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages. | |
726f69e2 KZ |
23 | * Sat Jun 3 20:44:38 1995: Patches from Andries.Brouwer@cwi.nl applied. |
24 | * Tue Sep 26 22:38:20 1995: aeb@cwi.nl, many changes | |
fd6b7a7f | 25 | * Fri Feb 23 13:47:00 1996: aeb@cwi.nl, loop device related changes |
6dbe3af9 | 26 | * |
fd6b7a7f KZ |
27 | * Fri Apr 5 01:13:33 1996: quinlan@bucknell.edu, fixed up iso9660 autodetect |
28 | * | |
29 | * Since then, many changes - aeb. | |
2b6fc908 KZ |
30 | * |
31 | * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com> | |
32 | * Implemented the "bg", "fg" and "retry" mount options for NFS. | |
6dbe3af9 KZ |
33 | */ |
34 | ||
fd6b7a7f KZ |
35 | #include <unistd.h> |
36 | #include <ctype.h> | |
37 | #include <errno.h> | |
38 | #include <string.h> | |
39 | #include <getopt.h> | |
2b6fc908 | 40 | #include <stdio.h> |
6dbe3af9 | 41 | |
fd6b7a7f KZ |
42 | #include <sys/types.h> |
43 | #include <sys/ioctl.h> | |
44 | #include <sys/stat.h> | |
45 | #include <sys/wait.h> | |
46 | #include <sys/mount.h> | |
47 | ||
2b6fc908 | 48 | #include "mount_constants.h" |
fd6b7a7f KZ |
49 | #include "sundries.h" |
50 | #include "fstab.h" | |
51 | #include "lomount.h" | |
52 | #include "loop.h" | |
2b6fc908 | 53 | #include "linux_fs.h" |
6dbe3af9 | 54 | |
726f69e2 KZ |
55 | #define PROC_FILESYSTEMS "/proc/filesystems" |
56 | #define SIZE(a) (sizeof(a)/sizeof(a[0])) | |
57 | ||
2b6fc908 KZ |
58 | #define DO_PS_FIDDLING |
59 | ||
60 | #ifdef DO_PS_FIDDLING | |
61 | #define PROC_NAME "mount: " | |
62 | static int argc0; | |
63 | static char** argv0; | |
64 | static char** envp0; | |
65 | extern char** environ; | |
66 | #endif | |
67 | ||
6dbe3af9 KZ |
68 | /* True for fake mount (-f). */ |
69 | int fake = 0; | |
70 | ||
71 | /* Don't write a entry in /etc/mtab (-n). */ | |
72 | int nomtab = 0; | |
73 | ||
fd6b7a7f | 74 | /* True for explicit readonly (-r). */ |
6dbe3af9 KZ |
75 | int readonly = 0; |
76 | ||
77 | /* Nonzero for chatty (-v). */ | |
78 | int verbose = 0; | |
79 | ||
2b6fc908 KZ |
80 | /* Nonzero for sloppy (-s). */ |
81 | int sloppy = 0; | |
82 | ||
fd6b7a7f | 83 | /* True for explicit read/write (-w). */ |
6dbe3af9 KZ |
84 | int readwrite = 0; |
85 | ||
86 | /* True for all mount (-a). */ | |
87 | int all = 0; | |
88 | ||
fd6b7a7f KZ |
89 | /* True for fork() during all mount (-F). */ |
90 | int optfork = 0; | |
91 | ||
6dbe3af9 KZ |
92 | /* True if ruid != euid. */ |
93 | int suid = 0; | |
94 | ||
95 | /* Map from -o and fstab option strings to the flag argument to mount(2). */ | |
96 | struct opt_map | |
97 | { | |
98 | const char *opt; /* option name */ | |
fd6b7a7f | 99 | int skip; /* skip in mtab option string */ |
6dbe3af9 KZ |
100 | int inv; /* true if flag value should be inverted */ |
101 | int mask; /* flag mask value */ | |
102 | }; | |
103 | ||
104 | /* Custom mount options for our own purposes. */ | |
fd6b7a7f KZ |
105 | /* We can use the high-order 16 bits, since the mount call |
106 | has MS_MGC_VAL there. */ | |
6dbe3af9 KZ |
107 | #define MS_NOAUTO 0x80000000 |
108 | #define MS_USER 0x40000000 | |
fd6b7a7f | 109 | #define MS_LOOP 0x00010000 |
6dbe3af9 KZ |
110 | |
111 | /* Options that we keep the mount system call from seeing. */ | |
fd6b7a7f | 112 | #define MS_NOSYS (MS_NOAUTO|MS_USER|MS_LOOP) |
6dbe3af9 KZ |
113 | |
114 | /* Options that we keep from appearing in the options field in the mtab. */ | |
115 | #define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USER) | |
116 | ||
117 | /* OPTIONS that we make ordinary users have by default. */ | |
118 | #define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) | |
119 | ||
fd6b7a7f KZ |
120 | const struct opt_map opt_map[] = { |
121 | { "defaults", 0, 0, 0 }, /* default options */ | |
122 | { "ro", 1, 0, MS_RDONLY }, /* read-only */ | |
123 | { "rw", 1, 1, MS_RDONLY }, /* read-write */ | |
124 | { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ | |
125 | { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ | |
126 | { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ | |
127 | { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ | |
128 | { "dev", 0, 1, MS_NODEV }, /* interpret device files */ | |
129 | { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ | |
130 | { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ | |
131 | { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ | |
132 | { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ | |
133 | { "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */ | |
134 | { "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */ | |
135 | { "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */ | |
136 | { "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */ | |
6dbe3af9 KZ |
137 | /* add new options here */ |
138 | #ifdef MS_NOSUB | |
fd6b7a7f KZ |
139 | { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ |
140 | { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ | |
141 | #endif | |
142 | #ifdef MS_SILENT | |
143 | { "quiet", 0, 0, MS_SILENT }, /* be quiet */ | |
144 | { "loud", 0, 1, MS_SILENT }, /* print out messages. */ | |
726f69e2 | 145 | #endif |
fd6b7a7f KZ |
146 | #ifdef MS_MANDLOCK |
147 | { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ | |
148 | { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ | |
6dbe3af9 | 149 | #endif |
fd6b7a7f KZ |
150 | { "loop", 1, 0, MS_LOOP }, /* use a loop device */ |
151 | #ifdef MS_NOATIME | |
152 | { "atime", 0, 1, MS_NOATIME }, /* Update access time */ | |
153 | { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ | |
2b6fc908 KZ |
154 | #endif |
155 | #ifdef MS_NODIRATIME | |
156 | { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ | |
157 | { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ | |
fd6b7a7f KZ |
158 | #endif |
159 | { NULL, 0, 0, 0 } | |
160 | }; | |
161 | ||
162 | char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption; | |
163 | ||
164 | struct string_opt_map { | |
165 | char *tag; | |
166 | int skip; | |
167 | char **valptr; | |
168 | } string_opt_map[] = { | |
169 | { "loop=", 0, &opt_loopdev }, | |
170 | { "vfs=", 1, &opt_vfstype }, | |
171 | { "offset=", 0, &opt_offset }, | |
172 | { "encryption=", 0, &opt_encryption }, | |
173 | { NULL, 0, NULL } | |
6dbe3af9 KZ |
174 | }; |
175 | ||
fd6b7a7f KZ |
176 | static void |
177 | clear_string_opts(void) { | |
178 | struct string_opt_map *m; | |
179 | ||
180 | for (m = &string_opt_map[0]; m->tag; m++) | |
181 | *(m->valptr) = NULL; | |
182 | } | |
183 | ||
184 | static int | |
185 | parse_string_opt(char *s) { | |
186 | struct string_opt_map *m; | |
187 | int lth; | |
188 | ||
189 | for (m = &string_opt_map[0]; m->tag; m++) { | |
190 | lth = strlen(m->tag); | |
191 | if (!strncmp(s, m->tag, lth)) { | |
192 | *(m->valptr) = xstrdup(s + lth); | |
193 | return 1; | |
194 | } | |
195 | } | |
196 | return 0; | |
197 | } | |
198 | ||
726f69e2 | 199 | int mount_quiet=0; |
6dbe3af9 KZ |
200 | |
201 | /* Report on a single mount. */ | |
202 | static void | |
fd6b7a7f KZ |
203 | print_one (const struct mntentchn *mc) { |
204 | if (mount_quiet) | |
205 | return; | |
206 | printf ("%s on %s", mc->mnt_fsname, mc->mnt_dir); | |
207 | if (mc->mnt_type != NULL && *(mc->mnt_type) != '\0') | |
208 | printf (" type %s", mc->mnt_type); | |
209 | if (mc->mnt_opts != NULL) | |
210 | printf (" (%s)", mc->mnt_opts); | |
211 | printf ("\n"); | |
6dbe3af9 KZ |
212 | } |
213 | ||
214 | /* Report on everything in mtab (of the specified types if any). */ | |
215 | static int | |
216 | print_all (string_list types) | |
217 | { | |
fd6b7a7f | 218 | struct mntentchn *mc; |
6dbe3af9 | 219 | |
fd6b7a7f KZ |
220 | for (mc = mtab_head()->nxt; mc; mc = mc->nxt) { |
221 | if (matching_type (mc->mnt_type, types)) | |
222 | print_one (mc); | |
223 | } | |
224 | exit (0); | |
6dbe3af9 KZ |
225 | } |
226 | ||
227 | ||
228 | /* Look for OPT in opt_map table and return mask value. If OPT isn't found, | |
fd6b7a7f | 229 | tack it onto extra_opts (which is non-NULL). */ |
6dbe3af9 KZ |
230 | static inline void |
231 | parse_opt (const char *opt, int *mask, char *extra_opts) | |
232 | { | |
233 | const struct opt_map *om; | |
234 | ||
235 | for (om = opt_map; om->opt != NULL; om++) | |
236 | if (streq (opt, om->opt)) | |
237 | { | |
238 | if (om->inv) | |
239 | *mask &= ~om->mask; | |
240 | else | |
241 | *mask |= om->mask; | |
242 | if (om->mask == MS_USER) | |
243 | *mask |= MS_SECURE; | |
726f69e2 KZ |
244 | #ifdef MS_SILENT |
245 | if (om->mask == MS_SILENT && om->inv) { | |
246 | mount_quiet = 1; | |
247 | verbose = 0; | |
248 | } | |
249 | #endif | |
6dbe3af9 KZ |
250 | return; |
251 | } | |
252 | if (*extra_opts) | |
253 | strcat(extra_opts, ","); | |
254 | strcat(extra_opts, opt); | |
255 | } | |
256 | ||
257 | /* Take -o options list and compute 4th and 5th args to mount(2). flags | |
258 | gets the standard options and extra_opts anything we don't recognize. */ | |
259 | static void | |
260 | parse_opts (char *opts, int *flags, char **extra_opts) | |
261 | { | |
262 | char *opt; | |
263 | ||
264 | *flags = 0; | |
265 | *extra_opts = NULL; | |
266 | ||
fd6b7a7f KZ |
267 | clear_string_opts(); |
268 | ||
6dbe3af9 KZ |
269 | if (opts != NULL) |
270 | { | |
271 | *extra_opts = xmalloc (strlen (opts) + 1); | |
272 | **extra_opts = '\0'; | |
273 | ||
fd6b7a7f KZ |
274 | for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ",")) |
275 | if (!parse_string_opt (opt)) | |
276 | parse_opt (opt, flags, *extra_opts); | |
6dbe3af9 KZ |
277 | } |
278 | ||
279 | if (readonly) | |
280 | *flags |= MS_RDONLY; | |
281 | if (readwrite) | |
282 | *flags &= ~MS_RDONLY; | |
283 | } | |
284 | ||
285 | /* Try to build a canonical options string. */ | |
286 | static char * | |
287 | fix_opts_string (int flags, char *extra_opts) | |
288 | { | |
289 | const struct opt_map *om; | |
fd6b7a7f | 290 | const struct string_opt_map *m; |
6dbe3af9 | 291 | char *new_opts; |
6dbe3af9 KZ |
292 | |
293 | new_opts = (flags & MS_RDONLY) ? "ro" : "rw"; | |
fd6b7a7f KZ |
294 | for (om = opt_map; om->opt != NULL; om++) { |
295 | if (om->skip) | |
6dbe3af9 KZ |
296 | continue; |
297 | if (om->inv || !om->mask || (flags & om->mask) != om->mask) | |
298 | continue; | |
fd6b7a7f | 299 | new_opts = xstrconcat3(new_opts, ",", om->opt); |
6dbe3af9 | 300 | flags &= ~om->mask; |
fd6b7a7f KZ |
301 | } |
302 | for (m = &string_opt_map[0]; m->tag; m++) { | |
303 | if (!m->skip && *(m->valptr)) | |
304 | new_opts = xstrconcat4(new_opts, ",", m->tag, *(m->valptr)); | |
305 | } | |
306 | if (extra_opts && *extra_opts) { | |
307 | new_opts = xstrconcat3(new_opts, ",", extra_opts); | |
308 | } | |
6dbe3af9 KZ |
309 | return new_opts; |
310 | } | |
311 | ||
fd6b7a7f KZ |
312 | /* Most file system types can be recognized by a `magic' number |
313 | in the superblock. Note that the order of the tests is | |
314 | significant: by coincidence a filesystem can have the | |
315 | magic numbers for several file system types simultaneously. | |
316 | For example, the romfs magic lives in the 1st sector; | |
317 | xiafs does not touch the 1st sector and has its magic in | |
318 | the 2nd sector; ext2 does not touch the first two sectors. */ | |
319 | ||
fd6b7a7f KZ |
320 | static inline unsigned short |
321 | swapped(unsigned short a) { | |
322 | return (a>>8) | (a<<8); | |
323 | } | |
6dbe3af9 KZ |
324 | |
325 | /* | |
326 | char *fstype(const char *device); | |
327 | ||
726f69e2 | 328 | Probes the device and attempts to determine the type of filesystem |
6dbe3af9 KZ |
329 | contained within. |
330 | ||
331 | Original routine by <jmorriso@bogomips.ww.ubc.ca>; made into a function | |
332 | for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>. | |
726f69e2 KZ |
333 | Read the superblock only once - aeb |
334 | Added a test for iso9660 - aeb | |
fd6b7a7f KZ |
335 | Added a test for high sierra (iso9660) - quinlan@bucknell.edu |
336 | Corrected the test for xiafs - aeb | |
2b6fc908 | 337 | Added romfs - aeb |
6dbe3af9 | 338 | |
fd6b7a7f | 339 | Currently supports: minix, ext, ext2, xiafs, iso9660, romfs |
6dbe3af9 | 340 | */ |
fd6b7a7f | 341 | char *magic_known[] = { "minix", "ext", "ext2", "xiafs", "iso9660", "romfs" }; |
726f69e2 KZ |
342 | |
343 | static int | |
344 | tested(const char *device) { | |
345 | char **m; | |
346 | ||
347 | for (m = magic_known; m - magic_known < SIZE(magic_known); m++) | |
348 | if (!strcmp(*m, device)) | |
349 | return 1; | |
350 | return 0; | |
351 | } | |
6dbe3af9 KZ |
352 | |
353 | static char * | |
354 | fstype(const char *device) | |
355 | { | |
356 | int fd; | |
fd6b7a7f | 357 | char *type = NULL; |
726f69e2 KZ |
358 | union { |
359 | struct minix_super_block ms; | |
360 | struct ext_super_block es; | |
361 | struct ext2_super_block e2s; | |
726f69e2 | 362 | } sb; |
fd6b7a7f KZ |
363 | union { |
364 | struct xiafs_super_block xiasb; | |
365 | char romfs_magic[8]; | |
366 | } xsb; | |
367 | union { | |
368 | struct iso_volume_descriptor iso; | |
369 | struct hs_volume_descriptor hs; | |
370 | } isosb; | |
726f69e2 KZ |
371 | struct stat statbuf; |
372 | ||
373 | /* opening and reading an arbitrary unknown path can have | |
374 | undesired side effects - first check that `device' refers | |
375 | to a block device */ | |
fd6b7a7f KZ |
376 | if (stat (device, &statbuf) || !S_ISBLK(statbuf.st_mode)) |
377 | return 0; | |
6dbe3af9 KZ |
378 | |
379 | fd = open(device, O_RDONLY); | |
fd6b7a7f KZ |
380 | if (fd < 0) |
381 | return 0; | |
726f69e2 | 382 | |
2b6fc908 | 383 | if (lseek(fd, 1024, SEEK_SET) != 1024 |
fd6b7a7f KZ |
384 | || read(fd, (char *) &sb, sizeof(sb)) != sizeof(sb)) |
385 | goto io_error; | |
6dbe3af9 | 386 | |
2b6fc908 KZ |
387 | if (ext2magic(sb.e2s) == EXT2_SUPER_MAGIC |
388 | || ext2magic(sb.e2s) == EXT2_PRE_02B_MAGIC | |
389 | || ext2magic(sb.e2s) == swapped(EXT2_SUPER_MAGIC)) | |
fd6b7a7f KZ |
390 | type = "ext2"; |
391 | ||
2b6fc908 KZ |
392 | else if (minixmagic(sb.ms) == MINIX_SUPER_MAGIC |
393 | || minixmagic(sb.ms) == MINIX_SUPER_MAGIC2) | |
fd6b7a7f KZ |
394 | type = "minix"; |
395 | ||
2b6fc908 | 396 | else if (extmagic(sb.es) == EXT_SUPER_MAGIC) |
fd6b7a7f KZ |
397 | type = "ext"; |
398 | ||
399 | if (!type) { | |
400 | if (lseek(fd, 0, SEEK_SET) != 0 | |
401 | || read(fd, (char *) &xsb, sizeof(xsb)) != sizeof(xsb)) | |
402 | goto io_error; | |
403 | ||
2b6fc908 | 404 | if (xiafsmagic(xsb.xiasb) == _XIAFS_SUPER_MAGIC) |
fd6b7a7f KZ |
405 | type = "xiafs"; |
406 | else if(!strncmp(xsb.romfs_magic, "-rom1fs-", 8)) | |
407 | type = "romfs"; | |
6dbe3af9 KZ |
408 | } |
409 | ||
fd6b7a7f KZ |
410 | if (!type) { |
411 | if (lseek(fd, 0x8000, SEEK_SET) != 0x8000 | |
412 | || read(fd, (char *) &isosb, sizeof(isosb)) != sizeof(isosb)) | |
413 | goto io_error; | |
6dbe3af9 | 414 | |
fd6b7a7f KZ |
415 | if(strncmp(isosb.iso.id, ISO_STANDARD_ID, sizeof(isosb.iso.id)) == 0 |
416 | || strncmp(isosb.hs.id, HS_STANDARD_ID, sizeof(isosb.hs.id)) == 0) | |
417 | type = "iso9660"; | |
726f69e2 | 418 | } |
6dbe3af9 | 419 | |
726f69e2 | 420 | close (fd); |
fd6b7a7f KZ |
421 | return(type); |
422 | ||
423 | io_error: | |
424 | perror(device); | |
425 | close(fd); | |
426 | return 0; | |
726f69e2 | 427 | } |
6dbe3af9 | 428 | |
726f69e2 KZ |
429 | FILE *procfs; |
430 | ||
431 | static void | |
432 | procclose(void) { | |
433 | if (procfs) | |
434 | fclose (procfs); | |
435 | procfs = 0; | |
436 | } | |
437 | ||
438 | static int | |
439 | procopen(void) { | |
440 | return ((procfs = fopen(PROC_FILESYSTEMS, "r")) != NULL); | |
441 | } | |
442 | ||
443 | static char * | |
444 | procnext(void) { | |
445 | char line[100]; | |
446 | static char fsname[50]; | |
447 | ||
448 | while (fgets(line, sizeof(line), procfs)) { | |
449 | if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue; | |
450 | if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue; | |
451 | return fsname; | |
452 | } | |
453 | return 0; | |
6dbe3af9 KZ |
454 | } |
455 | ||
726f69e2 KZ |
456 | static int |
457 | is_in_proc(char *type) { | |
458 | char *fsname; | |
459 | ||
460 | if (procopen()) { | |
461 | while ((fsname = procnext()) != NULL) | |
462 | if (!strcmp(fsname, type)) | |
463 | return 1; | |
464 | } | |
465 | return 0; | |
466 | } | |
467 | ||
468 | static int | |
469 | already (char *spec, char *node) { | |
fd6b7a7f | 470 | struct mntentchn *mc; |
726f69e2 KZ |
471 | int ret = 1; |
472 | ||
fd6b7a7f | 473 | if ((mc = getmntfile(node)) != NULL) |
726f69e2 | 474 | error ("mount: according to mtab, %s is already mounted on %s", |
fd6b7a7f KZ |
475 | mc->mnt_fsname, node); |
476 | else if ((mc = getmntfile(spec)) != NULL) | |
726f69e2 | 477 | error ("mount: according to mtab, %s is mounted on %s", |
fd6b7a7f | 478 | spec, mc->mnt_dir); |
726f69e2 KZ |
479 | else |
480 | ret = 0; | |
481 | return ret; | |
482 | } | |
6dbe3af9 | 483 | |
fd6b7a7f KZ |
484 | /* Create mtab with a root entry. */ |
485 | static void | |
486 | create_mtab (void) { | |
487 | struct mntentchn *fstab; | |
488 | struct mntent mnt; | |
489 | int flags; | |
490 | char *extra_opts; | |
491 | FILE *fp; | |
492 | ||
493 | lock_mtab(); | |
494 | ||
495 | if ((fp = setmntent (MOUNTED, "a+")) == NULL) | |
496 | die (EX_FILEIO, "mount: can't open %s for writing: %s", | |
497 | MOUNTED, strerror (errno)); | |
498 | ||
499 | /* Find the root entry by looking it up in fstab */ | |
500 | if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) { | |
501 | parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts); | |
502 | mnt.mnt_dir = "/"; | |
503 | mnt.mnt_fsname = canonicalize (fstab->mnt_fsname); | |
504 | mnt.mnt_type = fstab->mnt_type; | |
505 | mnt.mnt_opts = fix_opts_string (flags, extra_opts); | |
506 | mnt.mnt_freq = mnt.mnt_passno = 0; | |
507 | ||
508 | if (addmntent (fp, &mnt) == 1) | |
509 | die (EX_FILEIO, "mount: error writing %s: %s", | |
510 | MOUNTED, strerror (errno)); | |
511 | } | |
512 | if (fchmod (fileno (fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) | |
513 | if (errno != EROFS) | |
514 | die (EX_FILEIO, "mount: error changing mode of %s: %s", | |
515 | MOUNTED, strerror (errno)); | |
516 | endmntent (fp); | |
517 | ||
518 | unlock_mtab(); | |
519 | } | |
520 | ||
521 | /* count successful mount system calls */ | |
522 | static int mountcount = 0; | |
523 | ||
524 | static int | |
525 | mount5 (char *special, char *dir, char *type, int flags, void *data) { | |
526 | int ret = mount (special, dir, type, 0xC0ED0000 | (flags), data); | |
527 | if (ret == 0) | |
528 | mountcount++; | |
529 | return ret; | |
530 | } | |
531 | ||
6dbe3af9 KZ |
532 | /* Mount a single file system. Return status, |
533 | so don't exit on non-fatal errors. */ | |
534 | ||
535 | static int | |
536 | try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) { | |
726f69e2 | 537 | char *fsname; |
6dbe3af9 | 538 | |
fd6b7a7f | 539 | if (*type && strcasecmp (*type, "auto") == 0) |
726f69e2 KZ |
540 | *type = NULL; |
541 | ||
fd6b7a7f | 542 | if (!*type && !(flags & MS_REMOUNT)) { |
726f69e2 | 543 | *type = fstype(spec); |
726f69e2 KZ |
544 | if (verbose) { |
545 | printf ("mount: you didn't specify a filesystem type for %s\n", | |
546 | spec); | |
547 | if (*type) | |
548 | printf (" I will try type %s\n", *type); | |
549 | else | |
550 | printf (" I will try all types mentioned in %s\n", | |
551 | PROC_FILESYSTEMS); | |
552 | } | |
6dbe3af9 | 553 | } |
726f69e2 | 554 | |
fd6b7a7f | 555 | if (*type || (flags & MS_REMOUNT)) |
726f69e2 KZ |
556 | return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts); |
557 | ||
fd6b7a7f KZ |
558 | if (!procopen()) |
559 | return -1; | |
726f69e2 KZ |
560 | while ((fsname = procnext()) != NULL) { |
561 | if (tested (fsname)) | |
562 | continue; | |
6dbe3af9 | 563 | if (mount5 (spec, node, fsname, flags & ~MS_NOSYS, mount_opts) == 0) { |
726f69e2 KZ |
564 | *type = xstrdup(fsname); |
565 | procclose(); | |
566 | return 0; | |
567 | } else if (errno != EINVAL) { | |
568 | *type = "guess"; | |
569 | procclose(); | |
570 | return 1; | |
6dbe3af9 KZ |
571 | } |
572 | } | |
726f69e2 KZ |
573 | procclose(); |
574 | *type = NULL; | |
575 | ||
6dbe3af9 KZ |
576 | return -1; |
577 | } | |
578 | ||
2b6fc908 KZ |
579 | /* |
580 | * try_mount_one() | |
581 | * Try to mount one file system. When "bg" is 1, this is a retry | |
582 | * in the background. One additional exit code EX_BG is used here. | |
583 | * It is used to instruct the caller to retry the mount in the | |
584 | * background. | |
585 | */ | |
6dbe3af9 | 586 | static int |
2b6fc908 KZ |
587 | try_mount_one (char *spec0, char *node0, char *type0, char *opts0, |
588 | int freq, int pass, int bg) | |
6dbe3af9 | 589 | { |
fd6b7a7f | 590 | struct mntentchn mcn; |
6dbe3af9 KZ |
591 | struct mntent mnt; |
592 | int mnt_err; | |
593 | int flags; | |
fd6b7a7f KZ |
594 | char *extra_opts; /* written in mtab */ |
595 | char *mount_opts; /* actually used on system call */ | |
726f69e2 | 596 | static int added_ro = 0; |
fd6b7a7f KZ |
597 | int loop, looptype, offset; |
598 | char *spec, *node, *type, *opts, *loopdev, *loopfile; | |
2b6fc908 | 599 | struct stat statbuf; |
6dbe3af9 | 600 | |
fd6b7a7f KZ |
601 | spec = xstrdup(spec0); |
602 | node = xstrdup(node0); | |
603 | type = xstrdup(type0); | |
604 | opts = xstrdup(opts0); | |
605 | ||
6dbe3af9 KZ |
606 | parse_opts (xstrdup (opts), &flags, &extra_opts); |
607 | ||
608 | /* root may allow certain types of mounts by ordinary users */ | |
726f69e2 KZ |
609 | if (suid && !(flags & MS_USER)) { |
610 | if (already (spec, node)) | |
fd6b7a7f | 611 | die (EX_USAGE, "mount failed"); |
726f69e2 | 612 | else |
fd6b7a7f | 613 | die (EX_USAGE, "mount: only root can mount %s on %s", spec, node); |
726f69e2 | 614 | } |
6dbe3af9 KZ |
615 | |
616 | /* quietly succeed for fstab entries that don't get mounted automatically */ | |
617 | if (all && (flags & MS_NOAUTO)) | |
618 | return 0; | |
619 | ||
620 | mount_opts = extra_opts; | |
621 | ||
fd6b7a7f | 622 | /* |
2b6fc908 KZ |
623 | * In the case of a loop mount, either type is of the form lo@/dev/loop5 |
624 | * or the option "-o loop=/dev/loop5" or just "-o loop" is given, or | |
625 | * mount just has to figure things out for itself from the fact that | |
626 | * spec is not a block device. We do not test for a block device | |
627 | * immediately: maybe later other types of mountable objects will occur. | |
fd6b7a7f | 628 | */ |
2b6fc908 | 629 | |
fd6b7a7f KZ |
630 | loopdev = opt_loopdev; |
631 | ||
632 | looptype = (type && strncmp("lo@", type, 3) == 0); | |
633 | if (looptype) { | |
634 | if (loopdev) | |
635 | error("mount: loop device specified twice"); | |
636 | loopdev = type+3; | |
637 | type = opt_vfstype; | |
638 | } | |
639 | else if (opt_vfstype) { | |
640 | if (type) | |
641 | error("mount: type specified twice"); | |
642 | else | |
643 | type = opt_vfstype; | |
644 | } | |
6dbe3af9 | 645 | |
fd6b7a7f KZ |
646 | loop = ((flags & MS_LOOP) || loopdev || opt_offset || opt_encryption); |
647 | loopfile = spec; | |
648 | ||
649 | if (loop) { | |
650 | flags |= MS_LOOP; | |
651 | if (fake) { | |
652 | if (verbose) | |
653 | printf("mount: skipping the setup of a loop device\n"); | |
654 | } else { | |
655 | int loopro = (flags & MS_RDONLY); | |
656 | ||
657 | if (!loopdev || !*loopdev) | |
658 | loopdev = find_unused_loop_device(); | |
659 | if (!loopdev) | |
660 | return EX_SYSERR; /* no more loop devices */ | |
661 | if (verbose) | |
662 | printf("mount: going to use the loop device %s\n", loopdev); | |
663 | offset = opt_offset ? strtoul(opt_offset, NULL, 0) : 0; | |
664 | if (set_loop (loopdev, loopfile, offset, opt_encryption, &loopro)) | |
665 | return EX_FAIL; | |
666 | spec = loopdev; | |
667 | if (loopro) | |
668 | flags |= MS_RDONLY; | |
669 | } | |
6dbe3af9 KZ |
670 | } |
671 | ||
2b6fc908 | 672 | if (!fake && type && streq (type, "nfs")) { |
6dbe3af9 | 673 | #ifdef HAVE_NFS |
2b6fc908 KZ |
674 | mnt_err = nfsmount (spec, node, &flags, &extra_opts, &mount_opts, bg); |
675 | if (mnt_err) | |
676 | return mnt_err; | |
6dbe3af9 | 677 | #else |
fd6b7a7f KZ |
678 | die (EX_SOFTWARE, "mount: this version was compiled " |
679 | "without support for the type `nfs'"); | |
6dbe3af9 | 680 | #endif |
2b6fc908 | 681 | } |
6dbe3af9 | 682 | |
fd6b7a7f KZ |
683 | /* |
684 | * Call mount.TYPE for types that require a separate | |
685 | * mount program. For the moment these types are ncp and smb. | |
686 | */ | |
687 | if (type) | |
688 | #ifndef ALWAYS_STAT | |
689 | if (streq (type, "smb") || streq (type, "ncp")) | |
690 | #else | |
691 | if (strlen (type) < 100) | |
692 | #endif | |
693 | { | |
fd6b7a7f KZ |
694 | char mountprog[120]; |
695 | ||
696 | sprintf(mountprog, "/sbin/mount.%s", type); | |
697 | if (stat(mountprog, &statbuf) == 0) { | |
698 | if (fork() == 0) { | |
699 | char *oo, *mountargs[10]; | |
700 | int i = 0; | |
701 | ||
702 | setuid(getuid()); | |
703 | setgid(getgid()); | |
704 | oo = fix_opts_string (flags, extra_opts); | |
705 | mountargs[i++] = mountprog; | |
706 | mountargs[i++] = spec; | |
707 | mountargs[i++] = node; | |
708 | if (nomtab) | |
709 | mountargs[i++] = "-n"; | |
710 | if (verbose) | |
711 | mountargs[i++] = "-v"; | |
712 | if (oo && *oo) { | |
713 | mountargs[i++] = "-o"; | |
714 | mountargs[i++] = oo; | |
715 | } | |
716 | mountargs[i] = NULL; | |
717 | execv(mountprog, mountargs); | |
718 | exit(1); /* exec failed */ | |
719 | } else if (fork() != -1) { | |
720 | int status; | |
721 | wait(&status); | |
722 | return status; | |
723 | } else | |
724 | error("cannot fork: %s", strerror(errno)); | |
725 | } | |
726 | } | |
727 | ||
6dbe3af9 KZ |
728 | block_signals (SIG_BLOCK); |
729 | ||
730 | if (fake | |
731 | || (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0) | |
fd6b7a7f | 732 | /* Mount succeeded, report this (if verbose) and write mtab entry. */ |
6dbe3af9 | 733 | { |
fd6b7a7f KZ |
734 | if (loop) |
735 | opt_loopdev = loopdev; | |
736 | ||
737 | mcn.mnt_fsname = mnt.mnt_fsname = canonicalize (loop ? loopfile : spec); | |
738 | mcn.mnt_dir = mnt.mnt_dir = canonicalize (node); | |
739 | mcn.mnt_type = mnt.mnt_type = type ? type : "unknown"; | |
740 | mcn.mnt_opts = mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB, extra_opts); | |
741 | mcn.nxt = 0; | |
742 | mnt.mnt_freq = freq; | |
743 | mnt.mnt_passno = pass; | |
6dbe3af9 | 744 | |
fd6b7a7f KZ |
745 | /* We get chatty now rather than after the update to mtab since the |
746 | mount succeeded, even if the write to /etc/mtab should fail. */ | |
747 | if (verbose) | |
748 | print_one (&mcn); | |
6dbe3af9 | 749 | |
fd6b7a7f | 750 | if (!nomtab && mtab_is_writable()) { |
6dbe3af9 | 751 | if (flags & MS_REMOUNT) |
fd6b7a7f KZ |
752 | update_mtab (mnt.mnt_dir, &mnt); |
753 | else { | |
754 | FILE *fp = setmntent(MOUNTED, "a+"); | |
755 | if (fp == NULL) | |
756 | error("mount: can't open %s: %s", MOUNTED, | |
757 | strerror (errno)); | |
758 | else { | |
759 | if ((addmntent (fp, &mnt)) == 1) | |
760 | error("mount: error writing %s: %s", MOUNTED, | |
761 | strerror (errno)); | |
762 | endmntent(fp); | |
763 | } | |
764 | } | |
765 | } | |
6dbe3af9 KZ |
766 | |
767 | block_signals (SIG_UNBLOCK); | |
768 | return 0; | |
769 | } | |
770 | ||
fd6b7a7f KZ |
771 | mnt_err = errno; |
772 | ||
6dbe3af9 KZ |
773 | if (loop) |
774 | del_loop(spec); | |
775 | ||
6dbe3af9 KZ |
776 | block_signals (SIG_UNBLOCK); |
777 | ||
778 | /* Mount failed, complain, but don't die. */ | |
726f69e2 KZ |
779 | |
780 | if (type == 0) | |
781 | error ("mount: you must specify the filesystem type"); | |
782 | else | |
6dbe3af9 KZ |
783 | switch (mnt_err) |
784 | { | |
785 | case EPERM: | |
fd6b7a7f | 786 | if (geteuid() == 0) { |
fd6b7a7f KZ |
787 | if (stat (node, &statbuf) || !S_ISDIR(statbuf.st_mode)) |
788 | error ("mount: mount point %s is not a directory", node); | |
789 | else | |
790 | error ("mount: permission denied"); | |
791 | } else | |
6dbe3af9 KZ |
792 | error ("mount: must be superuser to use mount"); |
793 | break; | |
794 | case EBUSY: | |
fd6b7a7f KZ |
795 | if (flags & MS_REMOUNT) { |
796 | error ("mount: %s is busy", node); | |
797 | } else { | |
798 | error ("mount: %s already mounted or %s busy", spec, node); | |
799 | already (spec, node); | |
800 | } | |
6dbe3af9 KZ |
801 | break; |
802 | case ENOENT: | |
2b6fc908 KZ |
803 | if (lstat (node, &statbuf)) |
804 | error ("mount: mount point %s does not exist", node); | |
805 | else if (stat (node, &statbuf)) | |
806 | error ("mount: mount point %s is a symbolic link to nowhere", | |
807 | node); | |
808 | else if (stat (spec, &statbuf)) | |
809 | error ("mount: special device %s does not exist", spec); | |
810 | else { | |
6dbe3af9 KZ |
811 | errno = mnt_err; |
812 | perror("mount"); | |
726f69e2 | 813 | } |
2b6fc908 | 814 | break; |
6dbe3af9 | 815 | case ENOTDIR: |
fd6b7a7f KZ |
816 | error ("mount: mount point %s is not a directory", node); |
817 | break; | |
6dbe3af9 | 818 | case EINVAL: |
fd6b7a7f | 819 | { int fd, size; |
fd6b7a7f KZ |
820 | |
821 | if (flags & MS_REMOUNT) { | |
2b6fc908 | 822 | error ("mount: %s not mounted already, or bad option", node); |
fd6b7a7f KZ |
823 | } else { |
824 | error ("mount: wrong fs type, bad option, bad superblock on %s,\n" | |
825 | " or too many mounted file systems", | |
826 | spec); | |
827 | ||
828 | if (stat (spec, &statbuf) == 0 && S_ISBLK(statbuf.st_mode) | |
829 | && (fd = open(spec, O_RDONLY)) >= 0) { | |
830 | if(ioctl(fd, BLKGETSIZE, &size) == 0 && size <= 2) | |
831 | error (" (aren't you trying to mount an extended partition,\n" | |
2b6fc908 | 832 | " instead of some logical partition inside?)"); |
fd6b7a7f KZ |
833 | close(fd); |
834 | } | |
835 | } | |
836 | break; | |
837 | } | |
6dbe3af9 KZ |
838 | case EMFILE: |
839 | error ("mount table full"); break; | |
840 | case EIO: | |
841 | error ("mount: %s: can't read superblock", spec); break; | |
842 | case ENODEV: | |
fd6b7a7f | 843 | if (is_in_proc(type) || !strcmp(type, "guess")) |
726f69e2 | 844 | error("mount: %s has wrong major or minor number", spec); |
fd6b7a7f KZ |
845 | else if (procfs) { |
846 | char *lowtype, *p; | |
847 | int u; | |
848 | ||
726f69e2 | 849 | error ("mount: fs type %s not supported by kernel", type); |
fd6b7a7f KZ |
850 | |
851 | /* maybe this loser asked for FAT or ISO9660 or isofs */ | |
852 | lowtype = xstrdup(type); | |
853 | u = 0; | |
854 | for(p=lowtype; *p; p++) { | |
855 | if(tolower(*p) != *p) { | |
856 | *p = tolower(*p); | |
857 | u++; | |
858 | } | |
859 | } | |
860 | if (u && is_in_proc(lowtype)) | |
861 | error ("mount: probably you meant %s", lowtype); | |
862 | else if (!strncmp(lowtype, "iso", 3) && is_in_proc("iso9660")) | |
863 | error ("mount: maybe you meant iso9660 ?"); | |
864 | free(lowtype); | |
865 | } else | |
726f69e2 KZ |
866 | error ("mount: %s has wrong device number or fs type %s not supported", |
867 | spec, type); | |
868 | break; | |
6dbe3af9 | 869 | case ENOTBLK: |
2b6fc908 KZ |
870 | if (stat (spec, &statbuf)) /* strange ... */ |
871 | error ("mount: %s is not a block device, and stat fails?", spec); | |
872 | else if (S_ISBLK(statbuf.st_mode)) | |
873 | error ("mount: the kernel does not recognize %s as a block device\n" | |
874 | " (maybe `insmod driver'?)", spec); | |
875 | else if (S_ISREG(statbuf.st_mode)) | |
876 | error ("mount: %s is not a block device (maybe try `-o loop'?)", | |
fd6b7a7f | 877 | spec); |
2b6fc908 KZ |
878 | else |
879 | error ("mount: %s is not a block device", spec); | |
fd6b7a7f | 880 | break; |
6dbe3af9 KZ |
881 | case ENXIO: |
882 | error ("mount: %s is not a valid block device", spec); break; | |
fd6b7a7f | 883 | case EACCES: /* pre-linux 1.1.38, 1.1.41 and later */ |
6dbe3af9 | 884 | case EROFS: /* linux 1.1.38 and later */ |
726f69e2 KZ |
885 | if (added_ro) { |
886 | error ("mount: block device %s is not permitted on its filesystem", | |
887 | spec); | |
6dbe3af9 | 888 | break; |
726f69e2 KZ |
889 | } else { |
890 | added_ro = 1; | |
fd6b7a7f KZ |
891 | if (loop) { |
892 | opts = opts0; | |
893 | type = type0; | |
894 | } | |
726f69e2 KZ |
895 | if (opts) { |
896 | opts = realloc(xstrdup(opts), strlen(opts)+4); | |
6dbe3af9 | 897 | strcat(opts, ",ro"); |
726f69e2 KZ |
898 | } else |
899 | opts = "ro"; | |
900 | if (type && !strcmp(type, "guess")) | |
901 | type = 0; | |
fd6b7a7f KZ |
902 | error ("mount: %s%s is write-protected, mounting read-only", |
903 | loop ? "" : "block device ", spec0); | |
2b6fc908 | 904 | return try_mount_one (spec0, node0, type, opts, freq, pass, bg); |
726f69e2 | 905 | } |
6dbe3af9 KZ |
906 | break; |
907 | default: | |
908 | error ("mount: %s", strerror (mnt_err)); break; | |
909 | } | |
fd6b7a7f | 910 | return EX_FAIL; |
6dbe3af9 KZ |
911 | } |
912 | ||
2b6fc908 KZ |
913 | /* |
914 | * set_proc_name() | |
915 | * Update the argument vector, so that this process may be easily | |
916 | * identified in a "ps" listing. | |
917 | */ | |
918 | static void | |
919 | set_proc_name (char *spec) | |
920 | { | |
921 | #ifdef DO_PS_FIDDLING | |
922 | int i, l; | |
923 | ||
924 | /* | |
925 | * Move the environment so we can reuse the memory. | |
926 | * (Code borrowed from sendmail.) | |
927 | * WARNING: ugly assumptions on memory layout here; if this ever causes | |
928 | * problems, #undef DO_PS_FIDDLING | |
929 | */ | |
930 | for (i = 0; envp0[i] != NULL; i++) | |
931 | continue; | |
932 | environ = (char **) xmalloc(sizeof(char *) * (i + 1)); | |
933 | for (i = 0; envp0[i] != NULL; i++) | |
934 | environ[i] = xstrdup(envp0[i]); | |
935 | environ[i] = NULL; | |
936 | ||
937 | if (i > 0) | |
938 | l = envp0[i-1] + strlen(envp0[i-1]) - argv0[0]; | |
939 | else | |
940 | l = argv0[argc0-1] + strlen(argv0[argc0-1]) - argv0[0]; | |
941 | if (l > sizeof(PROC_NAME)) { | |
942 | strcpy(argv0[0], PROC_NAME); | |
943 | strncpy(argv0[0] + sizeof(PROC_NAME) - 1, spec, l - sizeof(PROC_NAME) - 1); | |
944 | argv0[1] = NULL; | |
945 | } | |
946 | #endif | |
947 | } | |
948 | ||
949 | static int | |
950 | mount_one (char *spec, char *node, char *type, char *opts, char *cmdlineopts, | |
951 | int freq, int pass) | |
952 | { | |
953 | int status; | |
954 | int status2; | |
955 | ||
956 | /* Merge the fstab and command line options. */ | |
957 | if (opts == NULL) | |
958 | opts = cmdlineopts; | |
959 | else if (cmdlineopts != NULL) | |
960 | opts = xstrconcat3(opts, ",", cmdlineopts); | |
961 | ||
962 | if (type == NULL) { | |
963 | if (strchr (spec, ':') != NULL) { | |
964 | type = "nfs"; | |
965 | if (verbose) | |
966 | printf("mount: no type was given - " | |
967 | "I'll assume nfs because of the colon\n"); | |
968 | } | |
969 | } | |
970 | ||
971 | /* | |
972 | * Try to mount the file system. When the exit status is EX_BG, | |
973 | * we will retry in the background. Otherwise, we're done. | |
974 | */ | |
975 | status = try_mount_one (spec, node, type, opts, freq, pass, 0); | |
976 | if (status != EX_BG) | |
977 | return status; | |
978 | ||
979 | /* | |
980 | * Retry in the background. | |
981 | */ | |
982 | printf ("mount: backgrounding \"%s\"\n", spec); | |
983 | fflush( stdout ); /* prevent duplicate output */ | |
984 | if (fork() > 0) | |
985 | return 0; /* parent returns "success" */ | |
986 | spec = xstrdup(spec); /* arguments will be destroyed */ | |
987 | node = xstrdup(node); /* by set_proc_name() */ | |
988 | type = xstrdup(type); | |
989 | opts = xstrdup(opts); | |
990 | set_proc_name (spec); /* make a nice "ps" listing */ | |
991 | status2 = try_mount_one (spec, node, type, opts, freq, pass, 1); | |
992 | if (verbose && status2) | |
993 | printf ("mount: giving up \"%s\"\n", spec); | |
994 | exit (0); /* child stops here */ | |
995 | } | |
996 | ||
6dbe3af9 KZ |
997 | /* Check if an fsname/dir pair was already in the old mtab. */ |
998 | static int | |
fd6b7a7f KZ |
999 | mounted (char *spec, char *node) { |
1000 | struct mntentchn *mc; | |
6dbe3af9 | 1001 | |
fd6b7a7f KZ |
1002 | spec = canonicalize (spec); |
1003 | node = canonicalize (node); | |
6dbe3af9 | 1004 | |
fd6b7a7f KZ |
1005 | for (mc = mtab_head()->nxt; mc; mc = mc->nxt) |
1006 | if (streq (spec, mc->mnt_fsname) && streq (node, mc->mnt_dir)) | |
1007 | return 1; | |
1008 | return 0; | |
6dbe3af9 KZ |
1009 | } |
1010 | ||
fd6b7a7f KZ |
1011 | /* Mount all filesystems of the specified types except swap and root. */ |
1012 | /* With the --fork option: fork and let different incarnations of | |
1013 | mount handle different filesystems. However, try to avoid several | |
1014 | simultaneous mounts on the same physical disk, since that is very slow. */ | |
2b6fc908 | 1015 | #define DISKMAJOR(m) (((int) m) & ~0xf) |
6dbe3af9 | 1016 | |
fd6b7a7f | 1017 | static int |
2b6fc908 | 1018 | mount_all (string_list types, char *options) { |
fd6b7a7f | 1019 | struct mntentchn *mc, *mtmp; |
2b6fc908 | 1020 | int status = 0; |
fd6b7a7f KZ |
1021 | struct stat statbuf; |
1022 | struct child { | |
1023 | pid_t pid; | |
2b6fc908 | 1024 | char *group; |
fd6b7a7f KZ |
1025 | struct mntentchn *mec; |
1026 | struct mntentchn *meclast; | |
1027 | struct child *nxt; | |
1028 | } childhead, *childtail, *cp; | |
2b6fc908 KZ |
1029 | char major[22]; |
1030 | char *g, *colon; | |
fd6b7a7f KZ |
1031 | |
1032 | /* build a chain of what we have to do, or maybe | |
2b6fc908 | 1033 | several chains, one for each major or NFS host */ |
fd6b7a7f KZ |
1034 | childhead.nxt = 0; |
1035 | childtail = &childhead; | |
1036 | for (mc = fstab_head()->nxt; mc; mc = mc->nxt) { | |
1037 | if (matching_type (mc->mnt_type, types) | |
1038 | && !streq (mc->mnt_dir, "/") | |
1039 | && !streq (mc->mnt_dir, "root")) { | |
1040 | if (mounted (mc->mnt_fsname, mc->mnt_dir)) { | |
1041 | if (verbose) | |
1042 | printf("mount: %s already mounted on %s\n", | |
1043 | mc->mnt_fsname, mc->mnt_dir); | |
1044 | } else { | |
1045 | mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp)); | |
1046 | *mtmp = *mc; | |
1047 | mtmp->nxt = 0; | |
2b6fc908 KZ |
1048 | g = NULL; |
1049 | if (optfork) { | |
1050 | if (stat(mc->mnt_fsname, &statbuf) == 0 && | |
1051 | S_ISBLK(statbuf.st_mode)) { | |
1052 | sprintf(major, "#%x", DISKMAJOR(statbuf.st_rdev)); | |
1053 | g = major; | |
1054 | } | |
1055 | #ifdef HAVE_NFS | |
1056 | if (strcmp(mc->mnt_type, "nfs") == 0) { | |
1057 | g = xstrdup(mc->mnt_fsname); | |
1058 | colon = strchr(g, ':'); | |
1059 | if (colon) | |
1060 | *colon = '\0'; | |
1061 | } | |
1062 | #endif | |
1063 | } | |
1064 | if (g) { | |
fd6b7a7f | 1065 | for (cp = childhead.nxt; cp; cp = cp->nxt) |
2b6fc908 | 1066 | if (cp->group && strcmp(cp->group, g) == 0) { |
fd6b7a7f KZ |
1067 | cp->meclast->nxt = mtmp; |
1068 | cp->meclast = mtmp; | |
1069 | goto fnd; | |
1070 | } | |
1071 | } | |
1072 | cp = (struct child *) xmalloc(sizeof *cp); | |
1073 | cp->nxt = 0; | |
1074 | cp->mec = cp->meclast = mtmp; | |
2b6fc908 | 1075 | cp->group = xstrdup(g); |
fd6b7a7f KZ |
1076 | cp->pid = 0; |
1077 | childtail->nxt = cp; | |
1078 | childtail = cp; | |
1079 | fnd:; | |
1080 | } | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | /* now do everything */ | |
1085 | for (cp = childhead.nxt; cp; cp = cp->nxt) { | |
1086 | pid_t p = -1; | |
1087 | if (optfork) { | |
1088 | p = fork(); | |
1089 | if (p == -1) | |
1090 | error("mount: cannot fork: %s", strerror (errno)); | |
1091 | else if (p != 0) | |
1092 | cp->pid = p; | |
1093 | } | |
1094 | ||
1095 | /* if child, or not forked, do the mounting */ | |
1096 | if (p == 0 || p == -1) { | |
1097 | for (mc = cp->mec; mc; mc = mc->nxt) | |
1098 | status |= mount_one (mc->mnt_fsname, mc->mnt_dir, | |
2b6fc908 KZ |
1099 | mc->mnt_type, mc->mnt_opts, |
1100 | options, 0, 0); | |
fd6b7a7f KZ |
1101 | if (mountcount) |
1102 | status |= EX_SOMEOK; | |
1103 | if (p == 0) | |
1104 | exit(status); | |
1105 | } | |
1106 | } | |
1107 | ||
1108 | /* wait for children, if any */ | |
1109 | while ((cp = childhead.nxt) != NULL) { | |
1110 | childhead.nxt = cp->nxt; | |
1111 | if (cp->pid) { | |
1112 | int ret; | |
1113 | keep_waiting: | |
1114 | if(waitpid(cp->pid, &ret, 0) == -1) { | |
1115 | if (errno == EINTR) | |
1116 | goto keep_waiting; | |
1117 | perror("waitpid"); | |
1118 | } else if (WIFEXITED(ret)) | |
1119 | status |= WEXITSTATUS(ret); | |
1120 | else | |
1121 | status |= EX_SYSERR; | |
1122 | } | |
1123 | } | |
1124 | if (mountcount) | |
1125 | status |= EX_SOMEOK; | |
1126 | return status; | |
6dbe3af9 KZ |
1127 | } |
1128 | ||
1129 | extern char version[]; | |
1130 | static struct option longopts[] = | |
1131 | { | |
1132 | { "all", 0, 0, 'a' }, | |
1133 | { "fake", 0, 0, 'f' }, | |
fd6b7a7f | 1134 | { "fork", 0, 0, 'F' }, |
6dbe3af9 KZ |
1135 | { "help", 0, 0, 'h' }, |
1136 | { "no-mtab", 0, 0, 'n' }, | |
1137 | { "read-only", 0, 0, 'r' }, | |
1138 | { "ro", 0, 0, 'r' }, | |
1139 | { "verbose", 0, 0, 'v' }, | |
1140 | { "version", 0, 0, 'V' }, | |
1141 | { "read-write", 0, 0, 'w' }, | |
1142 | { "rw", 0, 0, 'w' }, | |
1143 | { "options", 1, 0, 'o' }, | |
1144 | { "types", 1, 0, 't' }, | |
1145 | { NULL, 0, 0, 0 } | |
1146 | }; | |
1147 | ||
1148 | const char *usage_string = "\ | |
1149 | usage: mount [-hV]\n\ | |
2b6fc908 KZ |
1150 | mount -a [-nfFrsvw] [-t vfstypes]\n\ |
1151 | mount [-nfrsvw] [-o options] special | node\n\ | |
1152 | mount [-nfrsvw] [-t vfstype] [-o options] special node\n\ | |
6dbe3af9 KZ |
1153 | "; |
1154 | ||
1155 | static void | |
1156 | usage (FILE *fp, int n) | |
1157 | { | |
1158 | fprintf (fp, "%s", usage_string); | |
726f69e2 | 1159 | unlock_mtab(); |
6dbe3af9 KZ |
1160 | exit (n); |
1161 | } | |
1162 | ||
1163 | int | |
fd6b7a7f KZ |
1164 | main (int argc, char *argv[]) { |
1165 | int c, result = 0; | |
1166 | char *options = NULL, *spec; | |
6dbe3af9 | 1167 | string_list types = NULL; |
fd6b7a7f | 1168 | struct mntentchn *mc; |
6dbe3af9 | 1169 | |
2b6fc908 KZ |
1170 | #ifdef DO_PS_FIDDLING |
1171 | argc0 = argc; | |
1172 | argv0 = argv; | |
1173 | envp0 = environ; | |
1174 | #endif | |
1175 | ||
1176 | while ((c = getopt_long (argc, argv, "afFhno:rsvVwt:", longopts, NULL)) | |
fd6b7a7f KZ |
1177 | != EOF) |
1178 | switch (c) { | |
6dbe3af9 KZ |
1179 | case 'a': /* mount everything in fstab */ |
1180 | ++all; | |
1181 | break; | |
1182 | case 'f': /* fake (don't actually do mount(2) call) */ | |
1183 | ++fake; | |
1184 | break; | |
fd6b7a7f KZ |
1185 | case 'F': |
1186 | ++optfork; | |
1187 | break; | |
6dbe3af9 KZ |
1188 | case 'h': /* help */ |
1189 | usage (stdout, 0); | |
1190 | break; | |
1191 | case 'n': /* mount without writing in /etc/mtab */ | |
1192 | ++nomtab; | |
1193 | break; | |
fd6b7a7f KZ |
1194 | case 'o': /* specify mount options */ |
1195 | if (options) | |
1196 | options = xstrconcat3(options, ",", optarg); | |
1197 | else | |
1198 | options = xstrdup(optarg); | |
1199 | break; | |
6dbe3af9 | 1200 | case 'r': /* mount readonly */ |
fd6b7a7f | 1201 | readonly = 1; |
6dbe3af9 KZ |
1202 | readwrite = 0; |
1203 | break; | |
2b6fc908 KZ |
1204 | case 's': /* allow sloppy mount options */ |
1205 | sloppy = 1; | |
1206 | break; | |
fd6b7a7f KZ |
1207 | case 't': /* specify file system types */ |
1208 | types = parse_list (optarg); | |
1209 | break; | |
1210 | case 'v': /* be chatty - very chatty if repeated */ | |
6dbe3af9 KZ |
1211 | ++verbose; |
1212 | break; | |
1213 | case 'V': /* version */ | |
726f69e2 | 1214 | printf ("mount: %s\n", version); |
6dbe3af9 KZ |
1215 | exit (0); |
1216 | case 'w': /* mount read/write */ | |
fd6b7a7f | 1217 | readwrite = 1; |
6dbe3af9 KZ |
1218 | readonly = 0; |
1219 | break; | |
6dbe3af9 KZ |
1220 | case 0: |
1221 | break; | |
1222 | case '?': | |
1223 | default: | |
fd6b7a7f KZ |
1224 | usage (stderr, EX_USAGE); |
1225 | } | |
6dbe3af9 KZ |
1226 | |
1227 | argc -= optind; | |
1228 | argv += optind; | |
1229 | ||
2b6fc908 | 1230 | if (argc == 0 && !all) { |
6dbe3af9 | 1231 | if (options) |
fd6b7a7f | 1232 | usage (stderr, EX_USAGE); |
2b6fc908 | 1233 | return print_all (types); |
fd6b7a7f | 1234 | } |
6dbe3af9 | 1235 | |
fd6b7a7f | 1236 | if (getuid () != geteuid ()) { |
6dbe3af9 KZ |
1237 | suid = 1; |
1238 | if (types || options || readwrite || nomtab || all || fake || argc != 1) | |
fd6b7a7f KZ |
1239 | die (EX_USAGE, "mount: only root can do that"); |
1240 | } | |
6dbe3af9 | 1241 | |
fd6b7a7f KZ |
1242 | if (!nomtab && mtab_does_not_exist()) { |
1243 | if (verbose > 1) | |
1244 | printf("mount: no %s found - creating it..\n", MOUNTED); | |
1245 | create_mtab (); | |
1246 | } | |
6dbe3af9 | 1247 | |
fd6b7a7f | 1248 | switch (argc) { |
6dbe3af9 KZ |
1249 | case 0: |
1250 | /* mount -a */ | |
2b6fc908 | 1251 | result = mount_all (types, options); |
fd6b7a7f KZ |
1252 | if (result == 0 && verbose) |
1253 | error("not mounted anything"); | |
6dbe3af9 KZ |
1254 | break; |
1255 | ||
1256 | case 1: | |
1257 | /* mount [-nfrvw] [-o options] special | node */ | |
1258 | if (types != NULL) | |
fd6b7a7f KZ |
1259 | usage (stderr, EX_USAGE); |
1260 | ||
6dbe3af9 KZ |
1261 | /* Try to find the other pathname in fstab. */ |
1262 | spec = canonicalize (*argv); | |
fd6b7a7f KZ |
1263 | if ((mc = getmntfile (spec)) == NULL && |
1264 | (mc = getfsspec (spec)) == NULL && (mc = getfsfile (spec)) == NULL) | |
1265 | die (EX_USAGE, "mount: can't find %s in %s or %s", | |
1266 | spec, MOUNTED, _PATH_FSTAB); | |
1267 | ||
fd6b7a7f | 1268 | result = mount_one (xstrdup (mc->mnt_fsname), xstrdup (mc->mnt_dir), |
2b6fc908 | 1269 | xstrdup (mc->mnt_type), mc->mnt_opts, options, 0, 0); |
6dbe3af9 KZ |
1270 | break; |
1271 | ||
1272 | case 2: | |
1273 | /* mount [-nfrvw] [-t vfstype] [-o options] special node */ | |
1274 | if (types == NULL) | |
2b6fc908 KZ |
1275 | result = mount_one (argv[0], argv[1], |
1276 | NULL, NULL, options, 0, 0); | |
6dbe3af9 | 1277 | else if (cdr (types) == NULL) |
2b6fc908 KZ |
1278 | result = mount_one (argv[0], argv[1], |
1279 | car (types), NULL, options, 0, 0); | |
6dbe3af9 | 1280 | else |
fd6b7a7f | 1281 | usage (stderr, EX_USAGE); |
6dbe3af9 KZ |
1282 | break; |
1283 | ||
1284 | default: | |
fd6b7a7f KZ |
1285 | usage (stderr, EX_USAGE); |
1286 | } | |
6dbe3af9 | 1287 | |
fd6b7a7f KZ |
1288 | if (result == EX_SOMEOK) |
1289 | result = 0; | |
6dbe3af9 KZ |
1290 | exit (result); |
1291 | } |