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 *
97 static struct mntentchn *
107 my_free(const void *s) {
113 discard_mntentchn(struct mntentchn *mc0) {
114 struct mntentchn *mc, *mc1;
116 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
118 my_free(mc->m.mnt_fsname);
119 my_free(mc->m.mnt_dir);
120 my_free(mc->m.mnt_type);
121 my_free(mc->m.mnt_opts);
128 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
129 struct mntentchn *mc = mc0;
132 while ((mnt = nfs_getmntent(mfp)) != NULL) {
133 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
134 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
142 if (ferror(mfp->mntent_fp)) {
144 nfs_error(_("warning: error reading %s: %s"),
145 fnam, strerror (errsv));
146 mc0->nxt = mc0->prev = NULL;
152 * Read /etc/mtab. If that fails, try /proc/mounts.
153 * This produces a linked list. The list head mounttable is a dummy.
154 * Return 0 on success.
160 struct mntentchn *mc = &mounttable;
163 mc->nxt = mc->prev = NULL;
166 mfp = nfs_setmntent (fnam, "r");
167 if (mfp == NULL || mfp->mntent_fp == NULL) {
170 mfp = nfs_setmntent (fnam, "r");
171 if (mfp == NULL || mfp->mntent_fp == NULL) {
172 nfs_error(_("warning: can't open %s: %s"),
173 MOUNTED, strerror (errsv));
177 printf (_("mount: could not open %s - "
178 "using %s instead\n"),
179 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 printf(_("Can't lock lock file %s: %s\n"),
404 MOUNTED_LOCK, strerror (errsv));
408 (void) unlink(linktargetfile);
410 static int tries = 0;
412 /* Someone else made the link. Wait. */
414 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
416 (void) unlink(linktargetfile);
417 die (EX_FILEIO, _("can't lock lock file %s: %s"),
418 MOUNTED_LOCK, (errno == EINTR) ?
419 _("timed out") : strerror (errsv));
422 /* Limit the number of iterations - maybe there
423 still is some old /etc/mtab~ */
425 if (tries % 200 == 0)
427 if (tries > 100000) {
428 (void) unlink(linktargetfile);
430 die (EX_FILEIO, _("Cannot create link %s\n"
431 "Perhaps there is a stale lock file?\n"),
441 * Used by umount with null INSTEAD: remove the last DIR entry.
442 * Used by mount upon a remount: update option part,
443 * and complain if a wrong device or type was given.
444 * [Note that often a remount will be a rw remount of /
445 * where there was no entry before, and we'll have to believe
446 * the values given in INSTEAD.]
450 update_mtab (const char *dir, struct mntent *instead)
452 mntFILE *mfp, *mftmp;
453 const char *fnam = MOUNTED;
454 struct mntentchn mtabhead; /* dummy */
455 struct mntentchn *mc, *mc0, *absent = NULL;
457 if (mtab_does_not_exist() || !mtab_is_writable())
462 /* having locked mtab, read it again */
463 mc0 = mc = &mtabhead;
464 mc->nxt = mc->prev = NULL;
466 mfp = nfs_setmntent(fnam, "r");
467 if (mfp == NULL || mfp->mntent_fp == NULL) {
469 nfs_error (_("cannot open %s (%s) - mtab not updated"),
470 fnam, strerror (errsv));
474 read_mntentchn(mfp, fnam, mc);
476 /* find last occurrence of dir */
477 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
478 if (streq(mc->m.mnt_dir, dir))
480 if (mc && mc != mc0) {
481 if (instead == NULL) {
482 /* An umount - remove entry */
483 if (mc && mc != mc0) {
484 mc->prev->nxt = mc->nxt;
485 mc->nxt->prev = mc->prev;
490 mc->m.mnt_opts = instead->mnt_opts;
492 } else if (instead) {
493 /* not found, add a new entry */
494 absent = xmalloc(sizeof(*absent));
495 absent->m = *instead;
497 absent->prev = mc0->prev;
499 if (mc0->nxt == NULL)
503 /* write chain to mtemp */
504 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
505 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
507 nfs_error (_("cannot open %s (%s) - mtab not updated"),
508 MOUNTED_TEMP, strerror (errsv));
512 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
513 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
515 die (EX_FILEIO, _("error writing %s: %s"),
516 MOUNTED_TEMP, strerror (errsv));
521 /* the chain might have strings copied from 'instead',
522 * so we cannot safely free it.
523 * And there is no need anyway because we are going to exit
524 * shortly. So just don't call discard_mntentchn....
526 discard_mntentchn(mc0);
528 if (fchmod (fileno (mftmp->mntent_fp),
529 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
531 fprintf(stderr, _("error changing mode of %s: %s\n"),
532 MOUNTED_TEMP, strerror (errsv));
534 nfs_endmntent (mftmp);
537 * If mount is setuid and some non-root user mounts sth,
538 * then mtab.tmp might get the group of this user. Copy uid/gid
539 * from the present mtab before renaming.
542 if (stat (MOUNTED, &sbuf) == 0)
543 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
546 /* rename mtemp to mtab */
547 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
549 fprintf(stderr, _("can't rename %s to %s: %s\n"),
550 MOUNTED_TEMP, MOUNTED, strerror(errsv));