/* * smbmnt.c * * Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke * extensively modified by Tridge * */ #include "includes.h" #include <mntent.h> #include <sys/utsname.h> #include <asm/types.h> #include <asm/posix_types.h> #include <linux/smb.h> #include <linux/smb_mount.h> #include <asm/unistd.h> #ifndef MS_MGC_VAL /* This may look strange but MS_MGC_VAL is what we are looking for and is what we need from <linux/fs.h> under libc systems and is provided in standard includes on glibc systems. So... We switch on what we need... */ #include <linux/fs.h> #endif static uid_t mount_uid; static gid_t mount_gid; static int mount_ro; static unsigned mount_fmask; static unsigned mount_dmask; static int user_mount; static char *options; static void help(void) { printf("\n"); printf("Usage: smbmnt mount-point [options]\n"); printf("Version %s\n\n",VERSION); printf("-s share share name on server\n" "-r mount read-only\n" "-u uid mount as uid\n" "-g gid mount as gid\n" "-f mask permission mask for files\n" "-d mask permission mask for directories\n" "-o options name=value, list of options\n" "-h print this help text\n"); } static int parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share) { int opt; while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF) { switch (opt) { case 's': *share = optarg; break; case 'u': if (!user_mount) { mount_uid = strtol(optarg, NULL, 0); } break; case 'g': if (!user_mount) { mount_gid = strtol(optarg, NULL, 0); } break; case 'r': mount_ro = 1; break; case 'f': mount_fmask = strtol(optarg, NULL, 8); break; case 'd': mount_dmask = strtol(optarg, NULL, 8); break; case 'o': options = optarg; break; default: return -1; } } return 0; } static char * fullpath(const char *p) { char path[MAXPATHLEN]; if (strlen(p) > MAXPATHLEN-1) { return NULL; } if (realpath(p, path) == NULL) { fprintf(stderr,"Failed to find real path for mount point\n"); exit(1); } return strdup(path); } /* Check whether user is allowed to mount on the specified mount point. If it's OK then we change into that directory - this prevents race conditions */ static int mount_ok(char *mount_point) { struct stat st; if (chdir(mount_point) != 0) { return -1; } if (stat(".", &st) != 0) { return -1; } if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return -1; } if ((getuid() != 0) && ((getuid() != st.st_uid) || ((st.st_mode & S_IRWXU) != S_IRWXU))) { errno = EPERM; return -1; } return 0; } /* Tries to mount using the appropriate format. For 2.2 the struct, for 2.4 the ascii version. */ static int do_mount(char *share_name, unsigned int flags, struct smb_mount_data *data) { pstring opts; struct utsname uts; char *release, *major, *minor; char *data1, *data2; uname(&uts); release = uts.release; major = strtok(release, "."); minor = strtok(NULL, "."); if (major && minor && atoi(major) == 2 && atoi(minor) < 4) { /* < 2.4, assume struct */ data1 = (char *) data; data2 = opts; } else { /* >= 2.4, assume ascii but fall back on struct */ data1 = opts; data2 = (char *) data; } slprintf(opts, sizeof(opts)-1, "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s", data->uid, data->gid, data->file_mode, data->dir_mode,options); if (mount(share_name, ".", "smbfs", flags, data1) == 0) return 0; return mount(share_name, ".", "smbfs", flags, data2); } int main(int argc, char *argv[]) { char *mount_point, *share_name = NULL; FILE *mtab; int fd; unsigned int flags; struct smb_mount_data data; struct mntent ment; memset(&data, 0, sizeof(struct smb_mount_data)); if (argc < 2) { help(); exit(1); } if (argv[1][0] == '-') { help(); exit(1); } if (getuid() != 0) { user_mount = 1; } if (geteuid() != 0) { fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid()); exit(1); } mount_uid = getuid(); mount_gid = getgid(); mount_fmask = umask(0); umask(mount_fmask); mount_fmask = ~mount_fmask; mount_point = fullpath(argv[1]); argv += 1; argc -= 1; if (mount_ok(mount_point) != 0) { fprintf(stderr, "cannot mount on %s: %s\n", mount_point, strerror(errno)); exit(1); } data.version = SMB_MOUNT_VERSION; /* getuid() gives us the real uid, who may umount the fs */ data.mounted_uid = getuid(); if (parse_args(argc, argv, &data, &share_name) != 0) { help(); return -1; } data.uid = mount_uid; data.gid = mount_gid; data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask; data.dir_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask; if (mount_dmask == 0) { data.dir_mode = data.file_mode; if ((data.dir_mode & S_IRUSR) != 0) data.dir_mode |= S_IXUSR; if ((data.dir_mode & S_IRGRP) != 0) data.dir_mode |= S_IXGRP; if ((data.dir_mode & S_IROTH) != 0) data.dir_mode |= S_IXOTH; } flags = MS_MGC_VAL; if (mount_ro) flags |= MS_RDONLY; if (do_mount(share_name, flags, &data) < 0) { switch (errno) { case ENODEV: fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n"); break; default: perror("mount error"); } fprintf(stderr, "Please refer to the smbmnt(8) manual page\n"); return -1; } ment.mnt_fsname = share_name ? share_name : "none"; ment.mnt_dir = mount_point; ment.mnt_type = "smbfs"; ment.mnt_opts = ""; ment.mnt_freq = 0; ment.mnt_passno= 0; mount_point = ment.mnt_dir; if (mount_point == NULL) { fprintf(stderr, "Mount point too long\n"); return -1; } if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) { fprintf(stderr, "Can't get "MOUNTED"~ lock file"); return 1; } close(fd); if ((mtab = setmntent(MOUNTED, "a+")) == NULL) { fprintf(stderr, "Can't open " MOUNTED); return 1; } if (addmntent(mtab, &ment) == 1) { fprintf(stderr, "Can't write mount entry"); return 1; } if (fchmod(fileno(mtab), 0644) == -1) { fprintf(stderr, "Can't set perms on "MOUNTED); return 1; } endmntent(mtab); if (unlink(MOUNTED"~") == -1) { fprintf(stderr, "Can't remove "MOUNTED"~"); return 1; } return 0; }