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 */
60 #include "include/config.h"
71 #define MAX_UNC_LEN 1024
73 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
76 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
79 #define MOUNT_PASSWD_SIZE 64
80 #define DOMAIN_SIZE 64
82 /* exit status - bits below are ORed */
83 #define EX_USAGE 1 /* incorrect invocation or permission */
84 #define EX_SYSERR 2 /* out of memory, cannot fork, ... */
85 #define EX_SOFTWARE 4 /* internal mount bug or wrong version */
86 #define EX_USER 8 /* user interrupt */
87 #define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */
88 #define EX_FAIL 32 /* mount failure */
89 #define EX_SOMEOK 64 /* some mount succeeded */
91 const char *thisprogram
;
93 static int got_password
= 0;
94 static int got_user
= 0;
95 static int got_domain
= 0;
96 static int got_ip
= 0;
97 static int got_unc
= 0;
98 static int got_uid
= 0;
99 static int got_gid
= 0;
100 static char * user_name
= NULL
;
101 static char * mountpassword
= NULL
;
102 char * domain_name
= NULL
;
103 char * prefixpath
= NULL
;
105 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
106 * don't link to libreplace so need them here. */
108 /* like strncpy but does not 0 fill the buffer and always null
109 * terminates. bufsize is the size of the destination buffer */
112 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
114 size_t len
= strlen(s
);
116 if (bufsize
<= 0) return 0;
117 if (len
>= bufsize
) len
= bufsize
-1;
124 /* like strncat but does not 0 fill the buffer and always null
125 * terminates. bufsize is the length of the buffer, which should
126 * be one more than the maximum resulting string length */
129 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
131 size_t len1
= strlen(d
);
132 size_t len2
= strlen(s
);
133 size_t ret
= len1
+ len2
;
135 if (len1
+len2
>= bufsize
) {
136 if (bufsize
< (len1
+1)) {
139 len2
= bufsize
- (len1
+1);
142 memcpy(d
+len1
, s
, len2
);
152 open nofollow - avoid symlink exposure?
153 get owner of dir see if matches self or if root
154 call system(umount argv) etc.
158 static char * check_for_domain(char **);
161 static void mount_cifs_usage(void)
163 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
164 printf("\nMount the remote target, specified as a UNC name,");
165 printf(" to a local directory.\n\nOptions:\n");
166 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
167 printf("\nLess commonly used options:");
168 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
169 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
170 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
171 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
172 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
173 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
174 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
175 printf("\n\nRarely used options:");
176 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
177 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
178 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
179 printf("\n\tin6_addr");
180 printf("\n\nOptions are described in more detail in the manual page");
181 printf("\n\tman 8 mount.cifs\n");
182 printf("\nTo display the version number of the mount helper:");
183 printf("\n\t%s -V\n",thisprogram
);
185 SAFE_FREE(mountpassword
);
189 /* caller frees username if necessary */
190 static char * getusername(void) {
191 char *username
= NULL
;
192 struct passwd
*password
= getpwuid(getuid());
195 username
= password
->pw_name
;
200 static char * parse_cifs_url(char * unc_name
)
202 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name
);
206 static int open_cred_file(char * file_name
)
212 fs
= fopen(file_name
,"r");
215 line_buf
= (char *)malloc(4096);
216 if(line_buf
== NULL
) {
221 while(fgets(line_buf
,4096,fs
)) {
222 /* parse line from credential file */
224 /* eat leading white space */
225 for(i
=0;i
<4086;i
++) {
226 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
228 /* if whitespace - skip past it */
230 if (strncasecmp("username",line_buf
+i
,8) == 0) {
231 temp_val
= strchr(line_buf
+ i
,'=');
233 /* go past equals sign */
235 for(length
= 0;length
<4087;length
++) {
236 if ((temp_val
[length
] == '\n')
237 || (temp_val
[length
] == '\0')) {
238 temp_val
[length
] = '\0';
243 printf("mount.cifs failed due to malformed username in credentials file");
244 memset(line_buf
,0,4096);
248 user_name
= (char *)calloc(1 + length
,1);
249 /* BB adding free of user_name string before exit,
250 not really necessary but would be cleaner */
251 strlcpy(user_name
,temp_val
, length
+1);
254 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
255 temp_val
= strchr(line_buf
+i
,'=');
257 /* go past equals sign */
259 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
260 if ((temp_val
[length
] == '\n')
261 || (temp_val
[length
] == '\0')) {
262 temp_val
[length
] = '\0';
266 if(length
> MOUNT_PASSWD_SIZE
) {
267 printf("mount.cifs failed: password in credentials file too long\n");
268 memset(line_buf
,0, 4096);
271 if(mountpassword
== NULL
) {
272 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
274 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
276 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
281 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
282 temp_val
= strchr(line_buf
+i
,'=');
284 /* go past equals sign */
287 printf("\nDomain %s\n",temp_val
);
288 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
289 if ((temp_val
[length
] == '\n')
290 || (temp_val
[length
] == '\0')) {
291 temp_val
[length
] = '\0';
295 if(length
> DOMAIN_SIZE
) {
296 printf("mount.cifs failed: domain in credentials file too long\n");
299 if(domain_name
== NULL
) {
300 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
302 memset(domain_name
,0,DOMAIN_SIZE
);
304 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
317 static int get_password_from_file(int file_descript
, char * filename
)
323 if(mountpassword
== NULL
)
324 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
326 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
328 if (mountpassword
== NULL
) {
329 printf("malloc failed\n");
333 if(filename
!= NULL
) {
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
)
394 printf("parsing options: %s\n", data
);
396 /* BB fixme check for separator override BB */
400 snprintf(user
,sizeof(user
),"%u",getuid());
402 snprintf(group
,sizeof(group
),"%u",getgid());
405 /* while ((data = strsep(&options, ",")) != NULL) { */
406 while(data
!= NULL
) {
407 /* check if ends with trailing comma */
411 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
412 /* data = next keyword */
413 /* value = next value ie stuff after equal sign */
415 next_keyword
= strchr(data
,','); /* BB handle sep= */
417 /* temporarily null terminate end of keyword=value pair */
421 /* temporarily null terminate keyword to make keyword and value distinct */
422 if ((value
= strchr(data
, '=')) != NULL
) {
427 if (strncmp(data
, "users",5) == 0) {
428 if(!value
|| !*value
) {
431 } else if (strncmp(data
, "user_xattr",10) == 0) {
432 /* do nothing - need to skip so not parsed as user name */
433 } else if (strncmp(data
, "user", 4) == 0) {
435 if (!value
|| !*value
) {
436 if(data
[4] == '\0') {
438 printf("\nskipping empty user mount parameter\n");
439 /* remove the parm since it would otherwise be confusing
440 to the kernel code which would think it was a real username */
443 printf("username specified with no parameter\n");
444 return 1; /* needs_arg; */
447 if (strnlen(value
, 260) < 260) {
449 percent_char
= strchr(value
,'%');
452 if(mountpassword
== NULL
)
453 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
456 printf("\nmount.cifs warning - password specified twice\n");
459 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
460 /* remove password from username */
461 while(*percent_char
!= 0) {
467 /* this is only case in which the user
468 name buf is not malloc - so we have to
469 check for domain name embedded within
470 the user name here since the later
471 call to check_for_domain will not be
473 domain_name
= check_for_domain(&value
);
475 printf("username too long\n");
479 } else if (strncmp(data
, "pass", 4) == 0) {
480 if (!value
|| !*value
) {
482 printf("\npassword specified twice, ignoring second\n");
485 } else if (strnlen(value
, 17) < 17) {
487 printf("\nmount.cifs warning - password specified twice\n");
490 printf("password too long\n");
493 } else if (strncmp(data
, "sec", 3) == 0) {
495 if (!strncmp(value
, "none", 4) ||
496 !strncmp(value
, "krb5", 4))
499 } else if (strncmp(data
, "ip", 2) == 0) {
500 if (!value
|| !*value
) {
501 printf("target ip address argument missing");
502 } else if (strnlen(value
, 35) < 35) {
504 printf("ip address %s override specified\n",value
);
507 printf("ip address too long\n");
510 } else if ((strncmp(data
, "unc", 3) == 0)
511 || (strncmp(data
, "target", 6) == 0)
512 || (strncmp(data
, "path", 4) == 0)) {
513 if (!value
|| !*value
) {
514 printf("invalid path to network resource\n");
515 return 1; /* needs_arg; */
516 } else if(strnlen(value
,5) < 5) {
517 printf("UNC name too short");
520 if (strnlen(value
, 300) < 300) {
522 if (strncmp(value
, "//", 2) == 0) {
524 printf("unc name specified twice, ignoring second\n");
527 } else if (strncmp(value
, "\\\\", 2) != 0) {
528 printf("UNC Path does not begin with // or \\\\ \n");
532 printf("unc name specified twice, ignoring second\n");
537 printf("CIFS: UNC name too long\n");
540 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
541 || (strncmp(data
, "workg", 5) == 0)) {
542 /* note this allows for synonyms of "domain"
543 such as "DOM" and "dom" and "workgroup"
544 and "WORKGRP" etc. */
545 if (!value
|| !*value
) {
546 printf("CIFS: invalid domain name\n");
547 return 1; /* needs_arg; */
549 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
552 printf("domain name too long\n");
555 } else if (strncmp(data
, "cred", 4) == 0) {
556 if (value
&& *value
) {
557 rc
= open_cred_file(value
);
559 printf("error %d (%s) opening credential file %s\n",
560 rc
, strerror(rc
), value
);
564 printf("invalid credential file name specified\n");
567 } else if (strncmp(data
, "uid", 3) == 0) {
568 if (value
&& *value
) {
570 if (!isdigit(*value
)) {
573 if (!(pw
= getpwnam(value
))) {
574 printf("bad user name \"%s\"\n", value
);
577 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
579 strlcpy(user
,value
,sizeof(user
));
583 } else if (strncmp(data
, "gid", 3) == 0) {
584 if (value
&& *value
) {
586 if (!isdigit(*value
)) {
589 if (!(gr
= getgrnam(value
))) {
590 printf("bad group name \"%s\"\n", value
);
593 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
595 strlcpy(group
,value
,sizeof(group
));
599 /* fmask and dmask synonyms for people used to smbfs syntax */
600 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
601 if (!value
|| !*value
) {
602 printf ("Option '%s' requires a numerical argument\n", data
);
606 if (value
[0] != '0') {
607 printf ("WARNING: '%s' not expressed in octal.\n", data
);
610 if (strcmp (data
, "fmask") == 0) {
611 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
612 data
= "file_mode"; /* BB fix this */
614 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
615 if (!value
|| !*value
) {
616 printf ("Option '%s' requires a numerical argument\n", data
);
620 if (value
[0] != '0') {
621 printf ("WARNING: '%s' not expressed in octal.\n", data
);
624 if (strcmp (data
, "dmask") == 0) {
625 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
628 /* the following eight mount options should be
629 stripped out from what is passed into the kernel
630 since these eight options are best passed as the
631 mount flags rather than redundantly to the kernel
632 and could generate spurious warnings depending on the
633 level of the corresponding cifs vfs kernel code */
634 } else if (strncmp(data
, "nosuid", 6) == 0) {
635 *filesys_flags
|= MS_NOSUID
;
636 } else if (strncmp(data
, "suid", 4) == 0) {
637 *filesys_flags
&= ~MS_NOSUID
;
638 } else if (strncmp(data
, "nodev", 5) == 0) {
639 *filesys_flags
|= MS_NODEV
;
640 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
641 (strncmp(data
, "nolock", 6) == 0)) {
642 *filesys_flags
&= ~MS_MANDLOCK
;
643 } else if (strncmp(data
, "dev", 3) == 0) {
644 *filesys_flags
&= ~MS_NODEV
;
645 } else if (strncmp(data
, "noexec", 6) == 0) {
646 *filesys_flags
|= MS_NOEXEC
;
647 } else if (strncmp(data
, "exec", 4) == 0) {
648 *filesys_flags
&= ~MS_NOEXEC
;
649 } else if (strncmp(data
, "guest", 5) == 0) {
651 } else if (strncmp(data
, "ro", 2) == 0) {
652 *filesys_flags
|= MS_RDONLY
;
653 } else if (strncmp(data
, "rw", 2) == 0) {
654 *filesys_flags
&= ~MS_RDONLY
;
655 } else if (strncmp(data
, "remount", 7) == 0) {
656 *filesys_flags
|= MS_REMOUNT
;
657 } /* else if (strnicmp(data, "port", 4) == 0) {
658 if (value && *value) {
660 simple_strtoul(value, &value, 0);
662 } else if (strnicmp(data, "rsize", 5) == 0) {
663 if (value && *value) {
665 simple_strtoul(value, &value, 0);
667 } else if (strnicmp(data, "wsize", 5) == 0) {
668 if (value && *value) {
670 simple_strtoul(value, &value, 0);
672 } else if (strnicmp(data, "version", 3) == 0) {
674 printf("CIFS: Unknown mount option %s\n",data);
675 } */ /* nothing to do on those four mount options above.
676 Just pass to kernel and ignore them here */
678 /* Copy (possibly modified) option to out */
679 word_len
= strlen(data
);
681 word_len
+= 1 + strlen(value
);
683 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
690 strlcat(out
, ",", out_len
+ word_len
+ 2);
695 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
697 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
698 out_len
= strlen(out
);
704 /* special-case the uid and gid */
706 word_len
= strlen(user
);
708 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
715 strlcat(out
, ",", out_len
+ word_len
+ 6);
718 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
719 out_len
= strlen(out
);
722 word_len
= strlen(group
);
724 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
731 strlcat(out
, ",", out_len
+ word_len
+ 6);
734 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
735 out_len
= strlen(out
);
738 SAFE_FREE(*optionsp
);
743 /* replace all (one or more) commas with double commas */
744 static void check_for_comma(char ** ppasswrd
)
749 int number_of_commas
= 0;
764 if(number_of_commas
== 0)
766 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
767 /* would otherwise overflow the mount options buffer */
768 printf("\nInvalid password. Password contains too many commas.\n");
772 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
773 if(new_pass_buf
== NULL
)
776 for(i
=0,j
=0;i
<len
;i
++,j
++) {
777 new_pass_buf
[j
] = pass
[i
];
780 new_pass_buf
[j
] = pass
[i
];
783 new_pass_buf
[len
+number_of_commas
] = 0;
785 SAFE_FREE(*ppasswrd
);
786 *ppasswrd
= new_pass_buf
;
791 /* Usernames can not have backslash in them and we use
792 [BB check if usernames can have forward slash in them BB]
793 backslash as domain\user separator character
795 static char * check_for_domain(char **ppuser
)
797 char * original_string
;
807 original_string
= *ppuser
;
809 if (original_string
== NULL
)
812 original_len
= strlen(original_string
);
814 usernm
= strchr(*ppuser
,'/');
815 if (usernm
== NULL
) {
816 usernm
= strchr(*ppuser
,'\\');
822 printf("Domain name specified twice. Username probably malformed\n");
828 if (domainnm
[0] != 0) {
831 printf("null domain\n");
833 len
= strlen(domainnm
);
834 /* reset domainm to new buffer, and copy
835 domain name into it */
836 domainnm
= (char *)malloc(len
+1);
840 strlcpy(domainnm
,*ppuser
,len
+1);
842 /* move_string(*ppuser, usernm+1) */
843 len
= strlen(usernm
+1);
845 if(len
>= original_len
) {
846 /* should not happen */
850 for(i
=0;i
<original_len
;i
++) {
852 original_string
[i
] = usernm
[i
+1];
853 else /* stuff with commas to remove last parm */
854 original_string
[i
] = ',';
857 /* BB add check for more than one slash?
865 /* replace all occurances of "from" in a string with "to" */
866 static void replace_char(char *string
, char from
, char to
, int maxlen
)
868 char *lastchar
= string
+ maxlen
;
870 string
= strchr(string
, from
);
873 if (string
>= lastchar
)
879 /* Note that caller frees the returned buffer if necessary */
880 static char * parse_server(char ** punc_name
)
882 char * unc_name
= *punc_name
;
883 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
885 char * ipaddress_string
= NULL
;
886 struct hostent
* host_entry
= NULL
;
887 struct in_addr server_ipaddr
;
889 if(length
> (MAX_UNC_LEN
- 1)) {
890 printf("mount error: UNC name too long");
893 if (strncasecmp("cifs://",unc_name
,7) == 0)
894 return parse_cifs_url(unc_name
+7);
895 if (strncasecmp("smb://",unc_name
,6) == 0) {
896 return parse_cifs_url(unc_name
+6);
900 /* BB add code to find DFS root here */
901 printf("\nMounting the DFS root for domain not implemented yet\n");
904 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
905 /* check for nfs syntax ie server:share */
906 share
= strchr(unc_name
,':');
908 *punc_name
= (char *)malloc(length
+3);
909 if(*punc_name
== NULL
) {
910 /* put the original string back if
912 *punc_name
= unc_name
;
916 strlcpy((*punc_name
)+2,unc_name
,length
+1);
918 unc_name
= *punc_name
;
919 unc_name
[length
+2] = 0;
920 goto continue_unc_parsing
;
922 printf("mount error: improperly formatted UNC name.");
923 printf(" %s does not begin with \\\\ or //\n",unc_name
);
927 continue_unc_parsing
:
932 /* allow for either delimiter between host and sharename */
933 if ((share
= strpbrk(unc_name
, "/\\"))) {
934 *share
= 0; /* temporarily terminate the string */
937 host_entry
= gethostbyname(unc_name
);
939 *(share
- 1) = '/'; /* put delimiter back */
941 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
942 if ((prefixpath
= strpbrk(share
, "/\\"))) {
943 *prefixpath
= 0; /* permanently terminate the string */
944 if (!strlen(++prefixpath
))
945 prefixpath
= NULL
; /* this needs to be done explicitly */
949 printf("ip address specified explicitly\n");
952 if(host_entry
== NULL
) {
953 printf("mount error: could not find target server. TCP name %s not found\n", unc_name
);
956 /* BB should we pass an alternate version of the share name as Unicode */
957 /* BB what about ipv6? BB */
958 /* BB add retries with alternate servers in list */
960 memcpy(&server_ipaddr
.s_addr
, host_entry
->h_addr
, 4);
962 ipaddress_string
= inet_ntoa(server_ipaddr
);
963 if(ipaddress_string
== NULL
) {
964 printf("mount error: could not get valid ip address for target server\n");
967 return ipaddress_string
;
970 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
971 printf("Mounting the DFS root for a particular server not implemented yet\n");
978 static struct option longopts
[] = {
979 { "all", 0, NULL
, 'a' },
980 { "help",0, NULL
, 'h' },
981 { "move",0, NULL
, 'm' },
982 { "bind",0, NULL
, 'b' },
983 { "read-only", 0, NULL
, 'r' },
984 { "ro", 0, NULL
, 'r' },
985 { "verbose", 0, NULL
, 'v' },
986 { "version", 0, NULL
, 'V' },
987 { "read-write", 0, NULL
, 'w' },
988 { "rw", 0, NULL
, 'w' },
989 { "options", 1, NULL
, 'o' },
990 { "type", 1, NULL
, 't' },
991 { "rsize",1, NULL
, 'R' },
992 { "wsize",1, NULL
, 'W' },
993 { "uid", 1, NULL
, '1'},
994 { "gid", 1, NULL
, '2'},
995 { "user",1,NULL
,'u'},
996 { "username",1,NULL
,'u'},
998 { "domain",1,NULL
,'d'},
999 { "password",1,NULL
,'p'},
1000 { "pass",1,NULL
,'p'},
1001 { "credentials",1,NULL
,'c'},
1002 { "port",1,NULL
,'P'},
1003 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1004 { NULL
, 0, NULL
, 0 }
1007 /* convert a string to uppercase. return false if the string
1008 * wasn't ASCII. Return success on a NULL ptr */
1010 uppercase_string(char *string
)
1016 /* check for unicode */
1017 if ((unsigned char) string
[0] & 0x80)
1019 *string
= toupper((unsigned char) *string
);
1026 int main(int argc
, char ** argv
)
1029 int flags
= MS_MANDLOCK
; /* no need to set legacy MS_MGC_VAL */
1030 char * orgoptions
= NULL
;
1031 char * share_name
= NULL
;
1032 char * ipaddr
= NULL
;
1034 char * mountpoint
= NULL
;
1035 char * options
= NULL
;
1036 char * resolved_path
= NULL
;
1047 size_t options_size
= 0;
1048 int retry
= 0; /* set when we have to retry mount with uppercase */
1049 struct stat statbuf
;
1050 struct utsname sysinfo
;
1051 struct mntent mountent
;
1054 /* setlocale(LC_ALL, "");
1055 bindtextdomain(PACKAGE, LOCALEDIR);
1056 textdomain(PACKAGE); */
1059 thisprogram
= argv
[0];
1065 if(thisprogram
== NULL
)
1066 thisprogram
= "mount.cifs";
1069 /* BB add workstation name and domain and pass down */
1071 /* #ifdef _GNU_SOURCE
1072 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1076 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1077 if (share_name
== NULL
) {
1078 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1081 mountpoint
= argv
[2];
1087 /* add sharename in opts string as unc= parm */
1089 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1090 longopts
, NULL
)) != -1) {
1092 /* No code to do the following options yet */
1094 list_with_volumelabel = 1;
1097 volumelabel = optarg;
1104 case 'h': /* help */
1105 mount_cifs_usage ();
1115 "option 'b' (MS_BIND) not supported\n");
1123 "option 'm' (MS_MOVE) not supported\n");
1127 orgoptions
= strdup(optarg
);
1129 case 'r': /* mount readonly */
1139 printf ("mount.cifs version: %s.%s%s\n",
1140 MOUNT_CIFS_VERSION_MAJOR
,
1141 MOUNT_CIFS_VERSION_MINOR
,
1142 MOUNT_CIFS_VENDOR_SUFFIX
);
1145 flags
&= ~MS_RDONLY
;
1148 rsize
= atoi(optarg
) ;
1151 wsize
= atoi(optarg
);
1154 if (isdigit(*optarg
)) {
1157 uid
= strtoul(optarg
, &ep
, 10);
1159 printf("bad uid value \"%s\"\n", optarg
);
1165 if (!(pw
= getpwnam(optarg
))) {
1166 printf("bad user name \"%s\"\n", optarg
);
1174 if (isdigit(*optarg
)) {
1177 gid
= strtoul(optarg
, &ep
, 10);
1179 printf("bad gid value \"%s\"\n", optarg
);
1185 if (!(gr
= getgrnam(optarg
))) {
1186 printf("bad user name \"%s\"\n", optarg
);
1198 domain_name
= optarg
; /* BB fix this - currently ignored */
1202 if(mountpassword
== NULL
)
1203 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1206 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1210 get_password_from_file(0 /* stdin */,NULL
);
1215 printf("unknown mount option %c\n",c
);
1221 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1226 if (getenv("PASSWD")) {
1227 if(mountpassword
== NULL
)
1228 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1230 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1233 } else if (getenv("PASSWD_FD")) {
1234 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1235 } else if (getenv("PASSWD_FILE")) {
1236 get_password_from_file(0, getenv("PASSWD_FILE"));
1239 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1243 ipaddr
= parse_server(&share_name
);
1244 if((ipaddr
== NULL
) && (got_ip
== 0)) {
1245 printf("No ip address specified and hostname not found\n");
1250 /* BB save off path and pop after mount returns? */
1251 resolved_path
= (char *)malloc(PATH_MAX
+1);
1253 /* Note that if we can not canonicalize the name, we get
1254 another chance to see if it is valid when we chdir to it */
1255 if (realpath(mountpoint
, resolved_path
)) {
1256 mountpoint
= resolved_path
;
1259 if(chdir(mountpoint
)) {
1260 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
1265 if(stat (".", &statbuf
)) {
1266 printf("mount error: mount point %s does not exist\n",mountpoint
);
1271 if (S_ISDIR(statbuf
.st_mode
) == 0) {
1272 printf("mount error: mount point %s is not a directory\n",mountpoint
);
1277 if((getuid() != 0) && (geteuid() == 0)) {
1278 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
1279 #ifndef CIFS_ALLOW_USR_SUID
1280 /* Do not allow user mounts to control suid flag
1281 for mount unless explicitly built that way */
1282 flags
|= MS_NOSUID
| MS_NODEV
;
1285 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1291 user_name
= getusername();
1295 if(got_password
== 0) {
1296 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1297 no good replacement yet. */
1298 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1299 if (!tmp_pass
|| !mountpassword
) {
1300 printf("Password not entered, exiting\n");
1303 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1306 /* FIXME launch daemon (handles dfs name resolution and credential change)
1307 remember to clear parms and overwrite password field before launching */
1310 optlen
= strlen(orgoptions
);
1315 optlen
+= strlen(share_name
) + 4;
1317 printf("No server share name specified\n");
1318 printf("\nMounting the DFS root for server not implemented yet\n");
1322 optlen
+= strlen(user_name
) + 6;
1324 optlen
+= strlen(ipaddr
) + 4;
1326 optlen
+= strlen(mountpassword
) + 6;
1328 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1329 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 */);
1331 if(options
== NULL
) {
1332 printf("Could not allocate memory for mount options\n");
1337 strlcpy(options
,"unc=",options_size
);
1338 strlcat(options
,share_name
,options_size
);
1339 /* scan backwards and reverse direction of slash */
1340 temp
= strrchr(options
, '/');
1341 if(temp
> options
+ 6)
1344 strlcat(options
,",ip=",options_size
);
1345 strlcat(options
,ipaddr
,options_size
);
1349 /* check for syntax like user=domain\user */
1351 domain_name
= check_for_domain(&user_name
);
1352 strlcat(options
,",user=",options_size
);
1353 strlcat(options
,user_name
,options_size
);
1357 /* extra length accounted for in option string above */
1358 strlcat(options
,",domain=",options_size
);
1359 strlcat(options
,domain_name
,options_size
);
1363 /* Commas have to be doubled, or else they will
1364 look like the parameter separator */
1365 /* if(sep is not set)*/
1367 check_for_comma(&mountpassword
);
1368 strlcat(options
,",pass=",options_size
);
1369 strlcat(options
,mountpassword
,options_size
);
1372 strlcat(options
,",ver=",options_size
);
1373 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1376 strlcat(options
,",",options_size
);
1377 strlcat(options
,orgoptions
,options_size
);
1380 strlcat(options
,",prefixpath=",options_size
);
1381 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1384 printf("\nmount.cifs kernel mount options %s \n",options
);
1386 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1387 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1389 if(mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1390 /* remember to kill daemon on error */
1393 printf("mount failed but no error number set\n");
1396 printf("mount error: cifs filesystem not supported by the system\n");
1401 if (uppercase_string(dev_name
) &&
1402 uppercase_string(share_name
) &&
1403 uppercase_string(prefixpath
)) {
1404 printf("retrying with upper case share name\n");
1409 printf("mount error %d = %s\n",errno
,strerror(errno
));
1411 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1414 pmntfile
= setmntent(MOUNTED
, "a+");
1416 mountent
.mnt_fsname
= dev_name
;
1417 mountent
.mnt_dir
= mountpoint
;
1418 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1419 mountent
.mnt_opts
= (char *)malloc(220);
1420 if(mountent
.mnt_opts
) {
1421 char * mount_user
= getusername();
1422 memset(mountent
.mnt_opts
,0,200);
1423 if(flags
& MS_RDONLY
)
1424 strlcat(mountent
.mnt_opts
,"ro",220);
1426 strlcat(mountent
.mnt_opts
,"rw",220);
1427 if(flags
& MS_MANDLOCK
)
1428 strlcat(mountent
.mnt_opts
,",mand",220);
1429 if(flags
& MS_NOEXEC
)
1430 strlcat(mountent
.mnt_opts
,",noexec",220);
1431 if(flags
& MS_NOSUID
)
1432 strlcat(mountent
.mnt_opts
,",nosuid",220);
1433 if(flags
& MS_NODEV
)
1434 strlcat(mountent
.mnt_opts
,",nodev",220);
1435 if(flags
& MS_SYNCHRONOUS
)
1436 strlcat(mountent
.mnt_opts
,",synch",220);
1439 strlcat(mountent
.mnt_opts
,",user=",220);
1440 strlcat(mountent
.mnt_opts
,mount_user
,220);
1442 /* free(mount_user); do not free static mem */
1445 mountent
.mnt_freq
= 0;
1446 mountent
.mnt_passno
= 0;
1447 rc
= addmntent(pmntfile
,&mountent
);
1448 endmntent(pmntfile
);
1449 SAFE_FREE(mountent
.mnt_opts
);
1453 printf("could not update mount table\n");
1459 int len
= strlen(mountpassword
);
1460 memset(mountpassword
,0,len
);
1461 SAFE_FREE(mountpassword
);
1465 SAFE_FREE(orgoptions
);
1466 SAFE_FREE(resolved_path
);
1467 SAFE_FREE(share_name
);