#include <fcntl.h>
#include <limits.h>
#include <paths.h>
+#include <libgen.h>
+#include <time.h>
#include <sys/mman.h>
#include <sys/wait.h>
+#ifdef HAVE_SYS_FSUID_H
+#include <sys/fsuid.h>
+#endif /* HAVE_SYS_FSUID_H */
#ifdef HAVE_LIBCAP_NG
#include <cap-ng.h>
#else /* HAVE_LIBCAP_NG */
#define MTAB_OPTIONS_LEN 220
/*
- * Maximum length of "share" portion of a UNC. I have no idea if this is at
- * all valid. According to MSDN, the typical max length of any component is
- * 255, so use that here.
+ * Max share name, username, password and domain sizes match the kernel's
+ * allowances for these string sizes which in turn match Microsoft's
+ * documentation.
*/
+
+/* Max length of the share name portion of a UNC. Share names over 80
+ * characters cannot be accessed via commandline in Windows 2000/XP. */
#define MAX_SHARE_LEN 256
-/* max length of username (somewhat made up here) */
-#define MAX_USERNAME_SIZE 32
+/* Max user name length. */
+#define MAX_USERNAME_SIZE 256
-#ifndef SAFE_FREE
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x = NULL; } } while (0)
-#endif
+/* Max domain size. */
+#define MAX_DOMAIN_SIZE 256
-#define MOUNT_PASSWD_SIZE 128
-#define MAX_DOMAIN_SIZE 64
-
-/*
- * value of the ver= option that gets passed to the kernel. Used to indicate
- * behavioral changes introduced in the mount helper.
- */
-#define OPTIONS_VERSION "1"
+/* Max password size. */
+#define MOUNT_PASSWD_SIZE 512
/*
* mount.cifs has been the subject of many "security" bugs that have arisen
#define OPT_REMOUNT 26
#define OPT_MAND 27
#define OPT_NOMAND 28
+#define OPT_CRUID 29
+#define OPT_BKUPUID 30
+#define OPT_BKUPGID 31
+#define OPT_NOFAIL 32
+#define OPT_SNAPSHOT 33
+
+#define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
+
+#define GMT_NAME_LEN 24 /* length of a @GMT- name */
+#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
+#define NTFS_TIME_OFFSET ((unsigned long long)(369*365 + 89) * 24 * 3600 * 10000000)
-/* struct for holding parsed mount info for use by privleged process */
+/* struct for holding parsed mount info for use by privileged process */
struct parsed_mount_info {
unsigned long flags;
char host[NI_MAXHOST + 1];
unsigned int fakemnt:1;
unsigned int nomtab:1;
unsigned int verboseflag:1;
+ unsigned int nofail:1;
+ unsigned int got_domain:1;
};
-const char *thisprogram;
-const char *cifs_fstype = "cifs";
-const char *smb2_fstype = "smb2";
+static const char *thisprogram;
+static const char *cifs_fstype;
-static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
+static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info, const char *progname);
static int check_setuid(void)
{
#if CIFS_DISABLE_SETUID_CAPABILITY
if (getuid() && !geteuid()) {
- printf("This mount.cifs program has been built with the "
+ printf("This program has been built with the "
"ability to run as a setuid root program disabled.\n");
return EX_USAGE;
}
* set of options. We don't want to trust what the user
* gave us, so just take whatever is in /etc/fstab.
*/
- free(*options);
*options = strdup(mnt->mnt_opts);
return 0;
}
BB end finish BB */
-static int mount_cifs_usage(FILE * stream)
+static int mount_usage(FILE * stream)
{
fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
thisprogram);
fprintf(stream,
"\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
fprintf(stream,
- "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
+ "\n\tnoserverino,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
fprintf(stream,
- "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,fsc");
+ "\n\tcache=<strict|none|loose>,nounix,cifsacl,sec=<authentication mechanism>,");
+ fprintf(stream,
+ "\n\tsign,seal,fsc,snapshot=<token|time>,nosharesock,persistenthandles,");
+ fprintf(stream,
+ "\n\tresilienthandles,rdma,vers=<smb_dialect>,cruid");
fprintf(stream,
"\n\nOptions not needed for servers supporting CIFS Unix extensions");
fprintf(stream,
"\n\t(e.g. unneeded for mounts to most Samba versions):");
fprintf(stream,
- "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
+ "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu,");
+ fprintf(stream,
+ "\n\tmfsymlinks,idsfromsid");
fprintf(stream, "\n\nRarely used options:");
fprintf(stream,
"\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
fprintf(stream,
"\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
fprintf(stream,
- "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
- fprintf(stream,
- "\n\nOptions are described in more detail in the manual page");
- fprintf(stream, "\n\tman 8 mount.cifs\n");
- fprintf(stream, "\nTo display the version number of the mount helper:");
- fprintf(stream, "\n\t%s -V\n", thisprogram);
-
- if (stream == stderr)
- return EX_USAGE;
- return 0;
-}
-
-static int mount_smb2_usage(FILE *stream)
-{
- fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n",
- thisprogram);
- fprintf(stream, "\nMount the remote target, specified as a UNC name,");
- fprintf(stream, " to a local directory.\n\nOptions:\n");
- fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
- fprintf(stream, "\nLess commonly used options:");
- fprintf(stream,
- "\n\tcredentials=<filename>,guest,perm,noperm,rw,ro,");
- fprintf(stream,
- "\n\tsep=<char>,iocharset=<codepage>,exec,noexec");
+ "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl,");
fprintf(stream,
- "\n\tnolock,directio,sec=<authentication mechanism>,sign");
+ "\n\techo_interval=<seconds>,actimeo=<seconds>,max_credits=<credits>,");
fprintf(stream,
- "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
- fprintf(stream, "\n\nRarely used options:");
- fprintf(stream,
- "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
- fprintf(stream,
- "\n\tdev,nodev,hard,soft,intr,");
- fprintf(stream,
- "\n\tnointr,ignorecase,noacl,prefixpath=<path>,nobrl");
+ "\n\tbsize=<size>");
fprintf(stream,
"\n\nOptions are described in more detail in the manual page");
- fprintf(stream, "\n\tman 8 mount.smb2\n");
+ fprintf(stream, "\n\tman 8 %s\n", thisprogram);
fprintf(stream, "\nTo display the version number of the mount helper:");
- fprintf(stream, "\n\tmount.smb2 -V\n");
+ fprintf(stream, "\n\t%s -V\n", thisprogram);
if (stream == stderr)
return EX_USAGE;
return 0;
}
-static int mount_usage(FILE *stream)
-{
- int rc;
-
- if (strcmp(thisprogram, "mount.smb2") == 0)
- rc = mount_smb2_usage(stream);
- else
- rc = mount_cifs_usage(stream);
-
- return rc;
-}
-
/*
* CIFS has to "escape" commas in the password field so that they don't
* end up getting confused for option delimiters. Copy password into pw
return 0;
}
-/* caller frees username if necessary */
-static char *getusername(uid_t uid)
-{
- char *username = NULL;
- struct passwd *password = getpwuid(uid);
-
- if (password)
- username = password->pw_name;
- return username;
-}
-
-/*
- * Parse a username string into parsed_mount_info fields. The format is:
- *
- * DOMAIN\username%password
- *
- * ...obviously the only required component is "username". The source string
- * is modified in the process, but it should remain unchanged at the end.
- */
-static int parse_username(char *rawuser, struct parsed_mount_info *parsed_info)
-{
- char *user, *password, slash;
- int rc = 0;
-
- /* everything after first % sign is a password */
- password = strchr(rawuser, '%');
- if (password) {
- rc = set_password(parsed_info, password + 1);
- if (rc)
- return rc;
- *password = '\0';
- }
-
- /* everything after first '/' or '\' is a username */
- user = strchr(rawuser, '/');
- if (!user)
- user = strchr(rawuser, '\\');
-
- /* everything before that slash is a domain */
- if (user) {
- slash = *user;
- *user = '\0';
- strlcpy(parsed_info->domain, rawuser,
- sizeof(parsed_info->domain));
- *(user++) = slash;
- } else {
- user = rawuser;
- }
-
- strlcpy(parsed_info->username, user, sizeof(parsed_info->username));
- parsed_info->got_user = 1;
- if (password)
- *password = '%';
-
- return 0;
-}
-
#ifdef HAVE_LIBCAP_NG
static int
drop_capabilities(int parent)
static int
toggle_dac_capability(int writable, int enable)
{
- int rc;
+ int rc = 0;
cap_t caps;
cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
- if (getuid() != 0)
- return 0;
-
caps = cap_get_proc();
if (caps == NULL) {
fprintf(stderr, "Unable to get current capability set: %s\n",
}
free_caps:
cap_free(caps);
- return 0;
+ return rc;
}
#else /* HAVE_LIBCAP */
static int
-drop_capabilities(int parent)
+drop_capabilities(int parent __attribute((unused)))
{
return 0;
}
static int
-toggle_dac_capability(int writable, int enable)
+toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
{
return 0;
}
static int open_cred_file(char *file_name,
struct parsed_mount_info *parsed_info)
{
- char *line_buf;
+ char *line_buf = NULL;
char *temp_val = NULL;
FILE *fs = NULL;
int i;
i = access(file_name, R_OK);
if (i) {
toggle_dac_capability(0, 0);
+ i = errno;
goto return_i;
}
/* parse next token */
switch (parse_cred_line(line_buf + i, &temp_val)) {
case CRED_USER:
- i = parse_username(temp_val, parsed_info);
- if (i)
- goto return_i;
+ strlcpy(parsed_info->username, temp_val,
+ sizeof(parsed_info->username));
+ parsed_info->got_user = 1;
break;
case CRED_PASS:
i = set_password(parsed_info, temp_val);
/* make sure passwords are scrubbed from memory */
if (line_buf != NULL)
memset(line_buf, 0, line_buf_size);
- SAFE_FREE(line_buf);
+ free(line_buf);
return i;
}
static int
get_password_from_file(int file_descript, char *filename,
- struct parsed_mount_info *parsed_info)
+ struct parsed_mount_info *parsed_info, const char *program)
{
int rc = 0;
char buf[sizeof(parsed_info->password) + 1];
rc = access(filename, R_OK);
if (rc) {
fprintf(stderr,
- "mount.cifs failed: access check of %s failed: %s\n",
- filename, strerror(errno));
+ "%s failed: access check of %s failed: %s\n",
+ program, filename, strerror(errno));
toggle_dac_capability(0, 0);
return EX_SYSERR;
}
file_descript = open(filename, O_RDONLY);
if (file_descript < 0) {
fprintf(stderr,
- "mount.cifs failed. %s attempting to open password file %s\n",
- strerror(errno), filename);
+ "%s failed. %s attempting to open password file %s\n",
+ program, strerror(errno), filename);
toggle_dac_capability(0, 0);
return EX_SYSERR;
}
rc = read(file_descript, buf, sizeof(buf) - 1);
if (rc < 0) {
fprintf(stderr,
- "mount.cifs failed. Error %s reading password file\n",
- strerror(errno));
+ "%s failed. Error %s reading password file\n",
+ program, strerror(errno));
rc = EX_SYSERR;
goto get_pw_exit;
}
if (token == NULL)
return OPT_ERROR;
- if (strncmp(token, "users", 5) == 0)
+ /*
+ * token is NULL terminated and contains exactly the
+ * keyword so we can match exactly
+ */
+ if (strcmp(token, "users") == 0)
return OPT_USERS;
- if (strncmp(token, "user_xattr", 10) == 0)
+ if (strcmp(token, "user_xattr") == 0)
return OPT_USER_XATTR;
- if (strncmp(token, "user", 4) == 0)
+ if (strcmp(token, "user") == 0 ||
+ strcmp(token, "username") == 0)
return OPT_USER;
- if (strncmp(token, "pass", 4) == 0)
+ if (strcmp(token, "pass") == 0 ||
+ strcmp(token, "password") == 0)
return OPT_PASS;
- if (strncmp(token, "sec", 3) == 0)
+ if (strcmp(token, "sec") == 0)
return OPT_SEC;
- if (strncmp(token, "ip", 2) == 0)
+ if (strcmp(token, "ip") == 0 ||
+ strcmp(token, "addr") == 0)
return OPT_IP;
- if (strncmp(token, "unc", 3) == 0 ||
- strncmp(token, "target", 6) == 0 ||
- strncmp(token, "path", 4) == 0)
+ if (strcmp(token, "unc") == 0 ||
+ strcmp(token, "target") == 0 ||
+ strcmp(token, "path") == 0)
return OPT_UNC;
- if (strncmp(token, "dom", 3) == 0 || strncmp(token, "workg", 5) == 0)
+ if (strcmp(token, "dom") == 0 ||
+ strcmp(token, "domain") == 0 ||
+ strcmp(token, "workgroup") == 0)
return OPT_DOM;
- if (strncmp(token, "cred", 4) == 0)
+ if (strcmp(token, "cred") == 0 || /* undocumented */
+ strcmp(token, "credentials") == 0)
return OPT_CRED;
- if (strncmp(token, "uid", 3) == 0)
+ if (strcmp(token, "uid") == 0)
return OPT_UID;
- if (strncmp(token, "gid", 3) == 0)
+ if (strcmp(token, "cruid") == 0)
+ return OPT_CRUID;
+ if (strcmp(token, "gid") == 0)
return OPT_GID;
- if (strncmp(token, "fmask", 5) == 0)
+ if (strcmp(token, "fmask") == 0)
return OPT_FMASK;
- if (strncmp(token, "file_mode", 9) == 0)
+ if (strcmp(token, "file_mode") == 0)
return OPT_FILE_MODE;
- if (strncmp(token, "dmask", 5) == 0)
+ if (strcmp(token, "dmask") == 0)
return OPT_DMASK;
- if (strncmp(token, "dir_mode", 4) == 0 || strncmp(token, "dirm", 4) == 0)
+ if (strcmp(token, "dir_mode") == 0 ||
+ strcmp(token, "dirm") == 0)
return OPT_DIR_MODE;
- if (strncmp(token, "nosuid", 6) == 0)
+ if (strcmp(token, "nosuid") == 0)
return OPT_NO_SUID;
- if (strncmp(token, "suid", 4) == 0)
+ if (strcmp(token, "suid") == 0)
return OPT_SUID;
- if (strncmp(token, "nodev", 5) == 0)
+ if (strcmp(token, "nodev") == 0)
return OPT_NO_DEV;
- if (strncmp(token, "nobrl", 5) == 0 || strncmp(token, "nolock", 6) == 0)
+ if (strcmp(token, "nobrl") == 0 ||
+ strcmp(token, "nolock") == 0)
return OPT_NO_LOCK;
- if (strncmp(token, "mand", 4) == 0)
+ if (strcmp(token, "mand") == 0)
return OPT_MAND;
- if (strncmp(token, "nomand", 6) == 0)
+ if (strcmp(token, "nomand") == 0)
return OPT_NOMAND;
- if (strncmp(token, "dev", 3) == 0)
+ if (strcmp(token, "dev") == 0)
return OPT_DEV;
- if (strncmp(token, "noexec", 6) == 0)
+ if (strcmp(token, "noexec") == 0)
return OPT_NO_EXEC;
- if (strncmp(token, "exec", 4) == 0)
+ if (strcmp(token, "exec") == 0)
return OPT_EXEC;
- if (strncmp(token, "guest", 5) == 0)
+ if (strcmp(token, "guest") == 0)
return OPT_GUEST;
- if (strncmp(token, "ro", 2) == 0)
+ if (strcmp(token, "ro") == 0)
return OPT_RO;
- if (strncmp(token, "rw", 2) == 0)
+ if (strcmp(token, "rw") == 0)
return OPT_RW;
- if (strncmp(token, "remount", 7) == 0)
+ if (strcmp(token, "remount") == 0)
return OPT_REMOUNT;
- if (strncmp(token, "_netdev", 7) == 0)
+ if (strcmp(token, "_netdev") == 0)
return OPT_IGNORE;
+ if (strcmp(token, "backupuid") == 0)
+ return OPT_BKUPUID;
+ if (strcmp(token, "backupgid") == 0)
+ return OPT_BKUPGID;
+ if (strcmp(token, "nofail") == 0)
+ return OPT_NOFAIL;
+ if (strncmp(token, "x-", 2) == 0)
+ return OPT_IGNORE;
+ if (strncmp(token, "snapshot", 8) == 0)
+ return OPT_SNAPSHOT;
return OPT_ERROR;
}
int out_len = 0;
int word_len;
int rc = 0;
+ int got_bkupuid = 0;
+ int got_bkupgid = 0;
int got_uid = 0;
+ int got_cruid = 0;
int got_gid = 0;
- uid_t uid;
- gid_t gid;
+ int got_snapshot = 0;
+ uid_t uid, cruid = 0, bkupuid = 0;
+ gid_t gid, bkupgid = 0;
char *ep;
struct passwd *pw;
struct group *gr;
/*
- * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
- * +1 for NULL, and +1 for good measure
+ * max 64-bit uint in decimal is 18446744073709551615 which is 20 chars
+ * wide +1 for NULL, and +1 for good measure
*/
- char txtbuf[12];
+ char txtbuf[22];
+ unsigned long long snapshot;
+ struct tm tm;
/* make sure we're starting from beginning */
out[0] = '\0';
return EX_USAGE;
}
} else {
- /* domain/username%password */
- const int max = MAX_DOMAIN_SIZE +
- MAX_USERNAME_SIZE +
- MOUNT_PASSWD_SIZE + 2;
- if (strnlen(value, max + 1) >= max + 1) {
- fprintf(stderr, "username too long\n");
- return EX_USAGE;
- }
- rc = parse_username(value, parsed_info);
- if (rc) {
- fprintf(stderr,
- "problem parsing username\n");
- return rc;
- }
+ strlcpy(parsed_info->username, value,
+ sizeof(parsed_info->username));
+ parsed_info->got_user = 1;
goto nocopy;
}
"invalid path to network resource\n");
return EX_USAGE;
}
- rc = parse_unc(value, parsed_info);
+ rc = parse_unc(value, parsed_info, thisprogram);
if (rc)
return rc;
break;
/* dom || workgroup */
case OPT_DOM:
- if (!value || !*value) {
- fprintf(stderr, "CIFS: invalid domain name\n");
- return EX_USAGE;
+ if (!value) {
+ /*
+ * An empty domain has been passed
+ */
+ /* not necessary but better safe than.. */
+ parsed_info->domain[0] = '\0';
+ parsed_info->got_domain = 1;
+ goto nocopy;
}
if (strnlen(value, sizeof(parsed_info->domain)) >=
sizeof(parsed_info->domain)) {
rc, strerror(rc), value);
return rc;
}
- break;
+ goto nocopy;
case OPT_UID:
if (!value || !*value)
goto nocopy;
got_uid = 1;
+ pw = getpwnam(value);
+ if (pw) {
+ uid = pw->pw_uid;
+ goto nocopy;
+ }
+
+ errno = 0;
uid = strtoul(value, &ep, 10);
- if (errno != EINVAL && *ep == '\0')
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ fprintf(stderr, "bad option uid=\"%s\"\n", value);
+ return EX_USAGE;
+ case OPT_CRUID:
+ if (!value || !*value)
goto nocopy;
+ got_cruid = 1;
pw = getpwnam(value);
- if (pw == NULL) {
- fprintf(stderr, "bad user name \"%s\"\n", value);
- return EX_USAGE;
+ if (pw) {
+ cruid = pw->pw_uid;
+ goto nocopy;
}
- uid = pw->pw_uid;
- goto nocopy;
+ errno = 0;
+ cruid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+ fprintf(stderr, "bad option: cruid=\"%s\"\n", value);
+ return EX_USAGE;
case OPT_GID:
if (!value || !*value)
goto nocopy;
got_gid = 1;
- gid = strtoul(value, &ep, 10);
- if (errno != EINVAL && *ep == '\0')
- goto nocopy;
-
gr = getgrnam(value);
- if (gr == NULL) {
- fprintf(stderr, "bad group name \"%s\"\n", value);
- return EX_USAGE;
+ if (gr) {
+ gid = gr->gr_gid;
+ goto nocopy;
}
- gid = gr->gr_gid;
- goto nocopy;
+ errno = 0;
+ gid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
- /* fmask fall through to file_mode */
+ fprintf(stderr, "bad option: gid=\"%s\"\n", value);
+ return EX_USAGE;
+ /* fmask falls through to file_mode */
case OPT_FMASK:
fprintf(stderr,
"WARNING: CIFS mount option 'fmask' is\
deprecated. Use 'file_mode' instead.\n");
data = "file_mode"; /* BB fix this */
+ /* Fallthrough */
case OPT_FILE_MODE:
if (!value || !*value) {
fprintf(stderr,
"WARNING: CIFS mount option 'dmask' is\
deprecated. Use 'dir_mode' instead.\n");
data = "dir_mode";
+ /* Fallthrough */
case OPT_DIR_MODE:
if (!value || !*value) {
fprintf(stderr,
"WARNING: '%s' not expressed in octal.\n",
data);
break;
-
- /* the following mount options should be
- stripped out from what is passed into the kernel
- since these options are best passed as the
- mount flags rather than redundantly to the kernel
- and could generate spurious warnings depending on the
- level of the corresponding cifs vfs kernel code */
case OPT_NO_SUID:
*filesys_flags |= MS_NOSUID;
- break;
+ goto nocopy;
case OPT_SUID:
*filesys_flags &= ~MS_NOSUID;
- break;
+ goto nocopy;
case OPT_NO_DEV:
*filesys_flags |= MS_NODEV;
- break;
- /* nolock || nobrl */
+ goto nocopy;
case OPT_NO_LOCK:
*filesys_flags &= ~MS_MANDLOCK;
break;
goto nocopy;
case OPT_DEV:
*filesys_flags &= ~MS_NODEV;
- break;
+ goto nocopy;
case OPT_NO_EXEC:
*filesys_flags |= MS_NOEXEC;
- break;
+ goto nocopy;
case OPT_EXEC:
*filesys_flags &= ~MS_NOEXEC;
- break;
+ goto nocopy;
case OPT_GUEST:
parsed_info->got_user = 1;
parsed_info->got_password = 1;
- break;
+ goto nocopy;
case OPT_RO:
*filesys_flags |= MS_RDONLY;
goto nocopy;
goto nocopy;
case OPT_REMOUNT:
*filesys_flags |= MS_REMOUNT;
- break;
+ goto nocopy;
case OPT_IGNORE:
goto nocopy;
+ case OPT_BKUPUID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_bkupuid = 1;
+ errno = 0;
+ bkupuid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ pw = getpwnam(value);
+ if (pw == NULL) {
+ fprintf(stderr,
+ "bad user name \"%s\"\n", value);
+ return EX_USAGE;
+ }
+
+ bkupuid = pw->pw_uid;
+ goto nocopy;
+ case OPT_BKUPGID:
+ if (!value || !*value)
+ goto nocopy;
+
+ got_bkupgid = 1;
+ errno = 0;
+ bkupgid = strtoul(value, &ep, 10);
+ if (errno == 0 && *ep == '\0')
+ goto nocopy;
+
+ gr = getgrnam(value);
+ if (gr == NULL) {
+ fprintf(stderr,
+ "bad group name \"%s\"\n", value);
+ return EX_USAGE;
+ }
+
+ bkupgid = gr->gr_gid;
+ goto nocopy;
+ case OPT_NOFAIL:
+ parsed_info->nofail = 1;
+ goto nocopy;
+ case OPT_SNAPSHOT:
+ if (!value || !*value)
+ goto nocopy;
+ if (strncmp(value, "@GMT-", 5))
+ break;
+ if ((strlen(value) != GMT_NAME_LEN) ||
+ (strptime(value, GMT_FORMAT, &tm) == NULL)) {
+ fprintf(stderr, "bad snapshot token\n");
+ return EX_USAGE;
+ }
+ snapshot = timegm(&tm) * 10000000 + NTFS_TIME_OFFSET;
+ got_snapshot = 1;
+ goto nocopy;
}
/* check size before copying option to buffer */
data = next_keyword;
}
+
/* special-case the uid and gid */
if (got_uid) {
word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
out_len = strlen(out);
}
+ if (got_cruid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
+
+ /* comma + "cruid=" + terminating NULL == 8 */
+ if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
+
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
+ out_len = strlen(out);
+ }
if (got_gid) {
word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
}
snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
}
+ if (got_bkupuid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
+
+ /* comma + "backupuid=" + terminating NULL == 12 */
+ if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
+
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
+ out_len = strlen(out);
+ }
+ if (got_bkupgid) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
+
+ /* comma + "backupgid=" + terminating NULL == 12 */
+ if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
+
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
+ }
+ if (got_snapshot) {
+ word_len = snprintf(txtbuf, sizeof(txtbuf), "%llu", snapshot);
+
+ /* comma + "snapshot=" + terminating NULL == 11 */
+ if (out_len + word_len + 11 > MAX_OPTIONS_LEN) {
+ fprintf(stderr, "Options string too long\n");
+ return EX_USAGE;
+ }
+
+ if (out_len) {
+ strlcat(out, ",", MAX_OPTIONS_LEN);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 11, "snapshot=%s", txtbuf);
+ }
return 0;
}
-static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
+static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info, const char *progname)
{
int length = strnlen(unc_name, MAX_UNC_LEN);
const char *host, *share, *prepath;
return EX_USAGE;
}
- /* Set up "host" and "share" pointers based on UNC format. */
if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
- /*
- * check for nfs syntax (server:/share/prepath)
- *
- * FIXME: IPv6 addresses?
- */
- host = unc_name;
- share = strchr(host, ':');
- if (!share) {
- fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
- return EX_USAGE;
- }
- hostlen = share - host;
- share++;
- if (*share == '/')
- ++share;
- } else {
- host = unc_name + 2;
- hostlen = strcspn(host, "/\\");
- if (!hostlen) {
- fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
- return EX_USAGE;
- }
- share = host + hostlen + 1;
+ fprintf(stderr, "%s: bad UNC (%s)\n", progname, unc_name);
+ return EX_USAGE;
+ }
+
+ host = unc_name + 2;
+ hostlen = strcspn(host, "/\\");
+ if (!hostlen) {
+ fprintf(stderr, "%s: bad UNC (%s)\n", progname, unc_name);
+ return EX_USAGE;
}
+ share = host + hostlen + 1;
if (hostlen + 1 > sizeof(parsed_info->host)) {
- fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
+ fprintf(stderr, "%s: host portion of UNC too long\n", progname);
return EX_USAGE;
}
sharelen = strcspn(share, "/\\");
if (sharelen + 1 > sizeof(parsed_info->share)) {
- fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
+ fprintf(stderr, "%s: share portion of UNC too long\n", progname);
return EX_USAGE;
}
prepathlen = strlen(prepath);
if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
- fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
+ fprintf(stderr, "%s: UNC prefixpath too long\n", progname);
return EX_USAGE;
}
return 0;
}
-static int get_pw_from_env(struct parsed_mount_info *parsed_info)
+static int get_pw_from_env(struct parsed_mount_info *parsed_info, const char *program)
{
int rc = 0;
rc = set_password(parsed_info, getenv("PASSWD"));
else if (getenv("PASSWD_FD"))
rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
- parsed_info);
+ parsed_info, program);
else if (getenv("PASSWD_FILE"))
rc = get_password_from_file(0, getenv("PASSWD_FILE"),
- parsed_info);
+ parsed_info, program);
return rc;
}
{"pass", 1, NULL, 'p'},
{"credentials", 1, NULL, 'c'},
{"port", 1, NULL, 'P'},
+ {"sloppy", 0, NULL, 's'},
{NULL, 0, NULL, 0}
};
return 1;
}
-static void print_cifs_mount_version(void)
+static void print_cifs_mount_version(const char *progname)
{
- printf("mount.cifs version: %s\n", VERSION);
+ printf("%s version: %s\n", progname, VERSION);
}
/*
static int check_mtab(const char *progname, const char *devname,
const char *dir)
{
- if (check_newline(progname, devname) == -1 ||
- check_newline(progname, dir) == -1)
+ if (check_newline(progname, devname) || check_newline(progname, dir))
return EX_USAGE;
return 0;
}
static int
add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
{
- int rc = 0;
+ int rc = 0, tmprc, fd;
uid_t uid;
char *mount_user = NULL;
struct mntent mountent;
+ struct stat statbuf;
FILE *pmntfile;
sigset_t mask, oldmask;
goto add_mtab_exit;
}
+ fd = fileno(pmntfile);
+ if (fd < 0) {
+ fprintf(stderr, "mntent does not appear to be valid\n");
+ unlock_mtab();
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
+
+ rc = fstat(fd, &statbuf);
+ if (rc != 0) {
+ fprintf(stderr, "unable to fstat open mtab\n");
+ endmntent(pmntfile);
+ unlock_mtab();
+ rc = EX_FILEIO;
+ goto add_mtab_exit;
+ }
+
mountent.mnt_fsname = devname;
mountent.mnt_dir = mountpoint;
mountent.mnt_type = (char *)(void *)fstype;
mountent.mnt_passno = 0;
rc = addmntent(pmntfile, &mountent);
if (rc) {
+ int ignore __attribute__((unused));
+
fprintf(stderr, "unable to add mount entry to mtab\n");
+ ignore = ftruncate(fd, statbuf.st_size);
+ rc = EX_FILEIO;
+ }
+ tmprc = my_endmntent(pmntfile, statbuf.st_size);
+ if (tmprc) {
+ fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
rc = EX_FILEIO;
}
- endmntent(pmntfile);
unlock_mtab();
- SAFE_FREE(mountent.mnt_opts);
+ free(mountent.mnt_opts);
add_mtab_exit:
toggle_dac_capability(1, 0);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return rc;
}
+static int
+del_mtab(char *mountpoint)
+{
+ int len, tmprc, rc = 0;
+ FILE *mnttmp, *mntmtab;
+ struct mntent *mountent;
+ char *mtabfile, *mtabdir, *mtabtmpfile = NULL;
+
+ mtabfile = strdup(MOUNTED);
+ if (!mtabfile) {
+ fprintf(stderr, "del_mtab: cannot strdup MOUNTED\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mtabdir = dirname(mtabfile);
+ len = strlen(mtabdir) + strlen(MNT_TMP_FILE);
+ mtabtmpfile = malloc(len + 1);
+ if (!mtabtmpfile) {
+ fprintf(stderr, "del_mtab: cannot allocate memory to tmp file\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ if (sprintf(mtabtmpfile, "%s%s", mtabdir, MNT_TMP_FILE) != len) {
+ fprintf(stderr, "del_mtab: error writing new string\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ atexit(unlock_mtab);
+ rc = lock_mtab();
+ if (rc) {
+ fprintf(stderr, "del_mtab: cannot lock mtab\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mtabtmpfile = mktemp(mtabtmpfile);
+ if (!mtabtmpfile) {
+ fprintf(stderr, "del_mtab: cannot setup tmp file destination\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mntmtab = setmntent(MOUNTED, "r");
+ if (!mntmtab) {
+ fprintf(stderr, "del_mtab: could not update mount table\n");
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ mnttmp = setmntent(mtabtmpfile, "w");
+ if (!mnttmp) {
+ fprintf(stderr, "del_mtab: could not update mount table\n");
+ endmntent(mntmtab);
+ rc = EX_FILEIO;
+ goto del_mtab_exit;
+ }
+
+ while ((mountent = getmntent(mntmtab)) != NULL) {
+ if (!strcmp(mountent->mnt_dir, mountpoint))
+ continue;
+ rc = addmntent(mnttmp, mountent);
+ if (rc) {
+ fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+ }
+
+ endmntent(mntmtab);
+
+ tmprc = my_endmntent(mnttmp, 0);
+ if (tmprc) {
+ fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+
+ if (rename(mtabtmpfile, MOUNTED)) {
+ fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
+ rc = EX_FILEIO;
+ goto del_mtab_error;
+ }
+
+del_mtab_exit:
+ unlock_mtab();
+ free(mtabtmpfile);
+ free(mtabfile);
+ return rc;
+
+del_mtab_error:
+ if (unlink(mtabtmpfile))
+ fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
+ strerror(errno));
+ goto del_mtab_exit;
+}
+
/* have the child drop root privileges */
static int
drop_child_privs(void)
return 0;
}
+#ifdef ENABLE_SYSTEMD
+static int get_passwd_by_systemd(const char *prompt, char *input, int capacity)
+{
+ int fd[2];
+ pid_t pid;
+ int offs = 0;
+ int rc = 1;
+
+ if (pipe(fd) == -1) {
+ fprintf(stderr, "Failed to create pipe: %s\n", strerror(errno));
+ return 1;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
+ close(fd[0]);
+ close(fd[1]);
+ return 1;
+ }
+ if (pid == 0) {
+ close(fd[0]);
+ dup2(fd[1], STDOUT_FILENO);
+ if (execlp("systemd-ask-password", "systemd-ask-password", prompt, NULL) == -1) {
+ fprintf(stderr, "Failed to execute systemd-ask-password: %s\n",
+ strerror(errno));
+ }
+ exit(1);
+ }
+
+ close(fd[1]);
+ for (;;) {
+ if (offs+1 >= capacity) {
+ fprintf(stderr, "Password too long.\n");
+ kill(pid, SIGTERM);
+ rc = 1;
+ break;
+ }
+ rc = read(fd[0], input + offs, capacity - offs);
+ if (rc == -1) {
+ fprintf(stderr, "Failed to read from pipe: %s\n", strerror(errno));
+ rc = 1;
+ break;
+ }
+ if (!rc)
+ break;
+ offs += rc;
+ input[offs] = '\0';
+ }
+ if (wait(&rc) == -1) {
+ fprintf(stderr, "Failed to wait child: %s\n", strerror(errno));
+ rc = 1;
+ goto out;
+ }
+ if (!WIFEXITED(rc) || WEXITSTATUS(rc)) {
+ rc = 1;
+ goto out;
+ }
+
+ rc = 0;
+
+out:
+ close(fd[0]);
+ return rc;
+}
+#endif
+
+/*
+ * If systemd is running and systemd-ask-password --
+ * is available, then use that else fallback on getpass(..)
+ *
+ * Returns: @input or NULL on error
+ */
+static char*
+get_password(const char *prompt, char *input, int capacity)
+{
+#ifdef ENABLE_SYSTEMD
+ int is_systemd_running;
+ struct stat a, b;
+
+ memset(input, 0, capacity);
+
+ /* We simply test whether the systemd cgroup hierarchy is
+ * mounted */
+ is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
+ && (lstat("/sys/fs/cgroup/systemd", &b) == 0)
+ && (a.st_dev != b.st_dev);
+
+ if (is_systemd_running && !get_passwd_by_systemd(prompt, input, capacity)) {
+ int len = strlen(input);
+ if (input[len - 1] == '\n')
+ input[len - 1] = '\0';
+ return input;
+ }
+#endif
+ memset(input, 0, capacity);
+
+ /*
+ * Falling back to getpass(..)
+ * getpass is obsolete, but there's apparently nothing that replaces it
+ */
+ char *tmp_pass = getpass(prompt);
+ if (!tmp_pass)
+ return NULL;
+
+ strncpy(input, tmp_pass, capacity - 1);
+ input[capacity - 1] = '\0';
+
+ /* zero-out the static buffer */
+ memset(tmp_pass, 0, strlen(tmp_pass));
+
+ return input;
+}
+
static int
assemble_mountinfo(struct parsed_mount_info *parsed_info,
const char *thisprogram, const char *mountpoint,
const char *orig_dev, char *orgoptions)
{
int rc;
+ char *newopts = NULL;
rc = drop_capabilities(0);
if (rc)
if (getuid()) {
rc = check_fstab(thisprogram, mountpoint, orig_dev,
- &orgoptions);
+ &newopts);
if (rc)
goto assemble_exit;
+ orgoptions = newopts;
/* enable any default user mount flags */
parsed_info->flags |= CIFS_SETUID_FLAGS;
}
- rc = get_pw_from_env(parsed_info);
+ rc = get_pw_from_env(parsed_info, thisprogram);
if (rc)
goto assemble_exit;
parsed_info->flags &= ~(MS_USERS | MS_USER);
- rc = parse_unc(orig_dev, parsed_info);
+ rc = parse_unc(orig_dev, parsed_info, thisprogram);
if (rc)
goto assemble_exit;
}
if (!parsed_info->got_password) {
- /* getpass is obsolete, but there's apparently nothing that replaces it */
- char *tmp_pass = getpass("Password: ");
- if (!tmp_pass) {
+ char tmp_pass[MOUNT_PASSWD_SIZE + 1];
+ char *prompt = NULL;
+
+ if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
+ prompt = NULL;
+
+ if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
+ rc = set_password(parsed_info, tmp_pass);
+ } else {
fprintf(stderr, "Error reading password, exiting\n");
rc = EX_SYSERR;
- goto assemble_exit;
}
- rc = set_password(parsed_info, tmp_pass);
+
+ free(prompt);
if (rc)
goto assemble_exit;
}
- /* copy in ver= string. It's not really needed, but what the hell */
- strlcat(parsed_info->options, ",ver=", sizeof(parsed_info->options));
- strlcat(parsed_info->options, OPTIONS_VERSION, sizeof(parsed_info->options));
-
/* copy in user= string */
if (parsed_info->got_user) {
- strlcat(parsed_info->options, ",user=",
+ if (*parsed_info->options)
+ strlcat(parsed_info->options, ",",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, "user=",
sizeof(parsed_info->options));
strlcat(parsed_info->options, parsed_info->username,
sizeof(parsed_info->options));
}
if (*parsed_info->domain) {
- strlcat(parsed_info->options, ",domain=",
+ if (*parsed_info->options)
+ strlcat(parsed_info->options, ",",
+ sizeof(parsed_info->options));
+ strlcat(parsed_info->options, "domain=",
sizeof(parsed_info->options));
strlcat(parsed_info->options, parsed_info->domain,
sizeof(parsed_info->options));
+ } else if (parsed_info->got_domain) {
+ strlcat(parsed_info->options, ",domain=",
+ sizeof(parsed_info->options));
}
assemble_exit:
+ free(newopts);
+ return rc;
+}
+
+/*
+ * chdir() into the mountpoint and determine "realpath". We assume here that
+ * "mountpoint" is a statically allocated string and does not need to be freed.
+ */
+static int
+acquire_mountpoint(char **mountpointp)
+{
+ int rc, dacrc;
+ uid_t realuid, oldfsuid;
+ gid_t oldfsgid;
+ char *mountpoint = NULL;
+
+ /*
+ * Acquire the necessary privileges to chdir to the mountpoint. If
+ * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
+ * it's not, then we change the fsuid to the real uid to ensure that
+ * the mounting user actually has access to the mountpoint.
+ *
+ * The mount(8) manpage does not state that users must be able to
+ * chdir into the mountpoint in order to mount onto it, but if we
+ * allow that, then an unprivileged user could use this program to
+ * "probe" into directories to which he does not have access.
+ */
+ realuid = getuid();
+ if (realuid == 0) {
+ dacrc = toggle_dac_capability(0, 1);
+ if (dacrc)
+ return dacrc;
+ } else {
+ oldfsuid = setfsuid(realuid);
+ oldfsgid = setfsgid(getgid());
+ }
+
+ rc = chdir(*mountpointp);
+ if (rc) {
+ fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
+ strerror(errno));
+ rc = EX_USAGE;
+ goto restore_privs;
+ }
+
+ mountpoint = realpath(".", NULL);
+ if (!mountpoint) {
+ fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
+ *mountpointp, strerror(errno));
+ rc = EX_SYSERR;
+ }
+
+ *mountpointp = mountpoint;
+restore_privs:
+ if (realuid == 0) {
+ dacrc = toggle_dac_capability(0, 0);
+ if (dacrc)
+ rc = rc ? rc : dacrc;
+ } else {
+ uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
+ gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
+ }
+
+ if (rc)
+ free(mountpoint);
+
return rc;
}
char *orgoptions = NULL;
char *mountpoint = NULL;
char *options = NULL;
- char *dev_name = NULL, *orig_dev = NULL;
+ char *orig_dev = NULL;
char *currentaddress, *nextaddress;
int rc = 0;
int already_uppercased = 0;
+ int sloppy = 0;
size_t options_size = MAX_OPTIONS_LEN;
- size_t dev_len;
struct parsed_mount_info *parsed_info = NULL;
pid_t pid;
- const char *fstype;
rc = check_setuid();
if (rc)
if (thisprogram == NULL)
thisprogram = "mount.cifs";
+ if(strcmp(thisprogram, "mount.cifs") == 0)
+ cifs_fstype = "cifs";
+
+ if(strcmp(thisprogram, "mount.smb3") == 0)
+ cifs_fstype = "smb3";
+
/* allocate parsed_info as shared anonymous memory range */
- parsed_info = mmap(0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
+ parsed_info = mmap((void *)0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (parsed_info == (struct parsed_mount_info *) -1) {
parsed_info = NULL;
}
/* add sharename in opts string as unc= parm */
- while ((c = getopt_long(argc, argv, "?fhno:rvVw",
+ while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
longopts, NULL)) != -1) {
switch (c) {
case '?':
++parsed_info->verboseflag;
break;
case 'V':
- print_cifs_mount_version();
+ print_cifs_mount_version(thisprogram);
exit(0);
case 'w':
parsed_info->flags &= ~MS_RDONLY;
case 'f':
++parsed_info->fakemnt;
break;
+ case 's':
+ ++sloppy;
+ break;
default:
fprintf(stderr, "unknown command-line option: %c\n", c);
rc = mount_usage(stderr);
}
}
- if (argc < 3 || argv[optind] == NULL || argv[optind + 1] == NULL) {
+ if (argc < optind + 2) {
rc = mount_usage(stderr);
goto mount_exit;
}
mountpoint = argv[optind + 1];
/* chdir into mountpoint as soon as possible */
- rc = toggle_dac_capability(0, 1);
- if (rc)
- return rc;
- rc = chdir(mountpoint);
+ rc = acquire_mountpoint(&mountpoint);
if (rc) {
- fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
- strerror(errno));
- rc = EX_USAGE;
- goto mount_exit;
- }
-
- mountpoint = realpath(".", NULL);
- if (!mountpoint) {
- fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
- mountpoint, strerror(errno));
- rc = EX_SYSERR;
- goto mount_exit;
- }
- rc = toggle_dac_capability(0, 0);
- if (rc)
+ free(orgoptions);
return rc;
+ }
/*
* mount.cifs does privilege separation. Most of the code to handle
/* child */
rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
orig_dev, orgoptions);
+ free(orgoptions);
+ free(mountpoint);
return rc;
} else {
/* parent */
goto mount_exit;
}
- /* lengths of different strings + slashes + trailing \0 */
- dev_len = strnlen(parsed_info->host, sizeof(parsed_info->host)) +
- strnlen(parsed_info->share, sizeof(parsed_info->share)) +
- strnlen(parsed_info->prefix, sizeof(parsed_info->prefix)) +
- 2 + 1 + 1 + 1;
- dev_name = calloc(dev_len, 1);
- if (!dev_name) {
- rc = EX_SYSERR;
- goto mount_exit;
- }
-
- /* rebuild device name with forward slashes */
- strlcpy(dev_name, "//", dev_len);
- strlcat(dev_name, parsed_info->host, dev_len);
- strlcat(dev_name, "/", dev_len);
- strlcat(dev_name, parsed_info->share, dev_len);
- strlcat(dev_name, "/", dev_len);
- strlcat(dev_name, parsed_info->prefix, dev_len);
-
currentaddress = parsed_info->addrlist;
nextaddress = strchr(currentaddress, ',');
if (nextaddress)
mount_retry:
if (!currentaddress) {
fprintf(stderr, "Unable to find suitable address.\n");
- rc = EX_SYSERR;
+ rc = parsed_info->nofail ? 0 : EX_FAIL;
goto mount_exit;
}
strlcpy(options, "ip=", options_size);
strlcat(options, parsed_info->prefix, options_size);
}
+ if (sloppy)
+ strlcat(options, ",sloppy", options_size);
+
if (parsed_info->verboseflag)
fprintf(stderr, "%s kernel mount options: %s",
thisprogram, options);
if (parsed_info->verboseflag)
fprintf(stderr, "\n");
- rc = check_mtab(thisprogram, dev_name, mountpoint);
+ rc = check_mtab(thisprogram, orig_dev, mountpoint);
if (rc)
goto mount_exit;
- if (strcmp(thisprogram, "mount.smb2") == 0)
- fstype = smb2_fstype;
- else
- fstype = cifs_fstype;
+ if (!parsed_info->fakemnt) {
+ toggle_dac_capability(0, 1);
+ rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
+ toggle_dac_capability(0, 0);
+ if (rc == 0)
+ goto do_mtab;
- if (!parsed_info->fakemnt
- && mount(dev_name, ".", fstype, parsed_info->flags, options)) {
switch (errno) {
case ECONNREFUSED:
case EHOSTUNREACH:
+ if (currentaddress) {
+ fprintf(stderr, "mount error(%d): could not connect to %s",
+ errno, currentaddress);
+ }
currentaddress = nextaddress;
if (currentaddress) {
nextaddress = strchr(currentaddress, ',');
goto mount_retry;
case ENODEV:
fprintf(stderr,
- "mount error: %s filesystem not supported by the system\n", fstype);
+ "mount error: %s filesystem not supported by the system\n", cifs_fstype);
+ break;
+ case EHOSTDOWN:
+ fprintf(stderr,
+ "mount error: Server abruptly closed the connection.\n"
+ "This can happen if the server does not support the SMB version you are trying to use.\n"
+ "The default SMB version recently changed from SMB1 to SMB2.1 and above. Try mounting with vers=1.0.\n");
break;
case ENXIO:
if (!already_uppercased &&
uppercase_string(parsed_info->host) &&
uppercase_string(parsed_info->share) &&
- uppercase_string(parsed_info->prefix)) {
+ uppercase_string(parsed_info->prefix) &&
+ uppercase_string(orig_dev)) {
fprintf(stderr,
"Retrying with upper case share name\n");
already_uppercased = 1;
strerror(errno));
fprintf(stderr,
"Refer to the %s(8) manual page (e.g. man "
- "%s)\n", thisprogram, thisprogram);
+ "%s) and kernel log messages (dmesg)\n", thisprogram, thisprogram);
rc = EX_FAIL;
goto mount_exit;
}
- if (!parsed_info->nomtab)
- rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, fstype);
+do_mtab:
+ if (!parsed_info->nomtab && !mtab_unusable()) {
+ if (parsed_info->flags & MS_REMOUNT) {
+ rc = del_mtab(mountpoint);
+ if (rc)
+ goto mount_exit;
+ }
+
+ rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
+ }
mount_exit:
if (parsed_info) {
memset(parsed_info->password, 0, sizeof(parsed_info->password));
munmap(parsed_info, sizeof(*parsed_info));
}
- SAFE_FREE(dev_name);
- SAFE_FREE(options);
- SAFE_FREE(orgoptions);
+ free(options);
+ free(orgoptions);
+ free(mountpoint);
return rc;
}