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 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#include <string.h>
#include <mntent.h>
#include <fcntl.h>
+#include <limits.h>
#define MOUNT_CIFS_VERSION_MAJOR "1"
-#define MOUNT_CIFS_VERSION_MINOR "10"
+#define MOUNT_CIFS_VERSION_MINOR "11"
#ifndef MOUNT_CIFS_VENDOR_SUFFIX
#ifdef _SAMBA_BUILD_
#define MS_BIND 4096
#endif
+#define MAX_UNC_LEN 1024
+
#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
const char *thisprogram;
static int got_unc = 0;
static int got_uid = 0;
static int got_gid = 0;
-static int free_share_name = 0;
static char * user_name = NULL;
static char * mountpassword = NULL;
char * domain_name = NULL;
char * prefixpath = NULL;
+/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
+ * don't link to libreplace so need them here. */
+
+/* like strncpy but does not 0 fill the buffer and always null
+ * terminates. bufsize is the size of the destination buffer */
+size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize <= 0) return 0;
+ if (len >= bufsize) len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ return ret;
+}
+
+/* like strncat but does not 0 fill the buffer and always null
+ * terminates. bufsize is the length of the buffer, which should
+ * be one more than the maximum resulting string length */
+size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1+len2 >= bufsize) {
+ len2 = bufsize - (len1+1);
+ }
+ if (len2 > 0) {
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
/* BB finish BB
printf("\nLess commonly used options:");
printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
- printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
+ printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
+ printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
printf("\n\nRarely used options:");
printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
- printf("\n\tnointr,ignorecase,noposixpaths,noacl");
+ printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
+ printf("\n\tin6_addr");
printf("\n\nOptions are described in more detail in the manual page");
printf("\n\tman 8 mount.cifs\n");
printf("\nTo display the version number of the mount helper:");
/* go past equals sign */
temp_val++;
for(length = 0;length<4087;length++) {
- if(temp_val[length] == '\n')
+ if ((temp_val[length] == '\n')
+ || (temp_val[length] == '\0')) {
break;
+ }
}
if(length > 4086) {
printf("mount.cifs failed due to malformed username in credentials file");
/* go past equals sign */
temp_val++;
for(length = 0;length<65;length++) {
- if(temp_val[length] == '\n')
+ if ((temp_val[length] == '\n')
+ || (temp_val[length] == '\0')) {
break;
+ }
}
if(length > 64) {
printf("mount.cifs failed: password in credentials file too long\n");
if(verboseflag)
printf("\nDomain %s\n",temp_val);
for(length = 0;length<65;length++) {
- if(temp_val[length] == '\n')
- break;
+ if ((temp_val[length] == '\n')
+ || (temp_val[length] == '\0')) {
+ break;
+ }
}
if(length > 64) {
printf("mount.cifs failed: domain in credentials file too long\n");
int out_len = 0;
int word_len;
int rc = 0;
+ char user[32];
+ char group[32];
if (!optionsp || !*optionsp)
return 1;
/* BB fixme check for separator override BB */
+ if (getuid()) {
+ got_uid = 1;
+ snprintf(user,sizeof(user),"%u",getuid());
+ got_gid = 1;
+ snprintf(group,sizeof(group),"%u",getgid());
+ }
+
/* while ((data = strsep(&options, ",")) != NULL) { */
while(data != NULL) {
/* check if ends with trailing comma */
got_uid = 1;
if (!isdigit(*value)) {
struct passwd *pw;
- static char temp[32];
if (!(pw = getpwnam(value))) {
printf("bad user name \"%s\"\n", value);
exit(1);
}
- sprintf(temp, "%u", pw->pw_uid);
- value = temp;
- endpwent();
+ snprintf(user, sizeof(user), "%u", pw->pw_uid);
+ } else {
+ strlcpy(user,value,sizeof(user));
}
}
+ goto nocopy;
} else if (strncmp(data, "gid", 3) == 0) {
if (value && *value) {
got_gid = 1;
if (!isdigit(*value)) {
struct group *gr;
- static char temp[32];
if (!(gr = getgrnam(value))) {
printf("bad group name \"%s\"\n", value);
exit(1);
}
- sprintf(temp, "%u", gr->gr_gid);
- value = temp;
- endpwent();
+ snprintf(group, sizeof(group), "%u", gr->gr_gid);
+ } else {
+ strlcpy(group,value,sizeof(group));
}
}
+ goto nocopy;
/* fmask and dmask synonyms for people used to smbfs syntax */
} else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
if (!value || !*value) {
exit(1);
}
- if (out_len)
- out[out_len++] = ',';
+ if (out_len) {
+ strlcat(out, ",", out_len + word_len + 2);
+ out_len++;
+ }
+
if (value)
- sprintf(out + out_len, "%s=%s", data, value);
+ snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
else
- sprintf(out + out_len, "%s", data);
+ snprintf(out + out_len, word_len + 1, "%s", data);
out_len = strlen(out);
nocopy:
data = next_keyword;
}
+
+ /* special-case the uid and gid */
+ if (got_uid) {
+ word_len = strlen(user);
+
+ out = (char *)realloc(out, out_len + word_len + 6);
+ if (out == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ if (out_len) {
+ strlcat(out, ",", out_len + word_len + 6);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 5, "uid=%s", user);
+ out_len = strlen(out);
+ }
+ if (got_gid) {
+ word_len = strlen(group);
+
+ out = (char *)realloc(out, out_len + 1 + word_len + 6);
+ if (out == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+
+ if (out_len) {
+ strlcat(out, ",", out_len + word_len + 6);
+ out_len++;
+ }
+ snprintf(out + out_len, word_len + 5, "gid=%s", group);
+ out_len = strlen(out);
+ }
+
free(*optionsp);
*optionsp = out;
return 0;
if(domainnm == NULL)
return NULL;
- strcpy(domainnm,*ppuser);
+ strlcpy(domainnm,*ppuser,len+1);
/* move_string(*ppuser, usernm+1) */
len = strlen(usernm+1);
return domainnm;
}
+/* replace all occurances of "from" in a string with "to" */
+static void replace_char(char *string, char from, char to, int maxlen)
+{
+ char *lastchar = string + maxlen;
+ while (string) {
+ string = strchr(string, from);
+ if (string) {
+ *string = to;
+ if (string >= lastchar)
+ return;
+ }
+ }
+}
+
/* Note that caller frees the returned buffer if necessary */
static char * parse_server(char ** punc_name)
{
char * unc_name = *punc_name;
- int length = strnlen(unc_name,1024);
+ int length = strnlen(unc_name, MAX_UNC_LEN);
char * share;
char * ipaddress_string = NULL;
struct hostent * host_entry = NULL;
struct in_addr server_ipaddr;
- if(length > 1023) {
+ if(length > (MAX_UNC_LEN - 1)) {
printf("mount error: UNC name too long");
return NULL;
}
/* check for nfs syntax ie server:share */
share = strchr(unc_name,':');
if(share) {
- free_share_name = 1;
*punc_name = (char *)malloc(length+3);
if(*punc_name == NULL) {
/* put the original string back if
*punc_name = unc_name;
return NULL;
}
-
*share = '/';
strncpy((*punc_name)+2,unc_name,length);
+ free(unc_name);
unc_name = *punc_name;
unc_name[length+2] = 0;
goto continue_unc_parsing;
unc_name[0] = '/';
unc_name[1] = '/';
unc_name += 2;
- if ((share = strchr(unc_name, '/')) ||
- (share = strchr(unc_name,'\\'))) {
+
+ /* allow for either delimiter between host and sharename */
+ if ((share = strpbrk(unc_name, "/\\"))) {
*share = 0; /* temporarily terminate the string */
share += 1;
if(got_ip == 0) {
host_entry = gethostbyname(unc_name);
}
- *(share - 1) = '/'; /* put the slash back */
- if ((prefixpath = strchr(share, '/'))) {
+ *(share - 1) = '/'; /* put delimiter back */
+
+ /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
+ if ((prefixpath = strpbrk(share, "/\\"))) {
*prefixpath = 0; /* permanently terminate the string */
if (!strlen(++prefixpath))
prefixpath = NULL; /* this needs to be done explicitly */
{ NULL, 0, NULL, 0 }
};
+/* convert a string to uppercase. return false if the string
+ * wasn't ASCII or was a NULL ptr */
+static int
+uppercase_string(char *string)
+{
+ if (!string)
+ return 0;
+
+ while (*string) {
+ /* check for unicode */
+ if ((unsigned char) string[0] & 0x80)
+ return 0;
+ *string = toupper((unsigned char) *string);
+ string++;
+ }
+
+ return 1;
+}
+
int main(int argc, char ** argv)
{
int c;
char * options = NULL;
char * resolved_path = NULL;
char * temp;
+ char * dev_name;
int rc;
int rsize = 0;
int wsize = 0;
int gid = 0;
int optlen = 0;
int orgoptlen = 0;
+ size_t options_size = 0;
int retry = 0; /* set when we have to retry mount with uppercase */
struct stat statbuf;
struct utsname sysinfo;
printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
#endif */
if(argc > 2) {
- share_name = argv[1];
+ dev_name = argv[1];
+ share_name = strndup(argv[1], MAX_UNC_LEN);
+ if (share_name == NULL) {
+ fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
+ exit(1);
+ }
mountpoint = argv[2];
+ } else {
+ mount_cifs_usage();
+ exit(1);
}
/* add sharename in opts string as unc= parm */
}
}
- if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
+ if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
mount_cifs_usage();
exit(1);
}
optlen += strlen(mountpassword) + 6;
if(options)
free(options);
- options = (char *)malloc(optlen + 10 + 64 /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
+ options_size = optlen + 10 + 64;
+ options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
if(options == NULL) {
printf("Could not allocate memory for mount options\n");
return -1;
}
-
options[0] = 0;
- strncat(options,"unc=",4);
- strcat(options,share_name);
+ strlcpy(options,"unc=",options_size);
+ strlcat(options,share_name,options_size);
/* scan backwards and reverse direction of slash */
temp = strrchr(options, '/');
if(temp > options + 6)
*temp = '\\';
if(ipaddr) {
- strncat(options,",ip=",4);
- strcat(options,ipaddr);
+ strlcat(options,",ip=",options_size);
+ strlcat(options,ipaddr,options_size);
}
if(user_name) {
/* check for syntax like user=domain\user */
if(got_domain == 0)
domain_name = check_for_domain(&user_name);
- strncat(options,",user=",6);
- strcat(options,user_name);
+ strlcat(options,",user=",options_size);
+ strlcat(options,user_name,options_size);
}
if(retry == 0) {
- if(domain_name) {
+ if(domain_name) {
/* extra length accounted for in option string above */
- strncat(options,",domain=",8);
- strcat(options,domain_name);
+ strlcat(options,",domain=",options_size);
+ strlcat(options,domain_name,options_size);
}
}
if(mountpassword) {
/* if(sep is not set)*/
if(retry == 0)
check_for_comma(&mountpassword);
- strncat(options,",pass=",6);
- strcat(options,mountpassword);
+ strlcat(options,",pass=",options_size);
+ strlcat(options,mountpassword,options_size);
}
- strncat(options,",ver=",5);
- strcat(options,MOUNT_CIFS_VERSION_MAJOR);
+ strlcat(options,",ver=",options_size);
+ strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
if(orgoptions) {
- strcat(options,",");
- strcat(options,orgoptions);
+ strlcat(options,",",options_size);
+ strlcat(options,orgoptions,options_size);
}
if(prefixpath) {
- strncat(options,",prefixpath=",12);
- strcat(options,prefixpath); /* no need to cat the / */
- }
+ strlcat(options,",prefixpath=",options_size);
+ strlcat(options,prefixpath,options_size); /* no need to cat the / */
+ }
if(verboseflag)
printf("\nmount.cifs kernel mount options %s \n",options);
- if(mount(share_name, mountpoint, "cifs", flags, options)) {
- /* remember to kill daemon on error */
- char * tmp;
+ /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
+ replace_char(dev_name, '\\', '/', strlen(share_name));
+
+ if(mount(dev_name, mountpoint, "cifs", flags, options)) {
+ /* remember to kill daemon on error */
switch (errno) {
case 0:
printf("mount failed but no error number set\n");
case ENXIO:
if(retry == 0) {
retry = 1;
- tmp = share_name;
- while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
- *tmp = toupper((unsigned char)*tmp);
- tmp++;
- }
- if(!*tmp) {
+ if (uppercase_string(dev_name) &&
+ uppercase_string(share_name) &&
+ uppercase_string(prefixpath)) {
printf("retrying with upper case share name\n");
goto mount_retry;
}
} else {
pmntfile = setmntent(MOUNTED, "a+");
if(pmntfile) {
- mountent.mnt_fsname = share_name;
- mountent.mnt_dir = mountpoint;
- mountent.mnt_type = CONST_DISCARD(char *,"cifs");
+ mountent.mnt_fsname = dev_name;
+ mountent.mnt_dir = mountpoint;
+ mountent.mnt_type = CONST_DISCARD(char *,"cifs");
mountent.mnt_opts = (char *)malloc(220);
if(mountent.mnt_opts) {
char * mount_user = getusername();
memset(mountent.mnt_opts,0,200);
if(flags & MS_RDONLY)
- strcat(mountent.mnt_opts,"ro");
+ strlcat(mountent.mnt_opts,"ro",220);
else
- strcat(mountent.mnt_opts,"rw");
+ strlcat(mountent.mnt_opts,"rw",220);
if(flags & MS_MANDLOCK)
- strcat(mountent.mnt_opts,",mand");
+ strlcat(mountent.mnt_opts,",mand",220);
if(flags & MS_NOEXEC)
- strcat(mountent.mnt_opts,",noexec");
+ strlcat(mountent.mnt_opts,",noexec",220);
if(flags & MS_NOSUID)
- strcat(mountent.mnt_opts,",nosuid");
+ strlcat(mountent.mnt_opts,",nosuid",220);
if(flags & MS_NODEV)
- strcat(mountent.mnt_opts,",nodev");
+ strlcat(mountent.mnt_opts,",nodev",220);
if(flags & MS_SYNCHRONOUS)
- strcat(mountent.mnt_opts,",synch");
+ strlcat(mountent.mnt_opts,",synch",220);
if(mount_user) {
if(getuid() != 0) {
- strcat(mountent.mnt_opts,",user=");
- strcat(mountent.mnt_opts,mount_user);
+ strlcat(mountent.mnt_opts,",user=",220);
+ strlcat(mountent.mnt_opts,mount_user,220);
}
/* free(mount_user); do not free static mem */
}
free(resolved_path);
}
- if(free_share_name) {
- free(share_name);
- }
+ free(share_name);
return rc;
}
-