]> git.ipfire.org Git - thirdparty/util-linux.git/blob - mount/fstab.c
Imported from util-linux-2.12h tarball.
[thirdparty/util-linux.git] / mount / fstab.c
1 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
2 * - added Native Language Support
3 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4 * - fixed strerr(errno) in gettext calls
5 */
6
7 #include <unistd.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include "mntent.h"
13 #include "fstab.h"
14 #include "sundries.h" /* for xmalloc() etc */
15 #include "mount_blkid.h"
16 #include "paths.h"
17 #include "nls.h"
18
19 #define streq(s, t) (strcmp ((s), (t)) == 0)
20
21 #define PROC_MOUNTS "/proc/mounts"
22
23
24 /* Information about mtab. ------------------------------------*/
25 static int have_mtab_info = 0;
26 static int var_mtab_does_not_exist = 0;
27 static int var_mtab_is_a_symlink = 0;
28
29 static void
30 get_mtab_info(void) {
31 struct stat mtab_stat;
32
33 if (!have_mtab_info) {
34 if (lstat(MOUNTED, &mtab_stat))
35 var_mtab_does_not_exist = 1;
36 else if (S_ISLNK(mtab_stat.st_mode))
37 var_mtab_is_a_symlink = 1;
38 have_mtab_info = 1;
39 }
40 }
41
42 int
43 mtab_does_not_exist(void) {
44 get_mtab_info();
45 return var_mtab_does_not_exist;
46 }
47
48 int
49 mtab_is_a_symlink(void) {
50 get_mtab_info();
51 return var_mtab_is_a_symlink;
52 }
53
54 int
55 mtab_is_writable() {
56 static int ret = -1;
57
58 /* Should we write to /etc/mtab upon an update?
59 Probably not if it is a symlink to /proc/mounts, since that
60 would create a file /proc/mounts in case the proc filesystem
61 is not mounted. */
62 if (mtab_is_a_symlink())
63 return 0;
64
65 if (ret == -1) {
66 int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
67 if (fd >= 0) {
68 close(fd);
69 ret = 1;
70 } else
71 ret = 0;
72 }
73 return ret;
74 }
75
76 /* Contents of mtab and fstab ---------------------------------*/
77
78 struct mntentchn mounttable, fstab;
79 static int got_mtab = 0;
80 static int got_fstab = 0;
81
82 static void read_mounttable(void), read_fstab(void);
83
84 struct mntentchn *
85 mtab_head() {
86 if (!got_mtab)
87 read_mounttable();
88 return &mounttable;
89 }
90
91 struct mntentchn *
92 fstab_head() {
93 if (!got_fstab)
94 read_fstab();
95 return &fstab;
96 }
97
98 static void
99 my_free(const void *s) {
100 if (s)
101 free((void *) s);
102 }
103
104 static void
105 discard_mntentchn(struct mntentchn *mc0) {
106 struct mntentchn *mc, *mc1;
107
108 for (mc = mc0->nxt; mc != mc0; mc = mc1) {
109 mc1 = mc->nxt;
110 my_free(mc->m.mnt_fsname);
111 my_free(mc->m.mnt_dir);
112 my_free(mc->m.mnt_type);
113 my_free(mc->m.mnt_opts);
114 free(mc);
115 }
116 }
117
118 static void
119 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
120 struct mntentchn *mc = mc0;
121 struct my_mntent *mnt;
122
123 while ((mnt = my_getmntent(mfp)) != NULL) {
124 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
125 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
126 mc->nxt->prev = mc;
127 mc = mc->nxt;
128 mc->m = *mnt;
129 mc->nxt = mc0;
130 }
131 }
132 mc0->prev = mc;
133 if (ferror(mfp->mntent_fp)) {
134 int errsv = errno;
135 error(_("warning: error reading %s: %s"),
136 fnam, strerror (errsv));
137 mc0->nxt = mc0->prev = NULL;
138 }
139 my_endmntent(mfp);
140 }
141
142 /*
143 * Read /etc/mtab. If that fails, try /proc/mounts.
144 * This produces a linked list. The list head mounttable is a dummy.
145 * Return 0 on success.
146 */
147 static void
148 read_mounttable() {
149 mntFILE *mfp;
150 const char *fnam;
151 struct mntentchn *mc = &mounttable;
152
153 got_mtab = 1;
154 mc->nxt = mc->prev = NULL;
155
156 fnam = MOUNTED;
157 mfp = my_setmntent (fnam, "r");
158 if (mfp == NULL || mfp->mntent_fp == NULL) {
159 int errsv = errno;
160 fnam = PROC_MOUNTS;
161 mfp = my_setmntent (fnam, "r");
162 if (mfp == NULL || mfp->mntent_fp == NULL) {
163 error(_("warning: can't open %s: %s"),
164 MOUNTED, strerror (errsv));
165 return;
166 }
167 if (verbose)
168 printf (_("mount: could not open %s - "
169 "using %s instead\n"),
170 MOUNTED, PROC_MOUNTS);
171 }
172 read_mntentchn(mfp, fnam, mc);
173 }
174
175 static void
176 read_fstab() {
177 mntFILE *mfp = NULL;
178 const char *fnam;
179 struct mntentchn *mc = &fstab;
180
181 got_fstab = 1;
182 mc->nxt = mc->prev = NULL;
183
184 fnam = _PATH_FSTAB;
185 mfp = my_setmntent (fnam, "r");
186 if (mfp == NULL || mfp->mntent_fp == NULL) {
187 int errsv = errno;
188 error(_("warning: can't open %s: %s"),
189 _PATH_FSTAB, strerror (errsv));
190 return;
191 }
192 read_mntentchn(mfp, fnam, mc);
193 }
194
195
196 /* Given the name NAME, try to find it in mtab. */
197 struct mntentchn *
198 getmntfile (const char *name) {
199 struct mntentchn *mc, *mc0;
200
201 mc0 = mtab_head();
202 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
203 if (streq(mc->m.mnt_dir, name) ||
204 streq(mc->m.mnt_fsname, name))
205 return mc;
206 return NULL;
207 }
208
209 /*
210 * Given the directory name NAME, and the place MCPREV we found it last time,
211 * try to find more occurrences.
212 */
213 struct mntentchn *
214 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
215 struct mntentchn *mc, *mc0;
216
217 mc0 = mtab_head();
218 if (!mcprev)
219 mcprev = mc0;
220 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
221 if (streq(mc->m.mnt_dir, name))
222 return mc;
223 return NULL;
224 }
225
226 /*
227 * Given the device name NAME, and the place MCPREV we found it last time,
228 * try to find more occurrences.
229 */
230 struct mntentchn *
231 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
232 struct mntentchn *mc, *mc0;
233
234 mc0 = mtab_head();
235 if (!mcprev)
236 mcprev = mc0;
237 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
238 if (streq(mc->m.mnt_fsname, name))
239 return mc;
240 return NULL;
241 }
242
243 /*
244 * Given the name NAME, check that it occurs precisely once as dir or dev.
245 */
246 int
247 is_mounted_once(const char *name) {
248 struct mntentchn *mc, *mc0;
249 int ct = 0;
250
251 mc0 = mtab_head();
252 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
253 if (streq(mc->m.mnt_dir, name) ||
254 streq(mc->m.mnt_fsname, name))
255 ct++;
256 return (ct == 1);
257 }
258
259 /* Given the name FILE, try to find the option "loop=FILE" in mtab. */
260 struct mntentchn *
261 getmntoptfile (const char *file) {
262 struct mntentchn *mc, *mc0;
263 const char *opts, *s;
264 int l;
265
266 if (!file)
267 return NULL;
268
269 l = strlen(file);
270
271 mc0 = mtab_head();
272 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
273 if ((opts = mc->m.mnt_opts) != NULL
274 && (s = strstr(opts, "loop="))
275 && !strncmp(s+5, file, l)
276 && (s == opts || s[-1] == ',')
277 && (s[l+5] == 0 || s[l+5] == ','))
278 return mc;
279 return NULL;
280 }
281
282 static int
283 has_label(const char *device, const char *label) {
284 const char *devlabel;
285 int ret;
286
287 devlabel = mount_get_volume_label_by_spec(device);
288 ret = !strcmp(label, devlabel);
289 /* free(devlabel); */
290 return ret;
291 }
292
293 static int
294 has_uuid(const char *device, const char *uuid){
295 const char *devuuid;
296 int ret;
297
298 devuuid = mount_get_devname_by_uuid(device);
299 ret = !strcmp(uuid, devuuid);
300 /* free(devuuid); */
301 return ret;
302 }
303
304 /* Find the entry (SPEC,FILE) in fstab */
305 struct mntentchn *
306 getfsspecfile (const char *spec, const char *file) {
307 struct mntentchn *mc, *mc0;
308
309 mc0 = fstab_head();
310
311 /* first attempt: names occur precisely as given */
312 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
313 if (streq(mc->m.mnt_dir, file) &&
314 streq(mc->m.mnt_fsname, spec))
315 return mc;
316
317 /* second attempt: names found after symlink resolution */
318 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
319 if ((streq(mc->m.mnt_dir, file) ||
320 streq(canonicalize(mc->m.mnt_dir), file))
321 && (streq(mc->m.mnt_fsname, spec) ||
322 streq(canonicalize(mc->m.mnt_fsname), spec)))
323 return mc;
324
325 /* third attempt: names found after LABEL= or UUID= resolution */
326 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
327 if (!strncmp (mc->m.mnt_fsname, "LABEL=", 6) &&
328 (streq(mc->m.mnt_dir, file) ||
329 streq(canonicalize(mc->m.mnt_dir), file))) {
330 if (has_label(spec, mc->m.mnt_fsname+6))
331 return mc;
332 }
333 if (!strncmp (mc->m.mnt_fsname, "UUID=", 5) &&
334 (streq(mc->m.mnt_dir, file) ||
335 streq(canonicalize(mc->m.mnt_dir), file))) {
336 if (has_uuid(spec, mc->m.mnt_fsname+5))
337 return mc;
338 }
339 }
340 return NULL;
341 }
342
343 /* Find the dir FILE in fstab. */
344 struct mntentchn *
345 getfsfile (const char *file) {
346 struct mntentchn *mc, *mc0;
347
348 mc0 = fstab_head();
349 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
350 if (streq(mc->m.mnt_dir, file))
351 return mc;
352 return NULL;
353 }
354
355 /* Find the device SPEC in fstab. */
356 struct mntentchn *
357 getfsspec (const char *spec) {
358 struct mntentchn *mc, *mc0;
359
360 mc0 = fstab_head();
361 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
362 if (streq(mc->m.mnt_fsname, spec))
363 return mc;
364 return NULL;
365 }
366
367 /* Find the uuid UUID in fstab. */
368 struct mntentchn *
369 getfsuuidspec (const char *uuid) {
370 struct mntentchn *mc, *mc0;
371
372 mc0 = fstab_head();
373 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
374 if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0
375 && streq(mc->m.mnt_fsname + 5, uuid))
376 return mc;
377 return NULL;
378 }
379
380 /* Find the label LABEL in fstab. */
381 struct mntentchn *
382 getfsvolspec (const char *label) {
383 struct mntentchn *mc, *mc0;
384
385 mc0 = fstab_head();
386 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
387 if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0
388 && streq(mc->m.mnt_fsname + 6, label))
389 return mc;
390 return NULL;
391 }
392
393 /* Updating mtab ----------------------------------------------*/
394
395 /* Flag for already existing lock file. */
396 static int we_created_lockfile = 0;
397
398 /* Flag to indicate that signals have been set up. */
399 static int signals_have_been_setup = 0;
400
401 /* Ensure that the lock is released if we are interrupted. */
402 static void
403 handler (int sig) {
404 die (EX_USER, "%s", sys_siglist[sig]);
405 }
406
407 static void
408 setlkw_timeout (int sig) {
409 /* nothing, fcntl will fail anyway */
410 }
411
412 /* Create the lock file.
413 The lock file will be removed if we catch a signal or when we exit. */
414 /* The old code here used flock on a lock file /etc/mtab~ and deleted
415 this lock file afterwards. However, as rgooch remarks, that has a
416 race: a second mount may be waiting on the lock and proceed as
417 soon as the lock file is deleted by the first mount, and immediately
418 afterwards a third mount comes, creates a new /etc/mtab~, applies
419 flock to that, and also proceeds, so that the second and third mount
420 now both are scribbling in /etc/mtab.
421 The new code uses a link() instead of a creat(), where we proceed
422 only if it was us that created the lock, and hence we always have
423 to delete the lock afterwards. Now the use of flock() is in principle
424 superfluous, but avoids an arbitrary sleep(). */
425
426 /* Where does the link point to? Obvious choices are mtab and mtab~~.
427 HJLu points out that the latter leads to races. Right now we use
428 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
429 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
430 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
431
432 void
433 lock_mtab (void) {
434 int tries = 3;
435 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
436
437 if (!signals_have_been_setup) {
438 int sig = 0;
439 struct sigaction sa;
440
441 sa.sa_handler = handler;
442 sa.sa_flags = 0;
443 sigfillset (&sa.sa_mask);
444
445 while (sigismember (&sa.sa_mask, ++sig) != -1
446 && sig != SIGCHLD) {
447 if (sig == SIGALRM)
448 sa.sa_handler = setlkw_timeout;
449 else
450 sa.sa_handler = handler;
451 sigaction (sig, &sa, (struct sigaction *) 0);
452 }
453 signals_have_been_setup = 1;
454 }
455
456 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
457
458 /* Repeat until it was us who made the link */
459 while (!we_created_lockfile) {
460 struct flock flock;
461 int errsv, fd, i, j;
462
463 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
464 if (i < 0) {
465 int errsv = errno;
466 /* linktargetfile does not exist (as a file)
467 and we cannot create it. Read-only filesystem?
468 Too many files open in the system?
469 Filesystem full? */
470 die (EX_FILEIO, _("can't create lock file %s: %s "
471 "(use -n flag to override)"),
472 linktargetfile, strerror (errsv));
473 }
474 close(i);
475
476 j = link(linktargetfile, MOUNTED_LOCK);
477 errsv = errno;
478
479 (void) unlink(linktargetfile);
480
481 if (j == 0)
482 we_created_lockfile = 1;
483
484 if (j < 0 && errsv != EEXIST) {
485 die (EX_FILEIO, _("can't link lock file %s: %s "
486 "(use -n flag to override)"),
487 MOUNTED_LOCK, strerror (errsv));
488 }
489
490 fd = open (MOUNTED_LOCK, O_WRONLY);
491
492 if (fd < 0) {
493 int errsv = errno;
494 /* Strange... Maybe the file was just deleted? */
495 if (errno == ENOENT && tries-- > 0)
496 continue;
497 die (EX_FILEIO, _("can't open lock file %s: %s "
498 "(use -n flag to override)"),
499 MOUNTED_LOCK, strerror (errsv));
500 }
501
502 flock.l_type = F_WRLCK;
503 flock.l_whence = SEEK_SET;
504 flock.l_start = 0;
505 flock.l_len = 0;
506
507 if (j == 0) {
508 /* We made the link. Now claim the lock. */
509 if (fcntl (fd, F_SETLK, &flock) == -1) {
510 if (verbose) {
511 int errsv = errno;
512 printf(_("Can't lock lock file %s: %s\n"),
513 MOUNTED_LOCK, strerror (errsv));
514 }
515 /* proceed anyway */
516 }
517 } else {
518 static int tries = 0;
519
520 /* Someone else made the link. Wait. */
521 alarm(LOCK_TIMEOUT);
522 if (fcntl (fd, F_SETLKW, &flock) == -1) {
523 int errsv = errno;
524 die (EX_FILEIO, _("can't lock lock file %s: %s"),
525 MOUNTED_LOCK, (errno == EINTR) ?
526 _("timed out") : strerror (errsv));
527 }
528 alarm(0);
529 /* Limit the number of iterations - maybe there
530 still is some old /etc/mtab~ */
531 if (tries++ > 3) {
532 if (tries > 5)
533 die (EX_FILEIO, _("Cannot create link %s\n"
534 "Perhaps there is a stale lock file?\n"),
535 MOUNTED_LOCK);
536 sleep(1);
537 }
538 }
539
540 close(fd);
541 }
542 }
543
544 /* Remove lock file. */
545 void
546 unlock_mtab (void) {
547 if (we_created_lockfile) {
548 unlink (MOUNTED_LOCK);
549 we_created_lockfile = 0;
550 }
551 }
552
553 /*
554 * Update the mtab.
555 * Used by umount with null INSTEAD: remove the last DIR entry.
556 * Used by mount upon a remount: update option part,
557 * and complain if a wrong device or type was given.
558 * [Note that often a remount will be a rw remount of /
559 * where there was no entry before, and we'll have to believe
560 * the values given in INSTEAD.]
561 */
562
563 void
564 update_mtab (const char *dir, struct my_mntent *instead) {
565 mntFILE *mfp, *mftmp;
566 const char *fnam = MOUNTED;
567 struct mntentchn mtabhead; /* dummy */
568 struct mntentchn *mc, *mc0, *absent = NULL;
569
570 if (mtab_does_not_exist() || mtab_is_a_symlink())
571 return;
572
573 lock_mtab();
574
575 /* having locked mtab, read it again */
576 mc0 = mc = &mtabhead;
577 mc->nxt = mc->prev = NULL;
578
579 mfp = my_setmntent(fnam, "r");
580 if (mfp == NULL || mfp->mntent_fp == NULL) {
581 int errsv = errno;
582 error (_("cannot open %s (%s) - mtab not updated"),
583 fnam, strerror (errsv));
584 goto leave;
585 }
586
587 read_mntentchn(mfp, fnam, mc);
588
589 /* find last occurrence of dir */
590 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
591 if (streq(mc->m.mnt_dir, dir))
592 break;
593 if (mc && mc != mc0) {
594 if (instead == NULL) {
595 /* An umount - remove entry */
596 if (mc && mc != mc0) {
597 mc->prev->nxt = mc->nxt;
598 mc->nxt->prev = mc->prev;
599 free(mc);
600 }
601 } else {
602 /* A remount */
603 mc->m.mnt_opts = instead->mnt_opts;
604 }
605 } else if (instead) {
606 /* not found, add a new entry */
607 absent = xmalloc(sizeof(*absent));
608 absent->m = *instead;
609 absent->nxt = mc0;
610 absent->prev = mc0->prev;
611 mc0->prev = absent;
612 if (mc0->nxt == NULL)
613 mc0->nxt = absent;
614 }
615
616 /* write chain to mtemp */
617 mftmp = my_setmntent (MOUNTED_TEMP, "w");
618 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
619 int errsv = errno;
620 error (_("cannot open %s (%s) - mtab not updated"),
621 MOUNTED_TEMP, strerror (errsv));
622 goto leave;
623 }
624
625 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
626 if (my_addmntent(mftmp, &(mc->m)) == 1) {
627 int errsv = errno;
628 die (EX_FILEIO, _("error writing %s: %s"),
629 MOUNTED_TEMP, strerror (errsv));
630 }
631 }
632
633 discard_mntentchn(mc0);
634
635 if (fchmod (fileno (mftmp->mntent_fp),
636 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
637 int errsv = errno;
638 fprintf(stderr, _("error changing mode of %s: %s\n"),
639 MOUNTED_TEMP, strerror (errsv));
640 }
641 my_endmntent (mftmp);
642
643 { /*
644 * If mount is setuid and some non-root user mounts sth,
645 * then mtab.tmp might get the group of this user. Copy uid/gid
646 * from the present mtab before renaming.
647 */
648 struct stat sbuf;
649 if (stat (MOUNTED, &sbuf) == 0)
650 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
651 }
652
653 /* rename mtemp to mtab */
654 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
655 int errsv = errno;
656 fprintf(stderr, _("can't rename %s to %s: %s\n"),
657 MOUNTED_TEMP, MOUNTED, strerror(errsv));
658 }
659
660 leave:
661 unlock_mtab();
662 }