1 /* 1999-02-22 Arkadiusz Miskiewicz <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
6 * 2006-06-08 Amit Gud <agud@redhat.com>
7 * - Moved code to nfs-utils/support/nfs from util-linux/mount.
20 #include "nfs_mntent.h"
21 #include "nfs_paths.h"
24 #define LOCK_TIMEOUT 10
25 #define streq(s, t) (strcmp ((s), (t)) == 0)
26 #define PROC_MOUNTS "/proc/mounts"
28 extern char *progname;
31 /* Information about mtab. ------------------------------------*/
32 static int have_mtab_info = 0;
33 static int var_mtab_does_not_exist = 0;
34 static int var_mtab_is_a_symlink = 0;
38 struct stat mtab_stat;
40 if (!have_mtab_info) {
41 if (lstat(MOUNTED, &mtab_stat))
42 var_mtab_does_not_exist = 1;
43 else if (S_ISLNK(mtab_stat.st_mode))
44 var_mtab_is_a_symlink = 1;
50 reset_mtab_info(void) {
55 mtab_does_not_exist(void) {
57 return var_mtab_does_not_exist;
61 mtab_is_a_symlink(void) {
63 return var_mtab_is_a_symlink;
70 /* Should we write to /etc/mtab upon an update?
71 Probably not if it is a symlink to /proc/mounts, since that
72 would create a file /proc/mounts in case the proc filesystem
74 if (mtab_is_a_symlink())
77 fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
85 /* Contents of mtab and fstab ---------------------------------*/
87 struct mntentchn mounttable;
88 static int got_mtab = 0;
89 struct mntentchn procmounts;
90 static int got_procmounts = 0;
91 struct mntentchn fstab;
92 static int got_fstab = 0;
94 static void read_mounttable(void);
95 static void read_procmounts(void);
96 static void read_fstab(void);
98 static struct mntentchn *
106 static struct mntentchn *
107 procmounts_head(void)
114 static struct mntentchn *
124 my_free(const void *s) {
130 discard_mntentchn(struct mntentchn *mc0) {
131 struct mntentchn *mc, *mc1;
133 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
135 my_free(mc->m.mnt_fsname);
136 my_free(mc->m.mnt_dir);
137 my_free(mc->m.mnt_type);
138 my_free(mc->m.mnt_opts);
145 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
146 struct mntentchn *mc = mc0;
149 while ((mnt = nfs_getmntent(mfp)) != NULL) {
150 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
151 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
159 if (ferror(mfp->mntent_fp)) {
161 nfs_error(_("warning: error reading %s: %s"),
162 fnam, strerror (errsv));
163 mc0->nxt = mc0->prev = NULL;
169 * Read /etc/mtab. If that fails, try /proc/mounts.
170 * This produces a linked list. The list head mounttable is a dummy.
171 * Return 0 on success.
177 struct mntentchn *mc = &mounttable;
180 mc->nxt = mc->prev = NULL;
183 mfp = nfs_setmntent (fnam, "r");
184 if (mfp == NULL || mfp->mntent_fp == NULL) {
187 mfp = nfs_setmntent (fnam, "r");
188 if (mfp == NULL || mfp->mntent_fp == NULL) {
189 nfs_error(_("warning: can't open %s: %s"),
190 MOUNTED, strerror (errsv));
194 printf(_("%s: could not open %s; using %s instead\n"),
195 progname, MOUNTED, PROC_MOUNTS);
197 read_mntentchn(mfp, fnam, mc);
202 * This produces a linked list. The list head procmounts is a dummy.
203 * Return 0 on success.
209 struct mntentchn *mc = &procmounts;
212 mc->nxt = mc->prev = NULL;
215 mfp = nfs_setmntent(fnam, "r");
216 if (mfp == NULL || mfp->mntent_fp == NULL) {
217 nfs_error(_("warning: can't open %s: %s"),
218 PROC_MOUNTS, strerror (errno));
221 read_mntentchn(mfp, fnam, mc);
229 struct mntentchn *mc = &fstab;
232 mc->nxt = mc->prev = NULL;
235 mfp = nfs_setmntent (fnam, "r");
236 if (mfp == NULL || mfp->mntent_fp == NULL) {
238 nfs_error(_("warning: can't open %s: %s"),
239 _PATH_FSTAB, strerror (errsv));
242 read_mntentchn(mfp, fnam, mc);
246 * Given the directory name NAME, and the place MCPREV we found it last time,
247 * try to find more occurrences.
250 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
251 struct mntentchn *mc, *mc0;
256 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
257 if (streq(mc->m.mnt_dir, name))
263 * Given the directory name NAME, and the place MCPREV we found it last time,
264 * try to find more occurrences.
267 getprocmntdirbackward (const char *name, struct mntentchn *mcprev) {
268 struct mntentchn *mc, *mc0;
270 mc0 = procmounts_head();
273 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
274 if (streq(mc->m.mnt_dir, name))
280 * Given the device name NAME, and the place MCPREV we found it last time,
281 * try to find more occurrences.
284 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
285 struct mntentchn *mc, *mc0;
290 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
291 if (streq(mc->m.mnt_fsname, name))
296 /* Find the dir FILE in fstab. */
298 getfsfile (const char *file)
300 struct mntentchn *mc, *mc0;
303 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
304 if (streq(mc->m.mnt_dir, file))
309 /* Find the device SPEC in fstab. */
311 getfsspec (const char *spec)
313 struct mntentchn *mc, *mc0;
316 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
317 if (streq(mc->m.mnt_fsname, spec))
322 /* Updating mtab ----------------------------------------------*/
324 /* Flag for already existing lock file. */
325 static int we_created_lockfile = 0;
326 static int lockfile_fd = -1;
328 /* Flag to indicate that signals have been set up. */
329 static int signals_have_been_setup = 0;
331 /* Ensure that the lock is released if we are interrupted. */
332 extern char *strsignal(int sig); /* not always in <string.h> */
336 die(EX_USER, "%s", strsignal(sig));
340 setlkw_timeout (__attribute__((unused)) int sig) {
341 /* nothing, fcntl will fail anyway */
344 /* Remove lock file. */
347 if (we_created_lockfile) {
350 unlink (MOUNTED_LOCK);
351 we_created_lockfile = 0;
355 /* Create the lock file.
356 The lock file will be removed if we catch a signal or when we exit. */
357 /* The old code here used flock on a lock file /etc/mtab~ and deleted
358 this lock file afterwards. However, as rgooch remarks, that has a
359 race: a second mount may be waiting on the lock and proceed as
360 soon as the lock file is deleted by the first mount, and immediately
361 afterwards a third mount comes, creates a new /etc/mtab~, applies
362 flock to that, and also proceeds, so that the second and third mount
363 now both are scribbling in /etc/mtab.
364 The new code uses a link() instead of a creat(), where we proceed
365 only if it was us that created the lock, and hence we always have
366 to delete the lock afterwards. Now the use of flock() is in principle
367 superfluous, but avoids an arbitrary sleep(). */
369 /* Where does the link point to? Obvious choices are mtab and mtab~~.
370 HJLu points out that the latter leads to races. Right now we use
371 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
372 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
373 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
377 int tries = 100000, i;
378 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
380 at_die = unlock_mtab;
382 if (!signals_have_been_setup) {
387 sigfillset (&sa.sa_mask);
389 while (sigismember (&sa.sa_mask, ++sig) != -1) {
395 /* The cannot be caught, or should not,
400 sa.sa_handler = setlkw_timeout;
412 /* non-priv user can cause these to be
413 * generated, so ignore them.
415 sa.sa_handler = SIG_IGN;
418 /* The rest should not be possible, so just
419 * print a message and unlock mtab.
421 sa.sa_handler = handler;
423 sigaction (sig, &sa, (struct sigaction *) 0);
425 signals_have_been_setup = 1;
428 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
430 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
433 /* linktargetfile does not exist (as a file)
434 and we cannot create it. Read-only filesystem?
435 Too many files open in the system?
437 die (EX_FILEIO, _("can't create lock file %s: %s "
438 "(use -n flag to override)"),
439 linktargetfile, strerror (errsv));
443 /* Repeat until it was us who made the link */
444 while (!we_created_lockfile) {
448 j = link(linktargetfile, MOUNTED_LOCK);
454 we_created_lockfile = 1;
456 if (j < 0 && errsv != EEXIST) {
457 (void) unlink(linktargetfile);
458 die (EX_FILEIO, _("can't link lock file %s: %s "
459 "(use -n flag to override)"),
460 MOUNTED_LOCK, strerror (errsv));
464 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
466 if (lockfile_fd < 0) {
468 /* Strange... Maybe the file was just deleted? */
469 if (errno == ENOENT && tries-- > 0) {
470 if (tries % 200 == 0)
474 (void) unlink(linktargetfile);
475 die (EX_FILEIO, _("can't open lock file %s: %s "
476 "(use -n flag to override)"),
477 MOUNTED_LOCK, strerror (errsv));
480 flock.l_type = F_WRLCK;
481 flock.l_whence = SEEK_SET;
486 /* We made the link. Now claim the lock. */
487 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
490 nfs_error(_("%s: Can't lock lock file "
497 (void) unlink(linktargetfile);
499 static int retries = 0;
501 /* Someone else made the link. Wait. */
503 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
505 (void) unlink(linktargetfile);
506 die (EX_FILEIO, _("can't lock lock file %s: %s"),
507 MOUNTED_LOCK, (errno == EINTR) ?
508 _("timed out") : strerror (errsv));
511 /* Limit the number of iterations - maybe there
512 still is some old /etc/mtab~ */
514 if (retries % 200 == 0)
516 if (retries > 100000) {
517 (void) unlink(linktargetfile);
519 die (EX_FILEIO, _("Cannot create link %s\n"
520 "Perhaps there is a stale lock file?\n"),
530 * Used by umount with null INSTEAD: remove the last DIR entry.
531 * Used by mount upon a remount: update option part,
532 * and complain if a wrong device or type was given.
533 * [Note that often a remount will be a rw remount of /
534 * where there was no entry before, and we'll have to believe
535 * the values given in INSTEAD.]
539 update_mtab (const char *dir, struct mntent *instead)
541 mntFILE *mfp, *mftmp;
542 const char *fnam = MOUNTED;
543 struct mntentchn mtabhead; /* dummy */
544 struct mntentchn *mc, *mc0, *absent = NULL;
546 if (mtab_does_not_exist() || !mtab_is_writable())
551 /* having locked mtab, read it again */
552 mc0 = mc = &mtabhead;
553 mc->nxt = mc->prev = NULL;
555 mfp = nfs_setmntent(fnam, "r");
556 if (mfp == NULL || mfp->mntent_fp == NULL) {
558 nfs_error (_("cannot open %s (%s) - mtab not updated"),
559 fnam, strerror (errsv));
563 read_mntentchn(mfp, fnam, mc);
565 /* find last occurrence of dir */
566 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
567 if (streq(mc->m.mnt_dir, dir))
569 if (mc && mc != mc0) {
570 if (instead == NULL) {
571 /* An umount - remove entry */
572 if (mc && mc != mc0) {
573 mc->prev->nxt = mc->nxt;
574 mc->nxt->prev = mc->prev;
579 mc->m.mnt_opts = instead->mnt_opts;
581 } else if (instead) {
582 /* not found, add a new entry */
583 absent = xmalloc(sizeof(*absent));
584 absent->m = *instead;
586 absent->prev = mc0->prev;
588 if (mc0->nxt == NULL)
592 /* write chain to mtemp */
593 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
594 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
596 nfs_error (_("cannot open %s (%s) - mtab not updated"),
597 MOUNTED_TEMP, strerror (errsv));
601 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
602 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
604 die (EX_FILEIO, _("error writing %s: %s"),
605 MOUNTED_TEMP, strerror (errsv));
610 /* the chain might have strings copied from 'instead',
611 * so we cannot safely free it.
612 * And there is no need anyway because we are going to exit
613 * shortly. So just don't call discard_mntentchn....
615 discard_mntentchn(mc0);
617 if (fchmod (fileno (mftmp->mntent_fp),
618 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
620 nfs_error(_("%s: error changing mode of %s: %s"),
621 progname, MOUNTED_TEMP, strerror (errsv));
623 nfs_endmntent (mftmp);
626 * If mount is setuid and some non-root user mounts sth,
627 * then mtab.tmp might get the group of this user. Copy uid/gid
628 * from the present mtab before renaming.
631 if (stat (MOUNTED, &sbuf) == 0) {
632 if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
633 nfs_error(_("%s: error changing owner of %s: %s"),
634 progname, MOUNTED_TEMP, strerror (errno));
639 /* rename mtemp to mtab */
640 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
642 nfs_error(_("%s: can't rename %s to %s: %s\n"),
643 progname, MOUNTED_TEMP, MOUNTED,