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 fstab;
90 static int got_fstab = 0;
92 static void read_mounttable(void);
93 static void read_fstab(void);
95 static struct mntentchn *
103 static struct mntentchn *
113 my_free(const void *s) {
119 discard_mntentchn(struct mntentchn *mc0) {
120 struct mntentchn *mc, *mc1;
122 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
124 my_free(mc->m.mnt_fsname);
125 my_free(mc->m.mnt_dir);
126 my_free(mc->m.mnt_type);
127 my_free(mc->m.mnt_opts);
134 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
135 struct mntentchn *mc = mc0;
138 while ((mnt = nfs_getmntent(mfp)) != NULL) {
139 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
140 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
148 if (ferror(mfp->mntent_fp)) {
150 nfs_error(_("warning: error reading %s: %s"),
151 fnam, strerror (errsv));
152 mc0->nxt = mc0->prev = NULL;
158 * Read /etc/mtab. If that fails, try /proc/mounts.
159 * This produces a linked list. The list head mounttable is a dummy.
160 * Return 0 on success.
166 struct mntentchn *mc = &mounttable;
169 mc->nxt = mc->prev = NULL;
172 mfp = nfs_setmntent (fnam, "r");
173 if (mfp == NULL || mfp->mntent_fp == NULL) {
176 mfp = nfs_setmntent (fnam, "r");
177 if (mfp == NULL || mfp->mntent_fp == NULL) {
178 nfs_error(_("warning: can't open %s: %s"),
179 MOUNTED, strerror (errsv));
183 printf(_("%s: could not open %s; using %s instead\n"),
184 progname, MOUNTED, PROC_MOUNTS);
186 read_mntentchn(mfp, fnam, mc);
194 struct mntentchn *mc = &fstab;
197 mc->nxt = mc->prev = NULL;
200 mfp = nfs_setmntent (fnam, "r");
201 if (mfp == NULL || mfp->mntent_fp == NULL) {
203 nfs_error(_("warning: can't open %s: %s"),
204 _PATH_FSTAB, strerror (errsv));
207 read_mntentchn(mfp, fnam, mc);
211 * Given the directory name NAME, and the place MCPREV we found it last time,
212 * try to find more occurrences.
215 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
216 struct mntentchn *mc, *mc0;
221 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
222 if (streq(mc->m.mnt_dir, name))
228 * Given the device name NAME, and the place MCPREV we found it last time,
229 * try to find more occurrences.
232 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
233 struct mntentchn *mc, *mc0;
238 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
239 if (streq(mc->m.mnt_fsname, name))
244 /* Find the dir FILE in fstab. */
246 getfsfile (const char *file)
248 struct mntentchn *mc, *mc0;
251 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
252 if (streq(mc->m.mnt_dir, file))
257 /* Find the device SPEC in fstab. */
259 getfsspec (const char *spec)
261 struct mntentchn *mc, *mc0;
264 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
265 if (streq(mc->m.mnt_fsname, spec))
270 /* Updating mtab ----------------------------------------------*/
272 /* Flag for already existing lock file. */
273 static int we_created_lockfile = 0;
274 static int lockfile_fd = -1;
276 /* Flag to indicate that signals have been set up. */
277 static int signals_have_been_setup = 0;
279 /* Ensure that the lock is released if we are interrupted. */
280 extern char *strsignal(int sig); /* not always in <string.h> */
284 die(EX_USER, "%s", strsignal(sig));
288 setlkw_timeout (__attribute__((unused)) int sig) {
289 /* nothing, fcntl will fail anyway */
292 /* Remove lock file. */
295 if (we_created_lockfile) {
298 unlink (MOUNTED_LOCK);
299 we_created_lockfile = 0;
303 /* Create the lock file.
304 The lock file will be removed if we catch a signal or when we exit. */
305 /* The old code here used flock on a lock file /etc/mtab~ and deleted
306 this lock file afterwards. However, as rgooch remarks, that has a
307 race: a second mount may be waiting on the lock and proceed as
308 soon as the lock file is deleted by the first mount, and immediately
309 afterwards a third mount comes, creates a new /etc/mtab~, applies
310 flock to that, and also proceeds, so that the second and third mount
311 now both are scribbling in /etc/mtab.
312 The new code uses a link() instead of a creat(), where we proceed
313 only if it was us that created the lock, and hence we always have
314 to delete the lock afterwards. Now the use of flock() is in principle
315 superfluous, but avoids an arbitrary sleep(). */
317 /* Where does the link point to? Obvious choices are mtab and mtab~~.
318 HJLu points out that the latter leads to races. Right now we use
319 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
320 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
321 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
325 int tries = 100000, i;
326 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
328 at_die = unlock_mtab;
330 if (!signals_have_been_setup) {
335 sigfillset (&sa.sa_mask);
337 while (sigismember (&sa.sa_mask, ++sig) != -1) {
343 /* The cannot be caught, or should not,
348 sa.sa_handler = setlkw_timeout;
360 /* non-priv user can cause these to be
361 * generated, so ignore them.
363 sa.sa_handler = SIG_IGN;
366 /* The rest should not be possible, so just
367 * print a message and unlock mtab.
369 sa.sa_handler = handler;
371 sigaction (sig, &sa, (struct sigaction *) 0);
373 signals_have_been_setup = 1;
376 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
378 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
381 /* linktargetfile does not exist (as a file)
382 and we cannot create it. Read-only filesystem?
383 Too many files open in the system?
385 die (EX_FILEIO, _("can't create lock file %s: %s "
386 "(use -n flag to override)"),
387 linktargetfile, strerror (errsv));
391 /* Repeat until it was us who made the link */
392 while (!we_created_lockfile) {
396 j = link(linktargetfile, MOUNTED_LOCK);
402 we_created_lockfile = 1;
404 if (j < 0 && errsv != EEXIST) {
405 (void) unlink(linktargetfile);
406 die (EX_FILEIO, _("can't link lock file %s: %s "
407 "(use -n flag to override)"),
408 MOUNTED_LOCK, strerror (errsv));
412 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
414 if (lockfile_fd < 0) {
416 /* Strange... Maybe the file was just deleted? */
417 if (errno == ENOENT && tries-- > 0) {
418 if (tries % 200 == 0)
422 (void) unlink(linktargetfile);
423 die (EX_FILEIO, _("can't open lock file %s: %s "
424 "(use -n flag to override)"),
425 MOUNTED_LOCK, strerror (errsv));
428 flock.l_type = F_WRLCK;
429 flock.l_whence = SEEK_SET;
434 /* We made the link. Now claim the lock. */
435 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
438 nfs_error(_("%s: Can't lock lock file "
445 (void) unlink(linktargetfile);
447 static int retries = 0;
449 /* Someone else made the link. Wait. */
451 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
453 (void) unlink(linktargetfile);
454 die (EX_FILEIO, _("can't lock lock file %s: %s"),
455 MOUNTED_LOCK, (errno == EINTR) ?
456 _("timed out") : strerror (errsv));
459 /* Limit the number of iterations - maybe there
460 still is some old /etc/mtab~ */
462 if (retries % 200 == 0)
464 if (retries > 100000) {
465 (void) unlink(linktargetfile);
467 die (EX_FILEIO, _("Cannot create link %s\n"
468 "Perhaps there is a stale lock file?\n"),
478 * Used by umount with null INSTEAD: remove the last DIR entry.
479 * Used by mount upon a remount: update option part,
480 * and complain if a wrong device or type was given.
481 * [Note that often a remount will be a rw remount of /
482 * where there was no entry before, and we'll have to believe
483 * the values given in INSTEAD.]
487 update_mtab (const char *dir, struct mntent *instead)
489 mntFILE *mfp, *mftmp;
490 const char *fnam = MOUNTED;
491 struct mntentchn mtabhead; /* dummy */
492 struct mntentchn *mc, *mc0, *absent = NULL;
494 if (mtab_does_not_exist() || !mtab_is_writable())
499 /* having locked mtab, read it again */
500 mc0 = mc = &mtabhead;
501 mc->nxt = mc->prev = NULL;
503 mfp = nfs_setmntent(fnam, "r");
504 if (mfp == NULL || mfp->mntent_fp == NULL) {
506 nfs_error (_("cannot open %s (%s) - mtab not updated"),
507 fnam, strerror (errsv));
511 read_mntentchn(mfp, fnam, mc);
513 /* find last occurrence of dir */
514 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
515 if (streq(mc->m.mnt_dir, dir))
517 if (mc && mc != mc0) {
518 if (instead == NULL) {
519 /* An umount - remove entry */
520 if (mc && mc != mc0) {
521 mc->prev->nxt = mc->nxt;
522 mc->nxt->prev = mc->prev;
527 mc->m.mnt_opts = instead->mnt_opts;
529 } else if (instead) {
530 /* not found, add a new entry */
531 absent = xmalloc(sizeof(*absent));
532 absent->m = *instead;
534 absent->prev = mc0->prev;
536 if (mc0->nxt == NULL)
540 /* write chain to mtemp */
541 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
542 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
544 nfs_error (_("cannot open %s (%s) - mtab not updated"),
545 MOUNTED_TEMP, strerror (errsv));
549 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
550 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
552 die (EX_FILEIO, _("error writing %s: %s"),
553 MOUNTED_TEMP, strerror (errsv));
558 /* the chain might have strings copied from 'instead',
559 * so we cannot safely free it.
560 * And there is no need anyway because we are going to exit
561 * shortly. So just don't call discard_mntentchn....
563 discard_mntentchn(mc0);
565 if (fchmod (fileno (mftmp->mntent_fp),
566 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
568 nfs_error(_("%s: error changing mode of %s: %s"),
569 progname, MOUNTED_TEMP, strerror (errsv));
571 nfs_endmntent (mftmp);
574 * If mount is setuid and some non-root user mounts sth,
575 * then mtab.tmp might get the group of this user. Copy uid/gid
576 * from the present mtab before renaming.
579 if (stat (MOUNTED, &sbuf) == 0) {
580 if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
581 nfs_error(_("%s: error changing owner of %s: %s"),
582 progname, MOUNTED_TEMP, strerror (errno));
587 /* rename mtemp to mtab */
588 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
590 nfs_error(_("%s: can't rename %s to %s: %s\n"),
591 progname, MOUNTED_TEMP, MOUNTED,