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 /* currently maximum length of IPv6 address string */
84 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
86 const char *thisprogram;
89 static int got_password = 0;
90 static int got_user = 0;
91 static int got_domain = 0;
92 static int got_ip = 0;
93 static int got_unc = 0;
94 static int got_uid = 0;
95 static int got_gid = 0;
96 static char * user_name = NULL;
97 static char * mountpassword = NULL;
98 char * domain_name = NULL;
99 char * prefixpath = NULL;
101 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
102 * don't link to libreplace so need them here. */
104 /* like strncpy but does not 0 fill the buffer and always null
105 * terminates. bufsize is the size of the destination buffer */
108 static size_t strlcpy(char *d, const char *s, size_t bufsize)
110 size_t len = strlen(s);
112 if (bufsize <= 0) return 0;
113 if (len >= bufsize) len = bufsize-1;
120 /* like strncat but does not 0 fill the buffer and always null
121 * terminates. bufsize is the length of the buffer, which should
122 * be one more than the maximum resulting string length */
125 static size_t strlcat(char *d, const char *s, size_t bufsize)
127 size_t len1 = strlen(d);
128 size_t len2 = strlen(s);
129 size_t ret = len1 + len2;
131 if (len1+len2 >= bufsize) {
132 if (bufsize < (len1+1)) {
135 len2 = bufsize - (len1+1);
138 memcpy(d+len1, s, len2);
148 open nofollow - avoid symlink exposure?
149 get owner of dir see if matches self or if root
150 call system(umount argv) etc.
154 static char * check_for_domain(char **);
157 static void mount_cifs_usage(void)
159 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
160 printf("\nMount the remote target, specified as a UNC name,");
161 printf(" to a local directory.\n\nOptions:\n");
162 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
163 printf("\nLess commonly used options:");
164 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
165 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
166 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
167 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
168 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
169 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
170 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
171 printf("\n\nRarely used options:");
172 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
173 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
174 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
175 printf("\n\tin6_addr");
176 printf("\n\nOptions are described in more detail in the manual page");
177 printf("\n\tman 8 mount.cifs\n");
178 printf("\nTo display the version number of the mount helper:");
179 printf("\n\t%s -V\n",thisprogram);
181 SAFE_FREE(mountpassword);
185 /* caller frees username if necessary */
186 static char * getusername(void) {
187 char *username = NULL;
188 struct passwd *password = getpwuid(getuid());
191 username = password->pw_name;
196 static int open_cred_file(char * file_name)
202 fs = fopen(file_name,"r");
205 line_buf = (char *)malloc(4096);
206 if(line_buf == NULL) {
211 while(fgets(line_buf,4096,fs)) {
212 /* parse line from credential file */
214 /* eat leading white space */
215 for(i=0;i<4086;i++) {
216 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
218 /* if whitespace - skip past it */
220 if (strncasecmp("username",line_buf+i,8) == 0) {
221 temp_val = strchr(line_buf + i,'=');
223 /* go past equals sign */
225 for(length = 0;length<4087;length++) {
226 if ((temp_val[length] == '\n')
227 || (temp_val[length] == '\0')) {
228 temp_val[length] = '\0';
233 printf("mount.cifs failed due to malformed username in credentials file");
234 memset(line_buf,0,4096);
238 user_name = (char *)calloc(1 + length,1);
239 /* BB adding free of user_name string before exit,
240 not really necessary but would be cleaner */
241 strlcpy(user_name,temp_val, length+1);
244 } else if (strncasecmp("password",line_buf+i,8) == 0) {
245 temp_val = strchr(line_buf+i,'=');
247 /* go past equals sign */
249 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
250 if ((temp_val[length] == '\n')
251 || (temp_val[length] == '\0')) {
252 temp_val[length] = '\0';
256 if(length > MOUNT_PASSWD_SIZE) {
257 printf("mount.cifs failed: password in credentials file too long\n");
258 memset(line_buf,0, 4096);
261 if(mountpassword == NULL) {
262 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
264 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
266 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
271 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
272 temp_val = strchr(line_buf+i,'=');
274 /* go past equals sign */
277 printf("\nDomain %s\n",temp_val);
278 for(length = 0;length<DOMAIN_SIZE+1;length++) {
279 if ((temp_val[length] == '\n')
280 || (temp_val[length] == '\0')) {
281 temp_val[length] = '\0';
285 if(length > DOMAIN_SIZE) {
286 printf("mount.cifs failed: domain in credentials file too long\n");
289 if(domain_name == NULL) {
290 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
292 memset(domain_name,0,DOMAIN_SIZE);
294 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
307 static int get_password_from_file(int file_descript, char * filename)
313 if(mountpassword == NULL)
314 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
316 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
318 if (mountpassword == NULL) {
319 printf("malloc failed\n");
323 if(filename != NULL) {
324 file_descript = open(filename, O_RDONLY);
325 if(file_descript < 0) {
326 printf("mount.cifs failed. %s attempting to open password file %s\n",
327 strerror(errno),filename);
331 /* else file already open and fd provided */
333 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
334 rc = read(file_descript,&c,1);
336 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
338 close(file_descript);
341 if(mountpassword[0] == 0) {
343 printf("\nWarning: null password used since cifs password file empty");
346 } else /* read valid character */ {
347 if((c == 0) || (c == '\n')) {
348 mountpassword[i] = '\0';
351 mountpassword[i] = c;
354 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
355 printf("\nWarning: password longer than %d characters specified in cifs password file",
359 if(filename != NULL) {
360 close(file_descript);
366 static int parse_options(char ** optionsp, int * filesys_flags)
369 char * percent_char = NULL;
371 char * next_keyword = NULL;
379 if (!optionsp || !*optionsp)
384 printf("parsing options: %s\n", data);
386 /* BB fixme check for separator override BB */
390 snprintf(user,sizeof(user),"%u",getuid());
392 snprintf(group,sizeof(group),"%u",getgid());
395 /* while ((data = strsep(&options, ",")) != NULL) { */
396 while(data != NULL) {
397 /* check if ends with trailing comma */
401 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
402 /* data = next keyword */
403 /* value = next value ie stuff after equal sign */
405 next_keyword = strchr(data,','); /* BB handle sep= */
407 /* temporarily null terminate end of keyword=value pair */
411 /* temporarily null terminate keyword to make keyword and value distinct */
412 if ((value = strchr(data, '=')) != NULL) {
417 if (strncmp(data, "users",5) == 0) {
418 if(!value || !*value) {
421 } else if (strncmp(data, "user_xattr",10) == 0) {
422 /* do nothing - need to skip so not parsed as user name */
423 } else if (strncmp(data, "user", 4) == 0) {
425 if (!value || !*value) {
426 if(data[4] == '\0') {
428 printf("\nskipping empty user mount parameter\n");
429 /* remove the parm since it would otherwise be confusing
430 to the kernel code which would think it was a real username */
433 printf("username specified with no parameter\n");
435 return 1; /* needs_arg; */
438 if (strnlen(value, 260) < 260) {
440 percent_char = strchr(value,'%');
443 if(mountpassword == NULL)
444 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
447 printf("\nmount.cifs warning - password specified twice\n");
450 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
451 /* remove password from username */
452 while(*percent_char != 0) {
458 /* this is only case in which the user
459 name buf is not malloc - so we have to
460 check for domain name embedded within
461 the user name here since the later
462 call to check_for_domain will not be
464 domain_name = check_for_domain(&value);
466 printf("username too long\n");
471 } else if (strncmp(data, "pass", 4) == 0) {
472 if (!value || !*value) {
474 printf("\npassword specified twice, ignoring second\n");
477 } else if (strnlen(value, 17) < 17) {
479 printf("\nmount.cifs warning - password specified twice\n");
482 printf("password too long\n");
486 } else if (strncmp(data, "sec", 3) == 0) {
488 if (!strncmp(value, "none", 4) ||
489 !strncmp(value, "krb5", 4))
492 } else if (strncmp(data, "ip", 2) == 0) {
493 if (!value || !*value) {
494 printf("target ip address argument missing");
495 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
497 printf("ip address %s override specified\n",value);
500 printf("ip address too long\n");
504 } else if ((strncmp(data, "unc", 3) == 0)
505 || (strncmp(data, "target", 6) == 0)
506 || (strncmp(data, "path", 4) == 0)) {
507 if (!value || !*value) {
508 printf("invalid path to network resource\n");
510 return 1; /* needs_arg; */
511 } else if(strnlen(value,5) < 5) {
512 printf("UNC name too short");
515 if (strnlen(value, 300) < 300) {
517 if (strncmp(value, "//", 2) == 0) {
519 printf("unc name specified twice, ignoring second\n");
522 } else if (strncmp(value, "\\\\", 2) != 0) {
523 printf("UNC Path does not begin with // or \\\\ \n");
528 printf("unc name specified twice, ignoring second\n");
533 printf("CIFS: UNC name too long\n");
537 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
538 || (strncmp(data, "workg", 5) == 0)) {
539 /* note this allows for synonyms of "domain"
540 such as "DOM" and "dom" and "workgroup"
541 and "WORKGRP" etc. */
542 if (!value || !*value) {
543 printf("CIFS: invalid domain name\n");
545 return 1; /* needs_arg; */
547 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
550 printf("domain name too long\n");
554 } else if (strncmp(data, "cred", 4) == 0) {
555 if (value && *value) {
556 rc = open_cred_file(value);
558 printf("error %d (%s) opening credential file %s\n",
559 rc, strerror(rc), value);
564 printf("invalid credential file name specified\n");
568 } else if (strncmp(data, "uid", 3) == 0) {
569 if (value && *value) {
571 if (!isdigit(*value)) {
574 if (!(pw = getpwnam(value))) {
575 printf("bad user name \"%s\"\n", value);
578 snprintf(user, sizeof(user), "%u", pw->pw_uid);
580 strlcpy(user,value,sizeof(user));
584 } else if (strncmp(data, "gid", 3) == 0) {
585 if (value && *value) {
587 if (!isdigit(*value)) {
590 if (!(gr = getgrnam(value))) {
591 printf("bad group name \"%s\"\n", value);
594 snprintf(group, sizeof(group), "%u", gr->gr_gid);
596 strlcpy(group,value,sizeof(group));
600 /* fmask and dmask synonyms for people used to smbfs syntax */
601 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
602 if (!value || !*value) {
603 printf ("Option '%s' requires a numerical argument\n", data);
608 if (value[0] != '0') {
609 printf ("WARNING: '%s' not expressed in octal.\n", data);
612 if (strcmp (data, "fmask") == 0) {
613 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
614 data = "file_mode"; /* BB fix this */
616 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
617 if (!value || !*value) {
618 printf ("Option '%s' requires a numerical argument\n", data);
623 if (value[0] != '0') {
624 printf ("WARNING: '%s' not expressed in octal.\n", data);
627 if (strcmp (data, "dmask") == 0) {
628 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
631 /* the following eight mount options should be
632 stripped out from what is passed into the kernel
633 since these eight options are best passed as the
634 mount flags rather than redundantly to the kernel
635 and could generate spurious warnings depending on the
636 level of the corresponding cifs vfs kernel code */
637 } else if (strncmp(data, "nosuid", 6) == 0) {
638 *filesys_flags |= MS_NOSUID;
639 } else if (strncmp(data, "suid", 4) == 0) {
640 *filesys_flags &= ~MS_NOSUID;
641 } else if (strncmp(data, "nodev", 5) == 0) {
642 *filesys_flags |= MS_NODEV;
643 } else if ((strncmp(data, "nobrl", 5) == 0) ||
644 (strncmp(data, "nolock", 6) == 0)) {
645 *filesys_flags &= ~MS_MANDLOCK;
646 } else if (strncmp(data, "dev", 3) == 0) {
647 *filesys_flags &= ~MS_NODEV;
648 } else if (strncmp(data, "noexec", 6) == 0) {
649 *filesys_flags |= MS_NOEXEC;
650 } else if (strncmp(data, "exec", 4) == 0) {
651 *filesys_flags &= ~MS_NOEXEC;
652 } else if (strncmp(data, "guest", 5) == 0) {
654 } else if (strncmp(data, "ro", 2) == 0) {
655 *filesys_flags |= MS_RDONLY;
656 } else if (strncmp(data, "rw", 2) == 0) {
657 *filesys_flags &= ~MS_RDONLY;
658 } else if (strncmp(data, "remount", 7) == 0) {
659 *filesys_flags |= MS_REMOUNT;
660 } /* else if (strnicmp(data, "port", 4) == 0) {
661 if (value && *value) {
663 simple_strtoul(value, &value, 0);
665 } else if (strnicmp(data, "rsize", 5) == 0) {
666 if (value && *value) {
668 simple_strtoul(value, &value, 0);
670 } else if (strnicmp(data, "wsize", 5) == 0) {
671 if (value && *value) {
673 simple_strtoul(value, &value, 0);
675 } else if (strnicmp(data, "version", 3) == 0) {
677 printf("CIFS: Unknown mount option %s\n",data);
678 } */ /* nothing to do on those four mount options above.
679 Just pass to kernel and ignore them here */
681 /* Copy (possibly modified) option to out */
682 word_len = strlen(data);
684 word_len += 1 + strlen(value);
686 out = (char *)realloc(out, out_len + word_len + 2);
693 strlcat(out, ",", out_len + word_len + 2);
698 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
700 snprintf(out + out_len, word_len + 1, "%s", data);
701 out_len = strlen(out);
707 /* special-case the uid and gid */
709 word_len = strlen(user);
711 out = (char *)realloc(out, out_len + word_len + 6);
718 strlcat(out, ",", out_len + word_len + 6);
721 snprintf(out + out_len, word_len + 5, "uid=%s", user);
722 out_len = strlen(out);
725 word_len = strlen(group);
727 out = (char *)realloc(out, out_len + 1 + word_len + 6);
734 strlcat(out, ",", out_len + word_len + 6);
737 snprintf(out + out_len, word_len + 5, "gid=%s", group);
738 out_len = strlen(out);
741 SAFE_FREE(*optionsp);
746 /* replace all (one or more) commas with double commas */
747 static void check_for_comma(char ** ppasswrd)
752 int number_of_commas = 0;
767 if(number_of_commas == 0)
769 if(number_of_commas > MOUNT_PASSWD_SIZE) {
770 /* would otherwise overflow the mount options buffer */
771 printf("\nInvalid password. Password contains too many commas.\n");
775 new_pass_buf = (char *)malloc(len+number_of_commas+1);
776 if(new_pass_buf == NULL)
779 for(i=0,j=0;i<len;i++,j++) {
780 new_pass_buf[j] = pass[i];
783 new_pass_buf[j] = pass[i];
786 new_pass_buf[len+number_of_commas] = 0;
788 SAFE_FREE(*ppasswrd);
789 *ppasswrd = new_pass_buf;
794 /* Usernames can not have backslash in them and we use
795 [BB check if usernames can have forward slash in them BB]
796 backslash as domain\user separator character
798 static char * check_for_domain(char **ppuser)
800 char * original_string;
810 original_string = *ppuser;
812 if (original_string == NULL)
815 original_len = strlen(original_string);
817 usernm = strchr(*ppuser,'/');
818 if (usernm == NULL) {
819 usernm = strchr(*ppuser,'\\');
825 printf("Domain name specified twice. Username probably malformed\n");
831 if (domainnm[0] != 0) {
834 printf("null domain\n");
836 len = strlen(domainnm);
837 /* reset domainm to new buffer, and copy
838 domain name into it */
839 domainnm = (char *)malloc(len+1);
843 strlcpy(domainnm,*ppuser,len+1);
845 /* move_string(*ppuser, usernm+1) */
846 len = strlen(usernm+1);
848 if(len >= original_len) {
849 /* should not happen */
853 for(i=0;i<original_len;i++) {
855 original_string[i] = usernm[i+1];
856 else /* stuff with commas to remove last parm */
857 original_string[i] = ',';
860 /* BB add check for more than one slash?
868 /* replace all occurances of "from" in a string with "to" */
869 static void replace_char(char *string, char from, char to, int maxlen)
871 char *lastchar = string + maxlen;
873 string = strchr(string, from);
876 if (string >= lastchar)
882 /* Note that caller frees the returned buffer if necessary */
883 static struct addrinfo *
884 parse_server(char ** punc_name)
886 char * unc_name = *punc_name;
887 int length = strnlen(unc_name, MAX_UNC_LEN);
889 struct addrinfo *addrlist;
892 if(length > (MAX_UNC_LEN - 1)) {
893 printf("mount error: UNC name too long");
896 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
897 (strncasecmp("smb://", unc_name, 6) == 0)) {
898 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
903 /* BB add code to find DFS root here */
904 printf("\nMounting the DFS root for domain not implemented yet\n");
907 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
908 /* check for nfs syntax ie server:share */
909 share = strchr(unc_name,':');
911 *punc_name = (char *)malloc(length+3);
912 if(*punc_name == NULL) {
913 /* put the original string back if
915 *punc_name = unc_name;
919 strlcpy((*punc_name)+2,unc_name,length+1);
921 unc_name = *punc_name;
922 unc_name[length+2] = 0;
923 goto continue_unc_parsing;
925 printf("mount error: improperly formatted UNC name.");
926 printf(" %s does not begin with \\\\ or //\n",unc_name);
930 continue_unc_parsing:
935 /* allow for either delimiter between host and sharename */
936 if ((share = strpbrk(unc_name, "/\\"))) {
937 *share = 0; /* temporarily terminate the string */
940 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
942 printf("mount error: could not resolve address for %s: %s\n",
943 unc_name, gai_strerror(rc));
947 *(share - 1) = '/'; /* put delimiter back */
949 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
950 if ((prefixpath = strpbrk(share, "/\\"))) {
951 *prefixpath = 0; /* permanently terminate the string */
952 if (!strlen(++prefixpath))
953 prefixpath = NULL; /* this needs to be done explicitly */
957 printf("ip address specified explicitly\n");
960 /* BB should we pass an alternate version of the share name as Unicode */
964 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
965 printf("Mounting the DFS root for a particular server not implemented yet\n");
972 static struct option longopts[] = {
973 { "all", 0, NULL, 'a' },
974 { "help",0, NULL, 'h' },
975 { "move",0, NULL, 'm' },
976 { "bind",0, NULL, 'b' },
977 { "read-only", 0, NULL, 'r' },
978 { "ro", 0, NULL, 'r' },
979 { "verbose", 0, NULL, 'v' },
980 { "version", 0, NULL, 'V' },
981 { "read-write", 0, NULL, 'w' },
982 { "rw", 0, NULL, 'w' },
983 { "options", 1, NULL, 'o' },
984 { "type", 1, NULL, 't' },
985 { "rsize",1, NULL, 'R' },
986 { "wsize",1, NULL, 'W' },
987 { "uid", 1, NULL, '1'},
988 { "gid", 1, NULL, '2'},
989 { "user",1,NULL,'u'},
990 { "username",1,NULL,'u'},
992 { "domain",1,NULL,'d'},
993 { "password",1,NULL,'p'},
994 { "pass",1,NULL,'p'},
995 { "credentials",1,NULL,'c'},
996 { "port",1,NULL,'P'},
997 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1001 /* convert a string to uppercase. return false if the string
1002 * wasn't ASCII. Return success on a NULL ptr */
1004 uppercase_string(char *string)
1010 /* check for unicode */
1011 if ((unsigned char) string[0] & 0x80)
1013 *string = toupper((unsigned char) *string);
1020 int main(int argc, char ** argv)
1023 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1024 char * orgoptions = NULL;
1025 char * share_name = NULL;
1026 const char * ipaddr = NULL;
1028 char * mountpoint = NULL;
1029 char * options = NULL;
1031 char * resolved_path = NULL;
1042 size_t options_size = 0;
1044 int retry = 0; /* set when we have to retry mount with uppercase */
1045 struct addrinfo *addrhead = NULL, *addr;
1046 struct stat statbuf;
1047 struct utsname sysinfo;
1048 struct mntent mountent;
1049 struct sockaddr_in *addr4;
1050 struct sockaddr_in6 *addr6;
1053 /* setlocale(LC_ALL, "");
1054 bindtextdomain(PACKAGE, LOCALEDIR);
1055 textdomain(PACKAGE); */
1058 thisprogram = argv[0];
1064 if(thisprogram == NULL)
1065 thisprogram = "mount.cifs";
1068 /* BB add workstation name and domain and pass down */
1070 /* #ifdef _GNU_SOURCE
1071 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1075 share_name = strndup(argv[1], MAX_UNC_LEN);
1076 if (share_name == NULL) {
1077 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1080 mountpoint = argv[2];
1086 /* add sharename in opts string as unc= parm */
1088 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1089 longopts, NULL)) != -1) {
1091 /* No code to do the following options yet */
1093 list_with_volumelabel = 1;
1096 volumelabel = optarg;
1103 case 'h': /* help */
1104 mount_cifs_usage ();
1114 "option 'b' (MS_BIND) not supported\n");
1122 "option 'm' (MS_MOVE) not supported\n");
1126 orgoptions = strdup(optarg);
1128 case 'r': /* mount readonly */
1138 printf ("mount.cifs version: %s.%s%s\n",
1139 MOUNT_CIFS_VERSION_MAJOR,
1140 MOUNT_CIFS_VERSION_MINOR,
1141 MOUNT_CIFS_VENDOR_SUFFIX);
1144 flags &= ~MS_RDONLY;
1147 rsize = atoi(optarg) ;
1150 wsize = atoi(optarg);
1153 if (isdigit(*optarg)) {
1156 uid = strtoul(optarg, &ep, 10);
1158 printf("bad uid value \"%s\"\n", optarg);
1164 if (!(pw = getpwnam(optarg))) {
1165 printf("bad user name \"%s\"\n", optarg);
1173 if (isdigit(*optarg)) {
1176 gid = strtoul(optarg, &ep, 10);
1178 printf("bad gid value \"%s\"\n", optarg);
1184 if (!(gr = getgrnam(optarg))) {
1185 printf("bad user name \"%s\"\n", optarg);
1197 domain_name = optarg; /* BB fix this - currently ignored */
1201 if(mountpassword == NULL)
1202 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1205 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1209 get_password_from_file(0 /* stdin */,NULL);
1217 printf("unknown mount option %c\n",c);
1223 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1228 if (getenv("PASSWD")) {
1229 if(mountpassword == NULL)
1230 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1232 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1235 } else if (getenv("PASSWD_FD")) {
1236 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1237 } else if (getenv("PASSWD_FILE")) {
1238 get_password_from_file(0, getenv("PASSWD_FILE"));
1241 if (orgoptions && parse_options(&orgoptions, &flags)) {
1245 addrhead = addr = parse_server(&share_name);
1246 if((addrhead == NULL) && (got_ip == 0)) {
1247 printf("No ip address specified and hostname not found\n");
1252 /* BB save off path and pop after mount returns? */
1253 resolved_path = (char *)malloc(PATH_MAX+1);
1255 /* Note that if we can not canonicalize the name, we get
1256 another chance to see if it is valid when we chdir to it */
1257 if (realpath(mountpoint, resolved_path)) {
1258 mountpoint = resolved_path;
1261 if(chdir(mountpoint)) {
1262 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1267 if(stat (".", &statbuf)) {
1268 printf("mount error: mount point %s does not exist\n",mountpoint);
1273 if (S_ISDIR(statbuf.st_mode) == 0) {
1274 printf("mount error: mount point %s is not a directory\n",mountpoint);
1279 if((getuid() != 0) && (geteuid() == 0)) {
1280 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1281 #ifndef CIFS_ALLOW_USR_SUID
1282 /* Do not allow user mounts to control suid flag
1283 for mount unless explicitly built that way */
1284 flags |= MS_NOSUID | MS_NODEV;
1287 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1293 /* Note that the password will not be retrieved from the
1294 USER env variable (ie user%password form) as there is
1295 already a PASSWD environment varaible */
1297 user_name = strdup(getenv("USER"));
1298 if (user_name == NULL)
1299 user_name = getusername();
1303 if(got_password == 0) {
1304 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1305 no good replacement yet. */
1306 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1307 if (!tmp_pass || !mountpassword) {
1308 printf("Password not entered, exiting\n");
1311 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1314 /* FIXME launch daemon (handles dfs name resolution and credential change)
1315 remember to clear parms and overwrite password field before launching */
1317 optlen = strlen(orgoptions);
1322 optlen += strlen(share_name) + 4;
1324 printf("No server share name specified\n");
1325 printf("\nMounting the DFS root for server not implemented yet\n");
1329 optlen += strlen(user_name) + 6;
1330 optlen += MAX_ADDRESS_LEN + 4;
1332 optlen += strlen(mountpassword) + 6;
1335 options_size = optlen + 10 + DOMAIN_SIZE;
1336 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 */);
1338 if(options == NULL) {
1339 printf("Could not allocate memory for mount options\n");
1343 strlcpy(options, "unc=", options_size);
1344 strlcat(options,share_name,options_size);
1345 /* scan backwards and reverse direction of slash */
1346 temp = strrchr(options, '/');
1347 if(temp > options + 6)
1350 /* check for syntax like user=domain\user */
1352 domain_name = check_for_domain(&user_name);
1353 strlcat(options,",user=",options_size);
1354 strlcat(options,user_name,options_size);
1358 /* extra length accounted for in option string above */
1359 strlcat(options,",domain=",options_size);
1360 strlcat(options,domain_name,options_size);
1364 /* Commas have to be doubled, or else they will
1365 look like the parameter separator */
1366 /* if(sep is not set)*/
1368 check_for_comma(&mountpassword);
1369 strlcat(options,",pass=",options_size);
1370 strlcat(options,mountpassword,options_size);
1373 strlcat(options,",ver=",options_size);
1374 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1377 strlcat(options,",",options_size);
1378 strlcat(options,orgoptions,options_size);
1381 strlcat(options,",prefixpath=",options_size);
1382 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1385 printf("\nmount.cifs kernel mount options %s \n",options);
1387 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1388 replace_char(dev_name, '\\', '/', strlen(share_name));
1390 if (!got_ip && addr) {
1391 strlcat(options, ",ip=", options_size);
1392 current_len = strnlen(options, options_size);
1393 optionstail = options + current_len;
1394 switch (addr->ai_addr->sa_family) {
1396 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1397 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1398 options_size - current_len);
1401 addr4 = (struct sockaddr_in *) addr->ai_addr;
1402 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1403 options_size - current_len);
1407 /* if the address looks bogus, try the next one */
1409 addr = addr->ai_next;
1417 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1422 addr = addr->ai_next;
1428 printf("mount error: cifs filesystem not supported by the system\n");
1433 if (uppercase_string(dev_name) &&
1434 uppercase_string(share_name) &&
1435 uppercase_string(prefixpath)) {
1436 printf("retrying with upper case share name\n");
1441 printf("mount error(%d): %s\n", errno, strerror(errno));
1442 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1449 atexit(unlock_mtab);
1452 printf("cannot lock mtab");
1455 pmntfile = setmntent(MOUNTED, "a+");
1457 printf("could not update mount table\n");
1462 mountent.mnt_fsname = dev_name;
1463 mountent.mnt_dir = mountpoint;
1464 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1465 mountent.mnt_opts = (char *)malloc(220);
1466 if(mountent.mnt_opts) {
1467 char * mount_user = getusername();
1468 memset(mountent.mnt_opts,0,200);
1469 if(flags & MS_RDONLY)
1470 strlcat(mountent.mnt_opts,"ro",220);
1472 strlcat(mountent.mnt_opts,"rw",220);
1473 if(flags & MS_MANDLOCK)
1474 strlcat(mountent.mnt_opts,",mand",220);
1475 if(flags & MS_NOEXEC)
1476 strlcat(mountent.mnt_opts,",noexec",220);
1477 if(flags & MS_NOSUID)
1478 strlcat(mountent.mnt_opts,",nosuid",220);
1479 if(flags & MS_NODEV)
1480 strlcat(mountent.mnt_opts,",nodev",220);
1481 if(flags & MS_SYNCHRONOUS)
1482 strlcat(mountent.mnt_opts,",sync",220);
1485 strlcat(mountent.mnt_opts,
1487 strlcat(mountent.mnt_opts,
1492 mountent.mnt_freq = 0;
1493 mountent.mnt_passno = 0;
1494 rc = addmntent(pmntfile,&mountent);
1495 endmntent(pmntfile);
1497 SAFE_FREE(mountent.mnt_opts);
1502 int len = strlen(mountpassword);
1503 memset(mountpassword,0,len);
1504 SAFE_FREE(mountpassword);
1508 freeaddrinfo(addrhead);
1510 SAFE_FREE(orgoptions);
1511 SAFE_FREE(resolved_path);
1512 SAFE_FREE(share_name);