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 *
105 my_free(const void *s) {
111 discard_mntentchn(struct mntentchn *mc0) {
112 struct mntentchn *mc, *mc1;
114 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
116 my_free(mc->m.mnt_fsname);
117 my_free(mc->m.mnt_dir);
118 my_free(mc->m.mnt_type);
119 my_free(mc->m.mnt_opts);
125 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
126 struct mntentchn *mc = mc0;
129 while ((mnt = nfs_getmntent(mfp)) != NULL) {
130 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
131 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
139 if (ferror(mfp->mntent_fp)) {
141 nfs_error(_("warning: error reading %s: %s"),
142 fnam, strerror (errsv));
143 mc0->nxt = mc0->prev = NULL;
149 * Read /etc/mtab. If that fails, try /proc/mounts.
150 * This produces a linked list. The list head mounttable is a dummy.
151 * Return 0 on success.
157 struct mntentchn *mc = &mounttable;
160 mc->nxt = mc->prev = NULL;
163 mfp = nfs_setmntent (fnam, "r");
164 if (mfp == NULL || mfp->mntent_fp == NULL) {
167 mfp = nfs_setmntent (fnam, "r");
168 if (mfp == NULL || mfp->mntent_fp == NULL) {
169 nfs_error(_("warning: can't open %s: %s"),
170 MOUNTED, strerror (errsv));
174 printf (_("mount: could not open %s - "
175 "using %s instead\n"),
176 MOUNTED, PROC_MOUNTS);
178 read_mntentchn(mfp, fnam, mc);
186 struct mntentchn *mc = &fstab;
189 mc->nxt = mc->prev = NULL;
192 mfp = nfs_setmntent (fnam, "r");
193 if (mfp == NULL || mfp->mntent_fp == NULL) {
195 nfs_error(_("warning: can't open %s: %s"),
196 _PATH_FSTAB, strerror (errsv));
199 read_mntentchn(mfp, fnam, mc);
203 * Given the directory name NAME, and the place MCPREV we found it last time,
204 * try to find more occurrences.
207 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
208 struct mntentchn *mc, *mc0;
213 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
214 if (streq(mc->m.mnt_dir, name))
220 * Given the device name NAME, and the place MCPREV we found it last time,
221 * try to find more occurrences.
224 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
225 struct mntentchn *mc, *mc0;
230 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
231 if (streq(mc->m.mnt_fsname, name))
236 /* Find the dir FILE in fstab. */
238 getfsfile (const char *file)
240 struct mntentchn *mc, *mc0;
243 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
244 if (streq(mc->m.mnt_dir, file))
249 /* Find the device SPEC in fstab. */
251 getfsspec (const char *spec)
253 struct mntentchn *mc, *mc0;
256 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
257 if (streq(mc->m.mnt_fsname, spec))
262 /* Updating mtab ----------------------------------------------*/
264 /* Flag for already existing lock file. */
265 static int we_created_lockfile = 0;
266 static int lockfile_fd = -1;
268 /* Flag to indicate that signals have been set up. */
269 static int signals_have_been_setup = 0;
271 /* Ensure that the lock is released if we are interrupted. */
272 extern char *strsignal(int sig); /* not always in <string.h> */
276 die(EX_USER, "%s", strsignal(sig));
280 setlkw_timeout (int sig) {
281 /* nothing, fcntl will fail anyway */
284 /* Remove lock file. */
287 if (we_created_lockfile) {
290 unlink (MOUNTED_LOCK);
291 we_created_lockfile = 0;
295 /* Create the lock file.
296 The lock file will be removed if we catch a signal or when we exit. */
297 /* The old code here used flock on a lock file /etc/mtab~ and deleted
298 this lock file afterwards. However, as rgooch remarks, that has a
299 race: a second mount may be waiting on the lock and proceed as
300 soon as the lock file is deleted by the first mount, and immediately
301 afterwards a third mount comes, creates a new /etc/mtab~, applies
302 flock to that, and also proceeds, so that the second and third mount
303 now both are scribbling in /etc/mtab.
304 The new code uses a link() instead of a creat(), where we proceed
305 only if it was us that created the lock, and hence we always have
306 to delete the lock afterwards. Now the use of flock() is in principle
307 superfluous, but avoids an arbitrary sleep(). */
309 /* Where does the link point to? Obvious choices are mtab and mtab~~.
310 HJLu points out that the latter leads to races. Right now we use
311 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
312 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
313 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
317 int tries = 100000, i;
318 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
320 at_die = unlock_mtab;
322 if (!signals_have_been_setup) {
326 sa.sa_handler = handler;
328 sigfillset (&sa.sa_mask);
330 while (sigismember (&sa.sa_mask, ++sig) != -1
333 sa.sa_handler = setlkw_timeout;
335 sa.sa_handler = handler;
336 sigaction (sig, &sa, (struct sigaction *) 0);
338 signals_have_been_setup = 1;
341 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
343 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
346 /* linktargetfile does not exist (as a file)
347 and we cannot create it. Read-only filesystem?
348 Too many files open in the system?
350 die (EX_FILEIO, _("can't create lock file %s: %s "
351 "(use -n flag to override)"),
352 linktargetfile, strerror (errsv));
356 /* Repeat until it was us who made the link */
357 while (!we_created_lockfile) {
361 j = link(linktargetfile, MOUNTED_LOCK);
365 we_created_lockfile = 1;
367 if (j < 0 && errsv != EEXIST) {
368 (void) unlink(linktargetfile);
369 die (EX_FILEIO, _("can't link lock file %s: %s "
370 "(use -n flag to override)"),
371 MOUNTED_LOCK, strerror (errsv));
374 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
376 if (lockfile_fd < 0) {
378 /* Strange... Maybe the file was just deleted? */
379 if (errno == ENOENT && tries-- > 0) {
380 if (tries % 200 == 0)
384 (void) unlink(linktargetfile);
385 die (EX_FILEIO, _("can't open lock file %s: %s "
386 "(use -n flag to override)"),
387 MOUNTED_LOCK, strerror (errsv));
390 flock.l_type = F_WRLCK;
391 flock.l_whence = SEEK_SET;
396 /* We made the link. Now claim the lock. */
397 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
400 printf(_("Can't lock lock file %s: %s\n"),
401 MOUNTED_LOCK, strerror (errsv));
405 (void) unlink(linktargetfile);
407 static int tries = 0;
409 /* Someone else made the link. Wait. */
411 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
413 (void) unlink(linktargetfile);
414 die (EX_FILEIO, _("can't lock lock file %s: %s"),
415 MOUNTED_LOCK, (errno == EINTR) ?
416 _("timed out") : strerror (errsv));
419 /* Limit the number of iterations - maybe there
420 still is some old /etc/mtab~ */
422 if (tries % 200 == 0)
424 if (tries > 100000) {
425 (void) unlink(linktargetfile);
427 die (EX_FILEIO, _("Cannot create link %s\n"
428 "Perhaps there is a stale lock file?\n"),
438 * Used by umount with null INSTEAD: remove the last DIR entry.
439 * Used by mount upon a remount: update option part,
440 * and complain if a wrong device or type was given.
441 * [Note that often a remount will be a rw remount of /
442 * where there was no entry before, and we'll have to believe
443 * the values given in INSTEAD.]
447 update_mtab (const char *dir, struct mntent *instead) {
448 mntFILE *mfp, *mftmp;
449 const char *fnam = MOUNTED;
450 struct mntentchn mtabhead; /* dummy */
451 struct mntentchn *mc, *mc0, *absent = NULL;
453 if (mtab_does_not_exist() || !mtab_is_writable())
458 /* having locked mtab, read it again */
459 mc0 = mc = &mtabhead;
460 mc->nxt = mc->prev = NULL;
462 mfp = nfs_setmntent(fnam, "r");
463 if (mfp == NULL || mfp->mntent_fp == NULL) {
465 nfs_error (_("cannot open %s (%s) - mtab not updated"),
466 fnam, strerror (errsv));
470 read_mntentchn(mfp, fnam, mc);
472 /* find last occurrence of dir */
473 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
474 if (streq(mc->m.mnt_dir, dir))
476 if (mc && mc != mc0) {
477 if (instead == NULL) {
478 /* An umount - remove entry */
479 if (mc && mc != mc0) {
480 mc->prev->nxt = mc->nxt;
481 mc->nxt->prev = mc->prev;
486 mc->m.mnt_opts = instead->mnt_opts;
488 } else if (instead) {
489 /* not found, add a new entry */
490 absent = xmalloc(sizeof(*absent));
491 absent->m = *instead;
493 absent->prev = mc0->prev;
495 if (mc0->nxt == NULL)
499 /* write chain to mtemp */
500 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
501 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
503 nfs_error (_("cannot open %s (%s) - mtab not updated"),
504 MOUNTED_TEMP, strerror (errsv));
508 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
509 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
511 die (EX_FILEIO, _("error writing %s: %s"),
512 MOUNTED_TEMP, strerror (errsv));
516 discard_mntentchn(mc0);
518 if (fchmod (fileno (mftmp->mntent_fp),
519 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
521 fprintf(stderr, _("error changing mode of %s: %s\n"),
522 MOUNTED_TEMP, strerror (errsv));
524 nfs_endmntent (mftmp);
527 * If mount is setuid and some non-root user mounts sth,
528 * then mtab.tmp might get the group of this user. Copy uid/gid
529 * from the present mtab before renaming.
532 if (stat (MOUNTED, &sbuf) == 0)
533 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
536 /* rename mtemp to mtab */
537 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
539 fprintf(stderr, _("can't rename %s to %s: %s\n"),
540 MOUNTED_TEMP, MOUNTED, strerror(errsv));