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
;
774 } else if (strncmp(data
, "rw", 2) == 0) {
775 *filesys_flags
&= ~MS_RDONLY
;
776 } else if (strncmp(data
, "remount", 7) == 0) {
777 *filesys_flags
|= MS_REMOUNT
;
778 } /* else if (strnicmp(data, "port", 4) == 0) {
779 if (value && *value) {
781 simple_strtoul(value, &value, 0);
783 } else if (strnicmp(data, "rsize", 5) == 0) {
784 if (value && *value) {
786 simple_strtoul(value, &value, 0);
788 } else if (strnicmp(data, "wsize", 5) == 0) {
789 if (value && *value) {
791 simple_strtoul(value, &value, 0);
793 } else if (strnicmp(data, "version", 3) == 0) {
795 printf("CIFS: Unknown mount option %s\n",data);
796 } */ /* nothing to do on those four mount options above.
797 Just pass to kernel and ignore them here */
799 /* Copy (possibly modified) option to out */
800 word_len
= strlen(data
);
802 word_len
+= 1 + strlen(value
);
804 out
= (char *)realloc(out
, out_len
+ word_len
+ 2);
811 strlcat(out
, ",", out_len
+ word_len
+ 2);
816 snprintf(out
+ out_len
, word_len
+ 1, "%s=%s", data
, value
);
818 snprintf(out
+ out_len
, word_len
+ 1, "%s", data
);
819 out_len
= strlen(out
);
825 /* special-case the uid and gid */
827 word_len
= strlen(user
);
829 out
= (char *)realloc(out
, out_len
+ word_len
+ 6);
836 strlcat(out
, ",", out_len
+ word_len
+ 6);
839 snprintf(out
+ out_len
, word_len
+ 5, "uid=%s", user
);
840 out_len
= strlen(out
);
843 word_len
= strlen(group
);
845 out
= (char *)realloc(out
, out_len
+ 1 + word_len
+ 6);
852 strlcat(out
, ",", out_len
+ word_len
+ 6);
855 snprintf(out
+ out_len
, word_len
+ 5, "gid=%s", group
);
856 out_len
= strlen(out
);
859 SAFE_FREE(*optionsp
);
864 /* replace all (one or more) commas with double commas */
865 static void check_for_comma(char ** ppasswrd
)
870 int number_of_commas
= 0;
885 if(number_of_commas
== 0)
887 if(number_of_commas
> MOUNT_PASSWD_SIZE
) {
888 /* would otherwise overflow the mount options buffer */
889 printf("\nInvalid password. Password contains too many commas.\n");
893 new_pass_buf
= (char *)malloc(len
+number_of_commas
+1);
894 if(new_pass_buf
== NULL
)
897 for(i
=0,j
=0;i
<len
;i
++,j
++) {
898 new_pass_buf
[j
] = pass
[i
];
901 new_pass_buf
[j
] = pass
[i
];
904 new_pass_buf
[len
+number_of_commas
] = 0;
906 SAFE_FREE(*ppasswrd
);
907 *ppasswrd
= new_pass_buf
;
912 /* Usernames can not have backslash in them and we use
913 [BB check if usernames can have forward slash in them BB]
914 backslash as domain\user separator character
916 static char * check_for_domain(char **ppuser
)
918 char * original_string
;
928 original_string
= *ppuser
;
930 if (original_string
== NULL
)
933 original_len
= strlen(original_string
);
935 usernm
= strchr(*ppuser
,'/');
936 if (usernm
== NULL
) {
937 usernm
= strchr(*ppuser
,'\\');
943 printf("Domain name specified twice. Username probably malformed\n");
949 if (domainnm
[0] != 0) {
952 printf("null domain\n");
954 len
= strlen(domainnm
);
955 /* reset domainm to new buffer, and copy
956 domain name into it */
957 domainnm
= (char *)malloc(len
+1);
961 strlcpy(domainnm
,*ppuser
,len
+1);
963 /* move_string(*ppuser, usernm+1) */
964 len
= strlen(usernm
+1);
966 if(len
>= original_len
) {
967 /* should not happen */
971 for(i
=0;i
<original_len
;i
++) {
973 original_string
[i
] = usernm
[i
+1];
974 else /* stuff with commas to remove last parm */
975 original_string
[i
] = ',';
978 /* BB add check for more than one slash?
986 /* replace all occurances of "from" in a string with "to" */
987 static void replace_char(char *string
, char from
, char to
, int maxlen
)
989 char *lastchar
= string
+ maxlen
;
991 string
= strchr(string
, from
);
994 if (string
>= lastchar
)
1000 /* Note that caller frees the returned buffer if necessary */
1001 static struct addrinfo
*
1002 parse_server(char ** punc_name
)
1004 char * unc_name
= *punc_name
;
1005 int length
= strnlen(unc_name
, MAX_UNC_LEN
);
1007 struct addrinfo
*addrlist
;
1010 if(length
> (MAX_UNC_LEN
- 1)) {
1011 printf("mount error: UNC name too long");
1014 if ((strncasecmp("cifs://", unc_name
, 7) == 0) ||
1015 (strncasecmp("smb://", unc_name
, 6) == 0)) {
1016 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name
);
1021 /* BB add code to find DFS root here */
1022 printf("\nMounting the DFS root for domain not implemented yet\n");
1025 if(strncmp(unc_name
,"//",2) && strncmp(unc_name
,"\\\\",2)) {
1026 /* check for nfs syntax ie server:share */
1027 share
= strchr(unc_name
,':');
1029 *punc_name
= (char *)malloc(length
+3);
1030 if(*punc_name
== NULL
) {
1031 /* put the original string back if
1033 *punc_name
= unc_name
;
1037 strlcpy((*punc_name
)+2,unc_name
,length
+1);
1038 SAFE_FREE(unc_name
);
1039 unc_name
= *punc_name
;
1040 unc_name
[length
+2] = 0;
1041 goto continue_unc_parsing
;
1043 printf("mount error: improperly formatted UNC name.");
1044 printf(" %s does not begin with \\\\ or //\n",unc_name
);
1048 continue_unc_parsing
:
1053 /* allow for either delimiter between host and sharename */
1054 if ((share
= strpbrk(unc_name
, "/\\"))) {
1055 *share
= 0; /* temporarily terminate the string */
1058 rc
= getaddrinfo(unc_name
, NULL
, NULL
, &addrlist
);
1060 printf("mount error: could not resolve address for %s: %s\n",
1061 unc_name
, gai_strerror(rc
));
1065 *(share
- 1) = '/'; /* put delimiter back */
1067 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1068 if ((prefixpath
= strpbrk(share
, "/\\"))) {
1069 *prefixpath
= 0; /* permanently terminate the string */
1070 if (!strlen(++prefixpath
))
1071 prefixpath
= NULL
; /* this needs to be done explicitly */
1075 printf("ip address specified explicitly\n");
1078 /* BB should we pass an alternate version of the share name as Unicode */
1082 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1083 printf("Mounting the DFS root for a particular server not implemented yet\n");
1090 static struct option longopts
[] = {
1091 { "all", 0, NULL
, 'a' },
1092 { "help",0, NULL
, 'h' },
1093 { "move",0, NULL
, 'm' },
1094 { "bind",0, NULL
, 'b' },
1095 { "read-only", 0, NULL
, 'r' },
1096 { "ro", 0, NULL
, 'r' },
1097 { "verbose", 0, NULL
, 'v' },
1098 { "version", 0, NULL
, 'V' },
1099 { "read-write", 0, NULL
, 'w' },
1100 { "rw", 0, NULL
, 'w' },
1101 { "options", 1, NULL
, 'o' },
1102 { "type", 1, NULL
, 't' },
1103 { "rsize",1, NULL
, 'R' },
1104 { "wsize",1, NULL
, 'W' },
1105 { "uid", 1, NULL
, '1'},
1106 { "gid", 1, NULL
, '2'},
1107 { "user",1,NULL
,'u'},
1108 { "username",1,NULL
,'u'},
1109 { "dom",1,NULL
,'d'},
1110 { "domain",1,NULL
,'d'},
1111 { "password",1,NULL
,'p'},
1112 { "pass",1,NULL
,'p'},
1113 { "credentials",1,NULL
,'c'},
1114 { "port",1,NULL
,'P'},
1115 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1116 { NULL
, 0, NULL
, 0 }
1119 /* convert a string to uppercase. return false if the string
1120 * wasn't ASCII. Return success on a NULL ptr */
1122 uppercase_string(char *string
)
1128 /* check for unicode */
1129 if ((unsigned char) string
[0] & 0x80)
1131 *string
= toupper((unsigned char) *string
);
1138 static void print_cifs_mount_version(void)
1140 printf("mount.cifs version: %s.%s%s\n",
1141 MOUNT_CIFS_VERSION_MAJOR
,
1142 MOUNT_CIFS_VERSION_MINOR
,
1143 MOUNT_CIFS_VENDOR_SUFFIX
);
1146 int main(int argc
, char ** argv
)
1149 unsigned long flags
= MS_MANDLOCK
;
1150 char * orgoptions
= NULL
;
1151 char * share_name
= NULL
;
1152 const char * ipaddr
= NULL
;
1154 char * mountpoint
= NULL
;
1155 char * options
= NULL
;
1157 char * resolved_path
= NULL
;
1168 size_t options_size
= 0;
1170 int retry
= 0; /* set when we have to retry mount with uppercase */
1171 struct addrinfo
*addrhead
= NULL
, *addr
;
1172 struct utsname sysinfo
;
1173 struct mntent mountent
;
1174 struct sockaddr_in
*addr4
;
1175 struct sockaddr_in6
*addr6
;
1178 /* setlocale(LC_ALL, "");
1179 bindtextdomain(PACKAGE, LOCALEDIR);
1180 textdomain(PACKAGE); */
1183 thisprogram
= argv
[0];
1189 if(thisprogram
== NULL
)
1190 thisprogram
= "mount.cifs";
1193 /* BB add workstation name and domain and pass down */
1195 /* #ifdef _GNU_SOURCE
1196 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1200 share_name
= strndup(argv
[1], MAX_UNC_LEN
);
1201 if (share_name
== NULL
) {
1202 fprintf(stderr
, "%s: %s", argv
[0], strerror(ENOMEM
));
1205 mountpoint
= argv
[2];
1206 } else if (argc
== 2) {
1207 if ((strcmp(argv
[1], "-V") == 0) ||
1208 (strcmp(argv
[1], "--version") == 0))
1210 print_cifs_mount_version();
1214 if ((strcmp(argv
[1], "-h") == 0) ||
1215 (strcmp(argv
[1], "-?") == 0) ||
1216 (strcmp(argv
[1], "--help") == 0))
1230 /* add sharename in opts string as unc= parm */
1231 while ((c
= getopt_long (argc
, argv
, "afFhilL:no:O:rsSU:vVwt:",
1232 longopts
, NULL
)) != -1) {
1234 /* No code to do the following options yet */
1236 list_with_volumelabel = 1;
1239 volumelabel = optarg;
1246 case 'h': /* help */
1247 mount_cifs_usage ();
1257 "option 'b' (MS_BIND) not supported\n");
1265 "option 'm' (MS_MOVE) not supported\n");
1269 orgoptions
= strdup(optarg
);
1271 case 'r': /* mount readonly */
1281 print_cifs_mount_version();
1284 flags
&= ~MS_RDONLY
;
1287 rsize
= atoi(optarg
) ;
1290 wsize
= atoi(optarg
);
1293 if (isdigit(*optarg
)) {
1296 uid
= strtoul(optarg
, &ep
, 10);
1298 printf("bad uid value \"%s\"\n", optarg
);
1304 if (!(pw
= getpwnam(optarg
))) {
1305 printf("bad user name \"%s\"\n", optarg
);
1313 if (isdigit(*optarg
)) {
1316 gid
= strtoul(optarg
, &ep
, 10);
1318 printf("bad gid value \"%s\"\n", optarg
);
1324 if (!(gr
= getgrnam(optarg
))) {
1325 printf("bad user name \"%s\"\n", optarg
);
1337 domain_name
= optarg
; /* BB fix this - currently ignored */
1341 if(mountpassword
== NULL
)
1342 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1345 strlcpy(mountpassword
,optarg
,MOUNT_PASSWD_SIZE
+1);
1349 get_password_from_file(0 /* stdin */,NULL
);
1357 printf("unknown mount option %c\n",c
);
1363 if((argc
< 3) || (dev_name
== NULL
) || (mountpoint
== NULL
)) {
1368 /* make sure mountpoint is legit */
1369 rc
= check_mountpoint(thisprogram
, mountpoint
);
1373 /* sanity check for unprivileged mounts */
1375 rc
= check_fstab(thisprogram
, mountpoint
, dev_name
,
1380 /* enable any default user mount flags */
1381 flags
|= CIFS_SETUID_FLAGS
;
1384 if (getenv("PASSWD")) {
1385 if(mountpassword
== NULL
)
1386 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1388 strlcpy(mountpassword
,getenv("PASSWD"),MOUNT_PASSWD_SIZE
+1);
1391 } else if (getenv("PASSWD_FD")) {
1392 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL
);
1393 } else if (getenv("PASSWD_FILE")) {
1394 get_password_from_file(0, getenv("PASSWD_FILE"));
1397 if (orgoptions
&& parse_options(&orgoptions
, &flags
)) {
1403 #if !CIFS_LEGACY_SETUID_CHECK
1404 if (!(flags
& (MS_USERS
|MS_USER
))) {
1405 fprintf(stderr
, "%s: permission denied\n", thisprogram
);
1409 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1412 fprintf(stderr
, "%s: not installed setuid - \"user\" "
1413 "CIFS mounts not supported.",
1420 flags
&= ~(MS_USERS
|MS_USER
);
1422 addrhead
= addr
= parse_server(&share_name
);
1423 if((addrhead
== NULL
) && (got_ip
== 0)) {
1424 printf("No ip address specified and hostname not found\n");
1429 /* BB save off path and pop after mount returns? */
1430 resolved_path
= (char *)malloc(PATH_MAX
+1);
1432 /* Note that if we can not canonicalize the name, we get
1433 another chance to see if it is valid when we chdir to it */
1434 if (realpath(mountpoint
, resolved_path
)) {
1435 mountpoint
= resolved_path
;
1439 /* Note that the password will not be retrieved from the
1440 USER env variable (ie user%password form) as there is
1441 already a PASSWD environment varaible */
1443 user_name
= strdup(getenv("USER"));
1444 if (user_name
== NULL
)
1445 user_name
= getusername();
1449 if(got_password
== 0) {
1450 char *tmp_pass
= getpass("Password: "); /* BB obsolete sys call but
1451 no good replacement yet. */
1452 mountpassword
= (char *)calloc(MOUNT_PASSWD_SIZE
+1,1);
1453 if (!tmp_pass
|| !mountpassword
) {
1454 printf("Password not entered, exiting\n");
1457 strlcpy(mountpassword
, tmp_pass
, MOUNT_PASSWD_SIZE
+1);
1460 /* FIXME launch daemon (handles dfs name resolution and credential change)
1461 remember to clear parms and overwrite password field before launching */
1463 optlen
= strlen(orgoptions
);
1468 optlen
+= strlen(share_name
) + 4;
1470 printf("No server share name specified\n");
1471 printf("\nMounting the DFS root for server not implemented yet\n");
1475 optlen
+= strlen(user_name
) + 6;
1476 optlen
+= MAX_ADDRESS_LEN
+ 4;
1478 optlen
+= strlen(mountpassword
) + 6;
1481 options_size
= optlen
+ 10 + DOMAIN_SIZE
;
1482 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 */);
1484 if(options
== NULL
) {
1485 printf("Could not allocate memory for mount options\n");
1489 strlcpy(options
, "unc=", options_size
);
1490 strlcat(options
,share_name
,options_size
);
1491 /* scan backwards and reverse direction of slash */
1492 temp
= strrchr(options
, '/');
1493 if(temp
> options
+ 6)
1496 /* check for syntax like user=domain\user */
1498 domain_name
= check_for_domain(&user_name
);
1499 strlcat(options
,",user=",options_size
);
1500 strlcat(options
,user_name
,options_size
);
1504 /* extra length accounted for in option string above */
1505 strlcat(options
,",domain=",options_size
);
1506 strlcat(options
,domain_name
,options_size
);
1510 /* Commas have to be doubled, or else they will
1511 look like the parameter separator */
1512 /* if(sep is not set)*/
1514 check_for_comma(&mountpassword
);
1515 strlcat(options
,",pass=",options_size
);
1516 strlcat(options
,mountpassword
,options_size
);
1519 strlcat(options
,",ver=",options_size
);
1520 strlcat(options
,MOUNT_CIFS_VERSION_MAJOR
,options_size
);
1523 strlcat(options
,",",options_size
);
1524 strlcat(options
,orgoptions
,options_size
);
1527 strlcat(options
,",prefixpath=",options_size
);
1528 strlcat(options
,prefixpath
,options_size
); /* no need to cat the / */
1531 printf("\nmount.cifs kernel mount options %s \n",options
);
1533 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1534 replace_char(dev_name
, '\\', '/', strlen(share_name
));
1536 if (!got_ip
&& addr
) {
1537 strlcat(options
, ",ip=", options_size
);
1538 current_len
= strnlen(options
, options_size
);
1539 optionstail
= options
+ current_len
;
1540 switch (addr
->ai_addr
->sa_family
) {
1542 addr6
= (struct sockaddr_in6
*) addr
->ai_addr
;
1543 ipaddr
= inet_ntop(AF_INET6
, &addr6
->sin6_addr
, optionstail
,
1544 options_size
- current_len
);
1547 addr4
= (struct sockaddr_in
*) addr
->ai_addr
;
1548 ipaddr
= inet_ntop(AF_INET
, &addr4
->sin_addr
, optionstail
,
1549 options_size
- current_len
);
1555 /* if the address looks bogus, try the next one */
1557 addr
= addr
->ai_next
;
1565 if (!fakemnt
&& mount(dev_name
, mountpoint
, "cifs", flags
, options
)) {
1570 addr
= addr
->ai_next
;
1576 printf("mount error: cifs filesystem not supported by the system\n");
1581 if (uppercase_string(dev_name
) &&
1582 uppercase_string(share_name
) &&
1583 uppercase_string(prefixpath
)) {
1584 printf("retrying with upper case share name\n");
1589 printf("mount error(%d): %s\n", errno
, strerror(errno
));
1590 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1598 atexit(unlock_mtab
);
1601 printf("cannot lock mtab");
1604 pmntfile
= setmntent(MOUNTED
, "a+");
1606 printf("could not update mount table\n");
1611 mountent
.mnt_fsname
= dev_name
;
1612 mountent
.mnt_dir
= mountpoint
;
1613 mountent
.mnt_type
= CONST_DISCARD(char *,"cifs");
1614 mountent
.mnt_opts
= (char *)malloc(220);
1615 if(mountent
.mnt_opts
) {
1616 char * mount_user
= getusername();
1617 memset(mountent
.mnt_opts
,0,200);
1618 if(flags
& MS_RDONLY
)
1619 strlcat(mountent
.mnt_opts
,"ro",220);
1621 strlcat(mountent
.mnt_opts
,"rw",220);
1622 if(flags
& MS_MANDLOCK
)
1623 strlcat(mountent
.mnt_opts
,",mand",220);
1624 if(flags
& MS_NOEXEC
)
1625 strlcat(mountent
.mnt_opts
,",noexec",220);
1626 if(flags
& MS_NOSUID
)
1627 strlcat(mountent
.mnt_opts
,",nosuid",220);
1628 if(flags
& MS_NODEV
)
1629 strlcat(mountent
.mnt_opts
,",nodev",220);
1630 if(flags
& MS_SYNCHRONOUS
)
1631 strlcat(mountent
.mnt_opts
,",sync",220);
1634 strlcat(mountent
.mnt_opts
,
1636 strlcat(mountent
.mnt_opts
,
1641 mountent
.mnt_freq
= 0;
1642 mountent
.mnt_passno
= 0;
1643 rc
= addmntent(pmntfile
,&mountent
);
1644 endmntent(pmntfile
);
1646 SAFE_FREE(mountent
.mnt_opts
);
1651 int len
= strlen(mountpassword
);
1652 memset(mountpassword
,0,len
);
1653 SAFE_FREE(mountpassword
);
1657 freeaddrinfo(addrhead
);
1659 SAFE_FREE(orgoptions
);
1660 SAFE_FREE(resolved_path
);
1661 SAFE_FREE(share_name
);