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 int main(int argc
, char ** argv
)
1042 int flags
= MS_MANDLOCK
; /* no need to set legacy MS_MGC_VAL */
1043 char * orgoptions
= NULL
;
1044 char * share_name
= NULL
;
1045 const char * ipaddr
= NULL
;
1047 char * mountpoint
= NULL
;
1048 char * options
= NULL
;
1050 char * resolved_path
= NULL
;
1061 size_t options_size
= 0;
1063 int retry
= 0; /* set when we have to retry mount with uppercase */
1064 struct addrinfo
*addrhead
= NULL
, *addr
;
1065 struct stat statbuf
;
1066 struct utsname sysinfo
;
1067 struct mntent mountent
;
1068 struct sockaddr_in
*addr4
;
1069 struct sockaddr_in6
*addr6
;
1072 /* setlocale(LC_ALL, "");
1073 bindtextdomain(PACKAGE, LOCALEDIR);
1074 textdomain(PACKAGE); */
1077 thisprogram
= argv
[0];
1083 if(thisprogram
== NULL
)
1084 thisprogram
= "mount.cifs";
1087 /* BB add workstation name and domain and pass down */
1089 /* #ifdef _GNU_SOURCE
1090 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1094 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1095 if (share_name
== NULL
) {
1096 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1099 mountpoint
= argv
[2];
1101 if ((strcmp (argv
[1], "--version") == 0) ||
1102 ((strcmp (argv
[1], "-V") == 0))) {
1103 printf ("mount.cifs version: %s.%s%s\n",
1104 MOUNT_CIFS_VERSION_MAJOR
,
1105 MOUNT_CIFS_VERSION_MINOR
,
1106 MOUNT_CIFS_VENDOR_SUFFIX
);
1113 /* add sharename in opts string as unc= parm */
1115 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1116 longopts
, NULL
)) != -1) {
1118 /* No code to do the following options yet */
1120 list_with_volumelabel = 1;
1123 volumelabel = optarg;
1130 case 'h': /* help */
1131 mount_cifs_usage ();
1141 "option 'b' (MS_BIND) not supported\n");
1149 "option 'm' (MS_MOVE) not supported\n");
1153 orgoptions
= strdup(optarg
);
1155 case 'r': /* mount readonly */
1165 printf ("mount.cifs version: %s.%s%s\n",
1166 MOUNT_CIFS_VERSION_MAJOR
,
1167 MOUNT_CIFS_VERSION_MINOR
,
1168 MOUNT_CIFS_VENDOR_SUFFIX
);
1171 flags
&= ~MS_RDONLY
;
1174 rsize
= atoi(optarg
) ;
1177 wsize
= atoi(optarg
);
1180 if (isdigit(*optarg
)) {
1183 uid
= strtoul(optarg
, &ep
, 10);
1185 printf("bad uid value \"%s\"\n", optarg
);
1191 if (!(pw
= getpwnam(optarg
))) {
1192 printf("bad user name \"%s\"\n", optarg
);
1200 if (isdigit(*optarg
)) {
1203 gid
= strtoul(optarg
, &ep
, 10);
1205 printf("bad gid value \"%s\"\n", optarg
);
1211 if (!(gr
= getgrnam(optarg
))) {
1212 printf("bad user name \"%s\"\n", optarg
);
1224 domain_name
= optarg
; /* BB fix this - currently ignored */
1228 if(mountpassword
== NULL
)
1229 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1232 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1236 get_password_from_file(0 /* stdin */,NULL
);
1244 printf("unknown mount option %c\n",c
);
1250 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1255 if (getenv("PASSWD")) {
1256 if(mountpassword
== NULL
)
1257 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1259 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1262 } else if (getenv("PASSWD_FD")) {
1263 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1264 } else if (getenv("PASSWD_FILE")) {
1265 get_password_from_file(0, getenv("PASSWD_FILE"));
1268 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1272 addrhead
= addr
= parse_server(&share_name
);
1273 if((addrhead
== NULL
) && (got_ip
== 0)) {
1274 printf("No ip address specified and hostname not found\n");
1279 /* BB save off path and pop after mount returns? */
1280 resolved_path
= (char *)malloc(PATH_MAX
+1);
1282 /* Note that if we can not canonicalize the name, we get
1283 another chance to see if it is valid when we chdir to it */
1284 if (realpath(mountpoint
, resolved_path
)) {
1285 mountpoint
= resolved_path
;
1288 if(chdir(mountpoint
)) {
1289 printf("mount error: can not change directory into mount target %s\n",mountpoint
);
1294 if(stat (".", &statbuf
)) {
1295 printf("mount error: mount point %s does not exist\n",mountpoint
);
1300 if (S_ISDIR(statbuf
.st_mode
) == 0) {
1301 printf("mount error: mount point %s is not a directory\n",mountpoint
);
1306 if((getuid() != 0) && (geteuid() == 0)) {
1307 if((statbuf
.st_uid
== getuid()) && (S_IRWXU
== (statbuf
.st_mode
& S_IRWXU
))) {
1308 #ifndef CIFS_ALLOW_USR_SUID
1309 /* Do not allow user mounts to control suid flag
1310 for mount unless explicitly built that way */
1311 flags
|= MS_NOSUID
| MS_NODEV
;
1314 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1320 /* Note that the password will not be retrieved from the
1321 USER env variable (ie user%password form) as there is
1322 already a PASSWD environment varaible */
1324 user_name
= strdup(getenv("USER"));
1325 if (user_name
== NULL
)
1326 user_name
= getusername();
1330 if(got_password
== 0) {
1331 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1332 no good replacement yet. */
1333 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1334 if (!tmp_pass
|| !mountpassword
) {
1335 printf("Password not entered, exiting\n");
1338 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1341 /* FIXME launch daemon (handles dfs name resolution and credential change)
1342 remember to clear parms and overwrite password field before launching */
1344 optlen
= strlen(orgoptions
);
1349 optlen
+= strlen(share_name
) + 4;
1351 printf("No server share name specified\n");
1352 printf("\nMounting the DFS root for server not implemented yet\n");
1356 optlen
+= strlen(user_name
) + 6;
1357 optlen
+= MAX_ADDRESS_LEN
+ 4;
1359 optlen
+= strlen(mountpassword
) + 6;
1362 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1363 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 */);
1365 if(options
== NULL
) {
1366 printf("Could not allocate memory for mount options\n");
1370 strlcpy(options
, "unc=", options_size
);
1371 strlcat(options
,share_name
,options_size
);
1372 /* scan backwards and reverse direction of slash */
1373 temp
= strrchr(options
, '/');
1374 if(temp
> options
+ 6)
1377 /* check for syntax like user=domain\user */
1379 domain_name
= check_for_domain(&user_name
);
1380 strlcat(options
,",user=",options_size
);
1381 strlcat(options
,user_name
,options_size
);
1385 /* extra length accounted for in option string above */
1386 strlcat(options
,",domain=",options_size
);
1387 strlcat(options
,domain_name
,options_size
);
1391 strlcat(options
,",ver=",options_size
);
1392 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1395 strlcat(options
,",",options_size
);
1396 strlcat(options
,orgoptions
,options_size
);
1399 strlcat(options
,",prefixpath=",options_size
);
1400 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1403 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1404 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1406 if (!got_ip
&& addr
) {
1407 strlcat(options
, ",ip=", options_size
);
1408 current_len
= strnlen(options
, options_size
);
1409 optionstail
= options
+ current_len
;
1410 switch (addr
->ai_addr
->sa_family
) {
1412 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1413 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1414 options_size
- current_len
);
1417 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1418 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1419 options_size
- current_len
);
1423 /* if the address looks bogus, try the next one */
1425 addr
= addr
->ai_next
;
1434 fprintf(stderr
, "\nmount.cifs kernel mount options: %s", options
);
1436 if (mountpassword
) {
1438 * Commas have to be doubled, or else they will
1439 * look like the parameter separator
1442 check_for_comma(&mountpassword
);
1443 strlcat(options
,",pass=",options_size
);
1444 strlcat(options
,mountpassword
,options_size
);
1446 fprintf(stderr
, ",pass=********");
1450 fprintf(stderr
, "\n");
1452 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1457 addr
= addr
->ai_next
;
1463 printf("mount error: cifs filesystem not supported by the system\n");
1468 if (uppercase_string(dev_name
) &&
1469 uppercase_string(share_name
) &&
1470 uppercase_string(prefixpath
)) {
1471 printf("retrying with upper case share name\n");
1476 printf("mount error(%d): %s\n", errno
, strerror(errno
));
1477 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1485 atexit(unlock_mtab
);
1488 printf("cannot lock mtab");
1491 pmntfile
= setmntent(MOUNTED
, "a+");
1493 printf("could not update mount table\n");
1498 mountent
.mnt_fsname
= dev_name
;
1499 mountent
.mnt_dir
= mountpoint
;
1500 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1501 mountent
.mnt_opts
= (char *)malloc(220);
1502 if(mountent
.mnt_opts
) {
1503 char * mount_user
= getusername();
1504 memset(mountent
.mnt_opts
,0,200);
1505 if(flags
& MS_RDONLY
)
1506 strlcat(mountent
.mnt_opts
,"ro",220);
1508 strlcat(mountent
.mnt_opts
,"rw",220);
1509 if(flags
& MS_MANDLOCK
)
1510 strlcat(mountent
.mnt_opts
,",mand",220);
1511 if(flags
& MS_NOEXEC
)
1512 strlcat(mountent
.mnt_opts
,",noexec",220);
1513 if(flags
& MS_NOSUID
)
1514 strlcat(mountent
.mnt_opts
,",nosuid",220);
1515 if(flags
& MS_NODEV
)
1516 strlcat(mountent
.mnt_opts
,",nodev",220);
1517 if(flags
& MS_SYNCHRONOUS
)
1518 strlcat(mountent
.mnt_opts
,",sync",220);
1521 strlcat(mountent
.mnt_opts
,
1523 strlcat(mountent
.mnt_opts
,
1528 mountent
.mnt_freq
= 0;
1529 mountent
.mnt_passno
= 0;
1530 rc
= addmntent(pmntfile
,&mountent
);
1531 endmntent(pmntfile
);
1533 SAFE_FREE(mountent
.mnt_opts
);
1538 int len
= strlen(mountpassword
);
1539 memset(mountpassword
,0,len
);
1540 SAFE_FREE(mountpassword
);
1544 freeaddrinfo(addrhead
);
1546 SAFE_FREE(orgoptions
);
1547 SAFE_FREE(resolved_path
);
1548 SAFE_FREE(share_name
);