blob: 701d41e42e2e8725d1f8a2bb1c07d1d343a2a8a8 [file] [log] [blame]
/*
* mount_libmount.c -- Linux NFS [u]mount based on libmount
*
* Copyright (C) 2011 Karel Zak <kzak@redhat.com>
*
* 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, 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <libmount/libmount.h>
#include "nls.h"
#include "mount_config.h"
#include "nfs_mount.h"
#include "nfs4_mount.h"
#include "stropts.h"
#include "version.h"
#include "xcommon.h"
#include "error.h"
#include "utils.h"
char *progname;
int nfs_mount_data_version;
int verbose;
int sloppy;
int string;
int nomtab;
#define FOREGROUND (0)
#define BACKGROUND (1)
/*
* Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs.
*
* Note that on systems without /etc/mtab the fs-specific options are not
* managed by libmount at all. We have to use "mount attributes" that are
* private for mount.<type> helpers.
*/
static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts)
{
char *o = NULL;
mnt_fs_set_attributes(fs, nfs_opts); /* for non-mtab systems */
/* for mtab create a new options list */
mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL);
mnt_optstr_append_option(&o, nfs_opts, NULL);
mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL);
mnt_fs_set_options(fs, o);
free(o);
}
/*
* Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
*
* The result can passed to free().
*/
char *retrieve_mount_options(struct libmnt_fs *fs)
{
const char *opts;
if (!fs)
return NULL;
opts = mnt_fs_get_attributes(fs); /* /dev/.mount/utab */
if (opts)
return strdup(opts);
return mnt_fs_strdup_options(fs); /* /etc/mtab */
}
static int try_mount(struct libmnt_context *cxt, int bg)
{
struct libmnt_fs *fs;
const char *p;
char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
unsigned long flags = 0;
int fake, ret = 0;
fs = mnt_context_get_fs(cxt);
/* libmount returns read-only pointers (const char)
* so, reallocate for nfsmount() functions.
*/
if ((p = mnt_fs_get_source(fs))) /* spec */
src = strdup(p);
if ((p = mnt_fs_get_target(fs))) /* mountpoint */
tgt = strdup(p);
if ((p = mnt_fs_get_fstype(fs))) /* FS type */
type = strdup(p);
if ((p = mnt_fs_get_fs_options(fs))) /* mount options */
opts = strdup(p);
mnt_context_get_mflags(cxt, &flags); /* mount(2) flags */
fake = mnt_context_is_fake(cxt);
if (string)
ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
else if (strcmp(type, "nfs4") == 0)
ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
else
ret = nfsmount(src, tgt, flags, &opts, fake, bg);
/* Store mount options if not called with mount --no-mtab */
if (!ret && !mnt_context_is_nomtab(cxt))
store_mount_options(fs, opts);
free(src);
free(tgt);
free(type);
free(opts);
return ret;
}
/* returns: error = -1, success = 1 , not vers4 == 0 */
static int is_vers4(struct libmnt_context *cxt)
{
struct libmnt_fs *fs = mnt_context_get_fs(cxt);
struct libmnt_table *tb = NULL;
const char *src = mnt_context_get_source(cxt),
*tgt = mnt_context_get_target(cxt);
int rc = 0;
if (!src || !tgt)
return -1;
if (!mnt_fs_is_kernel(fs)) {
struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
if (!tb)
return -1;
fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
}
if (fs) {
const char *type = mnt_fs_get_fstype(fs);
if (type && strcmp(type, "nfs4") == 0)
rc = 1;
}
mnt_free_table(tb);
return rc;
}
static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
{
int rc, c;
char *spec = NULL, *opts = NULL;
int ret = EX_FAIL;
static const struct option longopts[] = {
{ "force", 0, 0, 'f' },
{ "help", 0, 0, 'h' },
{ "no-mtab", 0, 0, 'n' },
{ "verbose", 0, 0, 'v' },
{ "read-only", 0, 0, 'r' },
{ "lazy", 0, 0, 'l' },
{ "types", 1, 0, 't' },
{ NULL, 0, 0, 0 }
};
mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
rc = mnt_context_helper_setopt(cxt, c, optarg);
if (rc == 0) /* valid option */
continue;
if (rc < 0) /* error (probably ENOMEM) */
goto err;
/* rc==1 means unknow option */
umount_usage();
return EX_USAGE;
}
if (optind < argc)
spec = argv[optind++];
if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
nfs_error(_("%s: no mount point provided"), progname);
return EX_USAGE;
}
if (mnt_context_set_target(cxt, spec))
goto err;
/* read mtab/fstab, evaluate permissions, etc. */
rc = mnt_context_prepare_umount(cxt);
if (rc) {
nfs_error(_("%s: failed to prepare umount: %s\n"),
progname, strerror(-rc));
goto err;
}
if (mnt_context_get_fstype(cxt) &&
!mnt_match_fstype(mnt_context_get_fstype(cxt), "nfs,nfs4")) {
nfs_error(_("%s: %s: is not an NFS filesystem"), progname, spec);
ret = EX_USAGE;
goto err;
}
opts = retrieve_mount_options(mnt_context_get_fs(cxt));
if (!mnt_context_is_lazy(cxt)) {
if (opts) {
/* we have full FS description (e.g. from mtab or /proc) */
switch (is_vers4(cxt)) {
case 0:
/* We ignore the error from nfs_umount23.
* If the actual umount succeeds (in del_mtab),
* we don't want to signal an error, as that
* could cause /sbin/mount to retry!
*/
nfs_umount23(mnt_context_get_source(cxt), opts);
break;
case 1: /* unknown */
break;
default: /* error */
goto err;
}
} else
/* strange, no entry in mtab or /proc not mounted */
nfs_umount23(spec, "tcp,v3");
}
ret = EX_FILEIO;
rc = mnt_context_do_umount(cxt); /* call umount(2) syscall */
mnt_context_finalize_mount(cxt); /* mtab update */
if (rc && !mnt_context_get_status(cxt)) {
/* mnt_context_do_umount() returns errno if umount(2) failed */
umount_error(rc, spec);
goto err;
}
ret = EX_SUCCESS;
err:
free(opts);
return ret;
}
static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
{
int rc, c;
struct libmnt_fs *fs;
char *spec = NULL, *mount_point = NULL, *opts = NULL;
static const struct option longopts[] = {
{ "fake", 0, 0, 'f' },
{ "help", 0, 0, 'h' },
{ "no-mtab", 0, 0, 'n' },
{ "read-only", 0, 0, 'r' },
{ "ro", 0, 0, 'r' },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ "read-write", 0, 0, 'w' },
{ "rw", 0, 0, 'w' },
{ "options", 1, 0, 'o' },
{ "sloppy", 0, 0, 's' },
{ NULL, 0, 0, 0 }
};
mount_config_init(progname);
mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
rc = mnt_context_helper_setopt(cxt, c, optarg);
if (rc == 0) /* valid option */
continue;
if (rc < 0) /* error (probably ENOMEM) */
goto err;
/* rc==1 means unknow option */
switch (c) {
case 'V':
printf("%s: ("PACKAGE_STRING")\n", progname);
return EX_SUCCESS;
case 'h':
default:
mount_usage();
return EX_USAGE;
}
}
if (optind < argc)
spec = argv[optind++];
if (optind < argc)
mount_point = argv[optind++];
if (!mount_point) {
nfs_error(_("%s: no mount point provided"), progname);
goto err;
}
if (!spec) {
nfs_error(_("%s: no mount spec provided"), progname);
goto err;
}
if (geteuid() != 0) {
nfs_error(_("%s: not installed setuid - "
"\"user\" NFS mounts not supported."), progname);
goto err;
}
verbose = mnt_context_is_verbose(cxt);
sloppy = mnt_context_is_sloppy(cxt);
nomtab = mnt_context_is_nomtab(cxt);
if (strcmp(progname, "mount.nfs4") == 0)
mnt_context_set_fstype(cxt, "nfs4");
else
mnt_context_set_fstype(cxt, "nfs"); /* default */
rc = mnt_context_set_source(cxt, spec);
if (!rc)
mnt_context_set_target(cxt, mount_point);
if (rc) {
nfs_error(_("%s: failed to set spec or mountpoint: %s"),
progname, strerror(errno));
goto err;
}
mount_point = mnt_resolve_path(mount_point,
mnt_context_get_cache(cxt));
if (chk_mountpoint(mount_point))
goto err;
/*
* The libmount strictly uses only options from fstab if running in
* restricted mode (suid, non-root user). This is done in
* mnt_context_prepare_mount() by default.
*
* We have to read fstab before nfsmount.conf, otherwise the options
* from nfsmount.conf will be ignored (overwrited).
*/
rc = mnt_context_apply_fstab(cxt);
if (rc) {
nfs_error(_("%s: failed to apply fstab options\n"), progname);
goto err;
}
/*
* Concatenate mount options from the configuration file
*/
fs = mnt_context_get_fs(cxt);
if (fs) {
opts = mnt_fs_strdup_options(fs);
opts = mount_config_opts(spec, mount_point, opts);
mnt_fs_set_options(fs, opts);
}
rc = mnt_context_prepare_mount(cxt);
if (rc) {
nfs_error(_("%s: failed to prepare mount: %s\n"),
progname, strerror(-rc));
goto err;
}
rc = try_mount(cxt, FOREGROUND);
if (rc == EX_BG) {
printf(_("%s: backgrounding \"%s\"\n"),
progname, mnt_context_get_source(cxt));
printf(_("%s: mount options: \"%s\"\n"),
progname, opts);
fflush(stdout);
if (daemon(0, 0)) {
nfs_error(_("%s: failed to start "
"background process: %s\n"),
progname, strerror(errno));
exit(EX_FAIL);
}
rc = try_mount(cxt, BACKGROUND);
if (verbose && rc)
printf(_("%s: giving up \"%s\"\n"),
progname, mnt_context_get_source(cxt));
}
mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
mnt_context_finalize_mount(cxt); /* mtab update */
return rc;
err:
return EX_FAIL;
}
int main(int argc, char *argv[])
{
struct libmnt_context *cxt;
int rc;
mnt_init_debug(0);
cxt = mnt_new_context();
if (!cxt) {
nfs_error(_("Can't initilize libmount: %s"),
strerror(errno));
rc = EX_FAIL;
goto done;
}
progname = basename(argv[0]);
nfs_mount_data_version = discover_nfs_mount_data_version(&string);
if(strncmp(progname, "umount", 6) == 0)
rc = umount_main(cxt, argc, argv);
else
rc = mount_main(cxt, argc, argv);
done:
mnt_free_context(cxt);
return rc;
}