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;
84 static void read_mounttable(void);
94 my_free(const void *s) {
100 discard_mntentchn(struct mntentchn *mc0) {
101 struct mntentchn *mc, *mc1;
103 for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
105 my_free(mc->m.mnt_fsname);
106 my_free(mc->m.mnt_dir);
107 my_free(mc->m.mnt_type);
108 my_free(mc->m.mnt_opts);
114 read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
115 struct mntentchn *mc = mc0;
118 while ((mnt = nfs_getmntent(mfp)) != NULL) {
119 if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
120 mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
128 if (ferror(mfp->mntent_fp)) {
130 nfs_error(_("warning: error reading %s: %s"),
131 fnam, strerror (errsv));
132 mc0->nxt = mc0->prev = NULL;
138 * Read /etc/mtab. If that fails, try /proc/mounts.
139 * This produces a linked list. The list head mounttable is a dummy.
140 * Return 0 on success.
146 struct mntentchn *mc = &mounttable;
149 mc->nxt = mc->prev = NULL;
152 mfp = nfs_setmntent (fnam, "r");
153 if (mfp == NULL || mfp->mntent_fp == NULL) {
156 mfp = nfs_setmntent (fnam, "r");
157 if (mfp == NULL || mfp->mntent_fp == NULL) {
158 nfs_error(_("warning: can't open %s: %s"),
159 MOUNTED, strerror (errsv));
163 printf (_("mount: could not open %s - "
164 "using %s instead\n"),
165 MOUNTED, PROC_MOUNTS);
167 read_mntentchn(mfp, fnam, mc);
171 * Given the directory name NAME, and the place MCPREV we found it last time,
172 * try to find more occurrences.
175 getmntdirbackward (const char *name, struct mntentchn *mcprev) {
176 struct mntentchn *mc, *mc0;
181 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
182 if (streq(mc->m.mnt_dir, name))
188 * Given the device name NAME, and the place MCPREV we found it last time,
189 * try to find more occurrences.
192 getmntdevbackward (const char *name, struct mntentchn *mcprev) {
193 struct mntentchn *mc, *mc0;
198 for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
199 if (streq(mc->m.mnt_fsname, name))
204 /* Updating mtab ----------------------------------------------*/
206 /* Flag for already existing lock file. */
207 static int we_created_lockfile = 0;
208 static int lockfile_fd = -1;
210 /* Flag to indicate that signals have been set up. */
211 static int signals_have_been_setup = 0;
213 /* Ensure that the lock is released if we are interrupted. */
214 extern char *strsignal(int sig); /* not always in <string.h> */
218 die(EX_USER, "%s", strsignal(sig));
222 setlkw_timeout (int sig) {
223 /* nothing, fcntl will fail anyway */
226 /* Remove lock file. */
229 if (we_created_lockfile) {
232 unlink (MOUNTED_LOCK);
233 we_created_lockfile = 0;
237 /* Create the lock file.
238 The lock file will be removed if we catch a signal or when we exit. */
239 /* The old code here used flock on a lock file /etc/mtab~ and deleted
240 this lock file afterwards. However, as rgooch remarks, that has a
241 race: a second mount may be waiting on the lock and proceed as
242 soon as the lock file is deleted by the first mount, and immediately
243 afterwards a third mount comes, creates a new /etc/mtab~, applies
244 flock to that, and also proceeds, so that the second and third mount
245 now both are scribbling in /etc/mtab.
246 The new code uses a link() instead of a creat(), where we proceed
247 only if it was us that created the lock, and hence we always have
248 to delete the lock afterwards. Now the use of flock() is in principle
249 superfluous, but avoids an arbitrary sleep(). */
251 /* Where does the link point to? Obvious choices are mtab and mtab~~.
252 HJLu points out that the latter leads to races. Right now we use
253 mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
254 #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
255 #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
259 int tries = 100000, i;
260 char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
262 at_die = unlock_mtab;
264 if (!signals_have_been_setup) {
268 sa.sa_handler = handler;
270 sigfillset (&sa.sa_mask);
272 while (sigismember (&sa.sa_mask, ++sig) != -1
275 sa.sa_handler = setlkw_timeout;
277 sa.sa_handler = handler;
278 sigaction (sig, &sa, (struct sigaction *) 0);
280 signals_have_been_setup = 1;
283 sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
285 i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
288 /* linktargetfile does not exist (as a file)
289 and we cannot create it. Read-only filesystem?
290 Too many files open in the system?
292 die (EX_FILEIO, _("can't create lock file %s: %s "
293 "(use -n flag to override)"),
294 linktargetfile, strerror (errsv));
298 /* Repeat until it was us who made the link */
299 while (!we_created_lockfile) {
303 j = link(linktargetfile, MOUNTED_LOCK);
307 we_created_lockfile = 1;
309 if (j < 0 && errsv != EEXIST) {
310 (void) unlink(linktargetfile);
311 die (EX_FILEIO, _("can't link lock file %s: %s "
312 "(use -n flag to override)"),
313 MOUNTED_LOCK, strerror (errsv));
316 lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
318 if (lockfile_fd < 0) {
320 /* Strange... Maybe the file was just deleted? */
321 if (errno == ENOENT && tries-- > 0) {
322 if (tries % 200 == 0)
326 (void) unlink(linktargetfile);
327 die (EX_FILEIO, _("can't open lock file %s: %s "
328 "(use -n flag to override)"),
329 MOUNTED_LOCK, strerror (errsv));
332 flock.l_type = F_WRLCK;
333 flock.l_whence = SEEK_SET;
338 /* We made the link. Now claim the lock. */
339 if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
342 printf(_("Can't lock lock file %s: %s\n"),
343 MOUNTED_LOCK, strerror (errsv));
347 (void) unlink(linktargetfile);
349 static int tries = 0;
351 /* Someone else made the link. Wait. */
353 if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
355 (void) unlink(linktargetfile);
356 die (EX_FILEIO, _("can't lock lock file %s: %s"),
357 MOUNTED_LOCK, (errno == EINTR) ?
358 _("timed out") : strerror (errsv));
361 /* Limit the number of iterations - maybe there
362 still is some old /etc/mtab~ */
364 if (tries % 200 == 0)
366 if (tries > 100000) {
367 (void) unlink(linktargetfile);
369 die (EX_FILEIO, _("Cannot create link %s\n"
370 "Perhaps there is a stale lock file?\n"),
380 * Used by umount with null INSTEAD: remove the last DIR entry.
381 * Used by mount upon a remount: update option part,
382 * and complain if a wrong device or type was given.
383 * [Note that often a remount will be a rw remount of /
384 * where there was no entry before, and we'll have to believe
385 * the values given in INSTEAD.]
389 update_mtab (const char *dir, nfs_mntent_t *instead) {
390 mntFILE *mfp, *mftmp;
391 const char *fnam = MOUNTED;
392 struct mntentchn mtabhead; /* dummy */
393 struct mntentchn *mc, *mc0, *absent = NULL;
395 if (mtab_does_not_exist() || !mtab_is_writable())
400 /* having locked mtab, read it again */
401 mc0 = mc = &mtabhead;
402 mc->nxt = mc->prev = NULL;
404 mfp = nfs_setmntent(fnam, "r");
405 if (mfp == NULL || mfp->mntent_fp == NULL) {
407 nfs_error (_("cannot open %s (%s) - mtab not updated"),
408 fnam, strerror (errsv));
412 read_mntentchn(mfp, fnam, mc);
414 /* find last occurrence of dir */
415 for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
416 if (streq(mc->m.mnt_dir, dir))
418 if (mc && mc != mc0) {
419 if (instead == NULL) {
420 /* An umount - remove entry */
421 if (mc && mc != mc0) {
422 mc->prev->nxt = mc->nxt;
423 mc->nxt->prev = mc->prev;
428 mc->m.mnt_opts = instead->mnt_opts;
430 } else if (instead) {
431 /* not found, add a new entry */
432 absent = xmalloc(sizeof(*absent));
433 absent->m = *instead;
435 absent->prev = mc0->prev;
437 if (mc0->nxt == NULL)
441 /* write chain to mtemp */
442 mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
443 if (mftmp == NULL || mftmp->mntent_fp == NULL) {
445 nfs_error (_("cannot open %s (%s) - mtab not updated"),
446 MOUNTED_TEMP, strerror (errsv));
450 for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
451 if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
453 die (EX_FILEIO, _("error writing %s: %s"),
454 MOUNTED_TEMP, strerror (errsv));
458 discard_mntentchn(mc0);
460 if (fchmod (fileno (mftmp->mntent_fp),
461 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
463 fprintf(stderr, _("error changing mode of %s: %s\n"),
464 MOUNTED_TEMP, strerror (errsv));
466 nfs_endmntent (mftmp);
469 * If mount is setuid and some non-root user mounts sth,
470 * then mtab.tmp might get the group of this user. Copy uid/gid
471 * from the present mtab before renaming.
474 if (stat (MOUNTED, &sbuf) == 0)
475 chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid);
478 /* rename mtemp to mtab */
479 if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
481 fprintf(stderr, _("can't rename %s to %s: %s\n"),
482 MOUNTED_TEMP, MOUNTED, strerror(errsv));