2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include <sys/types.h>
30 #include <sys/mount.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
44 #define MOUNT_CIFS_VERSION_MAJOR "1"
45 #define MOUNT_CIFS_VERSION_MINOR "12"
47 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #include "include/version.h"
50 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
54 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #define MOUNT_CIFS_VENDOR_SUFFIX ""
57 #endif /* _SAMBA_BUILD_ */
58 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
61 #include "include/config.h"
72 #define MAX_UNC_LEN 1024
74 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
77 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
80 #define MOUNT_PASSWD_SIZE 64
81 #define DOMAIN_SIZE 64
83 /* currently maximum length of IPv6 address string */
84 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
86 const char *thisprogram
;
89 static int got_password
= 0;
90 static int got_user
= 0;
91 static int got_domain
= 0;
92 static int got_ip
= 0;
93 static int got_unc
= 0;
94 static int got_uid
= 0;
95 static int got_gid
= 0;
96 static char * user_name
= NULL
;
97 static char * mountpassword
= NULL
;
98 char * domain_name
= NULL
;
99 char * prefixpath
= NULL
;
101 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
102 * don't link to libreplace so need them here. */
104 /* like strncpy but does not 0 fill the buffer and always null
105 * terminates. bufsize is the size of the destination buffer */
108 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
110 size_t len
= strlen(s
);
112 if (bufsize
<= 0) return 0;
113 if (len
>= bufsize
) len
= bufsize
-1;
120 /* like strncat but does not 0 fill the buffer and always null
121 * terminates. bufsize is the length of the buffer, which should
122 * be one more than the maximum resulting string length */
125 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
127 size_t len1
= strlen(d
);
128 size_t len2
= strlen(s
);
129 size_t ret
= len1
+ len2
;
131 if (len1
+len2
>= bufsize
) {
132 if (bufsize
< (len1
+1)) {
135 len2
= bufsize
- (len1
+1);
138 memcpy(d
+len1
, s
, len2
);
148 open nofollow - avoid symlink exposure?
149 get owner of dir see if matches self or if root
150 call system(umount argv) etc.
154 static char * check_for_domain(char **);
157 static void mount_cifs_usage(void)
159 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
160 printf("\nMount the remote target, specified as a UNC name,");
161 printf(" to a local directory.\n\nOptions:\n");
162 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
163 printf("\nLess commonly used options:");
164 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
165 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
166 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
167 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
168 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
169 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
170 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
171 printf("\n\nRarely used options:");
172 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
173 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
174 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
175 printf("\n\tin6_addr");
176 printf("\n\nOptions are described in more detail in the manual page");
177 printf("\n\tman 8 mount.cifs\n");
178 printf("\nTo display the version number of the mount helper:");
179 printf("\n\t%s -V\n",thisprogram
);
181 SAFE_FREE(mountpassword
);
185 /* caller frees username if necessary */
186 static char * getusername(void) {
187 char *username
= NULL
;
188 struct passwd
*password
= getpwuid(getuid());
191 username
= password
->pw_name
;
196 static int open_cred_file(char * file_name
)
203 i
= access(file_name
, R_OK
);
207 fs
= fopen(file_name
,"r");
210 line_buf
= (char *)malloc(4096);
211 if(line_buf
== NULL
) {
216 while(fgets(line_buf
,4096,fs
)) {
217 /* parse line from credential file */
219 /* eat leading white space */
220 for(i
=0;i
<4086;i
++) {
221 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
223 /* if whitespace - skip past it */
225 if (strncasecmp("username",line_buf
+i
,8) == 0) {
226 temp_val
= strchr(line_buf
+ i
,'=');
228 /* go past equals sign */
230 for(length
= 0;length
<4087;length
++) {
231 if ((temp_val
[length
] == '\n')
232 || (temp_val
[length
] == '\0')) {
233 temp_val
[length
] = '\0';
238 printf("mount.cifs failed due to malformed username in credentials file");
239 memset(line_buf
,0,4096);
243 user_name
= (char *)calloc(1 + length
,1);
244 /* BB adding free of user_name string before exit,
245 not really necessary but would be cleaner */
246 strlcpy(user_name
,temp_val
, length
+1);
249 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
250 temp_val
= strchr(line_buf
+i
,'=');
252 /* go past equals sign */
254 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
255 if ((temp_val
[length
] == '\n')
256 || (temp_val
[length
] == '\0')) {
257 temp_val
[length
] = '\0';
261 if(length
> MOUNT_PASSWD_SIZE
) {
262 printf("mount.cifs failed: password in credentials file too long\n");
263 memset(line_buf
,0, 4096);
266 if(mountpassword
== NULL
) {
267 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
269 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
271 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
276 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
277 temp_val
= strchr(line_buf
+i
,'=');
279 /* go past equals sign */
282 printf("\nDomain %s\n",temp_val
);
283 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
284 if ((temp_val
[length
] == '\n')
285 || (temp_val
[length
] == '\0')) {
286 temp_val
[length
] = '\0';
290 if(length
> DOMAIN_SIZE
) {
291 printf("mount.cifs failed: domain in credentials file too long\n");
294 if(domain_name
== NULL
) {
295 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
297 memset(domain_name
,0,DOMAIN_SIZE
);
299 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
312 static int get_password_from_file(int file_descript
, char * filename
)
318 if(mountpassword
== NULL
)
319 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
321 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
323 if (mountpassword
== NULL
) {
324 printf("malloc failed\n");
328 if(filename
!= NULL
) {
329 rc
= access(filename
, R_OK
);
331 fprintf(stderr
, "mount.cifs failed: access check of %s failed: %s\n",
332 filename
, strerror(errno
));
335 file_descript
= open(filename
, O_RDONLY
);
336 if(file_descript
< 0) {
337 printf("mount.cifs failed. %s attempting to open password file %s\n",
338 strerror(errno
),filename
);
342 /* else file already open and fd provided */
344 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
345 rc
= read(file_descript
,&c
,1);
347 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno
));
349 close(file_descript
);
352 if(mountpassword
[0] == 0) {
354 printf("\nWarning: null password used since cifs password file empty");
357 } else /* read valid character */ {
358 if((c
== 0) || (c
== '\n')) {
359 mountpassword
[i
] = '\0';
362 mountpassword
[i
] = c
;
365 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
366 printf("\nWarning: password longer than %d characters specified in cifs password file",
370 if(filename
!= NULL
) {
371 close(file_descript
);
377 static int parse_options(char ** optionsp
, int * filesys_flags
)
380 char * percent_char
= NULL
;
382 char * next_keyword
= NULL
;
390 if (!optionsp
|| !*optionsp
)
394 /* BB fixme check for separator override BB */
398 snprintf(user
,sizeof(user
),"%u",getuid());
400 snprintf(group
,sizeof(group
),"%u",getgid());
403 /* while ((data = strsep(&options, ",")) != NULL) { */
404 while(data
!= NULL
) {
405 /* check if ends with trailing comma */
409 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
410 /* data = next keyword */
411 /* value = next value ie stuff after equal sign */
413 next_keyword
= strchr(data
,','); /* BB handle sep= */
415 /* temporarily null terminate end of keyword=value pair */
419 /* temporarily null terminate keyword to make keyword and value distinct */
420 if ((value
= strchr(data
, '=')) != NULL
) {
425 if (strncmp(data
, "users",5) == 0) {
426 if(!value
|| !*value
) {
429 } else if (strncmp(data
, "user_xattr",10) == 0) {
430 /* do nothing - need to skip so not parsed as user name */
431 } else if (strncmp(data
, "user", 4) == 0) {
433 if (!value
|| !*value
) {
434 if(data
[4] == '\0') {
436 printf("\nskipping empty user mount parameter\n");
437 /* remove the parm since it would otherwise be confusing
438 to the kernel code which would think it was a real username */
441 printf("username specified with no parameter\n");
443 return 1; /* needs_arg; */
446 if (strnlen(value
, 260) < 260) {
448 percent_char
= strchr(value
,'%');
451 if(mountpassword
== NULL
)
452 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
455 printf("\nmount.cifs warning - password specified twice\n");
458 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
459 /* remove password from username */
460 while(*percent_char
!= 0) {
466 /* this is only case in which the user
467 name buf is not malloc - so we have to
468 check for domain name embedded within
469 the user name here since the later
470 call to check_for_domain will not be
472 domain_name
= check_for_domain(&value
);
474 printf("username too long\n");
479 } else if (strncmp(data
, "pass", 4) == 0) {
480 if (!value
|| !*value
) {
482 fprintf(stderr
, "\npassword specified twice, ignoring second\n");
485 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
487 fprintf(stderr
, "\nmount.cifs warning - password specified twice\n");
489 mountpassword
= strndup(value
, MOUNT_PASSWD_SIZE
);
490 if (!mountpassword
) {
491 fprintf(stderr
, "mount.cifs error: %s", strerror(ENOMEM
));
498 fprintf(stderr
, "password too long\n");
503 } else if (strncmp(data
, "sec", 3) == 0) {
505 if (!strncmp(value
, "none", 4) ||
506 !strncmp(value
, "krb5", 4))
509 } else if (strncmp(data
, "ip", 2) == 0) {
510 if (!value
|| !*value
) {
511 printf("target ip address argument missing");
512 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
514 printf("ip address %s override specified\n",value
);
517 printf("ip address too long\n");
521 } else if ((strncmp(data
, "unc", 3) == 0)
522 || (strncmp(data
, "target", 6) == 0)
523 || (strncmp(data
, "path", 4) == 0)) {
524 if (!value
|| !*value
) {
525 printf("invalid path to network resource\n");
527 return 1; /* needs_arg; */
528 } else if(strnlen(value
,5) < 5) {
529 printf("UNC name too short");
532 if (strnlen(value
, 300) < 300) {
534 if (strncmp(value
, "//", 2) == 0) {
536 printf("unc name specified twice, ignoring second\n");
539 } else if (strncmp(value
, "\\\\", 2) != 0) {
540 printf("UNC Path does not begin with // or \\\\ \n");
545 printf("unc name specified twice, ignoring second\n");
550 printf("CIFS: UNC name too long\n");
554 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
555 || (strncmp(data
, "workg", 5) == 0)) {
556 /* note this allows for synonyms of "domain"
557 such as "DOM" and "dom" and "workgroup"
558 and "WORKGRP" etc. */
559 if (!value
|| !*value
) {
560 printf("CIFS: invalid domain name\n");
562 return 1; /* needs_arg; */
564 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
567 printf("domain name too long\n");
571 } else if (strncmp(data
, "cred", 4) == 0) {
572 if (value
&& *value
) {
573 rc
= open_cred_file(value
);
575 printf("error %d (%s) opening credential file %s\n",
576 rc
, strerror(rc
), value
);
581 printf("invalid credential file name specified\n");
585 } else if (strncmp(data
, "uid", 3) == 0) {
586 if (value
&& *value
) {
588 if (!isdigit(*value
)) {
591 if (!(pw
= getpwnam(value
))) {
592 printf("bad user name \"%s\"\n", value
);
595 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
597 strlcpy(user
,value
,sizeof(user
));
601 } else if (strncmp(data
, "gid", 3) == 0) {
602 if (value
&& *value
) {
604 if (!isdigit(*value
)) {
607 if (!(gr
= getgrnam(value
))) {
608 printf("bad group name \"%s\"\n", value
);
611 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
613 strlcpy(group
,value
,sizeof(group
));
617 /* fmask and dmask synonyms for people used to smbfs syntax */
618 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
619 if (!value
|| !*value
) {
620 printf ("Option '%s' requires a numerical argument\n", data
);
625 if (value
[0] != '0') {
626 printf ("WARNING: '%s' not expressed in octal.\n", data
);
629 if (strcmp (data
, "fmask") == 0) {
630 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
631 data
= "file_mode"; /* BB fix this */
633 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
634 if (!value
|| !*value
) {
635 printf ("Option '%s' requires a numerical argument\n", data
);
640 if (value
[0] != '0') {
641 printf ("WARNING: '%s' not expressed in octal.\n", data
);
644 if (strcmp (data
, "dmask") == 0) {
645 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
648 /* the following eight mount options should be
649 stripped out from what is passed into the kernel
650 since these eight options are best passed as the
651 mount flags rather than redundantly to the kernel
652 and could generate spurious warnings depending on the
653 level of the corresponding cifs vfs kernel code */
654 } else if (strncmp(data
, "nosuid", 6) == 0) {
655 *filesys_flags
|= MS_NOSUID
;
656 } else if (strncmp(data
, "suid", 4) == 0) {
657 *filesys_flags
&= ~MS_NOSUID
;
658 } else if (strncmp(data
, "nodev", 5) == 0) {
659 *filesys_flags
|= MS_NODEV
;
660 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
661 (strncmp(data
, "nolock", 6) == 0)) {
662 *filesys_flags
&= ~MS_MANDLOCK
;
663 } else if (strncmp(data
, "dev", 3) == 0) {
664 *filesys_flags
&= ~MS_NODEV
;
665 } else if (strncmp(data
, "noexec", 6) == 0) {
666 *filesys_flags
|= MS_NOEXEC
;
667 } else if (strncmp(data
, "exec", 4) == 0) {
668 *filesys_flags
&= ~MS_NOEXEC
;
669 } else if (strncmp(data
, "guest", 5) == 0) {
670 user_name
= (char *)calloc(1, 1);
673 } else if (strncmp(data
, "ro", 2) == 0) {
674 *filesys_flags
|= MS_RDONLY
;
675 } else if (strncmp(data
, "rw", 2) == 0) {
676 *filesys_flags
&= ~MS_RDONLY
;
677 } else if (strncmp(data
, "remount", 7) == 0) {
678 *filesys_flags
|= MS_REMOUNT
;
679 } /* else if (strnicmp(data, "port", 4) == 0) {
680 if (value && *value) {
682 simple_strtoul(value, &value, 0);
684 } else if (strnicmp(data, "rsize", 5) == 0) {
685 if (value && *value) {
687 simple_strtoul(value, &value, 0);
689 } else if (strnicmp(data, "wsize", 5) == 0) {
690 if (value && *value) {
692 simple_strtoul(value, &value, 0);
694 } else if (strnicmp(data, "version", 3) == 0) {
696 printf("CIFS: Unknown mount option %s\n",data);
697 } */ /* nothing to do on those four mount options above.
698 Just pass to kernel and ignore them here */
700 /* Copy (possibly modified) option to out */
701 word_len
= strlen(data
);
703 word_len
+= 1 + strlen(value
);
705 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
712 strlcat(out
, ",", out_len
+ word_len
+ 2);
717 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
719 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
720 out_len
= strlen(out
);
726 /* special-case the uid and gid */
728 word_len
= strlen(user
);
730 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
737 strlcat(out
, ",", out_len
+ word_len
+ 6);
740 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
741 out_len
= strlen(out
);
744 word_len
= strlen(group
);
746 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
753 strlcat(out
, ",", out_len
+ word_len
+ 6);
756 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
757 out_len
= strlen(out
);
760 SAFE_FREE(*optionsp
);
765 /* replace all (one or more) commas with double commas */
766 static void check_for_comma(char ** ppasswrd
)
771 int number_of_commas
= 0;
786 if(number_of_commas
== 0)
788 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
789 /* would otherwise overflow the mount options buffer */
790 printf("\nInvalid password. Password contains too many commas.\n");
794 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
795 if(new_pass_buf
== NULL
)
798 for(i
=0,j
=0;i
<len
;i
++,j
++) {
799 new_pass_buf
[j
] = pass
[i
];
802 new_pass_buf
[j
] = pass
[i
];
805 new_pass_buf
[len
+number_of_commas
] = 0;
807 SAFE_FREE(*ppasswrd
);
808 *ppasswrd
= new_pass_buf
;
813 /* Usernames can not have backslash in them and we use
814 [BB check if usernames can have forward slash in them BB]
815 backslash as domain\user separator character
817 static char * check_for_domain(char **ppuser
)
819 char * original_string
;
829 original_string
= *ppuser
;
831 if (original_string
== NULL
)
834 original_len
= strlen(original_string
);
836 usernm
= strchr(*ppuser
,'/');
837 if (usernm
== NULL
) {
838 usernm
= strchr(*ppuser
,'\\');
844 printf("Domain name specified twice. Username probably malformed\n");
850 if (domainnm
[0] != 0) {
853 printf("null domain\n");
855 len
= strlen(domainnm
);
856 /* reset domainm to new buffer, and copy
857 domain name into it */
858 domainnm
= (char *)malloc(len
+1);
862 strlcpy(domainnm
,*ppuser
,len
+1);
864 /* move_string(*ppuser, usernm+1) */
865 len
= strlen(usernm
+1);
867 if(len
>= original_len
) {
868 /* should not happen */
872 for(i
=0;i
<original_len
;i
++) {
874 original_string
[i
] = usernm
[i
+1];
875 else /* stuff with commas to remove last parm */
876 original_string
[i
] = ',';
879 /* BB add check for more than one slash?
887 /* replace all occurances of "from" in a string with "to" */
888 static void replace_char(char *string
, char from
, char to
, int maxlen
)
890 char *lastchar
= string
+ maxlen
;
892 string
= strchr(string
, from
);
895 if (string
>= lastchar
)
901 /* Note that caller frees the returned buffer if necessary */
902 static struct addrinfo
*
903 parse_server(char ** punc_name
)
905 char * unc_name
= *punc_name
;
906 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
908 struct addrinfo
*addrlist
;
911 if(length
> (MAX_UNC_LEN
- 1)) {
912 printf("mount error: UNC name too long");
915 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
916 (strncasecmp("smb://", unc_name
, 6) == 0)) {
917 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
922 /* BB add code to find DFS root here */
923 printf("\nMounting the DFS root for domain not implemented yet\n");
926 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
927 /* check for nfs syntax ie server:share */
928 share
= strchr(unc_name
,':');
930 *punc_name
= (char *)malloc(length
+3);
931 if(*punc_name
== NULL
) {
932 /* put the original string back if
934 *punc_name
= unc_name
;
938 strlcpy((*punc_name
)+2,unc_name
,length
+1);
940 unc_name
= *punc_name
;
941 unc_name
[length
+2] = 0;
942 goto continue_unc_parsing
;
944 printf("mount error: improperly formatted UNC name.");
945 printf(" %s does not begin with \\\\ or //\n",unc_name
);
949 continue_unc_parsing
:
954 /* allow for either delimiter between host and sharename */
955 if ((share
= strpbrk(unc_name
, "/\\"))) {
956 *share
= 0; /* temporarily terminate the string */
959 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
961 printf("mount error: could not resolve address for %s: %s\n",
962 unc_name
, gai_strerror(rc
));
966 *(share
- 1) = '/'; /* put delimiter back */
968 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
969 if ((prefixpath
= strpbrk(share
, "/\\"))) {
970 *prefixpath
= 0; /* permanently terminate the string */
971 if (!strlen(++prefixpath
))
972 prefixpath
= NULL
; /* this needs to be done explicitly */
976 printf("ip address specified explicitly\n");
979 /* BB should we pass an alternate version of the share name as Unicode */
983 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
984 printf("Mounting the DFS root for a particular server not implemented yet\n");
991 static struct option longopts
[] = {
992 { "all", 0, NULL
, 'a' },
993 { "help",0, NULL
, 'h' },
994 { "move",0, NULL
, 'm' },
995 { "bind",0, NULL
, 'b' },
996 { "read-only", 0, NULL
, 'r' },
997 { "ro", 0, NULL
, 'r' },
998 { "verbose", 0, NULL
, 'v' },
999 { "version", 0, NULL
, 'V' },
1000 { "read-write", 0, NULL
, 'w' },
1001 { "rw", 0, NULL
, 'w' },
1002 { "options", 1, NULL
, 'o' },
1003 { "type", 1, NULL
, 't' },
1004 { "rsize",1, NULL
, 'R' },
1005 { "wsize",1, NULL
, 'W' },
1006 { "uid", 1, NULL
, '1'},
1007 { "gid", 1, NULL
, '2'},
1008 { "user",1,NULL
,'u'},
1009 { "username",1,NULL
,'u'},
1010 { "dom",1,NULL
,'d'},
1011 { "domain",1,NULL
,'d'},
1012 { "password",1,NULL
,'p'},
1013 { "pass",1,NULL
,'p'},
1014 { "credentials",1,NULL
,'c'},
1015 { "port",1,NULL
,'P'},
1016 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1017 { NULL
, 0, NULL
, 0 }
1020 /* convert a string to uppercase. return false if the string
1021 * wasn't ASCII. Return success on a NULL ptr */
1023 uppercase_string(char *string
)
1029 /* check for unicode */
1030 if ((unsigned char) string
[0] & 0x80)
1032 *string
= toupper((unsigned char) *string
);
1039 static void print_cifs_mount_version(void)
1041 printf("mount.cifs version: %s.%s%s\n",
1042 MOUNT_CIFS_VERSION_MAJOR
,
1043 MOUNT_CIFS_VERSION_MINOR
,
1044 MOUNT_CIFS_VENDOR_SUFFIX
);
1047 int main(int argc
, char ** argv
)
1050 int flags
= MS_MANDLOCK
; /* no need to set legacy MS_MGC_VAL */
1051 char * orgoptions
= NULL
;
1052 char * share_name
= NULL
;
1053 const char * ipaddr
= NULL
;
1055 char * mountpoint
= NULL
;
1056 char * options
= NULL
;
1058 char * resolved_path
= NULL
;
1069 size_t options_size
= 0;
1071 int retry
= 0; /* set when we have to retry mount with uppercase */
1072 struct addrinfo
*addrhead
= NULL
, *addr
;
1073 struct stat statbuf
;
1074 struct utsname sysinfo
;
1075 struct mntent mountent
;
1076 struct sockaddr_in
*addr4
;
1077 struct sockaddr_in6
*addr6
;
1080 /* setlocale(LC_ALL, "");
1081 bindtextdomain(PACKAGE, LOCALEDIR);
1082 textdomain(PACKAGE); */
1085 thisprogram
= argv
[0];
1091 if(thisprogram
== NULL
)
1092 thisprogram
= "mount.cifs";
1095 /* BB add workstation name and domain and pass down */
1097 /* #ifdef _GNU_SOURCE
1098 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1102 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1103 if (share_name
== NULL
) {
1104 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1107 mountpoint
= argv
[2];
1108 } else if (argc
== 2) {
1109 if ((strcmp(argv
[1], "-V") == 0) ||
1110 (strcmp(argv
[1], "--version") == 0))
1112 print_cifs_mount_version();
1116 if ((strcmp(argv
[1], "-h") == 0) ||
1117 (strcmp(argv
[1], "-?") == 0) ||
1118 (strcmp(argv
[1], "--help") == 0))
1131 /* add sharename in opts string as unc= parm */
1133 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1134 longopts
, NULL
)) != -1) {
1136 /* No code to do the following options yet */
1138 list_with_volumelabel = 1;
1141 volumelabel = optarg;
1148 case 'h': /* help */
1149 mount_cifs_usage ();
1159 "option 'b' (MS_BIND) not supported\n");
1167 "option 'm' (MS_MOVE) not supported\n");
1171 orgoptions
= strdup(optarg
);
1173 case 'r': /* mount readonly */
1183 print_cifs_mount_version();
1186 flags
&= ~MS_RDONLY
;
1189 rsize
= atoi(optarg
) ;
1192 wsize
= atoi(optarg
);
1195 if (isdigit(*optarg
)) {
1198 uid
= strtoul(optarg
, &ep
, 10);
1200 printf("bad uid value \"%s\"\n", optarg
);
1206 if (!(pw
= getpwnam(optarg
))) {
1207 printf("bad user name \"%s\"\n", optarg
);
1215 if (isdigit(*optarg
)) {
1218 gid
= strtoul(optarg
, &ep
, 10);
1220 printf("bad gid value \"%s\"\n", optarg
);
1226 if (!(gr
= getgrnam(optarg
))) {
1227 printf("bad user name \"%s\"\n", optarg
);
1239 domain_name
= optarg
; /* BB fix this - currently ignored */
1243 if(mountpassword
== NULL
)
1244 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1247 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1251 get_password_from_file(0 /* stdin */,NULL
);
1259 printf("unknown mount option %c\n",c
);
1265 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1270 if (getenv("PASSWD")) {
1271 if(mountpassword
== NULL
)
1272 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1274 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1277 } else if (getenv("PASSWD_FD")) {
1278 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1279 } else if (getenv("PASSWD_FILE")) {
1280 get_password_from_file(0, getenv("PASSWD_FILE"));
1283 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1287 addrhead
= addr
= parse_server(&share_name
);
1288 if((addrhead
== NULL
) && (got_ip
== 0)) {
1289 printf("No ip address specified and hostname not found\n");
1294 /* BB save off path and pop after mount returns? */
1295 resolved_path
= (char *)malloc(PATH_MAX
+1);
1297 /* Note that if we can not canonicalize the name, we get
1298 another chance to see if it is valid when we chdir to it */
1299 if (realpath(mountpoint
, resolved_path
)) {
1300 mountpoint
= resolved_path
;
1303 if(chdir(mountpoint
)) {
1304 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
1309 if(stat (".", &statbuf
)) {
1310 printf("mount error: mount point %s does not exist\n",mountpoint
);
1315 if (S_ISDIR(statbuf
.st_mode
) == 0) {
1316 printf("mount error: mount point %s is not a directory\n",mountpoint
);
1321 if((getuid() != 0) && (geteuid() == 0)) {
1322 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
1323 #ifndef CIFS_ALLOW_USR_SUID
1324 /* Do not allow user mounts to control suid flag
1325 for mount unless explicitly built that way */
1326 flags
|= MS_NOSUID
| MS_NODEV
;
1329 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1335 /* Note that the password will not be retrieved from the
1336 USER env variable (ie user%password form) as there is
1337 already a PASSWD environment varaible */
1339 user_name
= strdup(getenv("USER"));
1340 if (user_name
== NULL
)
1341 user_name
= getusername();
1345 if(got_password
== 0) {
1346 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1347 no good replacement yet. */
1348 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1349 if (!tmp_pass
|| !mountpassword
) {
1350 printf("Password not entered, exiting\n");
1353 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1356 /* FIXME launch daemon (handles dfs name resolution and credential change)
1357 remember to clear parms and overwrite password field before launching */
1359 optlen
= strlen(orgoptions
);
1364 optlen
+= strlen(share_name
) + 4;
1366 printf("No server share name specified\n");
1367 printf("\nMounting the DFS root for server not implemented yet\n");
1371 optlen
+= strlen(user_name
) + 6;
1372 optlen
+= MAX_ADDRESS_LEN
+ 4;
1374 optlen
+= strlen(mountpassword
) + 6;
1377 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1378 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 */);
1380 if(options
== NULL
) {
1381 printf("Could not allocate memory for mount options\n");
1385 strlcpy(options
, "unc=", options_size
);
1386 strlcat(options
,share_name
,options_size
);
1387 /* scan backwards and reverse direction of slash */
1388 temp
= strrchr(options
, '/');
1389 if(temp
> options
+ 6)
1392 /* check for syntax like user=domain\user */
1394 domain_name
= check_for_domain(&user_name
);
1395 strlcat(options
,",user=",options_size
);
1396 strlcat(options
,user_name
,options_size
);
1400 /* extra length accounted for in option string above */
1401 strlcat(options
,",domain=",options_size
);
1402 strlcat(options
,domain_name
,options_size
);
1406 strlcat(options
,",ver=",options_size
);
1407 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1410 strlcat(options
,",",options_size
);
1411 strlcat(options
,orgoptions
,options_size
);
1414 strlcat(options
,",prefixpath=",options_size
);
1415 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1418 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1419 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1421 if (!got_ip
&& addr
) {
1422 strlcat(options
, ",ip=", options_size
);
1423 current_len
= strnlen(options
, options_size
);
1424 optionstail
= options
+ current_len
;
1425 switch (addr
->ai_addr
->sa_family
) {
1427 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1428 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1429 options_size
- current_len
);
1432 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1433 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1434 options_size
- current_len
);
1438 /* if the address looks bogus, try the next one */
1440 addr
= addr
->ai_next
;
1449 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1451 if (mountpassword
) {
1453 * Commas have to be doubled, or else they will
1454 * look like the parameter separator
1457 check_for_comma(&mountpassword
);
1458 strlcat(options
,",pass=",options_size
);
1459 strlcat(options
,mountpassword
,options_size
);
1461 fprintf(stderr
, ",pass=********");
1465 fprintf(stderr
, "\n");
1467 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1472 addr
= addr
->ai_next
;
1478 printf("mount error: cifs filesystem not supported by the system\n");
1483 if (uppercase_string(dev_name
) &&
1484 uppercase_string(share_name
) &&
1485 uppercase_string(prefixpath
)) {
1486 printf("retrying with upper case share name\n");
1491 printf("mount error(%d): %s\n", errno
, strerror(errno
));
1492 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1500 atexit(unlock_mtab
);
1503 printf("cannot lock mtab");
1506 pmntfile
= setmntent(MOUNTED
, "a+");
1508 printf("could not update mount table\n");
1513 mountent
.mnt_fsname
= dev_name
;
1514 mountent
.mnt_dir
= mountpoint
;
1515 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1516 mountent
.mnt_opts
= (char *)malloc(220);
1517 if(mountent
.mnt_opts
) {
1518 char * mount_user
= getusername();
1519 memset(mountent
.mnt_opts
,0,200);
1520 if(flags
& MS_RDONLY
)
1521 strlcat(mountent
.mnt_opts
,"ro",220);
1523 strlcat(mountent
.mnt_opts
,"rw",220);
1524 if(flags
& MS_MANDLOCK
)
1525 strlcat(mountent
.mnt_opts
,",mand",220);
1526 if(flags
& MS_NOEXEC
)
1527 strlcat(mountent
.mnt_opts
,",noexec",220);
1528 if(flags
& MS_NOSUID
)
1529 strlcat(mountent
.mnt_opts
,",nosuid",220);
1530 if(flags
& MS_NODEV
)
1531 strlcat(mountent
.mnt_opts
,",nodev",220);
1532 if(flags
& MS_SYNCHRONOUS
)
1533 strlcat(mountent
.mnt_opts
,",sync",220);
1536 strlcat(mountent
.mnt_opts
,
1538 strlcat(mountent
.mnt_opts
,
1543 mountent
.mnt_freq
= 0;
1544 mountent
.mnt_passno
= 0;
1545 rc
= addmntent(pmntfile
,&mountent
);
1546 endmntent(pmntfile
);
1548 SAFE_FREE(mountent
.mnt_opts
);
1553 int len
= strlen(mountpassword
);
1554 memset(mountpassword
,0,len
);
1555 SAFE_FREE(mountpassword
);
1559 freeaddrinfo(addrhead
);
1561 SAFE_FREE(orgoptions
);
1562 SAFE_FREE(resolved_path
);
1563 SAFE_FREE(share_name
);