2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
44 #define MOUNT_CIFS_VERSION_MAJOR "1"
45 #define MOUNT_CIFS_VERSION_MINOR "12"
47 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #include "include/version.h"
50 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
54 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #define MOUNT_CIFS_VENDOR_SUFFIX ""
57 #endif /* _SAMBA_BUILD_ */
58 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
61 #include "include/config.h"
72 #define MAX_UNC_LEN 1024
74 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
77 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
80 #define MOUNT_PASSWD_SIZE 64
81 #define DOMAIN_SIZE 64
83 const char *thisprogram;
85 static int got_password = 0;
86 static int got_user = 0;
87 static int got_domain = 0;
88 static int got_ip = 0;
89 static int got_unc = 0;
90 static int got_uid = 0;
91 static int got_gid = 0;
92 static char * user_name = NULL;
93 static char * mountpassword = NULL;
94 char * domain_name = NULL;
95 char * prefixpath = NULL;
97 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
98 * don't link to libreplace so need them here. */
100 /* like strncpy but does not 0 fill the buffer and always null
101 * terminates. bufsize is the size of the destination buffer */
104 static size_t strlcpy(char *d, const char *s, size_t bufsize)
106 size_t len = strlen(s);
108 if (bufsize <= 0) return 0;
109 if (len >= bufsize) len = bufsize-1;
116 /* like strncat but does not 0 fill the buffer and always null
117 * terminates. bufsize is the length of the buffer, which should
118 * be one more than the maximum resulting string length */
121 static size_t strlcat(char *d, const char *s, size_t bufsize)
123 size_t len1 = strlen(d);
124 size_t len2 = strlen(s);
125 size_t ret = len1 + len2;
127 if (len1+len2 >= bufsize) {
128 if (bufsize < (len1+1)) {
131 len2 = bufsize - (len1+1);
134 memcpy(d+len1, s, len2);
144 open nofollow - avoid symlink exposure?
145 get owner of dir see if matches self or if root
146 call system(umount argv) etc.
150 static char * check_for_domain(char **);
153 static void mount_cifs_usage(void)
155 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
156 printf("\nMount the remote target, specified as a UNC name,");
157 printf(" to a local directory.\n\nOptions:\n");
158 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
159 printf("\nLess commonly used options:");
160 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
161 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
162 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
163 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
164 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
165 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
166 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
167 printf("\n\nRarely used options:");
168 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
169 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
170 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
171 printf("\n\tin6_addr");
172 printf("\n\nOptions are described in more detail in the manual page");
173 printf("\n\tman 8 mount.cifs\n");
174 printf("\nTo display the version number of the mount helper:");
175 printf("\n\t%s -V\n",thisprogram);
177 SAFE_FREE(mountpassword);
181 /* caller frees username if necessary */
182 static char * getusername(void) {
183 char *username = NULL;
184 struct passwd *password = getpwuid(getuid());
187 username = password->pw_name;
192 static char * parse_cifs_url(char * unc_name)
194 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
198 static int open_cred_file(char * file_name)
204 fs = fopen(file_name,"r");
207 line_buf = (char *)malloc(4096);
208 if(line_buf == NULL) {
213 while(fgets(line_buf,4096,fs)) {
214 /* parse line from credential file */
216 /* eat leading white space */
217 for(i=0;i<4086;i++) {
218 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
220 /* if whitespace - skip past it */
222 if (strncasecmp("username",line_buf+i,8) == 0) {
223 temp_val = strchr(line_buf + i,'=');
225 /* go past equals sign */
227 for(length = 0;length<4087;length++) {
228 if ((temp_val[length] == '\n')
229 || (temp_val[length] == '\0')) {
230 temp_val[length] = '\0';
235 printf("mount.cifs failed due to malformed username in credentials file");
236 memset(line_buf,0,4096);
240 user_name = (char *)calloc(1 + length,1);
241 /* BB adding free of user_name string before exit,
242 not really necessary but would be cleaner */
243 strlcpy(user_name,temp_val, length+1);
246 } else if (strncasecmp("password",line_buf+i,8) == 0) {
247 temp_val = strchr(line_buf+i,'=');
249 /* go past equals sign */
251 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
252 if ((temp_val[length] == '\n')
253 || (temp_val[length] == '\0')) {
254 temp_val[length] = '\0';
258 if(length > MOUNT_PASSWD_SIZE) {
259 printf("mount.cifs failed: password in credentials file too long\n");
260 memset(line_buf,0, 4096);
263 if(mountpassword == NULL) {
264 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
266 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
268 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
273 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
274 temp_val = strchr(line_buf+i,'=');
276 /* go past equals sign */
279 printf("\nDomain %s\n",temp_val);
280 for(length = 0;length<DOMAIN_SIZE+1;length++) {
281 if ((temp_val[length] == '\n')
282 || (temp_val[length] == '\0')) {
283 temp_val[length] = '\0';
287 if(length > DOMAIN_SIZE) {
288 printf("mount.cifs failed: domain in credentials file too long\n");
291 if(domain_name == NULL) {
292 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
294 memset(domain_name,0,DOMAIN_SIZE);
296 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
309 static int get_password_from_file(int file_descript, char * filename)
315 if(mountpassword == NULL)
316 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
318 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
320 if (mountpassword == NULL) {
321 printf("malloc failed\n");
325 if(filename != NULL) {
326 file_descript = open(filename, O_RDONLY);
327 if(file_descript < 0) {
328 printf("mount.cifs failed. %s attempting to open password file %s\n",
329 strerror(errno),filename);
333 /* else file already open and fd provided */
335 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
336 rc = read(file_descript,&c,1);
338 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
340 close(file_descript);
343 if(mountpassword[0] == 0) {
345 printf("\nWarning: null password used since cifs password file empty");
348 } else /* read valid character */ {
349 if((c == 0) || (c == '\n')) {
350 mountpassword[i] = '\0';
353 mountpassword[i] = c;
356 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
357 printf("\nWarning: password longer than %d characters specified in cifs password file",
361 if(filename != NULL) {
362 close(file_descript);
368 static int parse_options(char ** optionsp, int * filesys_flags)
371 char * percent_char = NULL;
373 char * next_keyword = NULL;
381 if (!optionsp || !*optionsp)
386 printf("parsing options: %s\n", data);
388 /* BB fixme check for separator override BB */
392 snprintf(user,sizeof(user),"%u",getuid());
394 snprintf(group,sizeof(group),"%u",getgid());
397 /* while ((data = strsep(&options, ",")) != NULL) { */
398 while(data != NULL) {
399 /* check if ends with trailing comma */
403 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
404 /* data = next keyword */
405 /* value = next value ie stuff after equal sign */
407 next_keyword = strchr(data,','); /* BB handle sep= */
409 /* temporarily null terminate end of keyword=value pair */
413 /* temporarily null terminate keyword to make keyword and value distinct */
414 if ((value = strchr(data, '=')) != NULL) {
419 if (strncmp(data, "users",5) == 0) {
420 if(!value || !*value) {
423 } else if (strncmp(data, "user_xattr",10) == 0) {
424 /* do nothing - need to skip so not parsed as user name */
425 } else if (strncmp(data, "user", 4) == 0) {
427 if (!value || !*value) {
428 if(data[4] == '\0') {
430 printf("\nskipping empty user mount parameter\n");
431 /* remove the parm since it would otherwise be confusing
432 to the kernel code which would think it was a real username */
435 printf("username specified with no parameter\n");
437 return 1; /* needs_arg; */
440 if (strnlen(value, 260) < 260) {
442 percent_char = strchr(value,'%');
445 if(mountpassword == NULL)
446 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
449 printf("\nmount.cifs warning - password specified twice\n");
452 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
453 /* remove password from username */
454 while(*percent_char != 0) {
460 /* this is only case in which the user
461 name buf is not malloc - so we have to
462 check for domain name embedded within
463 the user name here since the later
464 call to check_for_domain will not be
466 domain_name = check_for_domain(&value);
468 printf("username too long\n");
473 } else if (strncmp(data, "pass", 4) == 0) {
474 if (!value || !*value) {
476 printf("\npassword specified twice, ignoring second\n");
479 } else if (strnlen(value, 17) < 17) {
481 printf("\nmount.cifs warning - password specified twice\n");
484 printf("password too long\n");
488 } else if (strncmp(data, "sec", 3) == 0) {
490 if (!strncmp(value, "none", 4) ||
491 !strncmp(value, "krb5", 4))
494 } else if (strncmp(data, "ip", 2) == 0) {
495 if (!value || !*value) {
496 printf("target ip address argument missing");
497 } else if (strnlen(value, 35) < 35) {
499 printf("ip address %s override specified\n",value);
502 printf("ip address too long\n");
506 } else if ((strncmp(data, "unc", 3) == 0)
507 || (strncmp(data, "target", 6) == 0)
508 || (strncmp(data, "path", 4) == 0)) {
509 if (!value || !*value) {
510 printf("invalid path to network resource\n");
512 return 1; /* needs_arg; */
513 } else if(strnlen(value,5) < 5) {
514 printf("UNC name too short");
517 if (strnlen(value, 300) < 300) {
519 if (strncmp(value, "//", 2) == 0) {
521 printf("unc name specified twice, ignoring second\n");
524 } else if (strncmp(value, "\\\\", 2) != 0) {
525 printf("UNC Path does not begin with // or \\\\ \n");
530 printf("unc name specified twice, ignoring second\n");
535 printf("CIFS: UNC name too long\n");
539 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
540 || (strncmp(data, "workg", 5) == 0)) {
541 /* note this allows for synonyms of "domain"
542 such as "DOM" and "dom" and "workgroup"
543 and "WORKGRP" etc. */
544 if (!value || !*value) {
545 printf("CIFS: invalid domain name\n");
547 return 1; /* needs_arg; */
549 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
552 printf("domain name too long\n");
556 } else if (strncmp(data, "cred", 4) == 0) {
557 if (value && *value) {
558 rc = open_cred_file(value);
560 printf("error %d (%s) opening credential file %s\n",
561 rc, strerror(rc), value);
566 printf("invalid credential file name specified\n");
570 } else if (strncmp(data, "uid", 3) == 0) {
571 if (value && *value) {
573 if (!isdigit(*value)) {
576 if (!(pw = getpwnam(value))) {
577 printf("bad user name \"%s\"\n", value);
580 snprintf(user, sizeof(user), "%u", pw->pw_uid);
582 strlcpy(user,value,sizeof(user));
586 } else if (strncmp(data, "gid", 3) == 0) {
587 if (value && *value) {
589 if (!isdigit(*value)) {
592 if (!(gr = getgrnam(value))) {
593 printf("bad group name \"%s\"\n", value);
596 snprintf(group, sizeof(group), "%u", gr->gr_gid);
598 strlcpy(group,value,sizeof(group));
602 /* fmask and dmask synonyms for people used to smbfs syntax */
603 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
604 if (!value || !*value) {
605 printf ("Option '%s' requires a numerical argument\n", data);
610 if (value[0] != '0') {
611 printf ("WARNING: '%s' not expressed in octal.\n", data);
614 if (strcmp (data, "fmask") == 0) {
615 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
616 data = "file_mode"; /* BB fix this */
618 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
619 if (!value || !*value) {
620 printf ("Option '%s' requires a numerical argument\n", data);
625 if (value[0] != '0') {
626 printf ("WARNING: '%s' not expressed in octal.\n", data);
629 if (strcmp (data, "dmask") == 0) {
630 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
633 /* the following eight mount options should be
634 stripped out from what is passed into the kernel
635 since these eight options are best passed as the
636 mount flags rather than redundantly to the kernel
637 and could generate spurious warnings depending on the
638 level of the corresponding cifs vfs kernel code */
639 } else if (strncmp(data, "nosuid", 6) == 0) {
640 *filesys_flags |= MS_NOSUID;
641 } else if (strncmp(data, "suid", 4) == 0) {
642 *filesys_flags &= ~MS_NOSUID;
643 } else if (strncmp(data, "nodev", 5) == 0) {
644 *filesys_flags |= MS_NODEV;
645 } else if ((strncmp(data, "nobrl", 5) == 0) ||
646 (strncmp(data, "nolock", 6) == 0)) {
647 *filesys_flags &= ~MS_MANDLOCK;
648 } else if (strncmp(data, "dev", 3) == 0) {
649 *filesys_flags &= ~MS_NODEV;
650 } else if (strncmp(data, "noexec", 6) == 0) {
651 *filesys_flags |= MS_NOEXEC;
652 } else if (strncmp(data, "exec", 4) == 0) {
653 *filesys_flags &= ~MS_NOEXEC;
654 } else if (strncmp(data, "guest", 5) == 0) {
656 } else if (strncmp(data, "ro", 2) == 0) {
657 *filesys_flags |= MS_RDONLY;
658 } else if (strncmp(data, "rw", 2) == 0) {
659 *filesys_flags &= ~MS_RDONLY;
660 } else if (strncmp(data, "remount", 7) == 0) {
661 *filesys_flags |= MS_REMOUNT;
662 } /* else if (strnicmp(data, "port", 4) == 0) {
663 if (value && *value) {
665 simple_strtoul(value, &value, 0);
667 } else if (strnicmp(data, "rsize", 5) == 0) {
668 if (value && *value) {
670 simple_strtoul(value, &value, 0);
672 } else if (strnicmp(data, "wsize", 5) == 0) {
673 if (value && *value) {
675 simple_strtoul(value, &value, 0);
677 } else if (strnicmp(data, "version", 3) == 0) {
679 printf("CIFS: Unknown mount option %s\n",data);
680 } */ /* nothing to do on those four mount options above.
681 Just pass to kernel and ignore them here */
683 /* Copy (possibly modified) option to out */
684 word_len = strlen(data);
686 word_len += 1 + strlen(value);
688 out = (char *)realloc(out, out_len + word_len + 2);
695 strlcat(out, ",", out_len + word_len + 2);
700 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
702 snprintf(out + out_len, word_len + 1, "%s", data);
703 out_len = strlen(out);
709 /* special-case the uid and gid */
711 word_len = strlen(user);
713 out = (char *)realloc(out, out_len + word_len + 6);
720 strlcat(out, ",", out_len + word_len + 6);
723 snprintf(out + out_len, word_len + 5, "uid=%s", user);
724 out_len = strlen(out);
727 word_len = strlen(group);
729 out = (char *)realloc(out, out_len + 1 + word_len + 6);
736 strlcat(out, ",", out_len + word_len + 6);
739 snprintf(out + out_len, word_len + 5, "gid=%s", group);
740 out_len = strlen(out);
743 SAFE_FREE(*optionsp);
748 /* replace all (one or more) commas with double commas */
749 static void check_for_comma(char ** ppasswrd)
754 int number_of_commas = 0;
769 if(number_of_commas == 0)
771 if(number_of_commas > MOUNT_PASSWD_SIZE) {
772 /* would otherwise overflow the mount options buffer */
773 printf("\nInvalid password. Password contains too many commas.\n");
777 new_pass_buf = (char *)malloc(len+number_of_commas+1);
778 if(new_pass_buf == NULL)
781 for(i=0,j=0;i<len;i++,j++) {
782 new_pass_buf[j] = pass[i];
785 new_pass_buf[j] = pass[i];
788 new_pass_buf[len+number_of_commas] = 0;
790 SAFE_FREE(*ppasswrd);
791 *ppasswrd = new_pass_buf;
796 /* Usernames can not have backslash in them and we use
797 [BB check if usernames can have forward slash in them BB]
798 backslash as domain\user separator character
800 static char * check_for_domain(char **ppuser)
802 char * original_string;
812 original_string = *ppuser;
814 if (original_string == NULL)
817 original_len = strlen(original_string);
819 usernm = strchr(*ppuser,'/');
820 if (usernm == NULL) {
821 usernm = strchr(*ppuser,'\\');
827 printf("Domain name specified twice. Username probably malformed\n");
833 if (domainnm[0] != 0) {
836 printf("null domain\n");
838 len = strlen(domainnm);
839 /* reset domainm to new buffer, and copy
840 domain name into it */
841 domainnm = (char *)malloc(len+1);
845 strlcpy(domainnm,*ppuser,len+1);
847 /* move_string(*ppuser, usernm+1) */
848 len = strlen(usernm+1);
850 if(len >= original_len) {
851 /* should not happen */
855 for(i=0;i<original_len;i++) {
857 original_string[i] = usernm[i+1];
858 else /* stuff with commas to remove last parm */
859 original_string[i] = ',';
862 /* BB add check for more than one slash?
870 /* replace all occurances of "from" in a string with "to" */
871 static void replace_char(char *string, char from, char to, int maxlen)
873 char *lastchar = string + maxlen;
875 string = strchr(string, from);
878 if (string >= lastchar)
884 /* Note that caller frees the returned buffer if necessary */
885 static char * parse_server(char ** punc_name)
887 char * unc_name = *punc_name;
888 int length = strnlen(unc_name, MAX_UNC_LEN);
890 char * ipaddress_string = NULL;
891 struct hostent * host_entry = NULL;
892 struct in_addr server_ipaddr;
894 if(length > (MAX_UNC_LEN - 1)) {
895 printf("mount error: UNC name too long");
898 if (strncasecmp("cifs://",unc_name,7) == 0)
899 return parse_cifs_url(unc_name+7);
900 if (strncasecmp("smb://",unc_name,6) == 0) {
901 return parse_cifs_url(unc_name+6);
905 /* BB add code to find DFS root here */
906 printf("\nMounting the DFS root for domain not implemented yet\n");
909 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
910 /* check for nfs syntax ie server:share */
911 share = strchr(unc_name,':');
913 *punc_name = (char *)malloc(length+3);
914 if(*punc_name == NULL) {
915 /* put the original string back if
917 *punc_name = unc_name;
921 strlcpy((*punc_name)+2,unc_name,length+1);
923 unc_name = *punc_name;
924 unc_name[length+2] = 0;
925 goto continue_unc_parsing;
927 printf("mount error: improperly formatted UNC name.");
928 printf(" %s does not begin with \\\\ or //\n",unc_name);
932 continue_unc_parsing:
937 /* allow for either delimiter between host and sharename */
938 if ((share = strpbrk(unc_name, "/\\"))) {
939 *share = 0; /* temporarily terminate the string */
942 host_entry = gethostbyname(unc_name);
944 *(share - 1) = '/'; /* put delimiter back */
946 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
947 if ((prefixpath = strpbrk(share, "/\\"))) {
948 *prefixpath = 0; /* permanently terminate the string */
949 if (!strlen(++prefixpath))
950 prefixpath = NULL; /* this needs to be done explicitly */
954 printf("ip address specified explicitly\n");
957 if(host_entry == NULL) {
958 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
961 /* BB should we pass an alternate version of the share name as Unicode */
962 /* BB what about ipv6? BB */
963 /* BB add retries with alternate servers in list */
965 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
967 ipaddress_string = inet_ntoa(server_ipaddr);
968 if(ipaddress_string == NULL) {
969 printf("mount error: could not get valid ip address for target server\n");
972 return ipaddress_string;
975 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
976 printf("Mounting the DFS root for a particular server not implemented yet\n");
983 static struct option longopts[] = {
984 { "all", 0, NULL, 'a' },
985 { "help",0, NULL, 'h' },
986 { "move",0, NULL, 'm' },
987 { "bind",0, NULL, 'b' },
988 { "read-only", 0, NULL, 'r' },
989 { "ro", 0, NULL, 'r' },
990 { "verbose", 0, NULL, 'v' },
991 { "version", 0, NULL, 'V' },
992 { "read-write", 0, NULL, 'w' },
993 { "rw", 0, NULL, 'w' },
994 { "options", 1, NULL, 'o' },
995 { "type", 1, NULL, 't' },
996 { "rsize",1, NULL, 'R' },
997 { "wsize",1, NULL, 'W' },
998 { "uid", 1, NULL, '1'},
999 { "gid", 1, NULL, '2'},
1000 { "user",1,NULL,'u'},
1001 { "username",1,NULL,'u'},
1002 { "dom",1,NULL,'d'},
1003 { "domain",1,NULL,'d'},
1004 { "password",1,NULL,'p'},
1005 { "pass",1,NULL,'p'},
1006 { "credentials",1,NULL,'c'},
1007 { "port",1,NULL,'P'},
1008 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1009 { NULL, 0, NULL, 0 }
1012 /* convert a string to uppercase. return false if the string
1013 * wasn't ASCII. Return success on a NULL ptr */
1015 uppercase_string(char *string)
1021 /* check for unicode */
1022 if ((unsigned char) string[0] & 0x80)
1024 *string = toupper((unsigned char) *string);
1031 int main(int argc, char ** argv)
1034 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1035 char * orgoptions = NULL;
1036 char * share_name = NULL;
1037 char * ipaddr = NULL;
1039 char * mountpoint = NULL;
1040 char * options = NULL;
1041 char * resolved_path = NULL;
1052 size_t options_size = 0;
1053 int retry = 0; /* set when we have to retry mount with uppercase */
1054 struct stat statbuf;
1055 struct utsname sysinfo;
1056 struct mntent mountent;
1059 /* setlocale(LC_ALL, "");
1060 bindtextdomain(PACKAGE, LOCALEDIR);
1061 textdomain(PACKAGE); */
1064 thisprogram = argv[0];
1070 if(thisprogram == NULL)
1071 thisprogram = "mount.cifs";
1074 /* BB add workstation name and domain and pass down */
1076 /* #ifdef _GNU_SOURCE
1077 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1081 share_name = strndup(argv[1], MAX_UNC_LEN);
1082 if (share_name == NULL) {
1083 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1086 mountpoint = argv[2];
1092 /* add sharename in opts string as unc= parm */
1094 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1095 longopts, NULL)) != -1) {
1097 /* No code to do the following options yet */
1099 list_with_volumelabel = 1;
1102 volumelabel = optarg;
1109 case 'h': /* help */
1110 mount_cifs_usage ();
1120 "option 'b' (MS_BIND) not supported\n");
1128 "option 'm' (MS_MOVE) not supported\n");
1132 orgoptions = strdup(optarg);
1134 case 'r': /* mount readonly */
1144 printf ("mount.cifs version: %s.%s%s\n",
1145 MOUNT_CIFS_VERSION_MAJOR,
1146 MOUNT_CIFS_VERSION_MINOR,
1147 MOUNT_CIFS_VENDOR_SUFFIX);
1150 flags &= ~MS_RDONLY;
1153 rsize = atoi(optarg) ;
1156 wsize = atoi(optarg);
1159 if (isdigit(*optarg)) {
1162 uid = strtoul(optarg, &ep, 10);
1164 printf("bad uid value \"%s\"\n", optarg);
1170 if (!(pw = getpwnam(optarg))) {
1171 printf("bad user name \"%s\"\n", optarg);
1179 if (isdigit(*optarg)) {
1182 gid = strtoul(optarg, &ep, 10);
1184 printf("bad gid value \"%s\"\n", optarg);
1190 if (!(gr = getgrnam(optarg))) {
1191 printf("bad user name \"%s\"\n", optarg);
1203 domain_name = optarg; /* BB fix this - currently ignored */
1207 if(mountpassword == NULL)
1208 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1211 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1215 get_password_from_file(0 /* stdin */,NULL);
1220 printf("unknown mount option %c\n",c);
1226 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1231 if (getenv("PASSWD")) {
1232 if(mountpassword == NULL)
1233 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1235 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1238 } else if (getenv("PASSWD_FD")) {
1239 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1240 } else if (getenv("PASSWD_FILE")) {
1241 get_password_from_file(0, getenv("PASSWD_FILE"));
1244 if (orgoptions && parse_options(&orgoptions, &flags)) {
1248 ipaddr = parse_server(&share_name);
1249 if((ipaddr == NULL) && (got_ip == 0)) {
1250 printf("No ip address specified and hostname not found\n");
1255 /* BB save off path and pop after mount returns? */
1256 resolved_path = (char *)malloc(PATH_MAX+1);
1258 /* Note that if we can not canonicalize the name, we get
1259 another chance to see if it is valid when we chdir to it */
1260 if (realpath(mountpoint, resolved_path)) {
1261 mountpoint = resolved_path;
1264 if(chdir(mountpoint)) {
1265 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1270 if(stat (".", &statbuf)) {
1271 printf("mount error: mount point %s does not exist\n",mountpoint);
1276 if (S_ISDIR(statbuf.st_mode) == 0) {
1277 printf("mount error: mount point %s is not a directory\n",mountpoint);
1282 if((getuid() != 0) && (geteuid() == 0)) {
1283 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1284 #ifndef CIFS_ALLOW_USR_SUID
1285 /* Do not allow user mounts to control suid flag
1286 for mount unless explicitly built that way */
1287 flags |= MS_NOSUID | MS_NODEV;
1290 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1296 user_name = getusername();
1300 if(got_password == 0) {
1301 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1302 no good replacement yet. */
1303 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1304 if (!tmp_pass || !mountpassword) {
1305 printf("Password not entered, exiting\n");
1308 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1311 /* FIXME launch daemon (handles dfs name resolution and credential change)
1312 remember to clear parms and overwrite password field before launching */
1315 optlen = strlen(orgoptions);
1320 optlen += strlen(share_name) + 4;
1322 printf("No server share name specified\n");
1323 printf("\nMounting the DFS root for server not implemented yet\n");
1327 optlen += strlen(user_name) + 6;
1329 optlen += strlen(ipaddr) + 4;
1331 optlen += strlen(mountpassword) + 6;
1333 options_size = optlen + 10 + DOMAIN_SIZE;
1334 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 */);
1336 if(options == NULL) {
1337 printf("Could not allocate memory for mount options\n");
1342 strlcpy(options,"unc=",options_size);
1343 strlcat(options,share_name,options_size);
1344 /* scan backwards and reverse direction of slash */
1345 temp = strrchr(options, '/');
1346 if(temp > options + 6)
1349 strlcat(options,",ip=",options_size);
1350 strlcat(options,ipaddr,options_size);
1354 /* check for syntax like user=domain\user */
1356 domain_name = check_for_domain(&user_name);
1357 strlcat(options,",user=",options_size);
1358 strlcat(options,user_name,options_size);
1362 /* extra length accounted for in option string above */
1363 strlcat(options,",domain=",options_size);
1364 strlcat(options,domain_name,options_size);
1368 /* Commas have to be doubled, or else they will
1369 look like the parameter separator */
1370 /* if(sep is not set)*/
1372 check_for_comma(&mountpassword);
1373 strlcat(options,",pass=",options_size);
1374 strlcat(options,mountpassword,options_size);
1377 strlcat(options,",ver=",options_size);
1378 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1381 strlcat(options,",",options_size);
1382 strlcat(options,orgoptions,options_size);
1385 strlcat(options,",prefixpath=",options_size);
1386 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1389 printf("\nmount.cifs kernel mount options %s \n",options);
1391 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1392 replace_char(dev_name, '\\', '/', strlen(share_name));
1394 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1395 /* remember to kill daemon on error */
1398 printf("mount failed but no error number set\n");
1401 printf("mount error: cifs filesystem not supported by the system\n");
1406 if (uppercase_string(dev_name) &&
1407 uppercase_string(share_name) &&
1408 uppercase_string(prefixpath)) {
1409 printf("retrying with upper case share name\n");
1414 printf("mount error %d = %s\n",errno,strerror(errno));
1416 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1419 atexit(unlock_mtab);
1422 printf("cannot lock mtab");
1425 pmntfile = setmntent(MOUNTED, "a+");
1427 printf("could not update mount table\n");
1432 mountent.mnt_fsname = dev_name;
1433 mountent.mnt_dir = mountpoint;
1434 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1435 mountent.mnt_opts = (char *)malloc(220);
1436 if(mountent.mnt_opts) {
1437 char * mount_user = getusername();
1438 memset(mountent.mnt_opts,0,200);
1439 if(flags & MS_RDONLY)
1440 strlcat(mountent.mnt_opts,"ro",220);
1442 strlcat(mountent.mnt_opts,"rw",220);
1443 if(flags & MS_MANDLOCK)
1444 strlcat(mountent.mnt_opts,",mand",220);
1445 if(flags & MS_NOEXEC)
1446 strlcat(mountent.mnt_opts,",noexec",220);
1447 if(flags & MS_NOSUID)
1448 strlcat(mountent.mnt_opts,",nosuid",220);
1449 if(flags & MS_NODEV)
1450 strlcat(mountent.mnt_opts,",nodev",220);
1451 if(flags & MS_SYNCHRONOUS)
1452 strlcat(mountent.mnt_opts,",sync",220);
1455 strlcat(mountent.mnt_opts,
1457 strlcat(mountent.mnt_opts,
1462 mountent.mnt_freq = 0;
1463 mountent.mnt_passno = 0;
1464 rc = addmntent(pmntfile,&mountent);
1465 endmntent(pmntfile);
1467 SAFE_FREE(mountent.mnt_opts);
1473 int len = strlen(mountpassword);
1474 memset(mountpassword,0,len);
1475 SAFE_FREE(mountpassword);
1479 SAFE_FREE(orgoptions);
1480 SAFE_FREE(resolved_path);
1481 SAFE_FREE(share_name);