diff options
Diffstat (limited to 'mount/fstab.c')
-rw-r--r-- | mount/fstab.c | 419 |
1 files changed, 356 insertions, 63 deletions
diff --git a/mount/fstab.c b/mount/fstab.c index d13280f8f..524779a63 100644 --- a/mount/fstab.c +++ b/mount/fstab.c @@ -1,92 +1,385 @@ -/* $Header: /home/faith/cvs/util-linux/mount/fstab.c,v 1.2 1995/10/07 01:32:03 faith Exp $ */ - -#include "fstab.h" +#include <unistd.h> +#include <mntent.h> +#include <errno.h> #include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include "fstab.h" +#include "sundries.h" /* for xmalloc() etc */ + #define streq(s, t) (strcmp ((s), (t)) == 0) -/* These routines are superseded by mntent(3), but I use them for - convenience. Mntent(3) is used in the implementation, so be - very careful about the static buffers that are returned. */ +#define PROC_MOUNTS "/proc/mounts" + +/* 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 FILE *F_fstab = NULL; +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; + } +} -/* Open fstab or rewind if already open. */ int -setfsent (void) -{ - if (F_fstab) - return (fseek (F_fstab, 0L, SEEK_SET) == 0); +mtab_does_not_exist(void) { + get_mtab_info(); + return var_mtab_does_not_exist; +} - F_fstab = setmntent (_PATH_FSTAB, "r"); - return (F_fstab != NULL); +int +mtab_is_a_symlink(void) { + get_mtab_info(); + return var_mtab_is_a_symlink; } -/* Close fstab. */ -void -endfsent (void) -{ - endmntent (F_fstab); +int +mtab_is_writable() { + static int ret = -1; + + /* 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; + + if (ret == -1) { + int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644); + if (fd >= 0) { + close(fd); + ret = 1; + } else + ret = 0; + } + return ret; } -/* Return next entry in fstab, skipping ignore entries. I also put - in some ugly hacks here to skip comments and blank lines. */ -struct mntent * -getfsent (void) -{ - struct mntent *fstab; - - if (!F_fstab && !setfsent()) - return 0; - - for (;;) - { - fstab = getmntent (F_fstab); - if (fstab == NULL) - { - if (!feof (F_fstab) && !ferror (F_fstab)) - continue; - else +/* Contents of mtab and fstab ---------------------------------*/ + +struct mntentchn mounttable, fstab; +static int got_mtab = 0; +static int got_fstab = 0; + +static void read_mounttable(void), read_fstab(void); + +struct mntentchn * +mtab_head() { + if (!got_mtab) + read_mounttable(); + return &mounttable; +} + +struct mntentchn * +fstab_head() { + if (!got_fstab) + read_fstab(); + return &fstab; +} + +static void +read_mntentchn(FILE *fp, const char *fnam, struct mntentchn *mc0) { + struct mntentchn *mc = mc0; + struct mntent *mnt; + + while (!feof(fp) && !ferror(fp)) { + if ((mnt = getmntent (fp)) != NULL /* ignore blank lines */ + && *mnt->mnt_fsname != '#' /* and comment lines */ + && !streq (mnt->mnt_type, MNTTYPE_IGNORE)) { + mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc)); + mc->nxt->prev = mc; + mc = mc->nxt; + mc->mnt_fsname = xstrdup(mnt->mnt_fsname); + mc->mnt_dir = xstrdup(mnt->mnt_dir); + mc->mnt_type = xstrdup(mnt->mnt_type); + mc->mnt_opts = xstrdup(mnt->mnt_opts); + mc->nxt = NULL; + } + } + mc0->prev = mc; + if (ferror (fp)) { + error("warning: error reading %s: %s", fnam, strerror (errno)); + mc0->nxt = mc0->prev = NULL; + } + endmntent(fp); +} + +/* + * 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() { + FILE *fp = NULL; + const char *fnam; + struct mntentchn *mc = &mounttable; + + got_mtab = 1; + mc->nxt = mc->prev = NULL; + + fnam = MOUNTED; + if ((fp = setmntent (fnam, "r")) == NULL) { + int errsv = errno; + fnam = PROC_MOUNTS; + if ((fp = setmntent (fnam, "r")) == NULL) { + 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(fp, fnam, mc); +} + +static void +read_fstab() { + FILE *fp = NULL; + const char *fnam; + struct mntentchn *mc = &fstab; + + got_fstab = 1; + mc->nxt = mc->prev = NULL; + + fnam = _PATH_FSTAB; + if ((fp = setmntent (fnam, "r")) == NULL) { + error("warning: can't open %s: %s", _PATH_FSTAB, strerror (errno)); + return; + } + read_mntentchn(fp, fnam, mc); +} + + +/* Given the name NAME, try to find it in mtab. */ +struct mntentchn * +getmntfile (const char *name) { + struct mntentchn *mc; + + for (mc = mtab_head()->nxt; mc; mc = mc->nxt) + if (streq (mc->mnt_dir, name) || (streq (mc->mnt_fsname, name))) break; - } - else if ((*fstab->mnt_fsname != '#') - && !streq (fstab->mnt_type, MNTTYPE_IGNORE)) - break; - } - return fstab; + + return mc; } -/* Find the dir FILE in fstab. */ -struct mntent * -getfsfile (const char *file) +/* Given the name FILE, try to find the option "loop=FILE" in mtab. */ +struct mntentchn * +getmntoptfile (const char *file) { - struct mntent *fstab; + struct mntentchn *mc; + char *opts, *s; + int l; - /* Open or rewind fstab. */ - if (!setfsent ()) - return 0; + if (!file) + return NULL; - while ((fstab = getfsent ())) - if (streq (fstab->mnt_dir, file)) - break; + l = strlen(file); - return fstab; + for (mc = mtab_head()->nxt; mc; mc = mc->nxt) + if ((opts = mc->mnt_opts) != NULL + && (s = strstr(opts, "loop=")) + && !strncmp(s+5, file, l) + && (s == opts || s[-1] == ',') + && (s[l+5] == 0 || s[l+5] == ',')) + return mc; + + return NULL; +} + +/* Find the dir FILE in fstab. */ +struct mntentchn * +getfsfile (const char *file) { + struct mntentchn *mc; + + for (mc = fstab_head()->nxt; mc; mc = mc->nxt) + if (streq (mc->mnt_dir, file)) + break; + + return mc; } /* Find the device SPEC in fstab. */ -struct mntent * +struct mntentchn * getfsspec (const char *spec) { - struct mntent *fstab; + struct mntentchn *mc; + + for (mc = fstab_head()->nxt; mc; mc = mc->nxt) + if (streq (mc->mnt_fsname, spec)) + break; + + return mc; +} + +/* Updating mtab ----------------------------------------------*/ + +/* File descriptor for lock. Value tested in unlock_mtab() to remove race. */ +static int lock = -1; + +/* Flag for already existing lock file. */ +static int old_lockfile = 1; + +/* Ensure that the lock is released if we are interrupted. */ +static void +handler (int sig) { + die (EX_USER, "%s", sys_siglist[sig]); +} + +static void +setlkw_timeout (int sig) { + /* nothing, fcntl will fail anyway */ +} + +/* Create the lock file. The lock file will be removed if we catch a signal + or when we exit. The value of lock is tested to remove the race. */ +void +lock_mtab (void) { + int sig = 0; + struct sigaction sa; + struct flock flock; + + /* If this is the first time, ensure that the lock will be removed. */ + if (lock < 0) { + struct stat st; + 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); + } + + /* This stat is performed so we know when not to be overly eager + when cleaning up after signals. The window between stat and + open is not significant. */ + if (lstat (MOUNTED_LOCK, &st) < 0 && errno == ENOENT) + old_lockfile = 0; + + lock = open (MOUNTED_LOCK, O_WRONLY|O_CREAT, 0); + if (lock < 0) { + die (EX_FILEIO, "can't create lock file %s: %s " + "(use -n flag to override)", + MOUNTED_LOCK, strerror (errno)); + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + alarm(LOCK_BUSY); + if (fcntl (lock, F_SETLKW, &flock) < 0) { + close (lock); + /* The file should not be removed */ + lock = -1; + die (EX_FILEIO, "can't lock lock file %s: %s", + MOUNTED_LOCK, + errno == EINTR ? "timed out" : strerror (errno)); + } + /* We have now access to the lock, and it can always be removed */ + old_lockfile = 0; + } +} + +/* Remove lock file. */ +void +unlock_mtab (void) { + if (lock != -1) { + close (lock); + if (!old_lockfile) + unlink (MOUNTED_LOCK); + } +} + +/* + * Update the mtab. + * Used by umount with null INSTEAD: remove any DIR entries. + * 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, struct mntent *instead) { + struct mntent *mnt; + struct mntent *next; + struct mntent remnt; + int added = 0; + FILE *fp, *ftmp; + + if (mtab_does_not_exist() || mtab_is_a_symlink()) + return; + + lock_mtab(); + + if ((fp = setmntent(MOUNTED, "r")) == NULL) { + error ("cannot open %s (%s) - mtab not updated", + MOUNTED, strerror (errno)); + goto leave; + } + + if ((ftmp = setmntent (MOUNTED_TEMP, "w")) == NULL) { + error ("can't open %s (%s) - mtab not updated", + MOUNTED_TEMP, strerror (errno)); + goto leave; + } + + while ((mnt = getmntent (fp))) { + if (streq (mnt->mnt_dir, dir)) { + added++; + if (instead) { /* a remount */ + remnt = *instead; + next = &remnt; + remnt.mnt_fsname = mnt->mnt_fsname; + remnt.mnt_type = mnt->mnt_type; + if (instead->mnt_fsname + && !streq(mnt->mnt_fsname, instead->mnt_fsname)) + printf("mount: warning: cannot change " + "mounted device with a remount\n"); + else if (instead->mnt_type + && !streq(instead->mnt_type, "unknown") + && !streq(mnt->mnt_type, instead->mnt_type)) + printf("mount: warning: cannot change " + "filesystem type with a remount\n"); + } else + next = NULL; + } else + next = mnt; + if (next && addmntent(ftmp, next) == 1) + die (EX_FILEIO, "error writing %s: %s", + MOUNTED_TEMP, strerror (errno)); + } + if (instead && !added && addmntent(ftmp, instead) == 1) + die (EX_FILEIO, "error writing %s: %s", + MOUNTED_TEMP, strerror (errno)); - /* Open or rewind fstab. */ - if (!setfsent()) - return 0; + endmntent (fp); + if (fchmod (fileno (ftmp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) + fprintf(stderr, "error changing mode of %s: %s", MOUNTED_TEMP, + strerror (errno)); + endmntent (ftmp); - while ((fstab = getfsent ())) - if (streq (fstab->mnt_fsname, spec)) - break; + if (rename (MOUNTED_TEMP, MOUNTED) < 0) + fprintf(stderr, "can't rename %s to %s: %s", MOUNTED_TEMP, MOUNTED, + strerror(errno)); - return fstab; +leave: + unlock_mtab(); } |