| /* |
| * mount.c -- Linux NFS mount |
| * |
| * Copyright (C) 2006 Amit Gud <agud@redhat.com> |
| * |
| * - Basic code and wrapper around mount and umount code of NFS. |
| * Based on util-linux/mount/mount.c. |
| * |
| * 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. |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/mount.h> |
| #include <getopt.h> |
| #include <mntent.h> |
| #include <pwd.h> |
| |
| #include "fstab.h" |
| #include "xcommon.h" |
| #include "nls.h" |
| #include "mount_constants.h" |
| #include "mount_config.h" |
| #include "nfs_paths.h" |
| #include "nfs_mntent.h" |
| |
| #include "nfs_mount.h" |
| #include "nfs4_mount.h" |
| #include "mount.h" |
| #include "error.h" |
| #include "stropts.h" |
| #include "version.h" |
| |
| char *progname; |
| int nfs_mount_data_version; |
| int nomtab; |
| int verbose; |
| int sloppy; |
| int string; |
| |
| #define FOREGROUND (0) |
| #define BACKGROUND (1) |
| |
| static 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' }, |
| { NULL, 0, 0, 0 } |
| }; |
| |
| /* |
| * Map from -o and fstab option strings to the flag argument to mount(2). |
| */ |
| struct opt_map { |
| const char *opt; /* option name */ |
| int skip; /* skip in mtab option string */ |
| int inv; /* true if flag value should be inverted */ |
| int mask; /* flag mask value */ |
| }; |
| |
| static const struct opt_map opt_map[] = { |
| { "defaults", 0, 0, 0 }, /* default options */ |
| { "ro", 1, 0, MS_RDONLY }, /* read-only */ |
| { "rw", 1, 1, MS_RDONLY }, /* read-write */ |
| { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ |
| { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ |
| { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ |
| { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ |
| { "dev", 0, 1, MS_NODEV }, /* interpret device files */ |
| { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ |
| { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ |
| { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ |
| { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */ |
| { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ |
| { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */ |
| { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */ |
| { "auto", 0, 0, MS_DUMMY }, /* Can be mounted using -a */ |
| { "noauto", 0, 0, MS_DUMMY }, /* Can only be mounted explicitly */ |
| { "users", 1, 0, MS_USERS }, /* Allow ordinary user to mount */ |
| { "nousers", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */ |
| { "user", 1, 0, MS_USER }, /* Allow ordinary user to mount */ |
| { "nouser", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */ |
| { "owner", 0, 0, MS_DUMMY }, /* Let the owner of the device mount */ |
| { "noowner", 0, 0, MS_DUMMY }, /* Device owner has no special privs */ |
| { "group", 0, 0, MS_DUMMY }, /* Let the group of the device mount */ |
| { "nogroup", 0, 0, MS_DUMMY }, /* Device group has no special privs */ |
| { "_netdev", 0, 0, MS_DUMMY}, /* Device requires network */ |
| { "comment", 0, 0, MS_DUMMY}, /* fstab comment only (kudzu,_netdev)*/ |
| |
| /* add new options here */ |
| #ifdef MS_NOSUB |
| { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ |
| { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ |
| #endif |
| #ifdef MS_SILENT |
| { "quiet", 0, 0, MS_SILENT }, /* be quiet */ |
| { "loud", 0, 1, MS_SILENT }, /* print out messages. */ |
| #endif |
| #ifdef MS_MANDLOCK |
| { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ |
| { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ |
| #endif |
| { "loop", 1, 0, MS_DUMMY }, /* use a loop device */ |
| #ifdef MS_NOATIME |
| { "atime", 0, 1, MS_NOATIME }, /* Update access time */ |
| { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ |
| #endif |
| #ifdef MS_NODIRATIME |
| { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ |
| { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ |
| #endif |
| #ifdef MS_RELATIME |
| { "relatime", 0, 0, MS_RELATIME }, /* Update access times relative to |
| mtime/ctime */ |
| { "norelatime", 0, 1, MS_RELATIME }, /* Update access time without regard |
| to mtime/ctime */ |
| #endif |
| { "noquota", 0, 0, MS_DUMMY }, /* Don't enforce quota */ |
| { "quota", 0, 0, MS_DUMMY }, /* Enforce user quota */ |
| { "usrquota", 0, 0, MS_DUMMY }, /* Enforce user quota */ |
| { "grpquota", 0, 0, MS_DUMMY }, /* Enforce group quota */ |
| { NULL, 0, 0, 0 } |
| }; |
| |
| static void parse_opts(const char *options, int *flags, char **extra_opts); |
| |
| /* |
| * Choose the version of the nfs_mount_data structure that is appropriate |
| * for the kernel that is doing the mount. |
| * |
| * NFS_MOUNT_VERSION: maximum version supported by these sources |
| * nfs_mount_data_version: maximum version supported by the running kernel |
| */ |
| static void discover_nfs_mount_data_version(void) |
| { |
| unsigned int kernel_version = linux_version_code(); |
| |
| if (kernel_version) { |
| if (kernel_version < MAKE_VERSION(2, 1, 32)) |
| nfs_mount_data_version = 1; |
| else if (kernel_version < MAKE_VERSION(2, 2, 18)) |
| nfs_mount_data_version = 3; |
| else if (kernel_version < MAKE_VERSION(2, 3, 0)) |
| nfs_mount_data_version = 4; |
| else if (kernel_version < MAKE_VERSION(2, 3, 99)) |
| nfs_mount_data_version = 3; |
| else if (kernel_version < MAKE_VERSION(2, 6, 3)) |
| nfs_mount_data_version = 4; |
| else |
| nfs_mount_data_version = 6; |
| } |
| if (nfs_mount_data_version > NFS_MOUNT_VERSION) |
| nfs_mount_data_version = NFS_MOUNT_VERSION; |
| else |
| if (kernel_version > MAKE_VERSION(2, 6, 22)) |
| string++; |
| } |
| |
| static void print_one(char *spec, char *node, char *type, char *opts) |
| { |
| if (!verbose) |
| return; |
| |
| if (opts) |
| printf(_("%s on %s type %s (%s)\n"), spec, node, type, opts); |
| else |
| printf(_("%s on %s type %s\n"), spec, node, type); |
| } |
| |
| /* |
| * Build a canonical mount option string for /etc/mtab. |
| */ |
| static char *fix_opts_string(int flags, const char *extra_opts) |
| { |
| const struct opt_map *om; |
| char *new_opts; |
| |
| new_opts = xstrdup((flags & MS_RDONLY) ? "ro" : "rw"); |
| if (flags & MS_USER) { |
| /* record who mounted this so they can unmount */ |
| struct passwd *pw = getpwuid(getuid()); |
| if(pw) |
| new_opts = xstrconcat3(new_opts, ",user=", pw->pw_name); |
| } |
| if (flags & MS_USERS) |
| new_opts = xstrconcat3(new_opts, ",users", ""); |
| |
| for (om = opt_map; om->opt != NULL; om++) { |
| if (om->skip) |
| continue; |
| if (om->inv || !om->mask || (flags & om->mask) != om->mask) |
| continue; |
| new_opts = xstrconcat3(new_opts, ",", om->opt); |
| flags &= ~om->mask; |
| } |
| if (extra_opts && *extra_opts) { |
| new_opts = xstrconcat3(new_opts, ",", extra_opts); |
| } |
| return new_opts; |
| } |
| |
| /* Create mtab with a root entry. */ |
| static void |
| create_mtab (void) { |
| struct mntentchn *fstab; |
| struct mntent mnt; |
| int flags; |
| mntFILE *mfp; |
| |
| lock_mtab(); |
| |
| mfp = nfs_setmntent (MOUNTED, "a+"); |
| if (mfp == NULL || mfp->mntent_fp == NULL) { |
| int errsv = errno; |
| die (EX_FILEIO, _("mount: can't open %s for writing: %s"), |
| MOUNTED, strerror (errsv)); |
| } |
| |
| /* Find the root entry by looking it up in fstab */ |
| if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) { |
| char *extra_opts; |
| parse_opts (fstab->m.mnt_opts, &flags, &extra_opts); |
| mnt.mnt_dir = "/"; |
| mnt.mnt_fsname = xstrdup(fstab->m.mnt_fsname); |
| mnt.mnt_type = fstab->m.mnt_type; |
| mnt.mnt_opts = fix_opts_string (flags, extra_opts); |
| mnt.mnt_freq = mnt.mnt_passno = 0; |
| free(extra_opts); |
| |
| if (nfs_addmntent (mfp, &mnt) == 1) { |
| int errsv = errno; |
| die (EX_FILEIO, _("mount: error writing %s: %s"), |
| _PATH_MOUNTED, strerror (errsv)); |
| } |
| } |
| if (fchmod (fileno (mfp->mntent_fp), 0644) < 0) |
| if (errno != EROFS) { |
| int errsv = errno; |
| die (EX_FILEIO, |
| _("mount: error changing mode of %s: %s"), |
| _PATH_MOUNTED, strerror (errsv)); |
| } |
| nfs_endmntent (mfp); |
| |
| unlock_mtab(); |
| |
| reset_mtab_info(); |
| } |
| |
| static int add_mtab(char *spec, char *mount_point, char *fstype, |
| int flags, char *opts, int freq, int pass) |
| { |
| struct mntent ment; |
| int result = EX_SUCCESS; |
| |
| ment.mnt_fsname = spec; |
| ment.mnt_dir = mount_point; |
| ment.mnt_type = fstype; |
| ment.mnt_opts = fix_opts_string(flags, opts); |
| ment.mnt_freq = freq; |
| ment.mnt_passno = pass; |
| |
| if (!nomtab && mtab_does_not_exist()) { |
| if (verbose > 1) |
| printf(_("mount: no %s found - creating it..\n"), |
| MOUNTED); |
| create_mtab (); |
| } |
| |
| if (!nomtab && mtab_is_writable()) { |
| if (flags & MS_REMOUNT) |
| update_mtab(ment.mnt_dir, &ment); |
| else { |
| mntFILE *mtab; |
| |
| lock_mtab(); |
| mtab = nfs_setmntent(MOUNTED, "a+"); |
| if (mtab == NULL || mtab->mntent_fp == NULL) { |
| nfs_error(_("Can't open mtab: %s"), |
| strerror(errno)); |
| result = EX_FILEIO; |
| } else { |
| if (nfs_addmntent(mtab, &ment) == 1) { |
| nfs_error(_("Can't write mount entry to mtab: %s"), |
| strerror(errno)); |
| result = EX_FILEIO; |
| } |
| } |
| nfs_endmntent(mtab); |
| unlock_mtab(); |
| } |
| } |
| |
| free(ment.mnt_opts); |
| |
| return result; |
| } |
| |
| void mount_usage(void) |
| { |
| printf(_("usage: %s remotetarget dir [-rvVwfnsih] [-o nfsoptions]\n"), |
| progname); |
| printf(_("options:\n")); |
| printf(_("\t-r\t\tMount file system readonly\n")); |
| printf(_("\t-v\t\tVerbose\n")); |
| printf(_("\t-V\t\tPrint version\n")); |
| printf(_("\t-w\t\tMount file system read-write\n")); |
| printf(_("\t-f\t\tFake mount, do not actually mount\n")); |
| printf(_("\t-n\t\tDo not update /etc/mtab\n")); |
| printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n")); |
| printf(_("\t-h\t\tPrint this help\n")); |
| printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n")); |
| } |
| |
| static void parse_opt(const char *opt, int *mask, char *extra_opts, int len) |
| { |
| const struct opt_map *om; |
| |
| for (om = opt_map; om->opt != NULL; om++) { |
| if (!strcmp (opt, om->opt)) { |
| if (om->inv) |
| *mask &= ~om->mask; |
| else |
| *mask |= om->mask; |
| return; |
| } |
| } |
| |
| len -= strlen(extra_opts); |
| |
| if (*extra_opts && --len > 0) |
| strcat(extra_opts, ","); |
| |
| if ((len -= strlen(opt)) > 0) |
| strcat(extra_opts, opt); |
| } |
| |
| /* |
| * Convert the provided mount command-line options into the 4th & |
| * 5th arguments to mount(2). Output parameter "@flags" gets the |
| * standard options (indicated by MS_ bits), and output parameter |
| * "@extra_opts" gets all the filesystem-specific options. |
| */ |
| static void parse_opts(const char *options, int *flags, char **extra_opts) |
| { |
| if (options != NULL) { |
| char *opts = xstrdup(options); |
| char *opt, *p; |
| int len = strlen(opts) + 1; /* include room for a null */ |
| int open_quote = 0; |
| |
| *extra_opts = xmalloc(len); |
| **extra_opts = '\0'; |
| |
| for (p = opts, opt = NULL; p && *p; p++) { |
| if (!opt) |
| opt = p; /* begin of the option item */ |
| if (*p == '"') |
| open_quote ^= 1; /* reverse the status */ |
| if (open_quote) |
| continue; /* still in a quoted block */ |
| if (*p == ',') |
| *p = '\0'; /* terminate the option item */ |
| |
| /* end of option item or last item */ |
| if (*p == '\0' || *(p + 1) == '\0') { |
| parse_opt(opt, flags, *extra_opts, len); |
| opt = NULL; |
| } |
| } |
| free(opts); |
| } |
| } |
| |
| static int chk_mountpoint(char *mount_point) |
| { |
| struct stat sb; |
| |
| if (stat(mount_point, &sb) < 0){ |
| mount_error(NULL, mount_point, errno); |
| return 1; |
| } |
| if (S_ISDIR(sb.st_mode) == 0){ |
| mount_error(NULL, mount_point, ENOTDIR); |
| return 1; |
| } |
| if (access(mount_point, X_OK) < 0) { |
| mount_error(NULL, mount_point, errno); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int try_mount(char *spec, char *mount_point, int flags, |
| char *fs_type, char **extra_opts, char *mount_opts, |
| int fake, int bg) |
| { |
| int ret; |
| |
| if (string) |
| ret = nfsmount_string(spec, mount_point, fs_type, flags, |
| extra_opts, fake, bg); |
| else { |
| if (strcmp(fs_type, "nfs4") == 0) |
| ret = nfs4mount(spec, mount_point, flags, |
| extra_opts, fake, bg); |
| else |
| ret = nfsmount(spec, mount_point, flags, |
| extra_opts, fake, bg); |
| } |
| |
| if (ret) |
| return ret; |
| |
| if (!fake) |
| print_one(spec, mount_point, fs_type, mount_opts); |
| |
| ret = add_mtab(spec, mount_point, fs_type, flags, *extra_opts, |
| 0, 0 /* these are always zero for NFS */ ); |
| return ret; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int c, flags = 0, mnt_err = 1, fake = 0; |
| char *spec, *mount_point, *fs_type = "nfs"; |
| char *extra_opts = NULL, *mount_opts = NULL; |
| uid_t uid = getuid(); |
| |
| progname = basename(argv[0]); |
| |
| discover_nfs_mount_data_version(); |
| |
| if(!strncmp(progname, "umount", strlen("umount"))) |
| exit(nfsumount(argc, argv)); |
| |
| if (argv[1] && argv[1][0] == '-') { |
| if(argv[1][1] == 'V') |
| printf("%s ("PACKAGE_STRING")\n", progname); |
| else |
| mount_usage(); |
| exit(EX_SUCCESS); |
| } |
| |
| if ((argc < 3)) { |
| mount_usage(); |
| exit(EX_USAGE); |
| } |
| |
| spec = argv[1]; |
| mount_point = argv[2]; |
| |
| mount_config_init(progname); |
| |
| argv[2] = argv[0]; /* so that getopt error messages are correct */ |
| while ((c = getopt_long(argc - 2, argv + 2, "rvVwfno:hs", |
| longopts, NULL)) != -1) { |
| switch (c) { |
| case 'r': |
| flags |= MS_RDONLY; |
| break; |
| case 'v': |
| ++verbose; |
| break; |
| case 'V': |
| printf("%s: ("PACKAGE_STRING")\n", progname); |
| exit(EX_SUCCESS); |
| case 'w': |
| flags &= ~MS_RDONLY; |
| break; |
| case 'f': |
| ++fake; |
| break; |
| case 'n': |
| ++nomtab; |
| break; |
| case 'o': /* specify mount options */ |
| if (mount_opts) |
| mount_opts = xstrconcat3(mount_opts, ",", optarg); |
| else |
| mount_opts = xstrdup(optarg); |
| break; |
| case 's': |
| ++sloppy; |
| break; |
| case 'h': |
| default: |
| mount_usage(); |
| goto out_usage; |
| } |
| } |
| |
| /* |
| * Extra non-option words at the end are bogus... |
| */ |
| if (optind != argc - 2) { |
| mount_usage(); |
| goto out_usage; |
| } |
| |
| if (strcmp(progname, "mount.nfs4") == 0) |
| fs_type = "nfs4"; |
| |
| /* |
| * If a non-root user is attempting to mount, make sure the |
| * user's requested options match the options specified in |
| * /etc/fstab; otherwise, don't allow the mount. |
| */ |
| if (uid != 0) { |
| struct mntentchn *mc; |
| |
| if ((mc = getfsfile(mount_point)) == NULL || |
| strcmp(mc->m.mnt_fsname, spec) != 0 || |
| strcmp(mc->m.mnt_type, fs_type) != 0) { |
| nfs_error(_("%s: permission denied: no match for %s " |
| "found in /etc/fstab"), progname, mount_point); |
| goto out_usage; |
| } |
| |
| /* |
| * 'mount' munges the options from fstab before passing them |
| * to us, so it is non-trivial to test that we have the correct |
| * set of options and we don't want to trust what the user |
| * gave us, so just take whatever is in /etc/fstab. |
| */ |
| mount_opts = strdup(mc->m.mnt_opts); |
| } |
| |
| mount_point = canonicalize(mount_point); |
| if (!mount_point) { |
| nfs_error(_("%s: no mount point provided"), progname); |
| goto out_usage; |
| } |
| if (mount_point[0] != '/') { |
| nfs_error(_("%s: unrecognized mount point %s"), |
| progname, mount_point); |
| mnt_err = EX_USAGE; |
| goto out; |
| } |
| /* |
| * Concatenate mount options from the configuration file |
| */ |
| mount_opts = mount_config_opts(spec, mount_point, mount_opts); |
| |
| parse_opts(mount_opts, &flags, &extra_opts); |
| |
| if (uid != 0) { |
| if (!(flags & (MS_USERS|MS_USER))) { |
| nfs_error(_("%s: permission denied"), progname); |
| mnt_err = EX_USAGE; |
| goto out; |
| } |
| |
| if (geteuid() != 0) { |
| nfs_error(_("%s: not installed setuid - " |
| "\"user\" NFS mounts not supported."), progname); |
| exit(EX_FAIL); |
| } |
| } |
| |
| if (chk_mountpoint(mount_point)) { |
| mnt_err = EX_USAGE; |
| goto out; |
| } |
| |
| mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts, |
| mount_opts, fake, FOREGROUND); |
| if (mnt_err == EX_BG) { |
| printf(_("%s: backgrounding \"%s\"\n"), |
| progname, spec); |
| printf(_("%s: mount options: \"%s\"\n"), |
| progname, extra_opts); |
| |
| fflush(stdout); |
| |
| /* |
| * Parent exits immediately with success. |
| */ |
| if (daemon(0, 0)) { |
| nfs_error(_("%s: failed to start " |
| "background process: %s\n"), |
| progname, strerror(errno)); |
| exit(EX_FAIL); |
| } |
| |
| mnt_err = try_mount(spec, mount_point, flags, fs_type, |
| &extra_opts, mount_opts, fake, |
| BACKGROUND); |
| if (verbose && mnt_err) |
| printf(_("%s: giving up \"%s\"\n"), |
| progname, spec); |
| } |
| |
| out: |
| free(mount_opts); |
| free(extra_opts); |
| free(mount_point); |
| exit(mnt_err); |
| |
| out_usage: |
| free(mount_opts); |
| exit(EX_USAGE); |
| } |