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);
184 /* caller frees username if necessary */
185 static char * getusername(void) {
186 char *username = NULL;
187 struct passwd *password = getpwuid(getuid());
190 username = password->pw_name;
195 static int open_cred_file(char * file_name)
202 i = access(file_name, R_OK);
206 fs = fopen(file_name,"r");
209 line_buf = (char *)malloc(4096);
210 if(line_buf == NULL) {
215 while(fgets(line_buf,4096,fs)) {
216 /* parse line from credential file */
218 /* eat leading white space */
219 for(i=0;i<4086;i++) {
220 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
222 /* if whitespace - skip past it */
224 if (strncasecmp("username",line_buf+i,8) == 0) {
225 temp_val = strchr(line_buf + i,'=');
227 /* go past equals sign */
229 for(length = 0;length<4087;length++) {
230 if ((temp_val[length] == '\n')
231 || (temp_val[length] == '\0')) {
232 temp_val[length] = '\0';
237 printf("mount.cifs failed due to malformed username in credentials file");
238 memset(line_buf,0,4096);
242 user_name = (char *)calloc(1 + length,1);
243 /* BB adding free of user_name string before exit,
244 not really necessary but would be cleaner */
245 strlcpy(user_name,temp_val, length+1);
248 } else if (strncasecmp("password",line_buf+i,8) == 0) {
249 temp_val = strchr(line_buf+i,'=');
251 /* go past equals sign */
253 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
254 if ((temp_val[length] == '\n')
255 || (temp_val[length] == '\0')) {
256 temp_val[length] = '\0';
260 if(length > MOUNT_PASSWD_SIZE) {
261 printf("mount.cifs failed: password in credentials file too long\n");
262 memset(line_buf,0, 4096);
265 if(mountpassword == NULL) {
266 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
268 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
270 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
275 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
276 temp_val = strchr(line_buf+i,'=');
278 /* go past equals sign */
281 printf("\nDomain %s\n",temp_val);
282 for(length = 0;length<DOMAIN_SIZE+1;length++) {
283 if ((temp_val[length] == '\n')
284 || (temp_val[length] == '\0')) {
285 temp_val[length] = '\0';
289 if(length > DOMAIN_SIZE) {
290 printf("mount.cifs failed: domain in credentials file too long\n");
293 if(domain_name == NULL) {
294 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
296 memset(domain_name,0,DOMAIN_SIZE);
298 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
311 static int get_password_from_file(int file_descript, char * filename)
317 if(mountpassword == NULL)
318 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
320 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
322 if (mountpassword == NULL) {
323 printf("malloc failed\n");
327 if(filename != NULL) {
328 rc = access(filename, R_OK);
330 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
331 filename, strerror(errno));
334 file_descript = open(filename, O_RDONLY);
335 if(file_descript < 0) {
336 printf("mount.cifs failed. %s attempting to open password file %s\n",
337 strerror(errno),filename);
341 /* else file already open and fd provided */
343 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
344 rc = read(file_descript,&c,1);
346 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
348 close(file_descript);
351 if(mountpassword[0] == 0) {
353 printf("\nWarning: null password used since cifs password file empty");
356 } else /* read valid character */ {
357 if((c == 0) || (c == '\n')) {
358 mountpassword[i] = '\0';
361 mountpassword[i] = c;
364 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
365 printf("\nWarning: password longer than %d characters specified in cifs password file",
369 if(filename != NULL) {
370 close(file_descript);
376 static int parse_options(char ** optionsp, int * filesys_flags)
379 char * percent_char = NULL;
381 char * next_keyword = NULL;
389 if (!optionsp || !*optionsp)
393 /* BB fixme check for separator override BB */
397 snprintf(user,sizeof(user),"%u",getuid());
399 snprintf(group,sizeof(group),"%u",getgid());
402 /* while ((data = strsep(&options, ",")) != NULL) { */
403 while(data != NULL) {
404 /* check if ends with trailing comma */
408 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
409 /* data = next keyword */
410 /* value = next value ie stuff after equal sign */
412 next_keyword = strchr(data,','); /* BB handle sep= */
414 /* temporarily null terminate end of keyword=value pair */
418 /* temporarily null terminate keyword to make keyword and value distinct */
419 if ((value = strchr(data, '=')) != NULL) {
424 if (strncmp(data, "users",5) == 0) {
425 if(!value || !*value) {
428 } else if (strncmp(data, "user_xattr",10) == 0) {
429 /* do nothing - need to skip so not parsed as user name */
430 } else if (strncmp(data, "user", 4) == 0) {
432 if (!value || !*value) {
433 if(data[4] == '\0') {
435 printf("\nskipping empty user mount parameter\n");
436 /* remove the parm since it would otherwise be confusing
437 to the kernel code which would think it was a real username */
440 printf("username specified with no parameter\n");
442 return 1; /* needs_arg; */
445 if (strnlen(value, 260) < 260) {
447 percent_char = strchr(value,'%');
450 if(mountpassword == NULL)
451 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
454 printf("\nmount.cifs warning - password specified twice\n");
457 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
458 /* remove password from username */
459 while(*percent_char != 0) {
465 /* this is only case in which the user
466 name buf is not malloc - so we have to
467 check for domain name embedded within
468 the user name here since the later
469 call to check_for_domain will not be
471 domain_name = check_for_domain(&value);
473 printf("username too long\n");
478 } else if (strncmp(data, "pass", 4) == 0) {
479 if (!value || !*value) {
481 fprintf(stderr, "\npassword specified twice, ignoring second\n");
484 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
486 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
488 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
489 if (!mountpassword) {
490 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
497 fprintf(stderr, "password too long\n");
502 } else if (strncmp(data, "sec", 3) == 0) {
504 if (!strncmp(value, "none", 4) ||
505 !strncmp(value, "krb5", 4))
508 } else if (strncmp(data, "ip", 2) == 0) {
509 if (!value || !*value) {
510 printf("target ip address argument missing");
511 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
513 printf("ip address %s override specified\n",value);
516 printf("ip address too long\n");
520 } else if ((strncmp(data, "unc", 3) == 0)
521 || (strncmp(data, "target", 6) == 0)
522 || (strncmp(data, "path", 4) == 0)) {
523 if (!value || !*value) {
524 printf("invalid path to network resource\n");
526 return 1; /* needs_arg; */
527 } else if(strnlen(value,5) < 5) {
528 printf("UNC name too short");
531 if (strnlen(value, 300) < 300) {
533 if (strncmp(value, "//", 2) == 0) {
535 printf("unc name specified twice, ignoring second\n");
538 } else if (strncmp(value, "\\\\", 2) != 0) {
539 printf("UNC Path does not begin with // or \\\\ \n");
544 printf("unc name specified twice, ignoring second\n");
549 printf("CIFS: UNC name too long\n");
553 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
554 || (strncmp(data, "workg", 5) == 0)) {
555 /* note this allows for synonyms of "domain"
556 such as "DOM" and "dom" and "workgroup"
557 and "WORKGRP" etc. */
558 if (!value || !*value) {
559 printf("CIFS: invalid domain name\n");
561 return 1; /* needs_arg; */
563 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
566 printf("domain name too long\n");
570 } else if (strncmp(data, "cred", 4) == 0) {
571 if (value && *value) {
572 rc = open_cred_file(value);
574 printf("error %d (%s) opening credential file %s\n",
575 rc, strerror(rc), value);
580 printf("invalid credential file name specified\n");
584 } else if (strncmp(data, "uid", 3) == 0) {
585 if (value && *value) {
587 if (!isdigit(*value)) {
590 if (!(pw = getpwnam(value))) {
591 printf("bad user name \"%s\"\n", value);
594 snprintf(user, sizeof(user), "%u", pw->pw_uid);
596 strlcpy(user,value,sizeof(user));
600 } else if (strncmp(data, "gid", 3) == 0) {
601 if (value && *value) {
603 if (!isdigit(*value)) {
606 if (!(gr = getgrnam(value))) {
607 printf("bad group name \"%s\"\n", value);
610 snprintf(group, sizeof(group), "%u", gr->gr_gid);
612 strlcpy(group,value,sizeof(group));
616 /* fmask and dmask synonyms for people used to smbfs syntax */
617 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
618 if (!value || !*value) {
619 printf ("Option '%s' requires a numerical argument\n", data);
624 if (value[0] != '0') {
625 printf ("WARNING: '%s' not expressed in octal.\n", data);
628 if (strcmp (data, "fmask") == 0) {
629 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
630 data = "file_mode"; /* BB fix this */
632 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
633 if (!value || !*value) {
634 printf ("Option '%s' requires a numerical argument\n", data);
639 if (value[0] != '0') {
640 printf ("WARNING: '%s' not expressed in octal.\n", data);
643 if (strcmp (data, "dmask") == 0) {
644 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
647 /* the following eight mount options should be
648 stripped out from what is passed into the kernel
649 since these eight options are best passed as the
650 mount flags rather than redundantly to the kernel
651 and could generate spurious warnings depending on the
652 level of the corresponding cifs vfs kernel code */
653 } else if (strncmp(data, "nosuid", 6) == 0) {
654 *filesys_flags |= MS_NOSUID;
655 } else if (strncmp(data, "suid", 4) == 0) {
656 *filesys_flags &= ~MS_NOSUID;
657 } else if (strncmp(data, "nodev", 5) == 0) {
658 *filesys_flags |= MS_NODEV;
659 } else if ((strncmp(data, "nobrl", 5) == 0) ||
660 (strncmp(data, "nolock", 6) == 0)) {
661 *filesys_flags &= ~MS_MANDLOCK;
662 } else if (strncmp(data, "dev", 3) == 0) {
663 *filesys_flags &= ~MS_NODEV;
664 } else if (strncmp(data, "noexec", 6) == 0) {
665 *filesys_flags |= MS_NOEXEC;
666 } else if (strncmp(data, "exec", 4) == 0) {
667 *filesys_flags &= ~MS_NOEXEC;
668 } else if (strncmp(data, "guest", 5) == 0) {
669 user_name = (char *)calloc(1, 1);
672 } else if (strncmp(data, "ro", 2) == 0) {
673 *filesys_flags |= MS_RDONLY;
674 } else if (strncmp(data, "rw", 2) == 0) {
675 *filesys_flags &= ~MS_RDONLY;
676 } else if (strncmp(data, "remount", 7) == 0) {
677 *filesys_flags |= MS_REMOUNT;
678 } /* else if (strnicmp(data, "port", 4) == 0) {
679 if (value && *value) {
681 simple_strtoul(value, &value, 0);
683 } else if (strnicmp(data, "rsize", 5) == 0) {
684 if (value && *value) {
686 simple_strtoul(value, &value, 0);
688 } else if (strnicmp(data, "wsize", 5) == 0) {
689 if (value && *value) {
691 simple_strtoul(value, &value, 0);
693 } else if (strnicmp(data, "version", 3) == 0) {
695 printf("CIFS: Unknown mount option %s\n",data);
696 } */ /* nothing to do on those four mount options above.
697 Just pass to kernel and ignore them here */
699 /* Copy (possibly modified) option to out */
700 word_len = strlen(data);
702 word_len += 1 + strlen(value);
704 out = (char *)realloc(out, out_len + word_len + 2);
711 strlcat(out, ",", out_len + word_len + 2);
716 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
718 snprintf(out + out_len, word_len + 1, "%s", data);
719 out_len = strlen(out);
725 /* special-case the uid and gid */
727 word_len = strlen(user);
729 out = (char *)realloc(out, out_len + word_len + 6);
736 strlcat(out, ",", out_len + word_len + 6);
739 snprintf(out + out_len, word_len + 5, "uid=%s", user);
740 out_len = strlen(out);
743 word_len = strlen(group);
745 out = (char *)realloc(out, out_len + 1 + word_len + 6);
752 strlcat(out, ",", out_len + word_len + 6);
755 snprintf(out + out_len, word_len + 5, "gid=%s", group);
756 out_len = strlen(out);
759 SAFE_FREE(*optionsp);
764 /* replace all (one or more) commas with double commas */
765 static void check_for_comma(char ** ppasswrd)
770 int number_of_commas = 0;
785 if(number_of_commas == 0)
787 if(number_of_commas > MOUNT_PASSWD_SIZE) {
788 /* would otherwise overflow the mount options buffer */
789 printf("\nInvalid password. Password contains too many commas.\n");
793 new_pass_buf = (char *)malloc(len+number_of_commas+1);
794 if(new_pass_buf == NULL)
797 for(i=0,j=0;i<len;i++,j++) {
798 new_pass_buf[j] = pass[i];
801 new_pass_buf[j] = pass[i];
804 new_pass_buf[len+number_of_commas] = 0;
806 SAFE_FREE(*ppasswrd);
807 *ppasswrd = new_pass_buf;
812 /* Usernames can not have backslash in them and we use
813 [BB check if usernames can have forward slash in them BB]
814 backslash as domain\user separator character
816 static char * check_for_domain(char **ppuser)
818 char * original_string;
828 original_string = *ppuser;
830 if (original_string == NULL)
833 original_len = strlen(original_string);
835 usernm = strchr(*ppuser,'/');
836 if (usernm == NULL) {
837 usernm = strchr(*ppuser,'\\');
843 printf("Domain name specified twice. Username probably malformed\n");
849 if (domainnm[0] != 0) {
852 printf("null domain\n");
854 len = strlen(domainnm);
855 /* reset domainm to new buffer, and copy
856 domain name into it */
857 domainnm = (char *)malloc(len+1);
861 strlcpy(domainnm,*ppuser,len+1);
863 /* move_string(*ppuser, usernm+1) */
864 len = strlen(usernm+1);
866 if(len >= original_len) {
867 /* should not happen */
871 for(i=0;i<original_len;i++) {
873 original_string[i] = usernm[i+1];
874 else /* stuff with commas to remove last parm */
875 original_string[i] = ',';
878 /* BB add check for more than one slash?
886 /* replace all occurances of "from" in a string with "to" */
887 static void replace_char(char *string, char from, char to, int maxlen)
889 char *lastchar = string + maxlen;
891 string = strchr(string, from);
894 if (string >= lastchar)
900 /* Note that caller frees the returned buffer if necessary */
901 static struct addrinfo *
902 parse_server(char ** punc_name)
904 char * unc_name = *punc_name;
905 int length = strnlen(unc_name, MAX_UNC_LEN);
907 struct addrinfo *addrlist;
910 if(length > (MAX_UNC_LEN - 1)) {
911 printf("mount error: UNC name too long");
914 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
915 (strncasecmp("smb://", unc_name, 6) == 0)) {
916 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
921 /* BB add code to find DFS root here */
922 printf("\nMounting the DFS root for domain not implemented yet\n");
925 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
926 /* check for nfs syntax ie server:share */
927 share = strchr(unc_name,':');
929 *punc_name = (char *)malloc(length+3);
930 if(*punc_name == NULL) {
931 /* put the original string back if
933 *punc_name = unc_name;
937 strlcpy((*punc_name)+2,unc_name,length+1);
939 unc_name = *punc_name;
940 unc_name[length+2] = 0;
941 goto continue_unc_parsing;
943 printf("mount error: improperly formatted UNC name.");
944 printf(" %s does not begin with \\\\ or //\n",unc_name);
948 continue_unc_parsing:
953 /* allow for either delimiter between host and sharename */
954 if ((share = strpbrk(unc_name, "/\\"))) {
955 *share = 0; /* temporarily terminate the string */
958 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
960 printf("mount error: could not resolve address for %s: %s\n",
961 unc_name, gai_strerror(rc));
965 *(share - 1) = '/'; /* put delimiter back */
967 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
968 if ((prefixpath = strpbrk(share, "/\\"))) {
969 *prefixpath = 0; /* permanently terminate the string */
970 if (!strlen(++prefixpath))
971 prefixpath = NULL; /* this needs to be done explicitly */
975 printf("ip address specified explicitly\n");
978 /* BB should we pass an alternate version of the share name as Unicode */
982 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
983 printf("Mounting the DFS root for a particular server not implemented yet\n");
990 static struct option longopts[] = {
991 { "all", 0, NULL, 'a' },
992 { "help",0, NULL, 'h' },
993 { "move",0, NULL, 'm' },
994 { "bind",0, NULL, 'b' },
995 { "read-only", 0, NULL, 'r' },
996 { "ro", 0, NULL, 'r' },
997 { "verbose", 0, NULL, 'v' },
998 { "version", 0, NULL, 'V' },
999 { "read-write", 0, NULL, 'w' },
1000 { "rw", 0, NULL, 'w' },
1001 { "options", 1, NULL, 'o' },
1002 { "type", 1, NULL, 't' },
1003 { "rsize",1, NULL, 'R' },
1004 { "wsize",1, NULL, 'W' },
1005 { "uid", 1, NULL, '1'},
1006 { "gid", 1, NULL, '2'},
1007 { "user",1,NULL,'u'},
1008 { "username",1,NULL,'u'},
1009 { "dom",1,NULL,'d'},
1010 { "domain",1,NULL,'d'},
1011 { "password",1,NULL,'p'},
1012 { "pass",1,NULL,'p'},
1013 { "credentials",1,NULL,'c'},
1014 { "port",1,NULL,'P'},
1015 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1016 { NULL, 0, NULL, 0 }
1019 /* convert a string to uppercase. return false if the string
1020 * wasn't ASCII. Return success on a NULL ptr */
1022 uppercase_string(char *string)
1028 /* check for unicode */
1029 if ((unsigned char) string[0] & 0x80)
1031 *string = toupper((unsigned char) *string);
1038 static void print_cifs_mount_version(void)
1040 printf("mount.cifs version: %s.%s%s\n",
1041 MOUNT_CIFS_VERSION_MAJOR,
1042 MOUNT_CIFS_VERSION_MINOR,
1043 MOUNT_CIFS_VENDOR_SUFFIX);
1046 int main(int argc, char ** argv)
1049 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1050 char * orgoptions = NULL;
1051 char * share_name = NULL;
1052 const char * ipaddr = NULL;
1054 char * mountpoint = NULL;
1055 char * options = NULL;
1057 char * resolved_path = NULL;
1068 size_t options_size = 0;
1070 int retry = 0; /* set when we have to retry mount with uppercase */
1071 struct addrinfo *addrhead = NULL, *addr;
1072 struct stat statbuf;
1073 struct utsname sysinfo;
1074 struct mntent mountent;
1075 struct sockaddr_in *addr4;
1076 struct sockaddr_in6 *addr6;
1079 /* setlocale(LC_ALL, "");
1080 bindtextdomain(PACKAGE, LOCALEDIR);
1081 textdomain(PACKAGE); */
1084 thisprogram = argv[0];
1090 if(thisprogram == NULL)
1091 thisprogram = "mount.cifs";
1094 /* BB add workstation name and domain and pass down */
1096 /* #ifdef _GNU_SOURCE
1097 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1101 share_name = strndup(argv[1], MAX_UNC_LEN);
1102 if (share_name == NULL) {
1103 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1106 mountpoint = argv[2];
1107 } else if (argc == 2) {
1108 if ((strcmp(argv[1], "-V") == 0) ||
1109 (strcmp(argv[1], "--version") == 0))
1111 print_cifs_mount_version();
1115 if ((strcmp(argv[1], "-h") == 0) ||
1116 (strcmp(argv[1], "-?") == 0) ||
1117 (strcmp(argv[1], "--help") == 0))
1130 /* add sharename in opts string as unc= parm */
1132 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1133 longopts, NULL)) != -1) {
1135 /* No code to do the following options yet */
1137 list_with_volumelabel = 1;
1140 volumelabel = optarg;
1147 case 'h': /* help */
1148 mount_cifs_usage ();
1158 "option 'b' (MS_BIND) not supported\n");
1166 "option 'm' (MS_MOVE) not supported\n");
1170 orgoptions = strdup(optarg);
1172 case 'r': /* mount readonly */
1182 print_cifs_mount_version();
1185 flags &= ~MS_RDONLY;
1188 rsize = atoi(optarg) ;
1191 wsize = atoi(optarg);
1194 if (isdigit(*optarg)) {
1197 uid = strtoul(optarg, &ep, 10);
1199 printf("bad uid value \"%s\"\n", optarg);
1205 if (!(pw = getpwnam(optarg))) {
1206 printf("bad user name \"%s\"\n", optarg);
1214 if (isdigit(*optarg)) {
1217 gid = strtoul(optarg, &ep, 10);
1219 printf("bad gid value \"%s\"\n", optarg);
1225 if (!(gr = getgrnam(optarg))) {
1226 printf("bad user name \"%s\"\n", optarg);
1238 domain_name = optarg; /* BB fix this - currently ignored */
1242 if(mountpassword == NULL)
1243 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1246 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1250 get_password_from_file(0 /* stdin */,NULL);
1258 printf("unknown mount option %c\n",c);
1264 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1269 if (getenv("PASSWD")) {
1270 if(mountpassword == NULL)
1271 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1273 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1276 } else if (getenv("PASSWD_FD")) {
1277 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1278 } else if (getenv("PASSWD_FILE")) {
1279 get_password_from_file(0, getenv("PASSWD_FILE"));
1282 if (orgoptions && parse_options(&orgoptions, &flags)) {
1286 addrhead = addr = parse_server(&share_name);
1287 if((addrhead == NULL) && (got_ip == 0)) {
1288 printf("No ip address specified and hostname not found\n");
1293 /* BB save off path and pop after mount returns? */
1294 resolved_path = (char *)malloc(PATH_MAX+1);
1296 /* Note that if we can not canonicalize the name, we get
1297 another chance to see if it is valid when we chdir to it */
1298 if (realpath(mountpoint, resolved_path)) {
1299 mountpoint = resolved_path;
1302 if(chdir(mountpoint)) {
1303 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1308 if(stat (".", &statbuf)) {
1309 printf("mount error: mount point %s does not exist\n",mountpoint);
1314 if (S_ISDIR(statbuf.st_mode) == 0) {
1315 printf("mount error: mount point %s is not a directory\n",mountpoint);
1320 if((getuid() != 0) && (geteuid() == 0)) {
1321 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1322 #ifndef CIFS_ALLOW_USR_SUID
1323 /* Do not allow user mounts to control suid flag
1324 for mount unless explicitly built that way */
1325 flags |= MS_NOSUID | MS_NODEV;
1328 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1334 /* Note that the password will not be retrieved from the
1335 USER env variable (ie user%password form) as there is
1336 already a PASSWD environment varaible */
1338 user_name = strdup(getenv("USER"));
1339 if (user_name == NULL)
1340 user_name = getusername();
1344 if(got_password == 0) {
1345 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1346 no good replacement yet. */
1347 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1348 if (!tmp_pass || !mountpassword) {
1349 printf("Password not entered, exiting\n");
1352 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1355 /* FIXME launch daemon (handles dfs name resolution and credential change)
1356 remember to clear parms and overwrite password field before launching */
1358 optlen = strlen(orgoptions);
1363 optlen += strlen(share_name) + 4;
1365 printf("No server share name specified\n");
1366 printf("\nMounting the DFS root for server not implemented yet\n");
1370 optlen += strlen(user_name) + 6;
1371 optlen += MAX_ADDRESS_LEN + 4;
1373 optlen += strlen(mountpassword) + 6;
1376 options_size = optlen + 10 + DOMAIN_SIZE;
1377 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 */);
1379 if(options == NULL) {
1380 printf("Could not allocate memory for mount options\n");
1384 strlcpy(options, "unc=", options_size);
1385 strlcat(options,share_name,options_size);
1386 /* scan backwards and reverse direction of slash */
1387 temp = strrchr(options, '/');
1388 if(temp > options + 6)
1391 /* check for syntax like user=domain\user */
1393 domain_name = check_for_domain(&user_name);
1394 strlcat(options,",user=",options_size);
1395 strlcat(options,user_name,options_size);
1399 /* extra length accounted for in option string above */
1400 strlcat(options,",domain=",options_size);
1401 strlcat(options,domain_name,options_size);
1405 strlcat(options,",ver=",options_size);
1406 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1409 strlcat(options,",",options_size);
1410 strlcat(options,orgoptions,options_size);
1413 strlcat(options,",prefixpath=",options_size);
1414 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1417 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1418 replace_char(dev_name, '\\', '/', strlen(share_name));
1420 if (!got_ip && addr) {
1421 strlcat(options, ",ip=", options_size);
1422 current_len = strnlen(options, options_size);
1423 optionstail = options + current_len;
1424 switch (addr->ai_addr->sa_family) {
1426 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1427 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1428 options_size - current_len);
1431 addr4 = (struct sockaddr_in *) addr->ai_addr;
1432 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1433 options_size - current_len);
1437 /* if the address looks bogus, try the next one */
1439 addr = addr->ai_next;
1448 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1450 if (mountpassword) {
1452 * Commas have to be doubled, or else they will
1453 * look like the parameter separator
1456 check_for_comma(&mountpassword);
1457 strlcat(options,",pass=",options_size);
1458 strlcat(options,mountpassword,options_size);
1460 fprintf(stderr, ",pass=********");
1464 fprintf(stderr, "\n");
1466 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1471 addr = addr->ai_next;
1477 printf("mount error: cifs filesystem not supported by the system\n");
1482 if (uppercase_string(dev_name) &&
1483 uppercase_string(share_name) &&
1484 uppercase_string(prefixpath)) {
1485 printf("retrying with upper case share name\n");
1490 printf("mount error(%d): %s\n", errno, strerror(errno));
1491 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1499 atexit(unlock_mtab);
1502 printf("cannot lock mtab");
1505 pmntfile = setmntent(MOUNTED, "a+");
1507 printf("could not update mount table\n");
1512 mountent.mnt_fsname = dev_name;
1513 mountent.mnt_dir = mountpoint;
1514 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1515 mountent.mnt_opts = (char *)malloc(220);
1516 if(mountent.mnt_opts) {
1517 char * mount_user = getusername();
1518 memset(mountent.mnt_opts,0,200);
1519 if(flags & MS_RDONLY)
1520 strlcat(mountent.mnt_opts,"ro",220);
1522 strlcat(mountent.mnt_opts,"rw",220);
1523 if(flags & MS_MANDLOCK)
1524 strlcat(mountent.mnt_opts,",mand",220);
1525 if(flags & MS_NOEXEC)
1526 strlcat(mountent.mnt_opts,",noexec",220);
1527 if(flags & MS_NOSUID)
1528 strlcat(mountent.mnt_opts,",nosuid",220);
1529 if(flags & MS_NODEV)
1530 strlcat(mountent.mnt_opts,",nodev",220);
1531 if(flags & MS_SYNCHRONOUS)
1532 strlcat(mountent.mnt_opts,",sync",220);
1535 strlcat(mountent.mnt_opts,
1537 strlcat(mountent.mnt_opts,
1542 mountent.mnt_freq = 0;
1543 mountent.mnt_passno = 0;
1544 rc = addmntent(pmntfile,&mountent);
1545 endmntent(pmntfile);
1547 SAFE_FREE(mountent.mnt_opts);
1552 int len = strlen(mountpassword);
1553 memset(mountpassword,0,len);
1554 SAFE_FREE(mountpassword);
1558 freeaddrinfo(addrhead);
1560 SAFE_FREE(orgoptions);
1561 SAFE_FREE(resolved_path);
1562 SAFE_FREE(share_name);