mirror of
https://github.com/nilfs-dev/nilfs-utils.git
synced 2026-01-26 05:37:51 +00:00
Apply the NILFS_UTILS_GITID() macro to all major executables and libraries to enable git revision tracking. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
597 lines
14 KiB
C
597 lines
14 KiB
C
/*
|
|
* mount_libmount.c - NILFS mount helper program (libmount version)
|
|
*
|
|
* Copyright (C) 2007-2012 Nippon Telegraph and Telephone Corporation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 021110-1307, USA.
|
|
*
|
|
* Written by Ryusuke Konishi <konishi.ryusuke@gmail.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#if HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif /* HAVE_SYS_TYPES_H */
|
|
|
|
#include <stdio.h>
|
|
|
|
#if HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif /* HAVE_STDLIB_H */
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
|
|
#if HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif /* HAVE_FCNTL_H */
|
|
|
|
#if HAVE_LIMITS_H
|
|
#include <limits.h> /* ULONG_MAX */
|
|
#endif /* HAVE_LIMITS_H */
|
|
|
|
#if HAVE_ERR_H
|
|
#include <err.h>
|
|
#endif /* HAVE_ERR_H */
|
|
|
|
#if HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif /* HAVE_STRINGS_H */
|
|
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif /* HAVE_STRING_H */
|
|
|
|
#if HAVE_SYS_IOCTL_H
|
|
#include <sys/ioctl.h> /* ioctl() */
|
|
#endif /* HAVE_SYS_IOCTL_H */
|
|
|
|
#if HAVE_SYS_MOUNT_H
|
|
#include <sys/mount.h> /* BLKROGET */
|
|
#endif /* HAVE_SYS_MOUNT_H */
|
|
|
|
#if HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif /* HAVE_SYS_STAT_H */
|
|
|
|
#if HAVE_SYSLOG_H
|
|
#include <syslog.h>
|
|
#endif /* HAVE_SYSLOG_H */
|
|
|
|
#include <libmount.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include "compat.h" /* getprogname() */
|
|
#include "libmount_compat.h"
|
|
#include "mount.nilfs2.h"
|
|
#include "mount_attrs.h"
|
|
#include "cleaner_exec.h"
|
|
#include "nls.h"
|
|
#include "util.h" /* NILFS_UTILS_GITID() */
|
|
|
|
#ifdef _GNU_SOURCE
|
|
#include <getopt.h>
|
|
#endif /* _GNU_SOURCE */
|
|
|
|
/* mount options */
|
|
static int verbose;
|
|
static int devro;
|
|
|
|
static char *mount_fstype;
|
|
|
|
/* global variables */
|
|
static const char fstype[] = NILFS2_FS_NAME;
|
|
|
|
NILFS_UTILS_GITID();
|
|
|
|
/* mount info */
|
|
struct nilfs_mount_info {
|
|
struct libmnt_context *cxt;
|
|
unsigned long mflags;
|
|
int type;
|
|
int mounted;
|
|
struct nilfs_mount_attrs old_attrs;
|
|
struct nilfs_mount_attrs new_attrs;
|
|
};
|
|
|
|
enum {
|
|
NORMAL_MOUNT,
|
|
RW2RO_REMOUNT,
|
|
RW2RW_REMOUNT,
|
|
};
|
|
|
|
|
|
static void nilfs_mount_logger(int priority, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if ((verbose && priority > LOG_INFO) || priority >= LOG_INFO)
|
|
return;
|
|
va_start(args, fmt);
|
|
vwarnx(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
#if 0
|
|
static int nilfs_libmount_table_errcb(struct libmnt_table *tb,
|
|
const char *filename, int line)
|
|
{
|
|
if (filename)
|
|
warnx(_("%s: parse error: ignore entry at line %d."),
|
|
filename, line);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Other routines
|
|
*/
|
|
static int device_is_readonly(const char *device, int *ro)
|
|
{
|
|
int fd, res = 0;
|
|
|
|
fd = open(device, O_RDONLY);
|
|
if (fd < 0)
|
|
return -errno;
|
|
|
|
if (ioctl(fd, BLKROGET, ro) < 0)
|
|
res = -errno;
|
|
close(fd);
|
|
return res;
|
|
}
|
|
|
|
static void show_version(void)
|
|
{
|
|
printf("%s (%s %s)\n", getprogname(), PACKAGE, PACKAGE_VERSION);
|
|
}
|
|
|
|
static void nilfs_mount_parse_options(int argc, char *argv[],
|
|
struct nilfs_mount_info *mi)
|
|
{
|
|
struct libmnt_context *cxt = mi->cxt;
|
|
struct libmnt_fs *fs;
|
|
int c, show_version_only = 0;
|
|
|
|
fs = mnt_context_get_fs(cxt);
|
|
if (!fs)
|
|
errx(MNT_EX_SYSERR, _("failed to get fs"));
|
|
|
|
while ((c = getopt(argc, argv, "fvnt:o:rwV")) != EOF) {
|
|
switch (c) {
|
|
case 'f':
|
|
mnt_context_enable_fake(cxt, 1);
|
|
break;
|
|
case 'v':
|
|
mnt_context_enable_verbose(cxt, 1);
|
|
verbose = 1;
|
|
break;
|
|
case 'n':
|
|
mnt_context_disable_mtab(cxt, 1);
|
|
break;
|
|
case 't':
|
|
mount_fstype = optarg;
|
|
break;
|
|
case 'o':
|
|
{
|
|
char *rest;
|
|
|
|
if (nilfs_mount_attrs_parse(&mi->new_attrs, optarg,
|
|
NULL, &rest, 0))
|
|
errx(MNT_EX_SYSERR,
|
|
_("failed to parse options"));
|
|
if (rest && mnt_context_append_options(cxt, rest))
|
|
errx(MNT_EX_SYSERR,
|
|
_("failed to append options"));
|
|
free(rest);
|
|
break;
|
|
}
|
|
case 'r':
|
|
if (mnt_context_append_options(cxt, "ro"))
|
|
errx(MNT_EX_SYSERR,
|
|
_("failed to append options"));
|
|
break;
|
|
case 'w':
|
|
if (mnt_context_append_options(cxt, "rw"))
|
|
errx(MNT_EX_SYSERR,
|
|
_("failed to append options"));
|
|
break;
|
|
case 'V':
|
|
show_version_only = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (show_version_only) {
|
|
show_version();
|
|
exit(MNT_EX_SUCCESS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nilfs_find_mount - find matching nilfs entry from mount table
|
|
* @cxt: mount context
|
|
* @mtab: pointer to tab
|
|
* @target: pointer to mountpoint path or %NULL
|
|
* @options: comma delimited list of options (and nooptions)
|
|
*
|
|
* This function searches in reverse order for an entry in the mount table
|
|
* given by @mtab that matches the device and file system type in @cxt.
|
|
* If @target and @options are not %NULL, the corresponding mount points and
|
|
* mount options are also added to the matching conditions.
|
|
*
|
|
* For details on the format of @options, see the specification of
|
|
* mnt_match_options().
|
|
*
|
|
* Return: If a matching entry is found, its FS description, or %NULL if not
|
|
* found.
|
|
*/
|
|
static struct libmnt_fs *nilfs_find_mount(struct libmnt_context *cxt,
|
|
struct libmnt_table *mtab,
|
|
const char *target,
|
|
const char *options)
|
|
{
|
|
struct libmnt_iter *iter = mnt_new_iter(MNT_ITER_BACKWARD);
|
|
const char *src = mnt_context_get_source(cxt);
|
|
const char *type = mnt_context_get_fstype(cxt);
|
|
struct libmnt_cache *cache = mnt_table_get_cache(mtab);
|
|
struct libmnt_fs *fs = NULL;
|
|
|
|
if (!iter)
|
|
errx(MNT_EX_SYSERR, _("libmount iterator allocation failed"));
|
|
|
|
while (mnt_table_next_fs(mtab, iter, &fs) == 0) {
|
|
if (mnt_fs_match_fstype(fs, type) &&
|
|
mnt_fs_match_source(fs, src, cache) &&
|
|
(!target || mnt_fs_match_target(fs, target, cache)) &&
|
|
(!options || mnt_fs_match_options(fs, options)))
|
|
break;
|
|
}
|
|
|
|
mnt_free_iter(iter);
|
|
return fs;
|
|
}
|
|
|
|
/**
|
|
* nilfs_find_rw_mount - find nilfs read/write mount entry from mount table
|
|
* @cxt: mount context
|
|
* @mtab: pointer to tab
|
|
*
|
|
* Return: If a matching entry is found, its FS description, or NULL if not
|
|
* found.
|
|
*/
|
|
static inline struct libmnt_fs *nilfs_find_rw_mount(struct libmnt_context *cxt,
|
|
struct libmnt_table *mtab)
|
|
{
|
|
return nilfs_find_mount(cxt, mtab, NULL, "rw");
|
|
}
|
|
|
|
static int nilfs_prepare_mount(struct nilfs_mount_info *mi)
|
|
{
|
|
struct libmnt_context *cxt = mi->cxt;
|
|
struct libmnt_fs *fs;
|
|
struct libmnt_table *mtab;
|
|
const char *attrs;
|
|
int res;
|
|
|
|
res = mnt_context_prepare_mount(cxt);
|
|
if (res < 0) {
|
|
warnx(_("preparation failed: %s"), strerror(-res));
|
|
goto failed;
|
|
}
|
|
/*
|
|
* mnt_context_prepare_mount() parses mtab (/etc/mtab or
|
|
* /proc/self/mountinfo + /run/mount/utabs or /proc/mounts)
|
|
*/
|
|
|
|
res = mnt_context_get_mflags(cxt, &mi->mflags);
|
|
if (res < 0) {
|
|
warnx(_("get mount flags failed: %s"), strerror(-res));
|
|
goto failed;
|
|
}
|
|
|
|
if (!(mi->mflags & MS_RDONLY) && !(mi->mflags & MS_BIND)) {
|
|
res = device_is_readonly(mnt_context_get_source(cxt),
|
|
&devro);
|
|
if (res < 0) {
|
|
warnx(_("device %s not accessible: %s"),
|
|
mnt_context_get_source(cxt), strerror(-res));
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
res = mnt_context_get_mtab(cxt, &mtab);
|
|
if (res < 0) {
|
|
warnx(_("libmount mount check failed: %s"), strerror(-res));
|
|
goto failed;
|
|
}
|
|
|
|
mi->mounted = mnt_table_is_fs_mounted(mtab, mnt_context_get_fs(cxt));
|
|
|
|
if (mi->mflags & MS_BIND)
|
|
return 0;
|
|
|
|
fs = nilfs_find_rw_mount(cxt, mtab);
|
|
if (fs == NULL)
|
|
return 0; /* no previous rw-mount */
|
|
|
|
switch (mi->mflags & (MS_RDONLY | MS_REMOUNT)) {
|
|
case 0: /* overlapping rw-mount */
|
|
warnx(_("the device already has a rw-mount on %s.\n"
|
|
"\t\tmultiple rw-mount is not allowed."),
|
|
mnt_fs_get_target(fs));
|
|
res = -EBUSY;
|
|
goto failed;
|
|
case MS_RDONLY: /* ro-mount (a rw-mount exists) */
|
|
break;
|
|
case MS_REMOUNT | MS_RDONLY: /* rw->ro remount */
|
|
case MS_REMOUNT: /* rw->rw remount */
|
|
mi->type = (mi->mflags & MS_RDONLY) ?
|
|
RW2RO_REMOUNT : RW2RW_REMOUNT;
|
|
|
|
attrs = mnt_fs_get_attributes(fs);
|
|
if (attrs) {
|
|
res = nilfs_mount_attrs_parse(&mi->old_attrs, attrs,
|
|
NULL, NULL, 1);
|
|
if (res < 0) {
|
|
warnx(_("libmount mount check failed: %s"),
|
|
strerror(-res));
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
if (!mnt_fs_match_target(fs, mnt_context_get_target(cxt),
|
|
mnt_table_get_cache(mtab))) {
|
|
warnx(_("different mount point (%s). remount failed."),
|
|
mnt_context_get_target(cxt));
|
|
res = -EINVAL;
|
|
goto failed;
|
|
}
|
|
|
|
if (mi->old_attrs.gcpid) {
|
|
res = nilfs_shutdown_cleanerd(
|
|
mnt_fs_get_source(fs), mi->old_attrs.gcpid);
|
|
if (res < 0) {
|
|
warnx(_("remount failed due to %s shutdown failure"),
|
|
NILFS_CLEANERD_NAME);
|
|
goto failed;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
res = 0;
|
|
failed:
|
|
return res;
|
|
}
|
|
|
|
static int nilfs_do_mount_one(struct nilfs_mount_info *mi)
|
|
{
|
|
struct libmnt_context *cxt = mi->cxt;
|
|
int res, ec;
|
|
|
|
res = mnt_context_do_mount(cxt);
|
|
if (!res)
|
|
goto out;
|
|
|
|
/*
|
|
* mnt_context_do_mount() returns:
|
|
* 0: success
|
|
* > 0: syscall error (positive errno)
|
|
* < 0: library error (negative error code)
|
|
*/
|
|
ec = res < 0 ? -res : res;
|
|
|
|
switch (ec) {
|
|
case ENODEV:
|
|
warnx(_("cannot find or load %s filesystem"), fstype);
|
|
break;
|
|
default:
|
|
warnx(_("Error while mounting %s on %s: %s"),
|
|
mnt_context_get_source(cxt),
|
|
mnt_context_get_target(cxt), strerror(ec));
|
|
break;
|
|
}
|
|
if (mi->type != RW2RO_REMOUNT && mi->type != RW2RW_REMOUNT)
|
|
goto out;
|
|
|
|
/* Cleaner daemon was stopped and it needs to run */
|
|
/* because filesystem is still mounted */
|
|
if (!mi->old_attrs.nogc) {
|
|
struct nilfs_mount_attrs mattrs = { .pp = mi->old_attrs.pp };
|
|
|
|
/* Restarting cleaner daemon */
|
|
if (nilfs_launch_cleanerd(mnt_context_get_source(cxt),
|
|
mnt_context_get_target(cxt),
|
|
mattrs.pp, &mattrs.gcpid) == 0) {
|
|
if (mnt_context_is_verbose(cxt))
|
|
printf(_("%s: restarted %s\n"),
|
|
getprogname(), NILFS_CLEANERD_NAME);
|
|
|
|
nilfs_mount_attrs_update(&mi->old_attrs, &mattrs, cxt);
|
|
mnt_context_finalize_mount(cxt);
|
|
} else {
|
|
warnx(_("failed to restart %s"), NILFS_CLEANERD_NAME);
|
|
}
|
|
} else {
|
|
printf(_("%s not restarted\n"), NILFS_CLEANERD_NAME);
|
|
}
|
|
out:
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* nilfs_mnt_context_complete_root - complete root of the mount
|
|
* @cxt: mount context
|
|
*
|
|
* This function tries to retrieve root from the mount entry information
|
|
* obtained by searching the mount table and sets it to @cxt.
|
|
*
|
|
* Return: 0 on success, or -errno on error.
|
|
*/
|
|
static int nilfs_mnt_context_complete_root(struct libmnt_context *cxt)
|
|
{
|
|
struct libmnt_table *mtab;
|
|
struct libmnt_fs *fs;
|
|
int res;
|
|
|
|
res = mnt_context_get_mtab(cxt, &mtab);
|
|
if (res < 0) {
|
|
warnx(_("failed to get mtab: %s"), strerror(-res));
|
|
goto failed;
|
|
}
|
|
|
|
fs = nilfs_find_mount(cxt, mtab, mnt_context_get_target(cxt), NULL);
|
|
if (fs) {
|
|
res = mnt_fs_set_root(mnt_context_get_fs(cxt),
|
|
mnt_fs_get_root(fs));
|
|
if (res < 0) {
|
|
warnx(_("failed to copy root of the mount: %s"),
|
|
strerror(-res));
|
|
goto failed;
|
|
}
|
|
}
|
|
res = 0;
|
|
failed:
|
|
return res;
|
|
}
|
|
|
|
static int nilfs_update_mount_state(struct nilfs_mount_info *mi)
|
|
{
|
|
struct libmnt_context *cxt = mi->cxt;
|
|
struct nilfs_mount_attrs *old_attrs;
|
|
int rungc, gc_ok;
|
|
|
|
if (!mnt_fs_get_root(mnt_context_get_fs(cxt)) &&
|
|
((mi->mflags & MS_REMOUNT) || mnt_context_is_fake(cxt))) {
|
|
/*
|
|
* Complement the mount root against remount or fake mount
|
|
* context to avoid problems due to incomplete utab entry
|
|
* with the mount root "ROOT" missing, whose attributes cannot
|
|
* be referenced by umount or remount, making cleanerd
|
|
* uncontrollable.
|
|
*/
|
|
nilfs_mnt_context_complete_root(cxt);
|
|
}
|
|
|
|
gc_ok = !(mi->mflags & MS_RDONLY) && !(mi->mflags & MS_BIND);
|
|
rungc = gc_ok && !mi->new_attrs.nogc;
|
|
old_attrs = (mi->mflags & MS_REMOUNT) ? &mi->old_attrs : NULL;
|
|
|
|
if (rungc) {
|
|
if (mi->new_attrs.pp == ULONG_MAX)
|
|
mi->new_attrs.pp = mi->old_attrs.pp;
|
|
|
|
if (nilfs_launch_cleanerd(mnt_context_get_source(cxt),
|
|
mnt_context_get_target(cxt),
|
|
mi->new_attrs.pp,
|
|
&mi->new_attrs.gcpid) < 0)
|
|
warnx(_("%s aborted"), NILFS_CLEANERD_NAME);
|
|
else if (mnt_context_is_verbose(cxt))
|
|
printf(_("%s: started %s\n"), getprogname(),
|
|
NILFS_CLEANERD_NAME);
|
|
}
|
|
|
|
nilfs_mount_attrs_update(old_attrs, &mi->new_attrs, cxt);
|
|
|
|
return mnt_context_finalize_mount(cxt);
|
|
}
|
|
|
|
static int nilfs_mount_one(struct nilfs_mount_info *mi)
|
|
{
|
|
int res, err = MNT_EX_FAIL;
|
|
|
|
res = nilfs_prepare_mount(mi);
|
|
if (res)
|
|
goto failed;
|
|
|
|
if (!mnt_context_is_fake(mi->cxt)) {
|
|
res = nilfs_do_mount_one(mi);
|
|
if (res)
|
|
goto failed;
|
|
}
|
|
res = nilfs_update_mount_state(mi);
|
|
if (!res)
|
|
err = 0;
|
|
failed:
|
|
return err;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct nilfs_mount_info mi = {0};
|
|
char *device, *mntdir;
|
|
int res = 0;
|
|
|
|
nilfs_cleaner_logger = nilfs_mount_logger;
|
|
|
|
mi.type = NORMAL_MOUNT;
|
|
|
|
mnt_init_debug(0);
|
|
mi.cxt = mnt_new_context();
|
|
if (!mi.cxt)
|
|
errx(MNT_EX_SYSERR, _("libmount context allocation failed"));
|
|
|
|
#if 0
|
|
mnt_context_set_tables_errcb(mi.cxt, nilfs_libmount_table_errcb);
|
|
#endif
|
|
if (mnt_context_set_fstype(mi.cxt, fstype))
|
|
errx(MNT_EX_SYSERR,
|
|
_("libmount FS description allocation failed"));
|
|
mnt_context_disable_helpers(mi.cxt, 1);
|
|
|
|
nilfs_mount_attrs_init(&mi.old_attrs);
|
|
nilfs_mount_attrs_init(&mi.new_attrs);
|
|
nilfs_mount_parse_options(argc, argv, &mi);
|
|
|
|
umask(022);
|
|
|
|
if (optind >= argc || !argv[optind])
|
|
errx(MNT_EX_USAGE, _("No device specified"));
|
|
|
|
device = argv[optind++];
|
|
|
|
if (optind >= argc || !argv[optind])
|
|
errx(MNT_EX_USAGE, _("No mountpoint specified"));
|
|
|
|
mntdir = argv[optind++];
|
|
|
|
if (mnt_context_set_source(mi.cxt, device) ||
|
|
mnt_context_set_target(mi.cxt, mntdir))
|
|
errx(MNT_EX_SYSERR, _("Mount entry allocation failed"));
|
|
|
|
if (mount_fstype && strncmp(mount_fstype, fstype, strlen(fstype)))
|
|
errx(MNT_EX_USAGE, _("Unknown filesystem (%s)"),
|
|
mount_fstype);
|
|
|
|
if (getuid() != geteuid())
|
|
errx(MNT_EX_USAGE,
|
|
_("mount by non-root user is not supported yet"));
|
|
|
|
res = nilfs_mount_one(&mi);
|
|
|
|
mnt_free_context(mi.cxt);
|
|
return res;
|
|
}
|