Logo Search packages:      
Sourcecode: watchdog version File versions  Download package

fstab.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "wd_mntent.h"
#include "fstab.h"
#include "sundries.h"         /* for xmalloc() etc */


#define streq(s, t)     (strcmp ((s), (t)) == 0)

#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 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;
}

int
mtab_is_a_symlink(void) {
     get_mtab_info();
     return var_mtab_is_a_symlink;
}

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;
}

/* 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(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
      struct mntentchn *mc = mc0;
      struct mntent *mnt;

      while ((mnt = my_getmntent (mfp)) != NULL
             && !streq (mnt->mnt_type, MNTTYPE_IGNORE)) {
            mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
            mc->nxt->prev = mc;
            mc = mc->nxt;
            mc->mnt_fsname = mnt->mnt_fsname;
            mc->mnt_dir = mnt->mnt_dir;
            mc->mnt_type = mnt->mnt_type;
            mc->mnt_opts = mnt->mnt_opts;
            mc->nxt = NULL;
      }
      mc0->prev = mc;
      if (ferror (mfp->mntent_fp)) {
            error("warning: error reading %s: %s", fnam, strerror (errno));
            mc0->nxt = mc0->prev = NULL;
      }
      my_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 = my_setmntent (fnam, "r");
     if (mfp == NULL || mfp->mntent_fp == NULL) {
        int errsv = errno;
        fnam = PROC_MOUNTS;
        mfp = my_setmntent (fnam, "r");
        if (mfp == NULL || mfp->mntent_fp == NULL) {
             error("warning: can't open %s: %s", MOUNTED, strerror (errsv));
             return;
        }
        if (mount_verbose)
             printf ("mount: could not open %s - using %s instead\n",
                   MOUNTED, PROC_MOUNTS);
     }
     read_mntentchn(mfp, fnam, mc);
}

static void
read_fstab() {
     mntFILE *mfp = NULL;
     const char *fnam;
     struct mntentchn *mc = &fstab;

     got_fstab = 1;
     mc->nxt = mc->prev = NULL;

     fnam = _PATH_FSTAB;
     mfp = my_setmntent (fnam, "r");
     if (mfp == NULL || mfp->mntent_fp == NULL) {
        error("warning: can't open %s: %s", _PATH_FSTAB, strerror (errno));
        return;
     }
     read_mntentchn(mfp, 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;

    return mc;
}

/* Given the name FILE, try to find the option "loop=FILE" in mtab.  */ 
struct mntentchn *
getmntoptfile (const char *file)
{
     struct mntentchn *mc;
     char *opts, *s;
     int l;

     if (!file)
        return NULL;

     l = strlen(file);

     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 mntentchn *
getfsspec (const char *spec)
{
    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_TIMEOUT);
        if (fcntl (lock, F_SETLKW, &flock) == -1) {
             int errnosv = errno;

             close (lock);
             lock = -1; /* The file should not be removed */
             die (EX_FILEIO, "can't lock lock file %s: %s",
                MOUNTED_LOCK,
                errnosv == 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;
     mntFILE *mfp, *mftmp;

     if (mtab_does_not_exist() || mtab_is_a_symlink())
        return;

     lock_mtab();

     mfp = my_setmntent(MOUNTED, "r");
     if (mfp == NULL || mfp->mntent_fp == NULL) {
        error ("cannot open %s (%s) - mtab not updated",
             MOUNTED, strerror (errno));
        goto leave;
     }

     mftmp = my_setmntent (MOUNTED_TEMP, "w");
     if (mftmp == NULL || mfp->mntent_fp == NULL) {
        error ("can't open %s (%s) - mtab not updated",
             MOUNTED_TEMP, strerror (errno));
        goto leave;
     }
  
     while ((mnt = my_getmntent (mfp))) {
        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 && my_addmntent(mftmp, next) == 1)
             die (EX_FILEIO, "error writing %s: %s",
                MOUNTED_TEMP, strerror (errno));
     }
     if (instead && !added && my_addmntent(mftmp, instead) == 1)
        die (EX_FILEIO, "error writing %s: %s",
             MOUNTED_TEMP, strerror (errno));

     my_endmntent (mfp);
     if (fchmod (fileno (mftmp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
        fprintf(stderr, "error changing mode of %s: %s\n", MOUNTED_TEMP,
              strerror (errno));
     my_endmntent (mftmp);

     if (rename (MOUNTED_TEMP, MOUNTED) < 0)
        fprintf(stderr, "can't rename %s to %s: %s\n", MOUNTED_TEMP, MOUNTED,
              strerror(errno));

leave:
     unlock_mtab();
}

Generated by  Doxygen 1.6.0   Back to index