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>
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "13"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
62 #include "include/config.h"
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
79 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
85 #define MOUNT_PASSWD_SIZE 128
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * By default, mount.cifs follows the conventions set forth by /bin/mount
93 * for user mounts. That is, it requires that the mount be listed in
94 * /etc/fstab with the "user" option when run as an unprivileged user and
95 * mount.cifs is setuid root.
97 * Older versions of mount.cifs however were "looser" in this regard. When
98 * made setuid root, a user could run mount.cifs directly and mount any share
99 * on a directory owned by that user.
101 * The legacy behavior is now disabled by default. To reenable it, set the
102 * following #define to true.
104 #define CIFS_LEGACY_SETUID_CHECK 0
107 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108 * flags by default. These defaults can be changed here.
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
112 const char *thisprogram
;
115 static int got_password
= 0;
116 static int got_user
= 0;
117 static int got_domain
= 0;
118 static int got_ip
= 0;
119 static int got_unc
= 0;
120 static int got_uid
= 0;
121 static int got_gid
= 0;
122 static char * user_name
= NULL
;
123 static char * mountpassword
= NULL
;
124 char * domain_name
= NULL
;
125 char * prefixpath
= NULL
;
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128 * don't link to libreplace so need them here. */
130 /* like strncpy but does not 0 fill the buffer and always null
131 * terminates. bufsize is the size of the destination buffer */
134 static size_t strlcpy(char *d
, const char *s
, size_t bufsize
)
136 size_t len
= strlen(s
);
138 if (bufsize
<= 0) return 0;
139 if (len
>= bufsize
) len
= bufsize
-1;
146 /* like strncat but does not 0 fill the buffer and always null
147 * terminates. bufsize is the length of the buffer, which should
148 * be one more than the maximum resulting string length */
151 static size_t strlcat(char *d
, const char *s
, size_t bufsize
)
153 size_t len1
= strlen(d
);
154 size_t len2
= strlen(s
);
155 size_t ret
= len1
+ len2
;
157 if (len1
+len2
>= bufsize
) {
158 if (bufsize
< (len1
+1)) {
161 len2
= bufsize
- (len1
+1);
164 memcpy(d
+len1
, s
, len2
);
172 * If an unprivileged user is doing the mounting then we need to ensure
173 * that the entry is in /etc/fstab.
176 check_mountpoint(const char *progname
, char *mountpoint
)
181 /* does mountpoint exist and is it a directory? */
182 err
= stat(mountpoint
, &statbuf
);
184 fprintf(stderr
, "%s: failed to stat %s: %s\n", progname
,
185 mountpoint
, strerror(errno
));
189 if (!S_ISDIR(statbuf
.st_mode
)) {
190 fprintf(stderr
, "%s: %s is not a directory!", progname
,
195 #if CIFS_LEGACY_SETUID_CHECK
196 /* do extra checks on mountpoint for legacy setuid behavior */
197 if (!getuid() || geteuid())
200 if (statbuf
.st_uid
!= getuid()) {
201 fprintf(stderr
, "%s: %s is not owned by user\n", progname
,
206 if ((statbuf
.st_mode
& S_IRWXU
) != S_IRWXU
) {
207 fprintf(stderr
, "%s: invalid permissions on %s\n", progname
,
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
216 #if CIFS_LEGACY_SETUID_CHECK
218 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
223 #else /* CIFS_LEGACY_SETUID_CHECK */
225 check_fstab(const char *progname
, char *mountpoint
, char *devname
,
231 /* make sure this mount is listed in /etc/fstab */
232 fstab
= setmntent(_PATH_FSTAB
, "r");
234 fprintf(stderr
, "Couldn't open %s for reading!\n",
239 while((mnt
= getmntent(fstab
))) {
240 if (!strcmp(mountpoint
, mnt
->mnt_dir
))
245 if (mnt
== NULL
|| strcmp(mnt
->mnt_fsname
, devname
)) {
246 fprintf(stderr
, "%s: permission denied: no match for "
247 "%s found in %s\n", progname
, mountpoint
,
253 * 'mount' munges the options from fstab before passing them
254 * to us. It is non-trivial to test that we have the correct
255 * set of options. We don't want to trust what the user
256 * gave us, so just take whatever is in /etc/fstab.
259 *options
= strdup(mnt
->mnt_opts
);
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
267 open nofollow - avoid symlink exposure?
268 get owner of dir see if matches self or if root
269 call system(umount argv) etc.
273 static char * check_for_domain(char **);
276 static void mount_cifs_usage(void)
278 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram
);
279 printf("\nMount the remote target, specified as a UNC name,");
280 printf(" to a local directory.\n\nOptions:\n");
281 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 printf("\nLess commonly used options:");
283 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
289 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 printf("\n\nRarely used options:");
291 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 printf("\n\tin6_addr");
295 printf("\n\nOptions are described in more detail in the manual page");
296 printf("\n\tman 8 mount.cifs\n");
297 printf("\nTo display the version number of the mount helper:");
298 printf("\n\t%s -V\n",thisprogram
);
300 SAFE_FREE(mountpassword
);
303 /* caller frees username if necessary */
304 static char * getusername(void) {
305 char *username
= NULL
;
306 struct passwd
*password
= getpwuid(getuid());
309 username
= password
->pw_name
;
314 static int open_cred_file(char * file_name
)
320 fs
= fopen(file_name
,"r");
323 line_buf
= (char *)malloc(4096);
324 if(line_buf
== NULL
) {
329 while(fgets(line_buf
,4096,fs
)) {
330 /* parse line from credential file */
332 /* eat leading white space */
333 for(i
=0;i
<4086;i
++) {
334 if((line_buf
[i
] != ' ') && (line_buf
[i
] != '\t'))
336 /* if whitespace - skip past it */
338 if (strncasecmp("username",line_buf
+i
,8) == 0) {
339 temp_val
= strchr(line_buf
+ i
,'=');
341 /* go past equals sign */
343 for(length
= 0;length
<4087;length
++) {
344 if ((temp_val
[length
] == '\n')
345 || (temp_val
[length
] == '\0')) {
346 temp_val
[length
] = '\0';
351 printf("mount.cifs failed due to malformed username in credentials file");
352 memset(line_buf
,0,4096);
356 user_name
= (char *)calloc(1 + length
,1);
357 /* BB adding free of user_name string before exit,
358 not really necessary but would be cleaner */
359 strlcpy(user_name
,temp_val
, length
+1);
362 } else if (strncasecmp("password",line_buf
+i
,8) == 0) {
363 temp_val
= strchr(line_buf
+i
,'=');
365 /* go past equals sign */
367 for(length
= 0;length
<MOUNT_PASSWD_SIZE
+1;length
++) {
368 if ((temp_val
[length
] == '\n')
369 || (temp_val
[length
] == '\0')) {
370 temp_val
[length
] = '\0';
374 if(length
> MOUNT_PASSWD_SIZE
) {
375 printf("mount.cifs failed: password in credentials file too long\n");
376 memset(line_buf
,0, 4096);
379 if(mountpassword
== NULL
) {
380 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
382 memset(mountpassword
,0,MOUNT_PASSWD_SIZE
);
384 strlcpy(mountpassword
,temp_val
,MOUNT_PASSWD_SIZE
+1);
389 } else if (strncasecmp("domain",line_buf
+i
,6) == 0) {
390 temp_val
= strchr(line_buf
+i
,'=');
392 /* go past equals sign */
395 printf("\nDomain %s\n",temp_val
);
396 for(length
= 0;length
<DOMAIN_SIZE
+1;length
++) {
397 if ((temp_val
[length
] == '\n')
398 || (temp_val
[length
] == '\0')) {
399 temp_val
[length
] = '\0';
403 if(length
> DOMAIN_SIZE
) {
404 printf("mount.cifs failed: domain in credentials file too long\n");
407 if(domain_name
== NULL
) {
408 domain_name
= (char *)calloc(DOMAIN_SIZE
+1,1);
410 memset(domain_name
,0,DOMAIN_SIZE
);
412 strlcpy(domain_name
,temp_val
,DOMAIN_SIZE
+1);
425 static int get_password_from_file(int file_descript
, char * filename
)
431 if(mountpassword
== NULL
)
432 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
434 memset(mountpassword
, 0, MOUNT_PASSWD_SIZE
);
436 if (mountpassword
== NULL
) {
437 printf("malloc failed\n");
441 if(filename
!= NULL
) {
442 file_descript
= open(filename
, O_RDONLY
);
443 if(file_descript
< 0) {
444 printf("mount.cifs failed. %s attempting to open password file %s\n",
445 strerror(errno
),filename
);
449 /* else file already open and fd provided */
451 for(i
=0;i
<MOUNT_PASSWD_SIZE
;i
++) {
452 rc
= read(file_descript
,&c
,1);
454 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno
));
456 close(file_descript
);
459 if(mountpassword
[0] == 0) {
461 printf("\nWarning: null password used since cifs password file empty");
464 } else /* read valid character */ {
465 if((c
== 0) || (c
== '\n')) {
466 mountpassword
[i
] = '\0';
469 mountpassword
[i
] = c
;
472 if((i
== MOUNT_PASSWD_SIZE
) && (verboseflag
)) {
473 printf("\nWarning: password longer than %d characters specified in cifs password file",
477 if(filename
!= NULL
) {
478 close(file_descript
);
484 static int parse_options(char ** optionsp
, unsigned long * filesys_flags
)
487 char * percent_char
= NULL
;
489 char * next_keyword
= NULL
;
497 if (!optionsp
|| !*optionsp
)
502 printf("parsing options: %s\n", data
);
504 /* BB fixme check for separator override BB */
508 snprintf(user
,sizeof(user
),"%u",getuid());
510 snprintf(group
,sizeof(group
),"%u",getgid());
513 /* while ((data = strsep(&options, ",")) != NULL) { */
514 while(data
!= NULL
) {
515 /* check if ends with trailing comma */
519 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
520 /* data = next keyword */
521 /* value = next value ie stuff after equal sign */
523 next_keyword
= strchr(data
,','); /* BB handle sep= */
525 /* temporarily null terminate end of keyword=value pair */
529 /* temporarily null terminate keyword to make keyword and value distinct */
530 if ((value
= strchr(data
, '=')) != NULL
) {
535 if (strncmp(data
, "users",5) == 0) {
536 if(!value
|| !*value
) {
537 *filesys_flags
|= MS_USERS
;
540 } else if (strncmp(data
, "user_xattr",10) == 0) {
541 /* do nothing - need to skip so not parsed as user name */
542 } else if (strncmp(data
, "user", 4) == 0) {
544 if (!value
|| !*value
) {
545 if(data
[4] == '\0') {
546 *filesys_flags
|= MS_USER
;
549 printf("username specified with no parameter\n");
551 return 1; /* needs_arg; */
554 if (strnlen(value
, 260) < 260) {
556 percent_char
= strchr(value
,'%');
559 if(mountpassword
== NULL
)
560 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
563 printf("\nmount.cifs warning - password specified twice\n");
566 strlcpy(mountpassword
, percent_char
,MOUNT_PASSWD_SIZE
+1);
567 /* remove password from username */
568 while(*percent_char
!= 0) {
574 /* this is only case in which the user
575 name buf is not malloc - so we have to
576 check for domain name embedded within
577 the user name here since the later
578 call to check_for_domain will not be
580 domain_name
= check_for_domain(&value
);
582 printf("username too long\n");
587 } else if (strncmp(data
, "pass", 4) == 0) {
588 if (!value
|| !*value
) {
590 printf("\npassword specified twice, ignoring second\n");
593 } else if (strnlen(value
, MOUNT_PASSWD_SIZE
) < MOUNT_PASSWD_SIZE
) {
595 printf("\nmount.cifs warning - password specified twice\n");
598 printf("password too long\n");
602 } else if (strncmp(data
, "sec", 3) == 0) {
604 if (!strncmp(value
, "none", 4) ||
605 !strncmp(value
, "krb5", 4))
608 } else if (strncmp(data
, "ip", 2) == 0) {
609 if (!value
|| !*value
) {
610 printf("target ip address argument missing");
611 } else if (strnlen(value
, MAX_ADDRESS_LEN
) <= MAX_ADDRESS_LEN
) {
613 printf("ip address %s override specified\n",value
);
616 printf("ip address too long\n");
620 } else if ((strncmp(data
, "unc", 3) == 0)
621 || (strncmp(data
, "target", 6) == 0)
622 || (strncmp(data
, "path", 4) == 0)) {
623 if (!value
|| !*value
) {
624 printf("invalid path to network resource\n");
626 return 1; /* needs_arg; */
627 } else if(strnlen(value
,5) < 5) {
628 printf("UNC name too short");
631 if (strnlen(value
, 300) < 300) {
633 if (strncmp(value
, "//", 2) == 0) {
635 printf("unc name specified twice, ignoring second\n");
638 } else if (strncmp(value
, "\\\\", 2) != 0) {
639 printf("UNC Path does not begin with // or \\\\ \n");
644 printf("unc name specified twice, ignoring second\n");
649 printf("CIFS: UNC name too long\n");
653 } else if ((strncmp(data
, "dom" /* domain */, 3) == 0)
654 || (strncmp(data
, "workg", 5) == 0)) {
655 /* note this allows for synonyms of "domain"
656 such as "DOM" and "dom" and "workgroup"
657 and "WORKGRP" etc. */
658 if (!value
|| !*value
) {
659 printf("CIFS: invalid domain name\n");
661 return 1; /* needs_arg; */
663 if (strnlen(value
, DOMAIN_SIZE
+1) < DOMAIN_SIZE
+1) {
666 printf("domain name too long\n");
670 } else if (strncmp(data
, "cred", 4) == 0) {
671 if (value
&& *value
) {
672 rc
= open_cred_file(value
);
674 printf("error %d (%s) opening credential file %s\n",
675 rc
, strerror(rc
), value
);
680 printf("invalid credential file name specified\n");
684 } else if (strncmp(data
, "uid", 3) == 0) {
685 if (value
&& *value
) {
687 if (!isdigit(*value
)) {
690 if (!(pw
= getpwnam(value
))) {
691 printf("bad user name \"%s\"\n", value
);
694 snprintf(user
, sizeof(user
), "%u", pw
->pw_uid
);
696 strlcpy(user
,value
,sizeof(user
));
700 } else if (strncmp(data
, "gid", 3) == 0) {
701 if (value
&& *value
) {
703 if (!isdigit(*value
)) {
706 if (!(gr
= getgrnam(value
))) {
707 printf("bad group name \"%s\"\n", value
);
710 snprintf(group
, sizeof(group
), "%u", gr
->gr_gid
);
712 strlcpy(group
,value
,sizeof(group
));
716 /* fmask and dmask synonyms for people used to smbfs syntax */
717 } else if (strcmp(data
, "file_mode") == 0 || strcmp(data
, "fmask")==0) {
718 if (!value
|| !*value
) {
719 printf ("Option '%s' requires a numerical argument\n", data
);
724 if (value
[0] != '0') {
725 printf ("WARNING: '%s' not expressed in octal.\n", data
);
728 if (strcmp (data
, "fmask") == 0) {
729 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
730 data
= "file_mode"; /* BB fix this */
732 } else if (strcmp(data
, "dir_mode") == 0 || strcmp(data
, "dmask")==0) {
733 if (!value
|| !*value
) {
734 printf ("Option '%s' requires a numerical argument\n", data
);
739 if (value
[0] != '0') {
740 printf ("WARNING: '%s' not expressed in octal.\n", data
);
743 if (strcmp (data
, "dmask") == 0) {
744 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
747 /* the following eight mount options should be
748 stripped out from what is passed into the kernel
749 since these eight options are best passed as the
750 mount flags rather than redundantly to the kernel
751 and could generate spurious warnings depending on the
752 level of the corresponding cifs vfs kernel code */
753 } else if (strncmp(data
, "nosuid", 6) == 0) {
754 *filesys_flags
|= MS_NOSUID
;
755 } else if (strncmp(data
, "suid", 4) == 0) {
756 *filesys_flags
&= ~MS_NOSUID
;
757 } else if (strncmp(data
, "nodev", 5) == 0) {
758 *filesys_flags
|= MS_NODEV
;
759 } else if ((strncmp(data
, "nobrl", 5) == 0) ||
760 (strncmp(data
, "nolock", 6) == 0)) {
761 *filesys_flags
&= ~MS_MANDLOCK
;
762 } else if (strncmp(data
, "dev", 3) == 0) {
763 *filesys_flags
&= ~MS_NODEV
;
764 } else if (strncmp(data
, "noexec", 6) == 0) {
765 *filesys_flags
|= MS_NOEXEC
;
766 } else if (strncmp(data
, "exec", 4) == 0) {
767 *filesys_flags
&= ~MS_NOEXEC
;
768 } else if (strncmp(data
, "guest", 5) == 0) {
769 user_name
= (char *)calloc(1, 1);
772 } else if (strncmp(data
, "ro", 2) == 0) {
773 *filesys_flags
|= MS_RDONLY
;
775 } else if (strncmp(data
, "rw", 2) == 0) {
776 *filesys_flags
&= ~MS_RDONLY
;
778 } else if (strncmp(data
, "remount", 7) == 0) {
779 *filesys_flags
|= MS_REMOUNT
;
780 } /* else if (strnicmp(data, "port", 4) == 0) {
781 if (value && *value) {
783 simple_strtoul(value, &value, 0);
785 } else if (strnicmp(data, "rsize", 5) == 0) {
786 if (value && *value) {
788 simple_strtoul(value, &value, 0);
790 } else if (strnicmp(data, "wsize", 5) == 0) {
791 if (value && *value) {
793 simple_strtoul(value, &value, 0);
795 } else if (strnicmp(data, "version", 3) == 0) {
797 printf("CIFS: Unknown mount option %s\n",data);
798 } */ /* nothing to do on those four mount options above.
799 Just pass to kernel and ignore them here */
801 /* Copy (possibly modified) option to out */
802 word_len
= strlen(data
);
804 word_len
+= 1 + strlen(value
);
806 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
813 strlcat(out
, ",", out_len
+ word_len
+ 2);
818 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
820 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
821 out_len
= strlen(out
);
827 /* special-case the uid and gid */
829 word_len
= strlen(user
);
831 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
838 strlcat(out
, ",", out_len
+ word_len
+ 6);
841 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
842 out_len
= strlen(out
);
845 word_len
= strlen(group
);
847 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
854 strlcat(out
, ",", out_len
+ word_len
+ 6);
857 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
858 out_len
= strlen(out
);
861 SAFE_FREE(*optionsp
);
866 /* replace all (one or more) commas with double commas */
867 static void check_for_comma(char ** ppasswrd
)
872 int number_of_commas
= 0;
887 if(number_of_commas
== 0)
889 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
890 /* would otherwise overflow the mount options buffer */
891 printf("\nInvalid password. Password contains too many commas.\n");
895 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
896 if(new_pass_buf
== NULL
)
899 for(i
=0,j
=0;i
<len
;i
++,j
++) {
900 new_pass_buf
[j
] = pass
[i
];
903 new_pass_buf
[j
] = pass
[i
];
906 new_pass_buf
[len
+number_of_commas
] = 0;
908 SAFE_FREE(*ppasswrd
);
909 *ppasswrd
= new_pass_buf
;
914 /* Usernames can not have backslash in them and we use
915 [BB check if usernames can have forward slash in them BB]
916 backslash as domain\user separator character
918 static char * check_for_domain(char **ppuser
)
920 char * original_string
;
930 original_string
= *ppuser
;
932 if (original_string
== NULL
)
935 original_len
= strlen(original_string
);
937 usernm
= strchr(*ppuser
,'/');
938 if (usernm
== NULL
) {
939 usernm
= strchr(*ppuser
,'\\');
945 printf("Domain name specified twice. Username probably malformed\n");
951 if (domainnm
[0] != 0) {
954 printf("null domain\n");
956 len
= strlen(domainnm
);
957 /* reset domainm to new buffer, and copy
958 domain name into it */
959 domainnm
= (char *)malloc(len
+1);
963 strlcpy(domainnm
,*ppuser
,len
+1);
965 /* move_string(*ppuser, usernm+1) */
966 len
= strlen(usernm
+1);
968 if(len
>= original_len
) {
969 /* should not happen */
973 for(i
=0;i
<original_len
;i
++) {
975 original_string
[i
] = usernm
[i
+1];
976 else /* stuff with commas to remove last parm */
977 original_string
[i
] = ',';
980 /* BB add check for more than one slash?
988 /* replace all occurances of "from" in a string with "to" */
989 static void replace_char(char *string
, char from
, char to
, int maxlen
)
991 char *lastchar
= string
+ maxlen
;
993 string
= strchr(string
, from
);
996 if (string
>= lastchar
)
1002 /* Note that caller frees the returned buffer if necessary */
1003 static struct addrinfo
*
1004 parse_server(char ** punc_name
)
1006 char * unc_name
= *punc_name
;
1007 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1009 struct addrinfo
*addrlist
;
1012 if(length
> (MAX_UNC_LEN
- 1)) {
1013 printf("mount error: UNC name too long");
1016 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1017 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1018 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1023 /* BB add code to find DFS root here */
1024 printf("\nMounting the DFS root for domain not implemented yet\n");
1027 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1028 /* check for nfs syntax ie server:share */
1029 share
= strchr(unc_name
,':');
1031 *punc_name
= (char *)malloc(length
+3);
1032 if(*punc_name
== NULL
) {
1033 /* put the original string back if
1035 *punc_name
= unc_name
;
1039 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1040 SAFE_FREE(unc_name
);
1041 unc_name
= *punc_name
;
1042 unc_name
[length
+2] = 0;
1043 goto continue_unc_parsing
;
1045 printf("mount error: improperly formatted UNC name.");
1046 printf(" %s does not begin with \\\\ or //\n",unc_name
);
1050 continue_unc_parsing
:
1055 /* allow for either delimiter between host and sharename */
1056 if ((share
= strpbrk(unc_name
, "/\\"))) {
1057 *share
= 0; /* temporarily terminate the string */
1060 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1062 printf("mount error: could not resolve address for %s: %s\n",
1063 unc_name
, gai_strerror(rc
));
1067 *(share
- 1) = '/'; /* put delimiter back */
1069 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1070 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1071 *prefixpath
= 0; /* permanently terminate the string */
1072 if (!strlen(++prefixpath
))
1073 prefixpath
= NULL
; /* this needs to be done explicitly */
1077 printf("ip address specified explicitly\n");
1080 /* BB should we pass an alternate version of the share name as Unicode */
1084 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1085 printf("Mounting the DFS root for a particular server not implemented yet\n");
1092 static struct option longopts
[] = {
1093 { "all", 0, NULL
, 'a' },
1094 { "help",0, NULL
, 'h' },
1095 { "move",0, NULL
, 'm' },
1096 { "bind",0, NULL
, 'b' },
1097 { "read-only", 0, NULL
, 'r' },
1098 { "ro", 0, NULL
, 'r' },
1099 { "verbose", 0, NULL
, 'v' },
1100 { "version", 0, NULL
, 'V' },
1101 { "read-write", 0, NULL
, 'w' },
1102 { "rw", 0, NULL
, 'w' },
1103 { "options", 1, NULL
, 'o' },
1104 { "type", 1, NULL
, 't' },
1105 { "rsize",1, NULL
, 'R' },
1106 { "wsize",1, NULL
, 'W' },
1107 { "uid", 1, NULL
, '1'},
1108 { "gid", 1, NULL
, '2'},
1109 { "user",1,NULL
,'u'},
1110 { "username",1,NULL
,'u'},
1111 { "dom",1,NULL
,'d'},
1112 { "domain",1,NULL
,'d'},
1113 { "password",1,NULL
,'p'},
1114 { "pass",1,NULL
,'p'},
1115 { "credentials",1,NULL
,'c'},
1116 { "port",1,NULL
,'P'},
1117 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1118 { NULL
, 0, NULL
, 0 }
1121 /* convert a string to uppercase. return false if the string
1122 * wasn't ASCII. Return success on a NULL ptr */
1124 uppercase_string(char *string
)
1130 /* check for unicode */
1131 if ((unsigned char) string
[0] & 0x80)
1133 *string
= toupper((unsigned char) *string
);
1140 static void print_cifs_mount_version(void)
1142 printf("mount.cifs version: %s.%s%s\n",
1143 MOUNT_CIFS_VERSION_MAJOR
,
1144 MOUNT_CIFS_VERSION_MINOR
,
1145 MOUNT_CIFS_VENDOR_SUFFIX
);
1148 int main(int argc
, char ** argv
)
1151 unsigned long flags
= MS_MANDLOCK
;
1152 char * orgoptions
= NULL
;
1153 char * share_name
= NULL
;
1154 const char * ipaddr
= NULL
;
1156 char * mountpoint
= NULL
;
1157 char * options
= NULL
;
1159 char * resolved_path
= NULL
;
1170 size_t options_size
= 0;
1172 int retry
= 0; /* set when we have to retry mount with uppercase */
1173 struct addrinfo
*addrhead
= NULL
, *addr
;
1174 struct utsname sysinfo
;
1175 struct mntent mountent
;
1176 struct sockaddr_in
*addr4
;
1177 struct sockaddr_in6
*addr6
;
1180 /* setlocale(LC_ALL, "");
1181 bindtextdomain(PACKAGE, LOCALEDIR);
1182 textdomain(PACKAGE); */
1185 thisprogram
= argv
[0];
1191 if(thisprogram
== NULL
)
1192 thisprogram
= "mount.cifs";
1195 /* BB add workstation name and domain and pass down */
1197 /* #ifdef _GNU_SOURCE
1198 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1202 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1203 if (share_name
== NULL
) {
1204 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1207 mountpoint
= argv
[2];
1208 } else if (argc
== 2) {
1209 if ((strcmp(argv
[1], "-V") == 0) ||
1210 (strcmp(argv
[1], "--version") == 0))
1212 print_cifs_mount_version();
1216 if ((strcmp(argv
[1], "-h") == 0) ||
1217 (strcmp(argv
[1], "-?") == 0) ||
1218 (strcmp(argv
[1], "--help") == 0))
1232 /* add sharename in opts string as unc= parm */
1233 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1234 longopts
, NULL
)) != -1) {
1236 /* No code to do the following options yet */
1238 list_with_volumelabel = 1;
1241 volumelabel = optarg;
1248 case 'h': /* help */
1249 mount_cifs_usage ();
1259 "option 'b' (MS_BIND) not supported\n");
1267 "option 'm' (MS_MOVE) not supported\n");
1271 orgoptions
= strdup(optarg
);
1273 case 'r': /* mount readonly */
1283 print_cifs_mount_version();
1286 flags
&= ~MS_RDONLY
;
1289 rsize
= atoi(optarg
) ;
1292 wsize
= atoi(optarg
);
1295 if (isdigit(*optarg
)) {
1298 uid
= strtoul(optarg
, &ep
, 10);
1300 printf("bad uid value \"%s\"\n", optarg
);
1306 if (!(pw
= getpwnam(optarg
))) {
1307 printf("bad user name \"%s\"\n", optarg
);
1315 if (isdigit(*optarg
)) {
1318 gid
= strtoul(optarg
, &ep
, 10);
1320 printf("bad gid value \"%s\"\n", optarg
);
1326 if (!(gr
= getgrnam(optarg
))) {
1327 printf("bad user name \"%s\"\n", optarg
);
1339 domain_name
= optarg
; /* BB fix this - currently ignored */
1343 if(mountpassword
== NULL
)
1344 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1347 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1351 get_password_from_file(0 /* stdin */,NULL
);
1359 printf("unknown mount option %c\n",c
);
1365 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1370 /* make sure mountpoint is legit */
1371 rc
= check_mountpoint(thisprogram
, mountpoint
);
1375 /* sanity check for unprivileged mounts */
1377 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1382 /* enable any default user mount flags */
1383 flags
|= CIFS_SETUID_FLAGS
;
1386 if (getenv("PASSWD")) {
1387 if(mountpassword
== NULL
)
1388 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1390 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1393 } else if (getenv("PASSWD_FD")) {
1394 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1395 } else if (getenv("PASSWD_FILE")) {
1396 get_password_from_file(0, getenv("PASSWD_FILE"));
1399 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1405 #if !CIFS_LEGACY_SETUID_CHECK
1406 if (!(flags
& (MS_USERS
|MS_USER
))) {
1407 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1411 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1414 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1415 "CIFS mounts not supported.",
1422 flags
&= ~(MS_USERS
|MS_USER
);
1424 addrhead
= addr
= parse_server(&share_name
);
1425 if((addrhead
== NULL
) && (got_ip
== 0)) {
1426 printf("No ip address specified and hostname not found\n");
1431 /* BB save off path and pop after mount returns? */
1432 resolved_path
= (char *)malloc(PATH_MAX
+1);
1434 /* Note that if we can not canonicalize the name, we get
1435 another chance to see if it is valid when we chdir to it */
1436 if (realpath(mountpoint
, resolved_path
)) {
1437 mountpoint
= resolved_path
;
1441 /* Note that the password will not be retrieved from the
1442 USER env variable (ie user%password form) as there is
1443 already a PASSWD environment varaible */
1445 user_name
= strdup(getenv("USER"));
1446 if (user_name
== NULL
)
1447 user_name
= getusername();
1451 if(got_password
== 0) {
1452 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1453 no good replacement yet. */
1454 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1455 if (!tmp_pass
|| !mountpassword
) {
1456 printf("Password not entered, exiting\n");
1459 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1462 /* FIXME launch daemon (handles dfs name resolution and credential change)
1463 remember to clear parms and overwrite password field before launching */
1465 optlen
= strlen(orgoptions
);
1470 optlen
+= strlen(share_name
) + 4;
1472 printf("No server share name specified\n");
1473 printf("\nMounting the DFS root for server not implemented yet\n");
1477 optlen
+= strlen(user_name
) + 6;
1478 optlen
+= MAX_ADDRESS_LEN
+ 4;
1480 optlen
+= strlen(mountpassword
) + 6;
1483 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1484 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 */);
1486 if(options
== NULL
) {
1487 printf("Could not allocate memory for mount options\n");
1491 strlcpy(options
, "unc=", options_size
);
1492 strlcat(options
,share_name
,options_size
);
1493 /* scan backwards and reverse direction of slash */
1494 temp
= strrchr(options
, '/');
1495 if(temp
> options
+ 6)
1498 /* check for syntax like user=domain\user */
1500 domain_name
= check_for_domain(&user_name
);
1501 strlcat(options
,",user=",options_size
);
1502 strlcat(options
,user_name
,options_size
);
1506 /* extra length accounted for in option string above */
1507 strlcat(options
,",domain=",options_size
);
1508 strlcat(options
,domain_name
,options_size
);
1512 /* Commas have to be doubled, or else they will
1513 look like the parameter separator */
1514 /* if(sep is not set)*/
1516 check_for_comma(&mountpassword
);
1517 strlcat(options
,",pass=",options_size
);
1518 strlcat(options
,mountpassword
,options_size
);
1521 strlcat(options
,",ver=",options_size
);
1522 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1525 strlcat(options
,",",options_size
);
1526 strlcat(options
,orgoptions
,options_size
);
1529 strlcat(options
,",prefixpath=",options_size
);
1530 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1533 printf("\nmount.cifs kernel mount options %s \n",options
);
1535 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1536 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1538 if (!got_ip
&& addr
) {
1539 strlcat(options
, ",ip=", options_size
);
1540 current_len
= strnlen(options
, options_size
);
1541 optionstail
= options
+ current_len
;
1542 switch (addr
->ai_addr
->sa_family
) {
1544 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1545 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1546 options_size
- current_len
);
1549 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1550 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1551 options_size
- current_len
);
1557 /* if the address looks bogus, try the next one */
1559 addr
= addr
->ai_next
;
1567 if (addr
->ai_addr
->sa_family
== AF_INET6
&& addr6
->sin6_scope_id
) {
1568 strlcat(options
, "%", options_size
);
1569 current_len
= strnlen(options
, options_size
);
1570 optionstail
= options
+ current_len
;
1571 snprintf(optionstail
, options_size
- current_len
, "%u",
1572 addr6
->sin6_scope_id
);
1575 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1580 addr
= addr
->ai_next
;
1586 printf("mount error: cifs filesystem not supported by the system\n");
1591 if (uppercase_string(dev_name
) &&
1592 uppercase_string(share_name
) &&
1593 uppercase_string(prefixpath
)) {
1594 printf("retrying with upper case share name\n");
1599 printf("mount error(%d): %s\n", errno
, strerror(errno
));
1600 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1608 atexit(unlock_mtab
);
1611 printf("cannot lock mtab");
1614 pmntfile
= setmntent(MOUNTED
, "a+");
1616 printf("could not update mount table\n");
1621 mountent
.mnt_fsname
= dev_name
;
1622 mountent
.mnt_dir
= mountpoint
;
1623 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1624 mountent
.mnt_opts
= (char *)malloc(220);
1625 if(mountent
.mnt_opts
) {
1626 char * mount_user
= getusername();
1627 memset(mountent
.mnt_opts
,0,200);
1628 if(flags
& MS_RDONLY
)
1629 strlcat(mountent
.mnt_opts
,"ro",220);
1631 strlcat(mountent
.mnt_opts
,"rw",220);
1632 if(flags
& MS_MANDLOCK
)
1633 strlcat(mountent
.mnt_opts
,",mand",220);
1634 if(flags
& MS_NOEXEC
)
1635 strlcat(mountent
.mnt_opts
,",noexec",220);
1636 if(flags
& MS_NOSUID
)
1637 strlcat(mountent
.mnt_opts
,",nosuid",220);
1638 if(flags
& MS_NODEV
)
1639 strlcat(mountent
.mnt_opts
,",nodev",220);
1640 if(flags
& MS_SYNCHRONOUS
)
1641 strlcat(mountent
.mnt_opts
,",sync",220);
1644 strlcat(mountent
.mnt_opts
,
1646 strlcat(mountent
.mnt_opts
,
1651 mountent
.mnt_freq
= 0;
1652 mountent
.mnt_passno
= 0;
1653 rc
= addmntent(pmntfile
,&mountent
);
1654 endmntent(pmntfile
);
1656 SAFE_FREE(mountent
.mnt_opts
);
1661 int len
= strlen(mountpassword
);
1662 memset(mountpassword
,0,len
);
1663 SAFE_FREE(mountpassword
);
1667 freeaddrinfo(addrhead
);
1669 SAFE_FREE(orgoptions
);
1670 SAFE_FREE(resolved_path
);
1671 SAFE_FREE(share_name
);