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
15 #include "mount_mntent.h"
20 #include "pathnames.h"
23 #define streq(s, t) (strcmp ((s), (t)) == 0)
25 /* Information about mtab. ------------------------------------*/
26 static int have_mtab_info = 0;
27 static int var_mtab_does_not_exist = 0;
28 static int var_mtab_is_a_symlink = 0;
32 if (!have_mtab_info) {
33 struct stat mtab_stat;
35 var_mtab_does_not_exist = 0;
36 var_mtab_is_a_symlink = 0;
38 if (lstat(_PATH_MOUNTED, &mtab_stat))
39 var_mtab_does_not_exist = 1;
40 else if (S_ISLNK(mtab_stat.st_mode))
41 var_mtab_is_a_symlink = 1;
47 reset_mtab_info(void) {
52 mtab_does_not_exist(void) {
54 return var_mtab_does_not_exist;
58 mtab_is_a_symlink(void) {
60 return var_mtab_is_a_symlink;
67 /* Should we write to /etc/mtab upon an update?
68 Probably not if it is a symlink to /proc/mounts, since that
69 would create a file /proc/mounts in case the proc filesystem
71 if (mtab_is_a_symlink())
74 fd = open(_PATH_MOUNTED, O_RDWR | O_CREAT, 0644);
82 /* Contents of mtab and fstab ---------------------------------*/
84 struct mntentchn mounttable, fstab;
85 static int got_mtab = 0;
86 static int got_fstab = 0;
88 static void read_mounttable(void), read_fstab(void);
105 my_free_mc(struct mntentchn *mc) {
107 my_free(mc->m.mnt_fsname);
108 my_free(mc->m.mnt_dir);
109 my_free(mc->m.mnt_type);
110 my_free(mc->m.mnt_opts);
117 discard_mntentchn(struct mntentchn *mc0) {
118 struct mntentchn *mc, *mc1;
120 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
127 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
128 struct mntentchn *mc = mc0;
129 struct my_mntent *mnt;
131 while ((mnt = my_getmntent(mfp)) != NULL) {
132 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
133 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
141 if (ferror(mfp->mntent_fp)) {
143 error(_("warning: error reading %s: %s"),
144 fnam, strerror (errsv));
145 mc0->nxt = mc0->prev = NULL;
151 * Read /etc/mtab. If that fails, try /proc/mounts.
152 * This produces a linked list. The list head mounttable is a dummy.
153 * Return 0 on success.
159 struct mntentchn *mc = &mounttable;
162 mc->nxt = mc->prev = NULL;
164 fnam = _PATH_MOUNTED;
165 mfp = my_setmntent (fnam, "r");
166 if (mfp == NULL || mfp->mntent_fp == NULL) {
168 fnam = _PATH_PROC_MOUNTS;
169 mfp = my_setmntent (fnam, "r");
170 if (mfp == NULL || mfp->mntent_fp == NULL) {
171 error(_("warning: can't open %s: %s"),
172 _PATH_MOUNTED, strerror (errsv));
176 printf (_("mount: could not open %s - "
177 "using %s instead\n"),
178 _PATH_MOUNTED, _PATH_PROC_MOUNTS);
180 read_mntentchn(mfp, fnam, mc);
187 struct mntentchn *mc = &fstab;
190 mc->nxt = mc->prev = NULL;
193 mfp = my_setmntent (fnam, "r");
194 if (mfp == NULL || mfp->mntent_fp == NULL) {
196 error(_("warning: can't open %s: %s"),
197 _PATH_MNTTAB, strerror (errsv));
200 read_mntentchn(mfp, fnam, mc);
204 /* Given the name NAME, try to find it in mtab. */
206 getmntfile (const char *name) {
207 struct mntentchn *mc, *mc0;
210 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
211 if (streq(mc->m.mnt_dir, name) ||
212 streq(mc->m.mnt_fsname, name))
218 * Given the directory name NAME, and the place MCPREV we found it last time,
219 * try to find more occurrences.
222 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
223 struct mntentchn *mc, *mc0;
228 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
229 if (streq(mc->m.mnt_dir, name))
235 * Given the device name NAME, and the place MCPREV we found it last time,
236 * try to find more occurrences.
239 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
240 struct mntentchn *mc, *mc0;
245 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
246 if (streq(mc->m.mnt_fsname, name))
252 * Given the name NAME, check that it occurs precisely once as dir or dev.
255 is_mounted_once(const char *name) {
256 struct mntentchn *mc, *mc0;
260 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
261 if (streq(mc->m.mnt_dir, name) ||
262 streq(mc->m.mnt_fsname, name))
267 /* Given the name FILE, try to find the option "loop=FILE" in mtab. */
269 getmntoptfile (const char *file) {
270 struct mntentchn *mc, *mc0;
271 const char *opts, *s;
280 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
281 if ((opts = mc->m.mnt_opts) != NULL
282 && (s = strstr(opts, "loop="))
283 && !strncmp(s+5, file, l)
284 && (s == opts || s[-1] == ',')
285 && (s[l+5] == 0 || s[l+5] == ','))
290 /* compares "quoted" or 'quoted' with unquoted */
292 streq_quoted(const char *quoted, const char *unquoted)
294 if (*quoted == '"' || *quoted == '\'')
295 return !strncmp(quoted + 1, unquoted, strlen(quoted) - 2);
297 return streq(quoted, unquoted);
301 has_label(const char *device, const char *label) {
302 const char *devlabel;
305 devlabel = fsprobe_get_label_by_devname(device);
309 ret = streq_quoted(label, devlabel);
315 has_uuid(const char *device, const char *uuid){
319 devuuid = fsprobe_get_uuid_by_devname(device);
323 ret = streq_quoted(uuid, devuuid);
328 /* Find the entry (DEV,DIR) in fstab -- spec and dir must be canonicalized! */
330 getfs_by_devdir (const char *dev, const char *dir) {
331 struct mntentchn *mc, *mc0;
335 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
339 if (!streq(mc->m.mnt_dir, dir)) {
340 char *dr = canonicalize(mc->m.mnt_dir);
346 if (ok && !streq(mc->m.mnt_fsname, dev)) {
347 const char *fs = mc->m.mnt_fsname;
349 if (strncmp (fs, "LABEL=", 6) == 0) {
350 ok = has_label(dev, fs + 6);
351 } else if (strncmp (fs, "UUID=", 5) == 0) {
352 ok = has_uuid(dev, fs + 5);
354 fs = canonicalize_spec(mc->m.mnt_fsname);
366 /* Find the dir DIR in fstab. */
368 getfs_by_dir (const char *dir) {
369 struct mntentchn *mc, *mc0;
373 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
374 if (streq(mc->m.mnt_dir, dir))
377 cdir = canonicalize(dir);
378 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
379 if (streq(mc->m.mnt_dir, cdir)) {
388 /* Find the device SPEC in fstab. */
390 getfs_by_spec (const char *spec) {
391 char *name = NULL, *value = NULL, *cspec;
392 struct mntentchn *mc = NULL;
397 if (fsprobe_parse_spec(spec, &name, &value) != 0)
398 return NULL; /* parse error */
401 if (!strcmp(name,"LABEL"))
402 mc = getfs_by_label (value);
403 else if (!strcmp(name,"UUID"))
404 mc = getfs_by_uuid (value);
411 cspec = canonicalize_spec(spec);
412 mc = getfs_by_devname(cspec);
416 /* noncanonical name like /dev/cdrom */
417 mc = getfs_by_devname(spec);
422 /* Find the device in fstab. */
424 getfs_by_devname (const char *devname) {
425 struct mntentchn *mc, *mc0;
429 /* canonical devname in fstab */
430 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
431 if (streq(mc->m.mnt_fsname, devname))
434 /* noncanonical devname in fstab */
435 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
438 if (strncmp(mc->m.mnt_fsname, "LABEL=", 6) == 0 ||
439 strncmp(mc->m.mnt_fsname, "UUID=", 5) == 0)
442 fs = canonicalize_spec(mc->m.mnt_fsname);
443 if (streq(fs, devname)) {
454 /* Find the uuid UUID in fstab. */
456 getfs_by_uuid (const char *uuid) {
457 struct mntentchn *mc, *mc0;
460 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
461 if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0
462 && streq_quoted(mc->m.mnt_fsname + 5, uuid))
467 /* Find the label LABEL in fstab. */
469 getfs_by_label (const char *label) {
470 struct mntentchn *mc, *mc0;
473 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
474 if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0
475 && streq_quoted(mc->m.mnt_fsname + 6, label))
480 /* Updating mtab ----------------------------------------------*/
482 /* Flag for already existing lock file. */
483 static int we_created_lockfile = 0;
484 static int lockfile_fd = -1;
486 /* Flag to indicate that signals have been set up. */
487 static int signals_have_been_setup = 0;
489 /* Ensure that the lock is released if we are interrupted. */
490 extern char *strsignal(int sig); /* not always in <string.h> */
494 die(EX_USER, "%s", strsignal(sig));
498 setlkw_timeout (int sig) {
499 /* nothing, fcntl will fail anyway */
502 /* Remove lock file. */
505 if (we_created_lockfile) {
508 unlink (_PATH_MOUNTED_LOCK);
509 we_created_lockfile = 0;
513 /* Create the lock file.
514 The lock file will be removed if we catch a signal or when we exit. */
515 /* The old code here used flock on a lock file /etc/mtab~ and deleted
516 this lock file afterwards. However, as rgooch remarks, that has a
517 race: a second mount may be waiting on the lock and proceed as
518 soon as the lock file is deleted by the first mount, and immediately
519 afterwards a third mount comes, creates a new /etc/mtab~, applies
520 flock to that, and also proceeds, so that the second and third mount
521 now both are scribbling in /etc/mtab.
522 The new code uses a link() instead of a creat(), where we proceed
523 only if it was us that created the lock, and hence we always have
524 to delete the lock afterwards. Now the use of flock() is in principle
525 superfluous, but avoids an arbitrary sleep(). */
527 /* Where does the link point to? Obvious choices are mtab and mtab~~.
528 HJLu points out that the latter leads to races. Right now we use
529 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
530 #define MOUNTLOCK_LINKTARGET _PATH_MOUNTED_LOCK "%d"
531 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(_PATH_MOUNTED_LOCK)+20)
534 * The original mount locking code has used sleep(1) between attempts and
535 * maximal number of attemps has been 5.
537 * There was very small number of attempts and extremely long waiting (1s)
538 * that is useless on machines with large number of concurret mount processes.
540 * Now we wait few thousand microseconds between attempts and we have global
541 * time limit (30s) rather than limit for number of attempts. The advantage
542 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
543 * number of attempts is not so much restricted.
545 * -- kzak@redhat.com [2007-Mar-2007]
548 /* maximum seconds between first and last attempt */
549 #define MOUNTLOCK_MAXTIME 30
551 /* sleep time (in microseconds, max=999999) between attempts */
552 #define MOUNTLOCK_WAITTIME 5000
557 struct timespec waittime;
558 struct timeval maxtime;
559 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
561 if (!signals_have_been_setup) {
565 sa.sa_handler = handler;
567 sigfillset (&sa.sa_mask);
569 while (sigismember (&sa.sa_mask, ++sig) != -1
572 sa.sa_handler = setlkw_timeout;
574 sa.sa_handler = handler;
575 sigaction (sig, &sa, (struct sigaction *) 0);
577 signals_have_been_setup = 1;
580 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
582 i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
585 /* linktargetfile does not exist (as a file)
586 and we cannot create it. Read-only filesystem?
587 Too many files open in the system?
589 die (EX_FILEIO, _("can't create lock file %s: %s "
590 "(use -n flag to override)"),
591 linktargetfile, strerror (errsv));
595 gettimeofday(&maxtime, NULL);
596 maxtime.tv_sec += MOUNTLOCK_MAXTIME;
599 waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
601 /* Repeat until it was us who made the link */
602 while (!we_created_lockfile) {
607 j = link(linktargetfile, _PATH_MOUNTED_LOCK);
611 we_created_lockfile = 1;
613 if (j < 0 && errsv != EEXIST) {
614 (void) unlink(linktargetfile);
615 die (EX_FILEIO, _("can't link lock file %s: %s "
616 "(use -n flag to override)"),
617 _PATH_MOUNTED_LOCK, strerror (errsv));
620 lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
622 if (lockfile_fd < 0) {
623 /* Strange... Maybe the file was just deleted? */
625 gettimeofday(&now, NULL);
626 if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
627 we_created_lockfile = 0;
630 (void) unlink(linktargetfile);
631 die (EX_FILEIO, _("can't open lock file %s: %s "
632 "(use -n flag to override)"),
633 _PATH_MOUNTED_LOCK, strerror (errsv));
636 flock.l_type = F_WRLCK;
637 flock.l_whence = SEEK_SET;
642 /* We made the link. Now claim the lock. */
643 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
646 printf(_("Can't lock lock file %s: %s\n"),
647 _PATH_MOUNTED_LOCK, strerror (errsv));
649 /* proceed, since it was us who created the lockfile anyway */
651 (void) unlink(linktargetfile);
653 /* Someone else made the link. Wait. */
654 gettimeofday(&now, NULL);
655 if (now.tv_sec < maxtime.tv_sec) {
656 alarm(maxtime.tv_sec - now.tv_sec);
657 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
659 (void) unlink(linktargetfile);
660 die (EX_FILEIO, _("can't lock lock file %s: %s"),
661 _PATH_MOUNTED_LOCK, (errno == EINTR) ?
662 _("timed out") : strerror (errsv));
666 nanosleep(&waittime, NULL);
668 (void) unlink(linktargetfile);
669 die (EX_FILEIO, _("Cannot create link %s\n"
670 "Perhaps there is a stale lock file?\n"),
679 get_option(const char *optname, const char *src, size_t *len)
687 opt = strstr(src, optname);
691 end = strchr(opt, ',');
692 sz = end ? end - opt : strlen(opt);
697 if ((opt == src || *(opt - 1) == ',') &&
698 (*(opt + sz) == '\0' || *(opt + sz) == ','))
705 cpy_option(const char *optname, char *dest, const char *src)
710 opt = get_option(optname, src, &sz);
712 /* the option doesn't exist */
715 if (get_option(optname, dest, NULL))
716 /* the options is already in dest */
720 dest = dest + strlen(dest);
721 *dest++ = ','; /* separator */
723 memcpy(dest, opt, sz); /* copy option */
724 *(dest + sz) = '\0'; /* terminator */
729 /* Generates (and allocates) new options for remount
731 * We cannot blindly replace the old options, otherwise we will lost some
732 * internally generated stuff (e.g loop=).
735 mk_remount_opts(const char *old, const char *instead)
740 if (old == NULL && instead == NULL)
743 return xstrdup(instead);
745 /* max size of new options is:
746 * old + new + '\0' + separator (for each copied option)
748 sz = strlen(old) + (instead ? strlen(instead) : 0) + 2;
750 if (instead && *instead)
751 strncpy(new, instead, sz);
755 cpy_option("loop=", new, old);
762 * Used by umount with null INSTEAD: remove the last DIR entry.
763 * Used by mount upon a remount: update option part,
764 * and complain if a wrong device or type was given.
765 * [Note that often a remount will be a rw remount of /
766 * where there was no entry before, and we'll have to believe
767 * the values given in INSTEAD.]
771 update_mtab (const char *dir, struct my_mntent *instead) {
772 mntFILE *mfp, *mftmp;
773 const char *fnam = _PATH_MOUNTED;
774 struct mntentchn mtabhead; /* dummy */
775 struct mntentchn *mc, *mc0, *absent = NULL;
779 if (mtab_does_not_exist() || !mtab_is_writable())
784 /* having locked mtab, read it again */
785 mc0 = mc = &mtabhead;
786 mc->nxt = mc->prev = NULL;
788 mfp = my_setmntent(fnam, "r");
789 if (mfp == NULL || mfp->mntent_fp == NULL) {
791 error (_("cannot open %s (%s) - mtab not updated"),
792 fnam, strerror (errsv));
796 read_mntentchn(mfp, fnam, mc);
798 /* find last occurrence of dir */
799 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
800 if (streq(mc->m.mnt_dir, dir))
802 if (mc && mc != mc0) {
803 if (instead == NULL) {
804 /* An umount - remove entry */
805 if (mc && mc != mc0) {
806 mc->prev->nxt = mc->nxt;
807 mc->nxt->prev = mc->prev;
810 } else if (!strcmp(mc->m.mnt_dir, instead->mnt_dir)) {
812 char *opts = mk_remount_opts(mc->m.mnt_opts,
814 my_free(mc->m.mnt_opts);
815 mc->m.mnt_opts = opts;
818 my_free(mc->m.mnt_dir);
819 mc->m.mnt_dir = xstrdup(instead->mnt_dir);
821 } else if (instead) {
822 /* not found, add a new entry */
823 absent = xmalloc(sizeof(*absent));
824 absent->m.mnt_fsname = xstrdup(instead->mnt_fsname);
825 absent->m.mnt_dir = xstrdup(instead->mnt_dir);
826 absent->m.mnt_type = xstrdup(instead->mnt_type);
827 absent->m.mnt_opts = xstrdup(instead->mnt_opts);
828 absent->m.mnt_freq = instead->mnt_freq;
829 absent->m.mnt_passno = instead->mnt_passno;
831 if (mc0->prev != NULL) {
832 absent->prev = mc0->prev;
833 mc0->prev->nxt = absent;
838 if (mc0->nxt == NULL)
842 /* write chain to mtemp */
843 mftmp = my_setmntent (_PATH_MOUNTED_TMP, "w");
844 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
846 error (_("cannot open %s (%s) - mtab not updated"),
847 _PATH_MOUNTED_TMP, strerror (errsv));
848 discard_mntentchn(mc0);
852 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
853 if (my_addmntent(mftmp, &(mc->m)) == 1) {
855 die (EX_FILEIO, _("error writing %s: %s"),
856 _PATH_MOUNTED_TMP, strerror (errsv));
860 discard_mntentchn(mc0);
861 fd = fileno(mftmp->mntent_fp);
864 * It seems that better is incomplete and broken /mnt/mtab that
865 * /mnt/mtab that is writeable for non-root users.
867 * We always skip rename() when chown() and chmod() failed.
868 * -- kzak, 11-Oct-2007
871 if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
873 fprintf(stderr, _("error changing mode of %s: %s\n"),
874 _PATH_MOUNTED_TMP, strerror (errsv));
879 * If mount is setuid and some non-root user mounts sth,
880 * then mtab.tmp might get the group of this user. Copy uid/gid
881 * from the present mtab before renaming.
883 if (stat(_PATH_MOUNTED, &sbuf) == 0) {
884 if (fchown(fd, sbuf.st_uid, sbuf.st_gid) < 0) {
886 fprintf (stderr, _("error changing owner of %s: %s\n"),
887 _PATH_MOUNTED_TMP, strerror(errsv));
892 my_endmntent (mftmp);
894 /* rename mtemp to mtab */
895 if (rename (_PATH_MOUNTED_TMP, _PATH_MOUNTED) < 0) {
897 fprintf(stderr, _("can't rename %s to %s: %s\n"),
898 _PATH_MOUNTED_TMP, _PATH_MOUNTED, strerror(errsv));
906 #ifdef MAIN_TEST_MTABLOCK
909 * This is mtab locking code test for:
911 * - performance (how many concurrent processes)
913 * - lock reliability (is possible to see corrupted data if more
914 * concurrent processes modify a same file)
916 * The test is very simple -- it reads a number from locked file, increments the
917 * number and writes the number back to the file.
920 char *fsprobe_get_label_by_devname(const char *spec) { return NULL; }
921 char *fsprobe_get_uuid_by_devname(const char *spec) { return NULL; }
922 int fsprobe_parse_spec(const char *spec, char **name, char **value) { return 0; }
923 struct my_mntent *my_getmntent (mntFILE *mfp) { return NULL; }
924 mntFILE *my_setmntent (const char *file, char *mode) { return NULL; }
925 void my_endmntent (mntFILE *mfp) { }
926 int my_addmntent (mntFILE *mfp, struct my_mntent *mnt) { return 0; }
929 main(int argc, char **argv)
934 pid_t pid = getpid();
944 "usage: %s <id> <synctime> <file> <nloops>\n",
948 synctime = (time_t) atol(argv[2]);
950 nloops = atoi(argv[4]);
952 if (stat(filename, &st) < -1)
953 die(EXIT_FAILURE, "%s: %s\n", filename, strerror(errno));
955 fprintf(stderr, "%05d (pid=%05d): START\n", id, pid);
957 gettimeofday(&tv, NULL);
958 if (synctime && synctime - tv.tv_sec > 1) {
959 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
960 (1000000UL - tv.tv_usec);
964 for (i = 0; i < nloops; i++) {
971 if (!(f = fopen(filename, "r"))) {
973 die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
974 "open for read failed\n", id, pid, i);
976 if (!fgets(buf, sizeof(buf), f)) {
978 die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
979 "read failed\n", id, pid, i);
985 if (!(f = fopen(filename, "w"))) {
987 die(EXIT_FAILURE, "ERROR: %d (pid=%d, loop=%d): "
988 "open for write failed\n", id, pid, i);
990 fprintf(f, "%ld", num);
995 gettimeofday(&tv, NULL);
997 fprintf(stderr, "%010ld.%06ld %04d (pid=%05d, loop=%05d): "
998 "num=%09ld last=%09ld\n",
999 tv.tv_sec, tv.tv_usec, id,
1003 /* The mount command usually finish after mtab update. We
1004 * simulate this via short sleep -- it's also enough to make
1005 * concurrent processes happy.
1010 fprintf(stderr, "%05d (pid=%05d): DONE\n", id, pid);