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
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"
30 /* Information about mtab. ------------------------------------*/
31 static int have_mtab_info = 0;
32 static int var_mtab_does_not_exist = 0;
33 static int var_mtab_is_a_symlink = 0;
37 struct stat mtab_stat;
39 if (!have_mtab_info) {
40 if (lstat(MOUNTED, &mtab_stat))
41 var_mtab_does_not_exist = 1;
42 else if (S_ISLNK(mtab_stat.st_mode))
43 var_mtab_is_a_symlink = 1;
49 mtab_does_not_exist(void) {
51 return var_mtab_does_not_exist;
55 mtab_is_a_symlink(void) {
57 return var_mtab_is_a_symlink;
64 /* Should we write to /etc/mtab upon an update?
65 Probably not if it is a symlink to /proc/mounts, since that
66 would create a file /proc/mounts in case the proc filesystem
68 if (mtab_is_a_symlink())
71 fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
79 /* Contents of mtab and fstab ---------------------------------*/
81 struct mntentchn mounttable;
82 static int got_mtab = 0;
83 struct mntentchn fstab;
84 static int got_fstab = 0;
86 static void read_mounttable(void);
87 static void read_fstab(void);
89 static struct mntentchn *
96 static struct mntentchn *
106 my_free(const void *s) {
112 discard_mntentchn(struct mntentchn *mc0) {
113 struct mntentchn *mc, *mc1;
115 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
117 my_free(mc->m.mnt_fsname);
118 my_free(mc->m.mnt_dir);
119 my_free(mc->m.mnt_type);
120 my_free(mc->m.mnt_opts);
127 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
128 struct mntentchn *mc = mc0;
131 while ((mnt = nfs_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 nfs_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;
165 mfp = nfs_setmntent (fnam, "r");
166 if (mfp == NULL || mfp->mntent_fp == NULL) {
169 mfp = nfs_setmntent (fnam, "r");
170 if (mfp == NULL || mfp->mntent_fp == NULL) {
171 nfs_error(_("warning: can't open %s: %s"),
172 MOUNTED, strerror (errsv));
176 printf (_("mount: could not open %s - "
177 "using %s instead\n"),
178 MOUNTED, PROC_MOUNTS);
180 read_mntentchn(mfp, fnam, mc);
188 struct mntentchn *mc = &fstab;
191 mc->nxt = mc->prev = NULL;
194 mfp = nfs_setmntent (fnam, "r");
195 if (mfp == NULL || mfp->mntent_fp == NULL) {
197 nfs_error(_("warning: can't open %s: %s"),
198 _PATH_FSTAB, strerror (errsv));
201 read_mntentchn(mfp, fnam, mc);
205 * Given the directory name NAME, and the place MCPREV we found it last time,
206 * try to find more occurrences.
209 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
210 struct mntentchn *mc, *mc0;
215 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
216 if (streq(mc->m.mnt_dir, name))
222 * Given the device name NAME, and the place MCPREV we found it last time,
223 * try to find more occurrences.
226 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
227 struct mntentchn *mc, *mc0;
232 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
233 if (streq(mc->m.mnt_fsname, name))
238 /* Find the dir FILE in fstab. */
240 getfsfile (const char *file)
242 struct mntentchn *mc, *mc0;
245 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
246 if (streq(mc->m.mnt_dir, file))
251 /* Find the device SPEC in fstab. */
253 getfsspec (const char *spec)
255 struct mntentchn *mc, *mc0;
258 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
259 if (streq(mc->m.mnt_fsname, spec))
264 /* Updating mtab ----------------------------------------------*/
266 /* Flag for already existing lock file. */
267 static int we_created_lockfile = 0;
268 static int lockfile_fd = -1;
270 /* Flag to indicate that signals have been set up. */
271 static int signals_have_been_setup = 0;
273 /* Ensure that the lock is released if we are interrupted. */
274 extern char *strsignal(int sig); /* not always in <string.h> */
278 die(EX_USER, "%s", strsignal(sig));
282 setlkw_timeout (int sig) {
283 /* nothing, fcntl will fail anyway */
286 /* Remove lock file. */
289 if (we_created_lockfile) {
292 unlink (MOUNTED_LOCK);
293 we_created_lockfile = 0;
297 /* Create the lock file.
298 The lock file will be removed if we catch a signal or when we exit. */
299 /* The old code here used flock on a lock file /etc/mtab~ and deleted
300 this lock file afterwards. However, as rgooch remarks, that has a
301 race: a second mount may be waiting on the lock and proceed as
302 soon as the lock file is deleted by the first mount, and immediately
303 afterwards a third mount comes, creates a new /etc/mtab~, applies
304 flock to that, and also proceeds, so that the second and third mount
305 now both are scribbling in /etc/mtab.
306 The new code uses a link() instead of a creat(), where we proceed
307 only if it was us that created the lock, and hence we always have
308 to delete the lock afterwards. Now the use of flock() is in principle
309 superfluous, but avoids an arbitrary sleep(). */
311 /* Where does the link point to? Obvious choices are mtab and mtab~~.
312 HJLu points out that the latter leads to races. Right now we use
313 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
314 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
315 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
319 int tries = 100000, i;
320 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
322 at_die = unlock_mtab;
324 if (!signals_have_been_setup) {
328 sa.sa_handler = handler;
330 sigfillset (&sa.sa_mask);
332 while (sigismember (&sa.sa_mask, ++sig) != -1
335 sa.sa_handler = setlkw_timeout;
337 sa.sa_handler = handler;
338 sigaction (sig, &sa, (struct sigaction *) 0);
340 signals_have_been_setup = 1;
343 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
345 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
348 /* linktargetfile does not exist (as a file)
349 and we cannot create it. Read-only filesystem?
350 Too many files open in the system?
352 die (EX_FILEIO, _("can't create lock file %s: %s "
353 "(use -n flag to override)"),
354 linktargetfile, strerror (errsv));
358 /* Repeat until it was us who made the link */
359 while (!we_created_lockfile) {
363 j = link(linktargetfile, MOUNTED_LOCK);
367 we_created_lockfile = 1;
369 if (j < 0 && errsv != EEXIST) {
370 (void) unlink(linktargetfile);
371 die (EX_FILEIO, _("can't link lock file %s: %s "
372 "(use -n flag to override)"),
373 MOUNTED_LOCK, strerror (errsv));
376 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
378 if (lockfile_fd < 0) {
380 /* Strange... Maybe the file was just deleted? */
381 if (errno == ENOENT && tries-- > 0) {
382 if (tries % 200 == 0)
386 (void) unlink(linktargetfile);
387 die (EX_FILEIO, _("can't open lock file %s: %s "
388 "(use -n flag to override)"),
389 MOUNTED_LOCK, strerror (errsv));
392 flock.l_type = F_WRLCK;
393 flock.l_whence = SEEK_SET;
398 /* We made the link. Now claim the lock. */
399 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
402 printf(_("Can't lock lock file %s: %s\n"),
403 MOUNTED_LOCK, strerror (errsv));
407 (void) unlink(linktargetfile);
409 static int tries = 0;
411 /* Someone else made the link. Wait. */
413 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
415 (void) unlink(linktargetfile);
416 die (EX_FILEIO, _("can't lock lock file %s: %s"),
417 MOUNTED_LOCK, (errno == EINTR) ?
418 _("timed out") : strerror (errsv));
421 /* Limit the number of iterations - maybe there
422 still is some old /etc/mtab~ */
424 if (tries % 200 == 0)
426 if (tries > 100000) {
427 (void) unlink(linktargetfile);
429 die (EX_FILEIO, _("Cannot create link %s\n"
430 "Perhaps there is a stale lock file?\n"),
440 * Used by umount with null INSTEAD: remove the last DIR entry.
441 * Used by mount upon a remount: update option part,
442 * and complain if a wrong device or type was given.
443 * [Note that often a remount will be a rw remount of /
444 * where there was no entry before, and we'll have to believe
445 * the values given in INSTEAD.]
449 update_mtab (const char *dir, struct mntent *instead)
451 mntFILE *mfp, *mftmp;
452 const char *fnam = MOUNTED;
453 struct mntentchn mtabhead; /* dummy */
454 struct mntentchn *mc, *mc0, *absent = NULL;
456 if (mtab_does_not_exist() || !mtab_is_writable())
461 /* having locked mtab, read it again */
462 mc0 = mc = &mtabhead;
463 mc->nxt = mc->prev = NULL;
465 mfp = nfs_setmntent(fnam, "r");
466 if (mfp == NULL || mfp->mntent_fp == NULL) {
468 nfs_error (_("cannot open %s (%s) - mtab not updated"),
469 fnam, strerror (errsv));
473 read_mntentchn(mfp, fnam, mc);
475 /* find last occurrence of dir */
476 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
477 if (streq(mc->m.mnt_dir, dir))
479 if (mc && mc != mc0) {
480 if (instead == NULL) {
481 /* An umount - remove entry */
482 if (mc && mc != mc0) {
483 mc->prev->nxt = mc->nxt;
484 mc->nxt->prev = mc->prev;
489 mc->m.mnt_opts = instead->mnt_opts;
491 } else if (instead) {
492 /* not found, add a new entry */
493 absent = xmalloc(sizeof(*absent));
494 absent->m = *instead;
496 absent->prev = mc0->prev;
498 if (mc0->nxt == NULL)
502 /* write chain to mtemp */
503 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
504 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
506 nfs_error (_("cannot open %s (%s) - mtab not updated"),
507 MOUNTED_TEMP, strerror (errsv));
511 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
512 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
514 die (EX_FILEIO, _("error writing %s: %s"),
515 MOUNTED_TEMP, strerror (errsv));
520 /* the chain might have strings copied from 'instead',
521 * so we cannot safely free it.
522 * And there is no need anyway because we are going to exit
523 * shortly. So just don't call discard_mntentchn....
525 discard_mntentchn(mc0);
527 if (fchmod (fileno (mftmp->mntent_fp),
528 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
530 fprintf(stderr, _("error changing mode of %s: %s\n"),
531 MOUNTED_TEMP, strerror (errsv));
533 nfs_endmntent (mftmp);
536 * If mount is setuid and some non-root user mounts sth,
537 * then mtab.tmp might get the group of this user. Copy uid/gid
538 * from the present mtab before renaming.
541 if (stat (MOUNTED, &sbuf) == 0)
542 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
545 /* rename mtemp to mtab */
546 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
548 fprintf(stderr, _("can't rename %s to %s: %s\n"),
549 MOUNTED_TEMP, MOUNTED, strerror(errsv));