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"
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 mtab_does_not_exist(void) {
52 return var_mtab_does_not_exist;
56 mtab_is_a_symlink(void) {
58 return var_mtab_is_a_symlink;
65 /* Should we write to /etc/mtab upon an update?
66 Probably not if it is a symlink to /proc/mounts, since that
67 would create a file /proc/mounts in case the proc filesystem
69 if (mtab_is_a_symlink())
72 fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
80 /* Contents of mtab and fstab ---------------------------------*/
82 struct mntentchn mounttable;
83 static int got_mtab = 0;
84 struct mntentchn fstab;
85 static int got_fstab = 0;
87 static void read_mounttable(void);
88 static void read_fstab(void);
90 static struct mntentchn *
98 static struct mntentchn *
108 my_free(const void *s) {
114 discard_mntentchn(struct mntentchn *mc0) {
115 struct mntentchn *mc, *mc1;
117 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
119 my_free(mc->m.mnt_fsname);
120 my_free(mc->m.mnt_dir);
121 my_free(mc->m.mnt_type);
122 my_free(mc->m.mnt_opts);
129 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
130 struct mntentchn *mc = mc0;
133 while ((mnt = nfs_getmntent(mfp)) != NULL) {
134 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
135 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
143 if (ferror(mfp->mntent_fp)) {
145 nfs_error(_("warning: error reading %s: %s"),
146 fnam, strerror (errsv));
147 mc0->nxt = mc0->prev = NULL;
153 * Read /etc/mtab. If that fails, try /proc/mounts.
154 * This produces a linked list. The list head mounttable is a dummy.
155 * Return 0 on success.
161 struct mntentchn *mc = &mounttable;
164 mc->nxt = mc->prev = NULL;
167 mfp = nfs_setmntent (fnam, "r");
168 if (mfp == NULL || mfp->mntent_fp == NULL) {
171 mfp = nfs_setmntent (fnam, "r");
172 if (mfp == NULL || mfp->mntent_fp == NULL) {
173 nfs_error(_("warning: can't open %s: %s"),
174 MOUNTED, strerror (errsv));
178 printf(_("%s: could not open %s; using %s instead\n"),
179 progname, MOUNTED, PROC_MOUNTS);
181 read_mntentchn(mfp, fnam, mc);
189 struct mntentchn *mc = &fstab;
192 mc->nxt = mc->prev = NULL;
195 mfp = nfs_setmntent (fnam, "r");
196 if (mfp == NULL || mfp->mntent_fp == NULL) {
198 nfs_error(_("warning: can't open %s: %s"),
199 _PATH_FSTAB, strerror (errsv));
202 read_mntentchn(mfp, fnam, mc);
206 * Given the directory name NAME, and the place MCPREV we found it last time,
207 * try to find more occurrences.
210 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
211 struct mntentchn *mc, *mc0;
216 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
217 if (streq(mc->m.mnt_dir, name))
223 * Given the device name NAME, and the place MCPREV we found it last time,
224 * try to find more occurrences.
227 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
228 struct mntentchn *mc, *mc0;
233 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
234 if (streq(mc->m.mnt_fsname, name))
239 /* Find the dir FILE in fstab. */
241 getfsfile (const char *file)
243 struct mntentchn *mc, *mc0;
246 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
247 if (streq(mc->m.mnt_dir, file))
252 /* Find the device SPEC in fstab. */
254 getfsspec (const char *spec)
256 struct mntentchn *mc, *mc0;
259 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
260 if (streq(mc->m.mnt_fsname, spec))
265 /* Updating mtab ----------------------------------------------*/
267 /* Flag for already existing lock file. */
268 static int we_created_lockfile = 0;
269 static int lockfile_fd = -1;
271 /* Flag to indicate that signals have been set up. */
272 static int signals_have_been_setup = 0;
274 /* Ensure that the lock is released if we are interrupted. */
275 extern char *strsignal(int sig); /* not always in <string.h> */
279 die(EX_USER, "%s", strsignal(sig));
283 setlkw_timeout (int sig) {
284 /* nothing, fcntl will fail anyway */
287 /* Remove lock file. */
290 if (we_created_lockfile) {
293 unlink (MOUNTED_LOCK);
294 we_created_lockfile = 0;
298 /* Create the lock file.
299 The lock file will be removed if we catch a signal or when we exit. */
300 /* The old code here used flock on a lock file /etc/mtab~ and deleted
301 this lock file afterwards. However, as rgooch remarks, that has a
302 race: a second mount may be waiting on the lock and proceed as
303 soon as the lock file is deleted by the first mount, and immediately
304 afterwards a third mount comes, creates a new /etc/mtab~, applies
305 flock to that, and also proceeds, so that the second and third mount
306 now both are scribbling in /etc/mtab.
307 The new code uses a link() instead of a creat(), where we proceed
308 only if it was us that created the lock, and hence we always have
309 to delete the lock afterwards. Now the use of flock() is in principle
310 superfluous, but avoids an arbitrary sleep(). */
312 /* Where does the link point to? Obvious choices are mtab and mtab~~.
313 HJLu points out that the latter leads to races. Right now we use
314 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
315 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
316 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
320 int tries = 100000, i;
321 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
323 at_die = unlock_mtab;
325 if (!signals_have_been_setup) {
329 sa.sa_handler = handler;
331 sigfillset (&sa.sa_mask);
333 while (sigismember (&sa.sa_mask, ++sig) != -1
336 sa.sa_handler = setlkw_timeout;
338 sa.sa_handler = handler;
339 sigaction (sig, &sa, (struct sigaction *) 0);
341 signals_have_been_setup = 1;
344 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
346 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
349 /* linktargetfile does not exist (as a file)
350 and we cannot create it. Read-only filesystem?
351 Too many files open in the system?
353 die (EX_FILEIO, _("can't create lock file %s: %s "
354 "(use -n flag to override)"),
355 linktargetfile, strerror (errsv));
359 /* Repeat until it was us who made the link */
360 while (!we_created_lockfile) {
364 j = link(linktargetfile, MOUNTED_LOCK);
368 we_created_lockfile = 1;
370 if (j < 0 && errsv != EEXIST) {
371 (void) unlink(linktargetfile);
372 die (EX_FILEIO, _("can't link lock file %s: %s "
373 "(use -n flag to override)"),
374 MOUNTED_LOCK, strerror (errsv));
377 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
379 if (lockfile_fd < 0) {
381 /* Strange... Maybe the file was just deleted? */
382 if (errno == ENOENT && tries-- > 0) {
383 if (tries % 200 == 0)
387 (void) unlink(linktargetfile);
388 die (EX_FILEIO, _("can't open lock file %s: %s "
389 "(use -n flag to override)"),
390 MOUNTED_LOCK, strerror (errsv));
393 flock.l_type = F_WRLCK;
394 flock.l_whence = SEEK_SET;
399 /* We made the link. Now claim the lock. */
400 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
403 nfs_error(_("%s: Can't lock lock file "
410 (void) unlink(linktargetfile);
412 static int tries = 0;
414 /* Someone else made the link. Wait. */
416 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
418 (void) unlink(linktargetfile);
419 die (EX_FILEIO, _("can't lock lock file %s: %s"),
420 MOUNTED_LOCK, (errno == EINTR) ?
421 _("timed out") : strerror (errsv));
424 /* Limit the number of iterations - maybe there
425 still is some old /etc/mtab~ */
427 if (tries % 200 == 0)
429 if (tries > 100000) {
430 (void) unlink(linktargetfile);
432 die (EX_FILEIO, _("Cannot create link %s\n"
433 "Perhaps there is a stale lock file?\n"),
443 * Used by umount with null INSTEAD: remove the last DIR entry.
444 * Used by mount upon a remount: update option part,
445 * and complain if a wrong device or type was given.
446 * [Note that often a remount will be a rw remount of /
447 * where there was no entry before, and we'll have to believe
448 * the values given in INSTEAD.]
452 update_mtab (const char *dir, struct mntent *instead)
454 mntFILE *mfp, *mftmp;
455 const char *fnam = MOUNTED;
456 struct mntentchn mtabhead; /* dummy */
457 struct mntentchn *mc, *mc0, *absent = NULL;
459 if (mtab_does_not_exist() || !mtab_is_writable())
464 /* having locked mtab, read it again */
465 mc0 = mc = &mtabhead;
466 mc->nxt = mc->prev = NULL;
468 mfp = nfs_setmntent(fnam, "r");
469 if (mfp == NULL || mfp->mntent_fp == NULL) {
471 nfs_error (_("cannot open %s (%s) - mtab not updated"),
472 fnam, strerror (errsv));
476 read_mntentchn(mfp, fnam, mc);
478 /* find last occurrence of dir */
479 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
480 if (streq(mc->m.mnt_dir, dir))
482 if (mc && mc != mc0) {
483 if (instead == NULL) {
484 /* An umount - remove entry */
485 if (mc && mc != mc0) {
486 mc->prev->nxt = mc->nxt;
487 mc->nxt->prev = mc->prev;
492 mc->m.mnt_opts = instead->mnt_opts;
494 } else if (instead) {
495 /* not found, add a new entry */
496 absent = xmalloc(sizeof(*absent));
497 absent->m = *instead;
499 absent->prev = mc0->prev;
501 if (mc0->nxt == NULL)
505 /* write chain to mtemp */
506 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
507 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
509 nfs_error (_("cannot open %s (%s) - mtab not updated"),
510 MOUNTED_TEMP, strerror (errsv));
514 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
515 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
517 die (EX_FILEIO, _("error writing %s: %s"),
518 MOUNTED_TEMP, strerror (errsv));
523 /* the chain might have strings copied from 'instead',
524 * so we cannot safely free it.
525 * And there is no need anyway because we are going to exit
526 * shortly. So just don't call discard_mntentchn....
528 discard_mntentchn(mc0);
530 if (fchmod (fileno (mftmp->mntent_fp),
531 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
533 nfs_error(_("%s: error changing mode of %s: %s"),
534 progname, MOUNTED_TEMP, strerror (errsv));
536 nfs_endmntent (mftmp);
539 * If mount is setuid and some non-root user mounts sth,
540 * then mtab.tmp might get the group of this user. Copy uid/gid
541 * from the present mtab before renaming.
544 if (stat (MOUNTED, &sbuf) == 0)
545 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
548 /* rename mtemp to mtab */
549 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
551 nfs_error(_("%s: can't rename %s to %s: %s\n"),
552 progname, MOUNTED_TEMP, MOUNTED,