#include <sys/mount.h>
#include <getopt.h>
#include <mntent.h>
+#include <pwd.h>
#include "fstab.h"
#include "xcommon.h"
int mask; /* flag mask value */
};
+/* Custom mount options for our own purposes. */
+#define MS_DUMMY 0x00000000
+#define MS_USERS 0x40000000
+#define MS_USER 0x80000000
+
static const struct opt_map opt_map[] = {
{ "defaults", 0, 0, 0 }, /* default options */
{ "ro", 1, 0, MS_RDONLY }, /* read-only */
{ "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
{ "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 */
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;
ment.mnt_freq = 0;
ment.mnt_passno= 0;
+ if(flags & MS_REMOUNT) {
+ update_mtab(ment.mnt_dir, &ment);
+ return 0;
+ }
+
lock_mtab();
if ((mtab = setmntent(MOUNTED, "a+")) == NULL) {
{
if (options != NULL) {
char *opts = xstrdup(options);
- char *opt;
- int len = strlen(opts) + 20;
+ 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 (opt = strtok(opts, ","); opt; opt = strtok(NULL, ","))
- parse_opt(opt, flags, *extra_opts, len);
-
+ 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 void mount_error(char *node)
{
switch(errno) {
case ENOTDIR:
- printf("%s: mount point %s is not a directory\n", progname, node);
+ fprintf(stderr, "%s: mount point %s is not a directory\n", progname, node);
break;
case EBUSY:
- printf("%s: %s is already mounted or busy\n", progname, node);
+ fprintf(stderr, "%s: %s is already mounted or busy\n", progname, node);
break;
case ENOENT:
- printf("%s: mount point %s does not exist\n", progname, node);
+ fprintf(stderr, "%s: mount point %s does not exist\n", progname, node);
break;
default:
- printf("%s: %s\n", progname, strerror(errno));
+ fprintf(stderr, "%s: %s\n", progname, strerror(errno));
}
}
-static void start_statd()
+extern u_short getport(
+ struct sockaddr_in *saddr,
+ u_long prog,
+ u_long vers,
+ u_int prot);
+
+static int probe_statd()
+{
+ struct sockaddr_in addr;
+ u_short port;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ port = getport(&addr, 100024, 1, IPPROTO_UDP);
+
+ if (port == 0)
+ return 0;
+ addr.sin_port = htons(port);
+
+ if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
+ return 0;
+
+ return 1;
+}
+
+static int start_statd()
{
/* If /var/run/rpc.statd.pid exists and is non-empty,
* assume statd already running.
* else run that file (typically a shell script)
*/
struct stat stb;
- if (stat("/var/run/rpc.statd.pid", &stb) == 0 &&
- stb.st_size > 0)
- return;
+
+ if (probe_statd())
+ return 1;
#ifdef START_STATD
if (stat(START_STATD, &stb) ==0 &&
S_ISREG(stb.st_mode) &&
- (stb.st_mode & S_IXUSR))
+ (stb.st_mode & S_IXUSR)) {
system(START_STATD);
+ if (probe_statd())
+ return 1;
+ }
#endif
+ return 0;
}
int main(int argc, char *argv[])
int c, flags = 0, nfs_mount_vers = 0, mnt_err = 1, fake = 0;
char *spec, *mount_point, *extra_opts = NULL;
char *mount_opts = NULL, *p;
+ uid_t uid = getuid();
progname = argv[0];
+ if (!progname)
+ exit(2);
+
if ((p = strrchr(progname, '/')) != NULL)
progname = p+1;
- if (getuid() != 0) {
- printf("%s: only root can do that.\n", progname);
- exit(1);
- }
-
if(!strncmp(progname, "umount", strlen("umount"))) {
if(argc < 2) {
umount_usage();
exit(nfsumount(argc, argv) ? 0 : 1);
}
- if ((argc < 2)) {
- mount_usage();
- exit(1);
- }
-
- if(argv[1][0] == '-') {
+ if(argv[1] && argv[1][0] == '-') {
if(argv[1][1] == 'V')
printf("%s ("PACKAGE_STRING")\n", progname);
else
return 0;
}
+ if ((argc < 3)) {
+ mount_usage();
+ exit(1);
+ }
+
+ spec = argv[1];
+ mount_point = argv[2];
+
+ argv[2] = argv[0]; /* so that getopt error messages are correct */
while ((c = getopt_long (argc - 2, argv + 2, "rt:vVwfno:hs",
longopts, NULL)) != -1) {
switch (c) {
flags |= MS_RDONLY;
break;
case 't':
- nfs_mount_vers = (strncmp(optarg, "nfs4", 4)) ? 0 : 4;
+ if (strcmp(optarg, "nfs4") == 0)
+ nfs_mount_vers = 4;
+ else if (strcmp(optarg, "nfs") == 0)
+ nfs_mount_vers = 0;
+ else {
+ fprintf(stderr, "%s: unknown filesystem type: %s\n",
+ progname, optarg);
+ exit(1);
+ }
break;
case 'v':
++verbose;
exit(1);
}
}
+ if (optind != argc-2) {
+ /* Extra non-option words at the end... */
+ mount_usage();
+ exit(1);
+ }
- spec = argv[1];
- mount_point = canonicalize(argv[2]);
+ if (strcmp(progname, "mount.nfs4") == 0)
+ nfs_mount_vers = 4;
+
+ if (uid != 0) {
+ /* don't even think about it unless options exactly
+ * match fstab
+ */
+ struct mntentchn *mc;
+
+ if ((mc = getfsfile(mount_point)) == NULL ||
+ strcmp(mc->m.mnt_fsname, spec) != 0 ||
+ strcmp(mc->m.mnt_type, (nfs_mount_vers == 4 ? "nfs4":"nfs")) != 0
+ ) {
+ fprintf(stderr, "%s: permission died - no match for fstab\n",
+ progname);
+ exit(1);
+ }
+ /* '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 fstab
+ */
+ mount_opts = strdup(mc->m.mnt_opts);
+ mounttype = 0;
+ }
+
+ mount_point = canonicalize(mount_point);
+ if (mount_point == NULL ||
+ mount_point[0] != '/') {
+ fprintf(stderr, "%s: unknown mount point %s\n",
+ progname, mount_point ? : "");
+ exit(1);
+ }
parse_opts(mount_opts, &flags, &extra_opts);
- if (!strcmp(progname, "mount.nfs4") || nfs_mount_vers == 4) {
- nfs_mount_vers = 4;
- mnt_err = nfs4mount(spec, mount_point, &flags, &extra_opts, &mount_opts, 0);
+ if (uid != 0) {
+ if (! (flags & (MS_USERS | MS_USER))) {
+ fprintf(stderr, "%s: permission denied\n", progname);
+ exit(1);
+ }
}
- else if (!strcmp(progname, "mount.nfs")) {
+
+ if (nfs_mount_vers == 4)
+ mnt_err = nfs4mount(spec, mount_point, &flags, &extra_opts, &mount_opts, 0);
+ else {
int need_statd = 0;
mnt_err = nfsmount(spec, mount_point, &flags,
- &extra_opts, &mount_opts, &nfs_mount_vers,
+ &extra_opts, &mount_opts,
0, &need_statd);
- if (!mnt_err && !fake && need_statd)
- start_statd();
+ if (!mnt_err && !fake && need_statd) {
+ if (!start_statd()) {
+ fprintf(stderr,
+ "%s: rpc.statd is not running but is "
+ "required for remote locking\n"
+ " Either use \"-o nolocks\" to keep "
+ "locks local, or start statd.\n",
+ progname);
+ exit(1);
+ }
+ }
}
- if (fake)
- return 0;
if (mnt_err)
exit(EX_FAIL);
- mnt_err = do_mount_syscall(spec, mount_point,
- nfs_mount_vers == 4 ? "nfs4" : "nfs",
- flags, mount_opts);
-
- if (mnt_err) {
- mount_error(mount_point);
- exit(EX_FAIL);
+ if (!fake) {
+ mnt_err = do_mount_syscall(spec, mount_point,
+ nfs_mount_vers == 4 ? "nfs4" : "nfs",
+ flags & ~(MS_USER|MS_USERS) ,
+ mount_opts);
+
+ if (mnt_err) {
+ mount_error(mount_point);
+ exit(EX_FAIL);
+ }
}
if (!nomtab)