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) {
334 sa.sa_handler = handler;
336 sigfillset (&sa.sa_mask);
338 while (sigismember (&sa.sa_mask, ++sig) != -1
341 sa.sa_handler = setlkw_timeout;
343 sa.sa_handler = handler;
344 sigaction (sig, &sa, (struct sigaction *) 0);
346 signals_have_been_setup = 1;
349 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
351 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
354 /* linktargetfile does not exist (as a file)
355 and we cannot create it. Read-only filesystem?
356 Too many files open in the system?
358 die (EX_FILEIO, _("can't create lock file %s: %s "
359 "(use -n flag to override)"),
360 linktargetfile, strerror (errsv));
364 /* Repeat until it was us who made the link */
365 while (!we_created_lockfile) {
369 j = link(linktargetfile, MOUNTED_LOCK);
375 we_created_lockfile = 1;
377 if (j < 0 && errsv != EEXIST) {
378 (void) unlink(linktargetfile);
379 die (EX_FILEIO, _("can't link lock file %s: %s "
380 "(use -n flag to override)"),
381 MOUNTED_LOCK, strerror (errsv));
385 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
387 if (lockfile_fd < 0) {
389 /* Strange... Maybe the file was just deleted? */
390 if (errno == ENOENT && tries-- > 0) {
391 if (tries % 200 == 0)
395 (void) unlink(linktargetfile);
396 die (EX_FILEIO, _("can't open lock file %s: %s "
397 "(use -n flag to override)"),
398 MOUNTED_LOCK, strerror (errsv));
401 flock.l_type = F_WRLCK;
402 flock.l_whence = SEEK_SET;
407 /* We made the link. Now claim the lock. */
408 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
411 nfs_error(_("%s: Can't lock lock file "
418 (void) unlink(linktargetfile);
420 static int retries = 0;
422 /* Someone else made the link. Wait. */
424 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
426 (void) unlink(linktargetfile);
427 die (EX_FILEIO, _("can't lock lock file %s: %s"),
428 MOUNTED_LOCK, (errno == EINTR) ?
429 _("timed out") : strerror (errsv));
432 /* Limit the number of iterations - maybe there
433 still is some old /etc/mtab~ */
435 if (retries % 200 == 0)
437 if (retries > 100000) {
438 (void) unlink(linktargetfile);
440 die (EX_FILEIO, _("Cannot create link %s\n"
441 "Perhaps there is a stale lock file?\n"),
451 * Used by umount with null INSTEAD: remove the last DIR entry.
452 * Used by mount upon a remount: update option part,
453 * and complain if a wrong device or type was given.
454 * [Note that often a remount will be a rw remount of /
455 * where there was no entry before, and we'll have to believe
456 * the values given in INSTEAD.]
460 update_mtab (const char *dir, struct mntent *instead)
462 mntFILE *mfp, *mftmp;
463 const char *fnam = MOUNTED;
464 struct mntentchn mtabhead; /* dummy */
465 struct mntentchn *mc, *mc0, *absent = NULL;
467 if (mtab_does_not_exist() || !mtab_is_writable())
472 /* having locked mtab, read it again */
473 mc0 = mc = &mtabhead;
474 mc->nxt = mc->prev = NULL;
476 mfp = nfs_setmntent(fnam, "r");
477 if (mfp == NULL || mfp->mntent_fp == NULL) {
479 nfs_error (_("cannot open %s (%s) - mtab not updated"),
480 fnam, strerror (errsv));
484 read_mntentchn(mfp, fnam, mc);
486 /* find last occurrence of dir */
487 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
488 if (streq(mc->m.mnt_dir, dir))
490 if (mc && mc != mc0) {
491 if (instead == NULL) {
492 /* An umount - remove entry */
493 if (mc && mc != mc0) {
494 mc->prev->nxt = mc->nxt;
495 mc->nxt->prev = mc->prev;
500 mc->m.mnt_opts = instead->mnt_opts;
502 } else if (instead) {
503 /* not found, add a new entry */
504 absent = xmalloc(sizeof(*absent));
505 absent->m = *instead;
507 absent->prev = mc0->prev;
509 if (mc0->nxt == NULL)
513 /* write chain to mtemp */
514 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
515 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
517 nfs_error (_("cannot open %s (%s) - mtab not updated"),
518 MOUNTED_TEMP, strerror (errsv));
522 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
523 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
525 die (EX_FILEIO, _("error writing %s: %s"),
526 MOUNTED_TEMP, strerror (errsv));
531 /* the chain might have strings copied from 'instead',
532 * so we cannot safely free it.
533 * And there is no need anyway because we are going to exit
534 * shortly. So just don't call discard_mntentchn....
536 discard_mntentchn(mc0);
538 if (fchmod (fileno (mftmp->mntent_fp),
539 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
541 nfs_error(_("%s: error changing mode of %s: %s"),
542 progname, MOUNTED_TEMP, strerror (errsv));
544 nfs_endmntent (mftmp);
547 * If mount is setuid and some non-root user mounts sth,
548 * then mtab.tmp might get the group of this user. Copy uid/gid
549 * from the present mtab before renaming.
552 if (stat (MOUNTED, &sbuf) == 0) {
553 if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
554 nfs_error(_("%s: error changing owner of %s: %s"),
555 progname, MOUNTED_TEMP, strerror (errno));
560 /* rename mtemp to mtab */
561 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
563 nfs_error(_("%s: can't rename %s to %s: %s\n"),
564 progname, MOUNTED_TEMP, MOUNTED,