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>
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
48 #include "include/version.h"
49 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
55 #define MOUNT_CIFS_VENDOR_SUFFIX ""
56 #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
67 #define MAX_UNC_LEN 1024
69 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
72 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
75 #define MOUNT_PASSWD_SIZE 64
76 #define DOMAIN_SIZE 64
78 const char *thisprogram;
80 static int got_password = 0;
81 static int got_user = 0;
82 static int got_domain = 0;
83 static int got_ip = 0;
84 static int got_unc = 0;
85 static int got_uid = 0;
86 static int got_gid = 0;
87 static char * user_name = NULL;
88 static char * mountpassword = NULL;
89 char * domain_name = NULL;
90 char * prefixpath = NULL;
92 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93 * don't link to libreplace so need them here. */
95 /* like strncpy but does not 0 fill the buffer and always null
96 * terminates. bufsize is the size of the destination buffer */
97 size_t strlcpy(char *d, const char *s, size_t bufsize)
99 size_t len = strlen(s);
101 if (bufsize <= 0) return 0;
102 if (len >= bufsize) len = bufsize-1;
108 /* like strncat but does not 0 fill the buffer and always null
109 * terminates. bufsize is the length of the buffer, which should
110 * be one more than the maximum resulting string length */
111 size_t strlcat(char *d, const char *s, size_t bufsize)
113 size_t len1 = strlen(d);
114 size_t len2 = strlen(s);
115 size_t ret = len1 + len2;
117 if (len1+len2 >= bufsize) {
118 if (bufsize < (len1+1)) {
121 len2 = bufsize - (len1+1);
124 memcpy(d+len1, s, len2);
133 open nofollow - avoid symlink exposure?
134 get owner of dir see if matches self or if root
135 call system(umount argv) etc.
139 static char * check_for_domain(char **);
142 static void mount_cifs_usage(void)
144 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
145 printf("\nMount the remote target, specified as a UNC name,");
146 printf(" to a local directory.\n\nOptions:\n");
147 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
148 printf("\nLess commonly used options:");
149 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
150 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
151 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
152 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
153 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
154 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
155 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
156 printf("\n\nRarely used options:");
157 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
158 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
159 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
160 printf("\n\tin6_addr");
161 printf("\n\nOptions are described in more detail in the manual page");
162 printf("\n\tman 8 mount.cifs\n");
163 printf("\nTo display the version number of the mount helper:");
164 printf("\n\t%s -V\n",thisprogram);
166 SAFE_FREE(mountpassword);
170 /* caller frees username if necessary */
171 static char * getusername(void) {
172 char *username = NULL;
173 struct passwd *password = getpwuid(getuid());
176 username = password->pw_name;
181 static char * parse_cifs_url(char * unc_name)
183 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
187 static int open_cred_file(char * file_name)
193 fs = fopen(file_name,"r");
196 line_buf = (char *)malloc(4096);
197 if(line_buf == NULL) {
202 while(fgets(line_buf,4096,fs)) {
203 /* parse line from credential file */
205 /* eat leading white space */
206 for(i=0;i<4086;i++) {
207 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
209 /* if whitespace - skip past it */
211 if (strncasecmp("username",line_buf+i,8) == 0) {
212 temp_val = strchr(line_buf + i,'=');
214 /* go past equals sign */
216 for(length = 0;length<4087;length++) {
217 if ((temp_val[length] == '\n')
218 || (temp_val[length] == '\0')) {
219 temp_val[length] = '\0';
224 printf("mount.cifs failed due to malformed username in credentials file");
225 memset(line_buf,0,4096);
229 user_name = (char *)calloc(1 + length,1);
230 /* BB adding free of user_name string before exit,
231 not really necessary but would be cleaner */
232 strlcpy(user_name,temp_val, length+1);
235 } else if (strncasecmp("password",line_buf+i,8) == 0) {
236 temp_val = strchr(line_buf+i,'=');
238 /* go past equals sign */
240 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
241 if ((temp_val[length] == '\n')
242 || (temp_val[length] == '\0')) {
243 temp_val[length] = '\0';
247 if(length > MOUNT_PASSWD_SIZE) {
248 printf("mount.cifs failed: password in credentials file too long\n");
249 memset(line_buf,0, 4096);
252 if(mountpassword == NULL) {
253 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
255 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
257 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
262 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
263 temp_val = strchr(line_buf+i,'=');
265 /* go past equals sign */
268 printf("\nDomain %s\n",temp_val);
269 for(length = 0;length<DOMAIN_SIZE+1;length++) {
270 if ((temp_val[length] == '\n')
271 || (temp_val[length] == '\0')) {
272 temp_val[length] = '\0';
276 if(length > DOMAIN_SIZE) {
277 printf("mount.cifs failed: domain in credentials file too long\n");
280 if(domain_name == NULL) {
281 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
283 memset(domain_name,0,DOMAIN_SIZE);
285 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
298 static int get_password_from_file(int file_descript, char * filename)
304 if(mountpassword == NULL)
305 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
307 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
309 if (mountpassword == NULL) {
310 printf("malloc failed\n");
314 if(filename != NULL) {
315 file_descript = open(filename, O_RDONLY);
316 if(file_descript < 0) {
317 printf("mount.cifs failed. %s attempting to open password file %s\n",
318 strerror(errno),filename);
322 /* else file already open and fd provided */
324 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
325 rc = read(file_descript,&c,1);
327 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
329 close(file_descript);
332 if(mountpassword[0] == 0) {
334 printf("\nWarning: null password used since cifs password file empty");
337 } else /* read valid character */ {
338 if((c == 0) || (c == '\n')) {
339 mountpassword[i] = '\0';
342 mountpassword[i] = c;
345 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
346 printf("\nWarning: password longer than %d characters specified in cifs password file",
350 if(filename != NULL) {
351 close(file_descript);
357 static int parse_options(char ** optionsp, int * filesys_flags)
360 char * percent_char = NULL;
362 char * next_keyword = NULL;
370 if (!optionsp || !*optionsp)
375 printf("parsing options: %s\n", data);
377 /* BB fixme check for separator override BB */
381 snprintf(user,sizeof(user),"%u",getuid());
383 snprintf(group,sizeof(group),"%u",getgid());
386 /* while ((data = strsep(&options, ",")) != NULL) { */
387 while(data != NULL) {
388 /* check if ends with trailing comma */
392 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
393 /* data = next keyword */
394 /* value = next value ie stuff after equal sign */
396 next_keyword = strchr(data,','); /* BB handle sep= */
398 /* temporarily null terminate end of keyword=value pair */
402 /* temporarily null terminate keyword to make keyword and value distinct */
403 if ((value = strchr(data, '=')) != NULL) {
408 if (strncmp(data, "users",5) == 0) {
409 if(!value || !*value) {
412 } else if (strncmp(data, "user_xattr",10) == 0) {
413 /* do nothing - need to skip so not parsed as user name */
414 } else if (strncmp(data, "user", 4) == 0) {
416 if (!value || !*value) {
417 if(data[4] == '\0') {
419 printf("\nskipping empty user mount parameter\n");
420 /* remove the parm since it would otherwise be confusing
421 to the kernel code which would think it was a real username */
424 printf("username specified with no parameter\n");
425 return 1; /* needs_arg; */
428 if (strnlen(value, 260) < 260) {
430 percent_char = strchr(value,'%');
433 if(mountpassword == NULL)
434 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
437 printf("\nmount.cifs warning - password specified twice\n");
440 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
441 /* remove password from username */
442 while(*percent_char != 0) {
448 /* this is only case in which the user
449 name buf is not malloc - so we have to
450 check for domain name embedded within
451 the user name here since the later
452 call to check_for_domain will not be
454 domain_name = check_for_domain(&value);
456 printf("username too long\n");
460 } else if (strncmp(data, "pass", 4) == 0) {
461 if (!value || !*value) {
463 printf("\npassword specified twice, ignoring second\n");
466 } else if (strnlen(value, 17) < 17) {
468 printf("\nmount.cifs warning - password specified twice\n");
471 printf("password too long\n");
474 } else if (strncmp(data, "sec", 3) == 0) {
476 if (!strncmp(value, "none", 4) ||
477 !strncmp(value, "krb5", 4))
480 } else if (strncmp(data, "ip", 2) == 0) {
481 if (!value || !*value) {
482 printf("target ip address argument missing");
483 } else if (strnlen(value, 35) < 35) {
485 printf("ip address %s override specified\n",value);
488 printf("ip address too long\n");
491 } else if ((strncmp(data, "unc", 3) == 0)
492 || (strncmp(data, "target", 6) == 0)
493 || (strncmp(data, "path", 4) == 0)) {
494 if (!value || !*value) {
495 printf("invalid path to network resource\n");
496 return 1; /* needs_arg; */
497 } else if(strnlen(value,5) < 5) {
498 printf("UNC name too short");
501 if (strnlen(value, 300) < 300) {
503 if (strncmp(value, "//", 2) == 0) {
505 printf("unc name specified twice, ignoring second\n");
508 } else if (strncmp(value, "\\\\", 2) != 0) {
509 printf("UNC Path does not begin with // or \\\\ \n");
513 printf("unc name specified twice, ignoring second\n");
518 printf("CIFS: UNC name too long\n");
521 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
522 || (strncmp(data, "workg", 5) == 0)) {
523 /* note this allows for synonyms of "domain"
524 such as "DOM" and "dom" and "workgroup"
525 and "WORKGRP" etc. */
526 if (!value || !*value) {
527 printf("CIFS: invalid domain name\n");
528 return 1; /* needs_arg; */
530 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
533 printf("domain name too long\n");
536 } else if (strncmp(data, "cred", 4) == 0) {
537 if (value && *value) {
538 rc = open_cred_file(value);
540 printf("error %d opening credential file %s\n",rc, value);
544 printf("invalid credential file name specified\n");
547 } else if (strncmp(data, "uid", 3) == 0) {
548 if (value && *value) {
550 if (!isdigit(*value)) {
553 if (!(pw = getpwnam(value))) {
554 printf("bad user name \"%s\"\n", value);
557 snprintf(user, sizeof(user), "%u", pw->pw_uid);
559 strlcpy(user,value,sizeof(user));
563 } else if (strncmp(data, "gid", 3) == 0) {
564 if (value && *value) {
566 if (!isdigit(*value)) {
569 if (!(gr = getgrnam(value))) {
570 printf("bad group name \"%s\"\n", value);
573 snprintf(group, sizeof(group), "%u", gr->gr_gid);
575 strlcpy(group,value,sizeof(group));
579 /* fmask and dmask synonyms for people used to smbfs syntax */
580 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
581 if (!value || !*value) {
582 printf ("Option '%s' requires a numerical argument\n", data);
586 if (value[0] != '0') {
587 printf ("WARNING: '%s' not expressed in octal.\n", data);
590 if (strcmp (data, "fmask") == 0) {
591 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
592 data = "file_mode"; /* BB fix this */
594 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
595 if (!value || !*value) {
596 printf ("Option '%s' requires a numerical argument\n", data);
600 if (value[0] != '0') {
601 printf ("WARNING: '%s' not expressed in octal.\n", data);
604 if (strcmp (data, "dmask") == 0) {
605 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
608 /* the following eight mount options should be
609 stripped out from what is passed into the kernel
610 since these eight options are best passed as the
611 mount flags rather than redundantly to the kernel
612 and could generate spurious warnings depending on the
613 level of the corresponding cifs vfs kernel code */
614 } else if (strncmp(data, "nosuid", 6) == 0) {
615 *filesys_flags |= MS_NOSUID;
616 } else if (strncmp(data, "suid", 4) == 0) {
617 *filesys_flags &= ~MS_NOSUID;
618 } else if (strncmp(data, "nodev", 5) == 0) {
619 *filesys_flags |= MS_NODEV;
620 } else if ((strncmp(data, "nobrl", 5) == 0) ||
621 (strncmp(data, "nolock", 6) == 0)) {
622 *filesys_flags &= ~MS_MANDLOCK;
623 } else if (strncmp(data, "dev", 3) == 0) {
624 *filesys_flags &= ~MS_NODEV;
625 } else if (strncmp(data, "noexec", 6) == 0) {
626 *filesys_flags |= MS_NOEXEC;
627 } else if (strncmp(data, "exec", 4) == 0) {
628 *filesys_flags &= ~MS_NOEXEC;
629 } else if (strncmp(data, "guest", 5) == 0) {
631 } else if (strncmp(data, "ro", 2) == 0) {
632 *filesys_flags |= MS_RDONLY;
633 } else if (strncmp(data, "rw", 2) == 0) {
634 *filesys_flags &= ~MS_RDONLY;
635 } else if (strncmp(data, "remount", 7) == 0) {
636 *filesys_flags |= MS_REMOUNT;
637 } /* else if (strnicmp(data, "port", 4) == 0) {
638 if (value && *value) {
640 simple_strtoul(value, &value, 0);
642 } else if (strnicmp(data, "rsize", 5) == 0) {
643 if (value && *value) {
645 simple_strtoul(value, &value, 0);
647 } else if (strnicmp(data, "wsize", 5) == 0) {
648 if (value && *value) {
650 simple_strtoul(value, &value, 0);
652 } else if (strnicmp(data, "version", 3) == 0) {
654 printf("CIFS: Unknown mount option %s\n",data);
655 } */ /* nothing to do on those four mount options above.
656 Just pass to kernel and ignore them here */
658 /* Copy (possibly modified) option to out */
659 word_len = strlen(data);
661 word_len += 1 + strlen(value);
663 out = (char *)realloc(out, out_len + word_len + 2);
670 strlcat(out, ",", out_len + word_len + 2);
675 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
677 snprintf(out + out_len, word_len + 1, "%s", data);
678 out_len = strlen(out);
684 /* special-case the uid and gid */
686 word_len = strlen(user);
688 out = (char *)realloc(out, out_len + word_len + 6);
695 strlcat(out, ",", out_len + word_len + 6);
698 snprintf(out + out_len, word_len + 5, "uid=%s", user);
699 out_len = strlen(out);
702 word_len = strlen(group);
704 out = (char *)realloc(out, out_len + 1 + word_len + 6);
711 strlcat(out, ",", out_len + word_len + 6);
714 snprintf(out + out_len, word_len + 5, "gid=%s", group);
715 out_len = strlen(out);
718 SAFE_FREE(*optionsp);
723 /* replace all (one or more) commas with double commas */
724 static void check_for_comma(char ** ppasswrd)
729 int number_of_commas = 0;
744 if(number_of_commas == 0)
746 if(number_of_commas > MOUNT_PASSWD_SIZE) {
747 /* would otherwise overflow the mount options buffer */
748 printf("\nInvalid password. Password contains too many commas.\n");
752 new_pass_buf = (char *)malloc(len+number_of_commas+1);
753 if(new_pass_buf == NULL)
756 for(i=0,j=0;i<len;i++,j++) {
757 new_pass_buf[j] = pass[i];
760 new_pass_buf[j] = pass[i];
763 new_pass_buf[len+number_of_commas] = 0;
765 SAFE_FREE(*ppasswrd);
766 *ppasswrd = new_pass_buf;
771 /* Usernames can not have backslash in them and we use
772 [BB check if usernames can have forward slash in them BB]
773 backslash as domain\user separator character
775 static char * check_for_domain(char **ppuser)
777 char * original_string;
787 original_string = *ppuser;
789 if (original_string == NULL)
792 original_len = strlen(original_string);
794 usernm = strchr(*ppuser,'/');
795 if (usernm == NULL) {
796 usernm = strchr(*ppuser,'\\');
802 printf("Domain name specified twice. Username probably malformed\n");
808 if (domainnm[0] != 0) {
811 printf("null domain\n");
813 len = strlen(domainnm);
814 /* reset domainm to new buffer, and copy
815 domain name into it */
816 domainnm = (char *)malloc(len+1);
820 strlcpy(domainnm,*ppuser,len+1);
822 /* move_string(*ppuser, usernm+1) */
823 len = strlen(usernm+1);
825 if(len >= original_len) {
826 /* should not happen */
830 for(i=0;i<original_len;i++) {
832 original_string[i] = usernm[i+1];
833 else /* stuff with commas to remove last parm */
834 original_string[i] = ',';
837 /* BB add check for more than one slash?
845 /* replace all occurances of "from" in a string with "to" */
846 static void replace_char(char *string, char from, char to, int maxlen)
848 char *lastchar = string + maxlen;
850 string = strchr(string, from);
853 if (string >= lastchar)
859 /* Note that caller frees the returned buffer if necessary */
860 static char * parse_server(char ** punc_name)
862 char * unc_name = *punc_name;
863 int length = strnlen(unc_name, MAX_UNC_LEN);
865 char * ipaddress_string = NULL;
866 struct hostent * host_entry = NULL;
867 struct in_addr server_ipaddr;
869 if(length > (MAX_UNC_LEN - 1)) {
870 printf("mount error: UNC name too long");
873 if (strncasecmp("cifs://",unc_name,7) == 0)
874 return parse_cifs_url(unc_name+7);
875 if (strncasecmp("smb://",unc_name,6) == 0) {
876 return parse_cifs_url(unc_name+6);
880 /* BB add code to find DFS root here */
881 printf("\nMounting the DFS root for domain not implemented yet\n");
884 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
885 /* check for nfs syntax ie server:share */
886 share = strchr(unc_name,':');
888 *punc_name = (char *)malloc(length+3);
889 if(*punc_name == NULL) {
890 /* put the original string back if
892 *punc_name = unc_name;
896 strlcpy((*punc_name)+2,unc_name,length+1);
898 unc_name = *punc_name;
899 unc_name[length+2] = 0;
900 goto continue_unc_parsing;
902 printf("mount error: improperly formatted UNC name.");
903 printf(" %s does not begin with \\\\ or //\n",unc_name);
907 continue_unc_parsing:
912 /* allow for either delimiter between host and sharename */
913 if ((share = strpbrk(unc_name, "/\\"))) {
914 *share = 0; /* temporarily terminate the string */
917 host_entry = gethostbyname(unc_name);
919 *(share - 1) = '/'; /* put delimiter back */
921 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
922 if ((prefixpath = strpbrk(share, "/\\"))) {
923 *prefixpath = 0; /* permanently terminate the string */
924 if (!strlen(++prefixpath))
925 prefixpath = NULL; /* this needs to be done explicitly */
929 printf("ip address specified explicitly\n");
932 if(host_entry == NULL) {
933 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
936 /* BB should we pass an alternate version of the share name as Unicode */
937 /* BB what about ipv6? BB */
938 /* BB add retries with alternate servers in list */
940 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
942 ipaddress_string = inet_ntoa(server_ipaddr);
943 if(ipaddress_string == NULL) {
944 printf("mount error: could not get valid ip address for target server\n");
947 return ipaddress_string;
950 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
951 printf("Mounting the DFS root for a particular server not implemented yet\n");
958 static struct option longopts[] = {
959 { "all", 0, NULL, 'a' },
960 { "help",0, NULL, 'h' },
961 { "move",0, NULL, 'm' },
962 { "bind",0, NULL, 'b' },
963 { "read-only", 0, NULL, 'r' },
964 { "ro", 0, NULL, 'r' },
965 { "verbose", 0, NULL, 'v' },
966 { "version", 0, NULL, 'V' },
967 { "read-write", 0, NULL, 'w' },
968 { "rw", 0, NULL, 'w' },
969 { "options", 1, NULL, 'o' },
970 { "type", 1, NULL, 't' },
971 { "rsize",1, NULL, 'R' },
972 { "wsize",1, NULL, 'W' },
973 { "uid", 1, NULL, '1'},
974 { "gid", 1, NULL, '2'},
975 { "user",1,NULL,'u'},
976 { "username",1,NULL,'u'},
978 { "domain",1,NULL,'d'},
979 { "password",1,NULL,'p'},
980 { "pass",1,NULL,'p'},
981 { "credentials",1,NULL,'c'},
982 { "port",1,NULL,'P'},
983 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
987 /* convert a string to uppercase. return false if the string
988 * wasn't ASCII or was a NULL ptr */
990 uppercase_string(char *string)
996 /* check for unicode */
997 if ((unsigned char) string[0] & 0x80)
999 *string = toupper((unsigned char) *string);
1006 int main(int argc, char ** argv)
1009 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1010 char * orgoptions = NULL;
1011 char * share_name = NULL;
1012 char * ipaddr = NULL;
1014 char * mountpoint = NULL;
1015 char * options = NULL;
1016 char * resolved_path = NULL;
1027 size_t options_size = 0;
1028 int retry = 0; /* set when we have to retry mount with uppercase */
1029 struct stat statbuf;
1030 struct utsname sysinfo;
1031 struct mntent mountent;
1034 /* setlocale(LC_ALL, "");
1035 bindtextdomain(PACKAGE, LOCALEDIR);
1036 textdomain(PACKAGE); */
1039 thisprogram = argv[0];
1045 if(thisprogram == NULL)
1046 thisprogram = "mount.cifs";
1049 /* BB add workstation name and domain and pass down */
1051 /* #ifdef _GNU_SOURCE
1052 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1056 share_name = strndup(argv[1], MAX_UNC_LEN);
1057 if (share_name == NULL) {
1058 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1061 mountpoint = argv[2];
1067 /* add sharename in opts string as unc= parm */
1069 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1070 longopts, NULL)) != -1) {
1072 /* No code to do the following options yet */
1074 list_with_volumelabel = 1;
1077 volumelabel = optarg;
1084 case 'h': /* help */
1085 mount_cifs_usage ();
1095 "option 'b' (MS_BIND) not supported\n");
1103 "option 'm' (MS_MOVE) not supported\n");
1107 orgoptions = strdup(optarg);
1109 case 'r': /* mount readonly */
1119 printf ("mount.cifs version: %s.%s%s\n",
1120 MOUNT_CIFS_VERSION_MAJOR,
1121 MOUNT_CIFS_VERSION_MINOR,
1122 MOUNT_CIFS_VENDOR_SUFFIX);
1125 flags &= ~MS_RDONLY;
1128 rsize = atoi(optarg) ;
1131 wsize = atoi(optarg);
1134 if (isdigit(*optarg)) {
1137 uid = strtoul(optarg, &ep, 10);
1139 printf("bad uid value \"%s\"\n", optarg);
1145 if (!(pw = getpwnam(optarg))) {
1146 printf("bad user name \"%s\"\n", optarg);
1154 if (isdigit(*optarg)) {
1157 gid = strtoul(optarg, &ep, 10);
1159 printf("bad gid value \"%s\"\n", optarg);
1165 if (!(gr = getgrnam(optarg))) {
1166 printf("bad user name \"%s\"\n", optarg);
1178 domain_name = optarg; /* BB fix this - currently ignored */
1182 if(mountpassword == NULL)
1183 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1186 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1190 get_password_from_file(0 /* stdin */,NULL);
1195 printf("unknown mount option %c\n",c);
1201 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1206 if (getenv("PASSWD")) {
1207 if(mountpassword == NULL)
1208 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1210 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1213 } else if (getenv("PASSWD_FD")) {
1214 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1215 } else if (getenv("PASSWD_FILE")) {
1216 get_password_from_file(0, getenv("PASSWD_FILE"));
1219 if (orgoptions && parse_options(&orgoptions, &flags)) {
1223 ipaddr = parse_server(&share_name);
1224 if((ipaddr == NULL) && (got_ip == 0)) {
1225 printf("No ip address specified and hostname not found\n");
1230 /* BB save off path and pop after mount returns? */
1231 resolved_path = (char *)malloc(PATH_MAX+1);
1233 /* Note that if we can not canonicalize the name, we get
1234 another chance to see if it is valid when we chdir to it */
1235 if (realpath(mountpoint, resolved_path)) {
1236 mountpoint = resolved_path;
1239 if(chdir(mountpoint)) {
1240 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1245 if(stat (".", &statbuf)) {
1246 printf("mount error: mount point %s does not exist\n",mountpoint);
1251 if (S_ISDIR(statbuf.st_mode) == 0) {
1252 printf("mount error: mount point %s is not a directory\n",mountpoint);
1257 if((getuid() != 0) && (geteuid() == 0)) {
1258 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1259 #ifndef CIFS_ALLOW_USR_SUID
1260 /* Do not allow user mounts to control suid flag
1261 for mount unless explicitly built that way */
1262 flags |= MS_NOSUID | MS_NODEV;
1265 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1271 user_name = getusername();
1275 if(got_password == 0) {
1276 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1277 no good replacement yet. */
1278 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1279 if (!tmp_pass || !mountpassword) {
1280 printf("Password not entered, exiting\n");
1283 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1286 /* FIXME launch daemon (handles dfs name resolution and credential change)
1287 remember to clear parms and overwrite password field before launching */
1290 optlen = strlen(orgoptions);
1295 optlen += strlen(share_name) + 4;
1297 printf("No server share name specified\n");
1298 printf("\nMounting the DFS root for server not implemented yet\n");
1302 optlen += strlen(user_name) + 6;
1304 optlen += strlen(ipaddr) + 4;
1306 optlen += strlen(mountpassword) + 6;
1308 options_size = optlen + 10 + DOMAIN_SIZE;
1309 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 */);
1311 if(options == NULL) {
1312 printf("Could not allocate memory for mount options\n");
1317 strlcpy(options,"unc=",options_size);
1318 strlcat(options,share_name,options_size);
1319 /* scan backwards and reverse direction of slash */
1320 temp = strrchr(options, '/');
1321 if(temp > options + 6)
1324 strlcat(options,",ip=",options_size);
1325 strlcat(options,ipaddr,options_size);
1329 /* check for syntax like user=domain\user */
1331 domain_name = check_for_domain(&user_name);
1332 strlcat(options,",user=",options_size);
1333 strlcat(options,user_name,options_size);
1337 /* extra length accounted for in option string above */
1338 strlcat(options,",domain=",options_size);
1339 strlcat(options,domain_name,options_size);
1343 /* Commas have to be doubled, or else they will
1344 look like the parameter separator */
1345 /* if(sep is not set)*/
1347 check_for_comma(&mountpassword);
1348 strlcat(options,",pass=",options_size);
1349 strlcat(options,mountpassword,options_size);
1352 strlcat(options,",ver=",options_size);
1353 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1356 strlcat(options,",",options_size);
1357 strlcat(options,orgoptions,options_size);
1360 strlcat(options,",prefixpath=",options_size);
1361 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1364 printf("\nmount.cifs kernel mount options %s \n",options);
1366 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1367 replace_char(dev_name, '\\', '/', strlen(share_name));
1369 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1370 /* remember to kill daemon on error */
1373 printf("mount failed but no error number set\n");
1376 printf("mount error: cifs filesystem not supported by the system\n");
1381 if (uppercase_string(dev_name) &&
1382 uppercase_string(share_name) &&
1383 uppercase_string(prefixpath)) {
1384 printf("retrying with upper case share name\n");
1389 printf("mount error %d = %s\n",errno,strerror(errno));
1391 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1395 pmntfile = setmntent(MOUNTED, "a+");
1397 mountent.mnt_fsname = dev_name;
1398 mountent.mnt_dir = mountpoint;
1399 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1400 mountent.mnt_opts = (char *)malloc(220);
1401 if(mountent.mnt_opts) {
1402 char * mount_user = getusername();
1403 memset(mountent.mnt_opts,0,200);
1404 if(flags & MS_RDONLY)
1405 strlcat(mountent.mnt_opts,"ro",220);
1407 strlcat(mountent.mnt_opts,"rw",220);
1408 if(flags & MS_MANDLOCK)
1409 strlcat(mountent.mnt_opts,",mand",220);
1410 if(flags & MS_NOEXEC)
1411 strlcat(mountent.mnt_opts,",noexec",220);
1412 if(flags & MS_NOSUID)
1413 strlcat(mountent.mnt_opts,",nosuid",220);
1414 if(flags & MS_NODEV)
1415 strlcat(mountent.mnt_opts,",nodev",220);
1416 if(flags & MS_SYNCHRONOUS)
1417 strlcat(mountent.mnt_opts,",synch",220);
1420 strlcat(mountent.mnt_opts,",user=",220);
1421 strlcat(mountent.mnt_opts,mount_user,220);
1423 /* free(mount_user); do not free static mem */
1426 mountent.mnt_freq = 0;
1427 mountent.mnt_passno = 0;
1428 rc = addmntent(pmntfile,&mountent);
1429 endmntent(pmntfile);
1430 SAFE_FREE(mountent.mnt_opts);
1432 printf("could not update mount table\n");
1438 int len = strlen(mountpassword);
1439 memset(mountpassword,0,len);
1440 SAFE_FREE(mountpassword);
1444 SAFE_FREE(orgoptions);
1445 SAFE_FREE(resolved_path);
1446 SAFE_FREE(share_name);