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 (!strcmp(value
, "none"))
479 } else if (strncmp(data
, "ip", 2) == 0) {
480 if (!value
|| !*value
) {
481 printf("target ip address argument missing");
482 } else if (strnlen(value
, 35) < 35) {
484 printf("ip address %s override specified\n",value
);
487 printf("ip address too long\n");
490 } else if ((strncmp(data
, "unc", 3) == 0)
491 || (strncmp(data
, "target", 6) == 0)
492 || (strncmp(data
, "path", 4) == 0)) {
493 if (!value
|| !*value
) {
494 printf("invalid path to network resource\n");
495 return 1; /* needs_arg; */
496 } else if(strnlen(value
,5) < 5) {
497 printf("UNC name too short");
500 if (strnlen(value
, 300) < 300) {
502 if (strncmp(value
, "//", 2) == 0) {
504 printf("unc name specified twice, ignoring second\n");
507 } else if (strncmp(value
, "\\\\", 2) != 0) {
508 printf("UNC Path does not begin with // or \\\\ \n");
512 printf("unc name specified twice, ignoring second\n");
517 printf("CIFS: UNC name too long\n");
520 } else if ((strncmp(data
, "domain", 3) == 0)
521 || (strncmp(data
, "workgroup", 5) == 0)) {
522 if (!value
|| !*value
) {
523 printf("CIFS: invalid domain name\n");
524 return 1; /* needs_arg; */
526 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
529 printf("domain name too long\n");
532 } else if (strncmp(data
, "cred", 4) == 0) {
533 if (value
&& *value
) {
534 rc
= open_cred_file(value
);
536 printf("error %d (%s) opening credential file %s\n",
537 rc
, strerror(rc
), value
);
541 printf("invalid credential file name specified\n");
544 } else if (strncmp(data
, "uid", 3) == 0) {
545 if (value
&& *value
) {
547 if (!isdigit(*value
)) {
550 if (!(pw
= getpwnam(value
))) {
551 printf("bad user name \"%s\"\n", value
);
554 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
556 strlcpy(user
,value
,sizeof(user
));
560 } else if (strncmp(data
, "gid", 3) == 0) {
561 if (value
&& *value
) {
563 if (!isdigit(*value
)) {
566 if (!(gr
= getgrnam(value
))) {
567 printf("bad group name \"%s\"\n", value
);
570 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
572 strlcpy(group
,value
,sizeof(group
));
576 /* fmask and dmask synonyms for people used to smbfs syntax */
577 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
578 if (!value
|| !*value
) {
579 printf ("Option '%s' requires a numerical argument\n", data
);
583 if (value
[0] != '0') {
584 printf ("WARNING: '%s' not expressed in octal.\n", data
);
587 if (strcmp (data
, "fmask") == 0) {
588 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
589 data
= "file_mode"; /* BB fix this */
591 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
592 if (!value
|| !*value
) {
593 printf ("Option '%s' requires a numerical argument\n", data
);
597 if (value
[0] != '0') {
598 printf ("WARNING: '%s' not expressed in octal.\n", data
);
601 if (strcmp (data
, "dmask") == 0) {
602 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
605 /* the following eight mount options should be
606 stripped out from what is passed into the kernel
607 since these eight options are best passed as the
608 mount flags rather than redundantly to the kernel
609 and could generate spurious warnings depending on the
610 level of the corresponding cifs vfs kernel code */
611 } else if (strncmp(data
, "nosuid", 6) == 0) {
612 *filesys_flags
|= MS_NOSUID
;
613 } else if (strncmp(data
, "suid", 4) == 0) {
614 *filesys_flags
&= ~MS_NOSUID
;
615 } else if (strncmp(data
, "nodev", 5) == 0) {
616 *filesys_flags
|= MS_NODEV
;
617 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
618 (strncmp(data
, "nolock", 6) == 0)) {
619 *filesys_flags
&= ~MS_MANDLOCK
;
620 } else if (strncmp(data
, "dev", 3) == 0) {
621 *filesys_flags
&= ~MS_NODEV
;
622 } else if (strncmp(data
, "noexec", 6) == 0) {
623 *filesys_flags
|= MS_NOEXEC
;
624 } else if (strncmp(data
, "exec", 4) == 0) {
625 *filesys_flags
&= ~MS_NOEXEC
;
626 } else if (strncmp(data
, "guest", 5) == 0) {
628 } else if (strncmp(data
, "ro", 2) == 0) {
629 *filesys_flags
|= MS_RDONLY
;
630 } else if (strncmp(data
, "rw", 2) == 0) {
631 *filesys_flags
&= ~MS_RDONLY
;
632 } else if (strncmp(data
, "remount", 7) == 0) {
633 *filesys_flags
|= MS_REMOUNT
;
634 } /* else if (strnicmp(data, "port", 4) == 0) {
635 if (value && *value) {
637 simple_strtoul(value, &value, 0);
639 } else if (strnicmp(data, "rsize", 5) == 0) {
640 if (value && *value) {
642 simple_strtoul(value, &value, 0);
644 } else if (strnicmp(data, "wsize", 5) == 0) {
645 if (value && *value) {
647 simple_strtoul(value, &value, 0);
649 } else if (strnicmp(data, "version", 3) == 0) {
651 printf("CIFS: Unknown mount option %s\n",data);
652 } */ /* nothing to do on those four mount options above.
653 Just pass to kernel and ignore them here */
655 /* Copy (possibly modified) option to out */
656 word_len
= strlen(data
);
658 word_len
+= 1 + strlen(value
);
660 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
667 strlcat(out
, ",", out_len
+ word_len
+ 2);
672 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
674 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
675 out_len
= strlen(out
);
681 /* special-case the uid and gid */
683 word_len
= strlen(user
);
685 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
692 strlcat(out
, ",", out_len
+ word_len
+ 6);
695 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
696 out_len
= strlen(out
);
699 word_len
= strlen(group
);
701 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
708 strlcat(out
, ",", out_len
+ word_len
+ 6);
711 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
712 out_len
= strlen(out
);
715 SAFE_FREE(*optionsp
);
720 /* replace all (one or more) commas with double commas */
721 static void check_for_comma(char ** ppasswrd
)
726 int number_of_commas
= 0;
741 if(number_of_commas
== 0)
743 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
744 /* would otherwise overflow the mount options buffer */
745 printf("\nInvalid password. Password contains too many commas.\n");
749 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
750 if(new_pass_buf
== NULL
)
753 for(i
=0,j
=0;i
<len
;i
++,j
++) {
754 new_pass_buf
[j
] = pass
[i
];
757 new_pass_buf
[j
] = pass
[i
];
760 new_pass_buf
[len
+number_of_commas
] = 0;
762 SAFE_FREE(*ppasswrd
);
763 *ppasswrd
= new_pass_buf
;
768 /* Usernames can not have backslash in them and we use
769 [BB check if usernames can have forward slash in them BB]
770 backslash as domain\user separator character
772 static char * check_for_domain(char **ppuser
)
774 char * original_string
;
784 original_string
= *ppuser
;
786 if (original_string
== NULL
)
789 original_len
= strlen(original_string
);
791 usernm
= strchr(*ppuser
,'/');
792 if (usernm
== NULL
) {
793 usernm
= strchr(*ppuser
,'\\');
799 printf("Domain name specified twice. Username probably malformed\n");
805 if (domainnm
[0] != 0) {
808 printf("null domain\n");
810 len
= strlen(domainnm
);
811 /* reset domainm to new buffer, and copy
812 domain name into it */
813 domainnm
= (char *)malloc(len
+1);
817 strlcpy(domainnm
,*ppuser
,len
+1);
819 /* move_string(*ppuser, usernm+1) */
820 len
= strlen(usernm
+1);
822 if(len
>= original_len
) {
823 /* should not happen */
827 for(i
=0;i
<original_len
;i
++) {
829 original_string
[i
] = usernm
[i
+1];
830 else /* stuff with commas to remove last parm */
831 original_string
[i
] = ',';
834 /* BB add check for more than one slash?
842 /* replace all occurances of "from" in a string with "to" */
843 static void replace_char(char *string
, char from
, char to
, int maxlen
)
845 char *lastchar
= string
+ maxlen
;
847 string
= strchr(string
, from
);
850 if (string
>= lastchar
)
856 /* Note that caller frees the returned buffer if necessary */
857 static char * parse_server(char ** punc_name
)
859 char * unc_name
= *punc_name
;
860 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
862 char * ipaddress_string
= NULL
;
863 struct hostent
* host_entry
= NULL
;
864 struct in_addr server_ipaddr
;
866 if(length
> (MAX_UNC_LEN
- 1)) {
867 printf("mount error: UNC name too long");
870 if (strncasecmp("cifs://",unc_name
,7) == 0)
871 return parse_cifs_url(unc_name
+7);
872 if (strncasecmp("smb://",unc_name
,6) == 0) {
873 return parse_cifs_url(unc_name
+6);
877 /* BB add code to find DFS root here */
878 printf("\nMounting the DFS root for domain not implemented yet\n");
881 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
882 /* check for nfs syntax ie server:share */
883 share
= strchr(unc_name
,':');
885 *punc_name
= (char *)malloc(length
+3);
886 if(*punc_name
== NULL
) {
887 /* put the original string back if
889 *punc_name
= unc_name
;
893 strlcpy((*punc_name
)+2,unc_name
,length
+1);
895 unc_name
= *punc_name
;
896 unc_name
[length
+2] = 0;
897 goto continue_unc_parsing
;
899 printf("mount error: improperly formatted UNC name.");
900 printf(" %s does not begin with \\\\ or //\n",unc_name
);
904 continue_unc_parsing
:
909 /* allow for either delimiter between host and sharename */
910 if ((share
= strpbrk(unc_name
, "/\\"))) {
911 *share
= 0; /* temporarily terminate the string */
914 host_entry
= gethostbyname(unc_name
);
916 *(share
- 1) = '/'; /* put delimiter back */
918 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
919 if ((prefixpath
= strpbrk(share
, "/\\"))) {
920 *prefixpath
= 0; /* permanently terminate the string */
921 if (!strlen(++prefixpath
))
922 prefixpath
= NULL
; /* this needs to be done explicitly */
926 printf("ip address specified explicitly\n");
929 if(host_entry
== NULL
) {
930 printf("mount error: could not find target server. TCP name %s not found\n", unc_name
);
933 /* BB should we pass an alternate version of the share name as Unicode */
934 /* BB what about ipv6? BB */
935 /* BB add retries with alternate servers in list */
937 memcpy(&server_ipaddr
.s_addr
, host_entry
->h_addr
, 4);
939 ipaddress_string
= inet_ntoa(server_ipaddr
);
940 if(ipaddress_string
== NULL
) {
941 printf("mount error: could not get valid ip address for target server\n");
944 return ipaddress_string
;
947 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
948 printf("Mounting the DFS root for a particular server not implemented yet\n");
955 static struct option longopts
[] = {
956 { "all", 0, NULL
, 'a' },
957 { "help",0, NULL
, 'h' },
958 { "move",0, NULL
, 'm' },
959 { "bind",0, NULL
, 'b' },
960 { "read-only", 0, NULL
, 'r' },
961 { "ro", 0, NULL
, 'r' },
962 { "verbose", 0, NULL
, 'v' },
963 { "version", 0, NULL
, 'V' },
964 { "read-write", 0, NULL
, 'w' },
965 { "rw", 0, NULL
, 'w' },
966 { "options", 1, NULL
, 'o' },
967 { "type", 1, NULL
, 't' },
968 { "rsize",1, NULL
, 'R' },
969 { "wsize",1, NULL
, 'W' },
970 { "uid", 1, NULL
, '1'},
971 { "gid", 1, NULL
, '2'},
972 { "user",1,NULL
,'u'},
973 { "username",1,NULL
,'u'},
975 { "domain",1,NULL
,'d'},
976 { "password",1,NULL
,'p'},
977 { "pass",1,NULL
,'p'},
978 { "credentials",1,NULL
,'c'},
979 { "port",1,NULL
,'P'},
980 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
984 /* convert a string to uppercase. return false if the string
985 * wasn't ASCII or was a NULL ptr */
987 uppercase_string(char *string
)
993 /* check for unicode */
994 if ((unsigned char) string
[0] & 0x80)
996 *string
= toupper((unsigned char) *string
);
1003 int main(int argc
, char ** argv
)
1006 int flags
= MS_MANDLOCK
; /* no need to set legacy MS_MGC_VAL */
1007 char * orgoptions
= NULL
;
1008 char * share_name
= NULL
;
1009 char * ipaddr
= NULL
;
1011 char * mountpoint
= NULL
;
1012 char * options
= NULL
;
1013 char * resolved_path
= NULL
;
1024 size_t options_size
= 0;
1025 int retry
= 0; /* set when we have to retry mount with uppercase */
1026 struct stat statbuf
;
1027 struct utsname sysinfo
;
1028 struct mntent mountent
;
1031 /* setlocale(LC_ALL, "");
1032 bindtextdomain(PACKAGE, LOCALEDIR);
1033 textdomain(PACKAGE); */
1036 thisprogram
= argv
[0];
1042 if(thisprogram
== NULL
)
1043 thisprogram
= "mount.cifs";
1046 /* BB add workstation name and domain and pass down */
1048 /* #ifdef _GNU_SOURCE
1049 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1053 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1054 if (share_name
== NULL
) {
1055 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1058 mountpoint
= argv
[2];
1064 /* add sharename in opts string as unc= parm */
1066 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1067 longopts
, NULL
)) != -1) {
1069 /* No code to do the following options yet */
1071 list_with_volumelabel = 1;
1074 volumelabel = optarg;
1081 case 'h': /* help */
1082 mount_cifs_usage ();
1092 "option 'b' (MS_BIND) not supported\n");
1100 "option 'm' (MS_MOVE) not supported\n");
1104 orgoptions
= strdup(optarg
);
1106 case 'r': /* mount readonly */
1116 printf ("mount.cifs version: %s.%s%s\n",
1117 MOUNT_CIFS_VERSION_MAJOR
,
1118 MOUNT_CIFS_VERSION_MINOR
,
1119 MOUNT_CIFS_VENDOR_SUFFIX
);
1122 flags
&= ~MS_RDONLY
;
1125 rsize
= atoi(optarg
) ;
1128 wsize
= atoi(optarg
);
1131 if (isdigit(*optarg
)) {
1134 uid
= strtoul(optarg
, &ep
, 10);
1136 printf("bad uid value \"%s\"\n", optarg
);
1142 if (!(pw
= getpwnam(optarg
))) {
1143 printf("bad user name \"%s\"\n", optarg
);
1151 if (isdigit(*optarg
)) {
1154 gid
= strtoul(optarg
, &ep
, 10);
1156 printf("bad gid value \"%s\"\n", optarg
);
1162 if (!(gr
= getgrnam(optarg
))) {
1163 printf("bad user name \"%s\"\n", optarg
);
1175 domain_name
= optarg
; /* BB fix this - currently ignored */
1179 if(mountpassword
== NULL
)
1180 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1183 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1187 get_password_from_file(0 /* stdin */,NULL
);
1192 printf("unknown mount option %c\n",c
);
1198 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1203 if (getenv("PASSWD")) {
1204 if(mountpassword
== NULL
)
1205 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1207 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1210 } else if (getenv("PASSWD_FD")) {
1211 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1212 } else if (getenv("PASSWD_FILE")) {
1213 get_password_from_file(0, getenv("PASSWD_FILE"));
1216 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1220 ipaddr
= parse_server(&share_name
);
1221 if((ipaddr
== NULL
) && (got_ip
== 0)) {
1222 printf("No ip address specified and hostname not found\n");
1227 /* BB save off path and pop after mount returns? */
1228 resolved_path
= (char *)malloc(PATH_MAX
+1);
1230 /* Note that if we can not canonicalize the name, we get
1231 another chance to see if it is valid when we chdir to it */
1232 if (realpath(mountpoint
, resolved_path
)) {
1233 mountpoint
= resolved_path
;
1236 if(chdir(mountpoint
)) {
1237 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
1242 if(stat (".", &statbuf
)) {
1243 printf("mount error: mount point %s does not exist\n",mountpoint
);
1248 if (S_ISDIR(statbuf
.st_mode
) == 0) {
1249 printf("mount error: mount point %s is not a directory\n",mountpoint
);
1254 if((getuid() != 0) && (geteuid() == 0)) {
1255 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
1256 #ifndef CIFS_ALLOW_USR_SUID
1257 /* Do not allow user mounts to control suid flag
1258 for mount unless explicitly built that way */
1259 flags
|= MS_NOSUID
| MS_NODEV
;
1262 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1268 user_name
= getusername();
1272 if(got_password
== 0) {
1273 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1274 no good replacement yet. */
1275 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1276 if (!tmp_pass
|| !mountpassword
) {
1277 printf("Password not entered, exiting\n");
1280 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1283 /* FIXME launch daemon (handles dfs name resolution and credential change)
1284 remember to clear parms and overwrite password field before launching */
1287 optlen
= strlen(orgoptions
);
1292 optlen
+= strlen(share_name
) + 4;
1294 printf("No server share name specified\n");
1295 printf("\nMounting the DFS root for server not implemented yet\n");
1299 optlen
+= strlen(user_name
) + 6;
1301 optlen
+= strlen(ipaddr
) + 4;
1303 optlen
+= strlen(mountpassword
) + 6;
1305 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1306 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 */);
1308 if(options
== NULL
) {
1309 printf("Could not allocate memory for mount options\n");
1314 strlcpy(options
,"unc=",options_size
);
1315 strlcat(options
,share_name
,options_size
);
1316 /* scan backwards and reverse direction of slash */
1317 temp
= strrchr(options
, '/');
1318 if(temp
> options
+ 6)
1321 strlcat(options
,",ip=",options_size
);
1322 strlcat(options
,ipaddr
,options_size
);
1326 /* check for syntax like user=domain\user */
1328 domain_name
= check_for_domain(&user_name
);
1329 strlcat(options
,",user=",options_size
);
1330 strlcat(options
,user_name
,options_size
);
1334 /* extra length accounted for in option string above */
1335 strlcat(options
,",domain=",options_size
);
1336 strlcat(options
,domain_name
,options_size
);
1340 /* Commas have to be doubled, or else they will
1341 look like the parameter separator */
1342 /* if(sep is not set)*/
1344 check_for_comma(&mountpassword
);
1345 strlcat(options
,",pass=",options_size
);
1346 strlcat(options
,mountpassword
,options_size
);
1349 strlcat(options
,",ver=",options_size
);
1350 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1353 strlcat(options
,",",options_size
);
1354 strlcat(options
,orgoptions
,options_size
);
1357 strlcat(options
,",prefixpath=",options_size
);
1358 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1361 printf("\nmount.cifs kernel mount options %s \n",options
);
1363 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1364 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1366 if(mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1367 /* remember to kill daemon on error */
1370 printf("mount failed but no error number set\n");
1373 printf("mount error: cifs filesystem not supported by the system\n");
1378 if (uppercase_string(dev_name
) &&
1379 uppercase_string(share_name
) &&
1380 uppercase_string(prefixpath
)) {
1381 printf("retrying with upper case share name\n");
1386 printf("mount error %d = %s\n",errno
,strerror(errno
));
1388 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1392 pmntfile
= setmntent(MOUNTED
, "a+");
1394 mountent
.mnt_fsname
= dev_name
;
1395 mountent
.mnt_dir
= mountpoint
;
1396 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1397 mountent
.mnt_opts
= (char *)malloc(220);
1398 if(mountent
.mnt_opts
) {
1399 char * mount_user
= getusername();
1400 memset(mountent
.mnt_opts
,0,200);
1401 if(flags
& MS_RDONLY
)
1402 strlcat(mountent
.mnt_opts
,"ro",220);
1404 strlcat(mountent
.mnt_opts
,"rw",220);
1405 if(flags
& MS_MANDLOCK
)
1406 strlcat(mountent
.mnt_opts
,",mand",220);
1407 if(flags
& MS_NOEXEC
)
1408 strlcat(mountent
.mnt_opts
,",noexec",220);
1409 if(flags
& MS_NOSUID
)
1410 strlcat(mountent
.mnt_opts
,",nosuid",220);
1411 if(flags
& MS_NODEV
)
1412 strlcat(mountent
.mnt_opts
,",nodev",220);
1413 if(flags
& MS_SYNCHRONOUS
)
1414 strlcat(mountent
.mnt_opts
,",synch",220);
1417 strlcat(mountent
.mnt_opts
,",user=",220);
1418 strlcat(mountent
.mnt_opts
,mount_user
,220);
1420 /* free(mount_user); do not free static mem */
1423 mountent
.mnt_freq
= 0;
1424 mountent
.mnt_passno
= 0;
1425 rc
= addmntent(pmntfile
,&mountent
);
1426 endmntent(pmntfile
);
1427 SAFE_FREE(mountent
.mnt_opts
);
1429 printf("could not update mount table\n");
1435 int len
= strlen(mountpassword
);
1436 memset(mountpassword
,0,len
);
1437 SAFE_FREE(mountpassword
);
1441 SAFE_FREE(orgoptions
);
1442 SAFE_FREE(resolved_path
);
1443 SAFE_FREE(share_name
);