X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=support%2Fnfs%2Ffstab.c;fp=support%2Fnfs%2Ffstab.c;h=8dc7d66315376c4e51abbeeabfdb63a431ae7874;hb=4e2bae795e5eaf9922f0b966ab5df64994c836a2;hp=0000000000000000000000000000000000000000;hpb=a0520fa1a41bd33815b331b660b4545f2723495c;p=nfs-utils.git diff --git a/support/nfs/fstab.c b/support/nfs/fstab.c new file mode 100644 index 0000000..8dc7d66 --- /dev/null +++ b/support/nfs/fstab.c @@ -0,0 +1,487 @@ +/* 1999-02-22 Arkadiusz Mi¶kiewicz + * - added Native Language Support + * Sun Mar 21 1999 - Arnaldo Carvalho de Melo + * - fixed strerr(errno) in gettext calls + * + * 2006-06-08 Amit Gud + * - Moved code to nfs-utils/support/nfs from util-linux/mount. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "fstab.h" +#include "xcommon.h" +#include "nfs_mntent.h" +#include "nfs_paths.h" +#include "nls.h" + +#define LOCK_TIMEOUT 10 +#define streq(s, t) (strcmp ((s), (t)) == 0) +#define PROC_MOUNTS "/proc/mounts" + +extern int verbose; + +/* Information about mtab. ------------------------------------*/ +static int have_mtab_info = 0; +static int var_mtab_does_not_exist = 0; +static int var_mtab_is_a_symlink = 0; + +static void +get_mtab_info(void) { + struct stat mtab_stat; + + if (!have_mtab_info) { + if (lstat(MOUNTED, &mtab_stat)) + var_mtab_does_not_exist = 1; + else if (S_ISLNK(mtab_stat.st_mode)) + var_mtab_is_a_symlink = 1; + have_mtab_info = 1; + } +} + +int +mtab_does_not_exist(void) { + get_mtab_info(); + return var_mtab_does_not_exist; +} + +static int +mtab_is_a_symlink(void) { + get_mtab_info(); + return var_mtab_is_a_symlink; +} + +int +mtab_is_writable() { + int fd; + + /* Should we write to /etc/mtab upon an update? + Probably not if it is a symlink to /proc/mounts, since that + would create a file /proc/mounts in case the proc filesystem + is not mounted. */ + if (mtab_is_a_symlink()) + return 0; + + fd = open(MOUNTED, O_RDWR | O_CREAT, 0644); + if (fd >= 0) { + close(fd); + return 1; + } else + return 0; +} + +/* Contents of mtab and fstab ---------------------------------*/ + +struct mntentchn mounttable; +static int got_mtab = 0; + +static void read_mounttable(void); + +struct mntentchn * +mtab_head() { + if (!got_mtab) + read_mounttable(); + return &mounttable; +} + +static void +my_free(const void *s) { + if (s) + free((void *) s); +} + +static void +discard_mntentchn(struct mntentchn *mc0) { + struct mntentchn *mc, *mc1; + + for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) { + mc1 = mc->nxt; + my_free(mc->m.mnt_fsname); + my_free(mc->m.mnt_dir); + my_free(mc->m.mnt_type); + my_free(mc->m.mnt_opts); + free(mc); + } +} + +static void +read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) { + struct mntentchn *mc = mc0; + nfs_mntent_t *mnt; + + while ((mnt = nfs_getmntent(mfp)) != NULL) { + if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) { + mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc)); + mc->nxt->prev = mc; + mc = mc->nxt; + mc->m = *mnt; + mc->nxt = mc0; + } + } + mc0->prev = mc; + if (ferror(mfp->mntent_fp)) { + int errsv = errno; + nfs_error(_("warning: error reading %s: %s"), + fnam, strerror (errsv)); + mc0->nxt = mc0->prev = NULL; + } + nfs_endmntent(mfp); +} + +/* + * Read /etc/mtab. If that fails, try /proc/mounts. + * This produces a linked list. The list head mounttable is a dummy. + * Return 0 on success. + */ +static void +read_mounttable() { + mntFILE *mfp; + const char *fnam; + struct mntentchn *mc = &mounttable; + + got_mtab = 1; + mc->nxt = mc->prev = NULL; + + fnam = MOUNTED; + mfp = nfs_setmntent (fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + fnam = PROC_MOUNTS; + mfp = nfs_setmntent (fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + nfs_error(_("warning: can't open %s: %s"), + MOUNTED, strerror (errsv)); + return; + } + if (verbose) + printf (_("mount: could not open %s - " + "using %s instead\n"), + MOUNTED, PROC_MOUNTS); + } + read_mntentchn(mfp, fnam, mc); +} + +/* + * Given the directory name NAME, and the place MCPREV we found it last time, + * try to find more occurrences. + */ +struct mntentchn * +getmntdirbackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + if (!mcprev) + mcprev = mc0; + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, name)) + return mc; + return NULL; +} + +/* + * Given the device name NAME, and the place MCPREV we found it last time, + * try to find more occurrences. + */ +struct mntentchn * +getmntdevbackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = mtab_head(); + if (!mcprev) + mcprev = mc0; + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_fsname, name)) + return mc; + return NULL; +} + +/* Updating mtab ----------------------------------------------*/ + +/* Flag for already existing lock file. */ +static int we_created_lockfile = 0; +static int lockfile_fd = -1; + +/* Flag to indicate that signals have been set up. */ +static int signals_have_been_setup = 0; + +/* Ensure that the lock is released if we are interrupted. */ +extern char *strsignal(int sig); /* not always in */ + +static void +handler (int sig) { + die(EX_USER, "%s", strsignal(sig)); +} + +static void +setlkw_timeout (int sig) { + /* nothing, fcntl will fail anyway */ +} + +/* Remove lock file. */ +void +unlock_mtab (void) { + if (we_created_lockfile) { + close(lockfile_fd); + lockfile_fd = -1; + unlink (MOUNTED_LOCK); + we_created_lockfile = 0; + } +} + +/* Create the lock file. + The lock file will be removed if we catch a signal or when we exit. */ +/* The old code here used flock on a lock file /etc/mtab~ and deleted + this lock file afterwards. However, as rgooch remarks, that has a + race: a second mount may be waiting on the lock and proceed as + soon as the lock file is deleted by the first mount, and immediately + afterwards a third mount comes, creates a new /etc/mtab~, applies + flock to that, and also proceeds, so that the second and third mount + now both are scribbling in /etc/mtab. + The new code uses a link() instead of a creat(), where we proceed + only if it was us that created the lock, and hence we always have + to delete the lock afterwards. Now the use of flock() is in principle + superfluous, but avoids an arbitrary sleep(). */ + +/* Where does the link point to? Obvious choices are mtab and mtab~~. + HJLu points out that the latter leads to races. Right now we use + mtab~. instead. Use 20 as upper bound for the length of %d. */ +#define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d" +#define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20) + +void +lock_mtab (void) { + int tries = 100000, i; + char linktargetfile[MOUNTLOCK_LINKTARGET_LTH]; + + at_die = unlock_mtab; + + if (!signals_have_been_setup) { + int sig = 0; + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigfillset (&sa.sa_mask); + + while (sigismember (&sa.sa_mask, ++sig) != -1 + && sig != SIGCHLD) { + if (sig == SIGALRM) + sa.sa_handler = setlkw_timeout; + else + sa.sa_handler = handler; + sigaction (sig, &sa, (struct sigaction *) 0); + } + signals_have_been_setup = 1; + } + + sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ()); + + i = open (linktargetfile, O_WRONLY|O_CREAT, 0); + if (i < 0) { + int errsv = errno; + /* linktargetfile does not exist (as a file) + and we cannot create it. Read-only filesystem? + Too many files open in the system? + Filesystem full? */ + die (EX_FILEIO, _("can't create lock file %s: %s " + "(use -n flag to override)"), + linktargetfile, strerror (errsv)); + } + close(i); + + /* Repeat until it was us who made the link */ + while (!we_created_lockfile) { + struct flock flock; + int errsv, j; + + j = link(linktargetfile, MOUNTED_LOCK); + errsv = errno; + + if (j == 0) + we_created_lockfile = 1; + + if (j < 0 && errsv != EEXIST) { + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't link lock file %s: %s " + "(use -n flag to override)"), + MOUNTED_LOCK, strerror (errsv)); + } + + lockfile_fd = open (MOUNTED_LOCK, O_WRONLY); + + if (lockfile_fd < 0) { + int errsv = errno; + /* Strange... Maybe the file was just deleted? */ + if (errno == ENOENT && tries-- > 0) { + if (tries % 200 == 0) + usleep(30); + continue; + } + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't open lock file %s: %s " + "(use -n flag to override)"), + MOUNTED_LOCK, strerror (errsv)); + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (j == 0) { + /* We made the link. Now claim the lock. */ + if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) { + if (verbose) { + int errsv = errno; + printf(_("Can't lock lock file %s: %s\n"), + MOUNTED_LOCK, strerror (errsv)); + } + /* proceed anyway */ + } + (void) unlink(linktargetfile); + } else { + static int tries = 0; + + /* Someone else made the link. Wait. */ + alarm(LOCK_TIMEOUT); + if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) { + int errsv = errno; + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't lock lock file %s: %s"), + MOUNTED_LOCK, (errno == EINTR) ? + _("timed out") : strerror (errsv)); + } + alarm(0); + /* Limit the number of iterations - maybe there + still is some old /etc/mtab~ */ + ++tries; + if (tries % 200 == 0) + usleep(30); + if (tries > 100000) { + (void) unlink(linktargetfile); + close(lockfile_fd); + die (EX_FILEIO, _("Cannot create link %s\n" + "Perhaps there is a stale lock file?\n"), + MOUNTED_LOCK); + } + close(lockfile_fd); + } + } +} + +/* + * Update the mtab. + * Used by umount with null INSTEAD: remove the last DIR entry. + * Used by mount upon a remount: update option part, + * and complain if a wrong device or type was given. + * [Note that often a remount will be a rw remount of / + * where there was no entry before, and we'll have to believe + * the values given in INSTEAD.] + */ + +void +update_mtab (const char *dir, nfs_mntent_t *instead) { + mntFILE *mfp, *mftmp; + const char *fnam = MOUNTED; + struct mntentchn mtabhead; /* dummy */ + struct mntentchn *mc, *mc0, *absent = NULL; + + if (mtab_does_not_exist() || !mtab_is_writable()) + return; + + lock_mtab(); + + /* having locked mtab, read it again */ + mc0 = mc = &mtabhead; + mc->nxt = mc->prev = NULL; + + mfp = nfs_setmntent(fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + int errsv = errno; + nfs_error (_("cannot open %s (%s) - mtab not updated"), + fnam, strerror (errsv)); + goto leave; + } + + read_mntentchn(mfp, fnam, mc); + + /* find last occurrence of dir */ + for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, dir)) + break; + if (mc && mc != mc0) { + if (instead == NULL) { + /* An umount - remove entry */ + if (mc && mc != mc0) { + mc->prev->nxt = mc->nxt; + mc->nxt->prev = mc->prev; + free(mc); + } + } else { + /* A remount */ + mc->m.mnt_opts = instead->mnt_opts; + } + } else if (instead) { + /* not found, add a new entry */ + absent = xmalloc(sizeof(*absent)); + absent->m = *instead; + absent->nxt = mc0; + absent->prev = mc0->prev; + mc0->prev = absent; + if (mc0->nxt == NULL) + mc0->nxt = absent; + } + + /* write chain to mtemp */ + mftmp = nfs_setmntent (MOUNTED_TEMP, "w"); + if (mftmp == NULL || mftmp->mntent_fp == NULL) { + int errsv = errno; + nfs_error (_("cannot open %s (%s) - mtab not updated"), + MOUNTED_TEMP, strerror (errsv)); + goto leave; + } + + for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { + if (nfs_addmntent(mftmp, &(mc->m)) == 1) { + int errsv = errno; + die (EX_FILEIO, _("error writing %s: %s"), + MOUNTED_TEMP, strerror (errsv)); + } + } + + discard_mntentchn(mc0); + + if (fchmod (fileno (mftmp->mntent_fp), + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + int errsv = errno; + fprintf(stderr, _("error changing mode of %s: %s\n"), + MOUNTED_TEMP, strerror (errsv)); + } + nfs_endmntent (mftmp); + + { /* + * If mount is setuid and some non-root user mounts sth, + * then mtab.tmp might get the group of this user. Copy uid/gid + * from the present mtab before renaming. + */ + struct stat sbuf; + if (stat (MOUNTED, &sbuf) == 0) + chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid); + } + + /* rename mtemp to mtab */ + if (rename (MOUNTED_TEMP, MOUNTED) < 0) { + int errsv = errno; + fprintf(stderr, _("can't rename %s to %s: %s\n"), + MOUNTED_TEMP, MOUNTED, strerror(errsv)); + } + + leave: + unlock_mtab(); +}