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 "12"
48
#ifndef MOUNT_CIFS_VENDOR_SUFFIX
50
#include "include/version.h"
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
#define MAX_UNC_LEN 1024
75
#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
78
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
81
#define MOUNT_PASSWD_SIZE 64
82
#define DOMAIN_SIZE 64
84
/* currently maximum length of IPv6 address string */
85
#define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
87
const char *thisprogram;
90
static int got_password = 0;
91
static int got_user = 0;
92
static int got_domain = 0;
93
static int got_ip = 0;
94
static int got_unc = 0;
95
static int got_uid = 0;
96
static int got_gid = 0;
97
static char * user_name = NULL;
98
static char * mountpassword = NULL;
99
char * domain_name = NULL;
100
char * prefixpath = NULL;
102
/* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
103
* don't link to libreplace so need them here. */
105
/* like strncpy but does not 0 fill the buffer and always null
106
* terminates. bufsize is the size of the destination buffer */
109
static size_t strlcpy(char *d, const char *s, size_t bufsize)
111
size_t len = strlen(s);
113
if (bufsize <= 0) return 0;
114
if (len >= bufsize) len = bufsize-1;
121
/* like strncat but does not 0 fill the buffer and always null
122
* terminates. bufsize is the length of the buffer, which should
123
* be one more than the maximum resulting string length */
126
static size_t strlcat(char *d, const char *s, size_t bufsize)
128
size_t len1 = strlen(d);
129
size_t len2 = strlen(s);
130
size_t ret = len1 + len2;
132
if (len1+len2 >= bufsize) {
133
if (bufsize < (len1+1)) {
136
len2 = bufsize - (len1+1);
139
memcpy(d+len1, s, len2);
149
open nofollow - avoid symlink exposure?
150
get owner of dir see if matches self or if root
151
call system(umount argv) etc.
155
static char * check_for_domain(char **);
158
static void mount_cifs_usage(void)
160
printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
161
printf("\nMount the remote target, specified as a UNC name,");
162
printf(" to a local directory.\n\nOptions:\n");
163
printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
164
printf("\nLess commonly used options:");
165
printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
166
printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
167
printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
168
printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
169
printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
170
printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
171
printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
172
printf("\n\nRarely used options:");
173
printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
174
printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
175
printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
176
printf("\n\tin6_addr");
177
printf("\n\nOptions are described in more detail in the manual page");
178
printf("\n\tman 8 mount.cifs\n");
179
printf("\nTo display the version number of the mount helper:");
180
printf("\n\t%s -V\n",thisprogram);
182
SAFE_FREE(mountpassword);
185
/* caller frees username if necessary */
186
static char * getusername(uid_t uid) {
187
char *username = NULL;
188
struct passwd *password = getpwuid(uid);
191
username = password->pw_name;
196
static int open_cred_file(char * file_name)
203
i = access(file_name, R_OK);
207
fs = fopen(file_name,"r");
210
line_buf = (char *)malloc(4096);
211
if(line_buf == NULL) {
216
while(fgets(line_buf,4096,fs)) {
217
/* parse line from credential file */
219
/* eat leading white space */
220
for(i=0;i<4086;i++) {
221
if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
223
/* if whitespace - skip past it */
225
if (strncasecmp("username",line_buf+i,8) == 0) {
226
temp_val = strchr(line_buf + i,'=');
228
/* go past equals sign */
230
for(length = 0;length<4087;length++) {
231
if ((temp_val[length] == '\n')
232
|| (temp_val[length] == '\0')) {
233
temp_val[length] = '\0';
238
printf("mount.cifs failed due to malformed username in credentials file");
239
memset(line_buf,0,4096);
243
user_name = (char *)calloc(1 + length,1);
244
/* BB adding free of user_name string before exit,
245
not really necessary but would be cleaner */
246
strlcpy(user_name,temp_val, length+1);
249
} else if (strncasecmp("password",line_buf+i,8) == 0) {
250
temp_val = strchr(line_buf+i,'=');
252
/* go past equals sign */
254
for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
255
if ((temp_val[length] == '\n')
256
|| (temp_val[length] == '\0')) {
257
temp_val[length] = '\0';
261
if(length > MOUNT_PASSWD_SIZE) {
262
printf("mount.cifs failed: password in credentials file too long\n");
263
memset(line_buf,0, 4096);
266
if(mountpassword == NULL) {
267
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
269
memset(mountpassword,0,MOUNT_PASSWD_SIZE);
271
strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
276
} else if (strncasecmp("domain",line_buf+i,6) == 0) {
277
temp_val = strchr(line_buf+i,'=');
279
/* go past equals sign */
282
printf("\nDomain %s\n",temp_val);
283
for(length = 0;length<DOMAIN_SIZE+1;length++) {
284
if ((temp_val[length] == '\n')
285
|| (temp_val[length] == '\0')) {
286
temp_val[length] = '\0';
290
if(length > DOMAIN_SIZE) {
291
printf("mount.cifs failed: domain in credentials file too long\n");
294
if(domain_name == NULL) {
295
domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
297
memset(domain_name,0,DOMAIN_SIZE);
299
strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
312
static int get_password_from_file(int file_descript, char * filename)
318
if(mountpassword == NULL)
319
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
321
memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
323
if (mountpassword == NULL) {
324
printf("malloc failed\n");
328
if(filename != NULL) {
329
rc = access(filename, R_OK);
331
fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
332
filename, strerror(errno));
335
file_descript = open(filename, O_RDONLY);
336
if(file_descript < 0) {
337
printf("mount.cifs failed. %s attempting to open password file %s\n",
338
strerror(errno),filename);
342
/* else file already open and fd provided */
344
for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
345
rc = read(file_descript,&c,1);
347
printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
349
close(file_descript);
352
if(mountpassword[0] == 0) {
354
printf("\nWarning: null password used since cifs password file empty");
357
} else /* read valid character */ {
358
if((c == 0) || (c == '\n')) {
359
mountpassword[i] = '\0';
362
mountpassword[i] = c;
365
if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
366
printf("\nWarning: password longer than %d characters specified in cifs password file",
370
if(filename != NULL) {
371
close(file_descript);
377
static int parse_options(char ** optionsp, int * filesys_flags)
380
char * percent_char = NULL;
382
char * next_keyword = NULL;
390
if (!optionsp || !*optionsp)
394
/* BB fixme check for separator override BB */
398
snprintf(user,sizeof(user),"%u",getuid());
400
snprintf(group,sizeof(group),"%u",getgid());
403
/* while ((data = strsep(&options, ",")) != NULL) { */
404
while(data != NULL) {
405
/* check if ends with trailing comma */
409
/* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
410
/* data = next keyword */
411
/* value = next value ie stuff after equal sign */
413
next_keyword = strchr(data,','); /* BB handle sep= */
415
/* temporarily null terminate end of keyword=value pair */
419
/* temporarily null terminate keyword to make keyword and value distinct */
420
if ((value = strchr(data, '=')) != NULL) {
425
if (strncmp(data, "users",5) == 0) {
426
if(!value || !*value) {
429
} else if (strncmp(data, "user_xattr",10) == 0) {
430
/* do nothing - need to skip so not parsed as user name */
431
} else if (strncmp(data, "user", 4) == 0) {
433
if (!value || !*value) {
434
if(data[4] == '\0') {
436
printf("\nskipping empty user mount parameter\n");
437
/* remove the parm since it would otherwise be confusing
438
to the kernel code which would think it was a real username */
441
printf("username specified with no parameter\n");
443
return 1; /* needs_arg; */
446
if (strnlen(value, 260) < 260) {
448
percent_char = strchr(value,'%');
451
if(mountpassword == NULL)
452
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
455
printf("\nmount.cifs warning - password specified twice\n");
458
strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
459
/* remove password from username */
460
while(*percent_char != 0) {
466
/* this is only case in which the user
467
name buf is not malloc - so we have to
468
check for domain name embedded within
469
the user name here since the later
470
call to check_for_domain will not be
472
domain_name = check_for_domain(&value);
474
printf("username too long\n");
479
} else if (strncmp(data, "pass", 4) == 0) {
480
if (!value || !*value) {
482
fprintf(stderr, "\npassword specified twice, ignoring second\n");
485
} else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
487
fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
489
mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
490
if (!mountpassword) {
491
fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
498
fprintf(stderr, "password too long\n");
503
} else if (strncmp(data, "sec", 3) == 0) {
505
if (!strncmp(value, "none", 4) ||
506
!strncmp(value, "krb5", 4))
509
} else if (strncmp(data, "ip", 2) == 0) {
510
if (!value || !*value) {
511
printf("target ip address argument missing");
512
} else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
514
printf("ip address %s override specified\n",value);
517
printf("ip address too long\n");
521
} else if ((strncmp(data, "unc", 3) == 0)
522
|| (strncmp(data, "target", 6) == 0)
523
|| (strncmp(data, "path", 4) == 0)) {
524
if (!value || !*value) {
525
printf("invalid path to network resource\n");
527
return 1; /* needs_arg; */
528
} else if(strnlen(value,5) < 5) {
529
printf("UNC name too short");
532
if (strnlen(value, 300) < 300) {
534
if (strncmp(value, "//", 2) == 0) {
536
printf("unc name specified twice, ignoring second\n");
539
} else if (strncmp(value, "\\\\", 2) != 0) {
540
printf("UNC Path does not begin with // or \\\\ \n");
545
printf("unc name specified twice, ignoring second\n");
550
printf("CIFS: UNC name too long\n");
554
} else if ((strncmp(data, "dom" /* domain */, 3) == 0)
555
|| (strncmp(data, "workg", 5) == 0)) {
556
/* note this allows for synonyms of "domain"
557
such as "DOM" and "dom" and "workgroup"
558
and "WORKGRP" etc. */
559
if (!value || !*value) {
560
printf("CIFS: invalid domain name\n");
562
return 1; /* needs_arg; */
564
if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
567
printf("domain name too long\n");
571
} else if (strncmp(data, "cred", 4) == 0) {
572
if (value && *value) {
573
rc = open_cred_file(value);
575
printf("error %d (%s) opening credential file %s\n",
576
rc, strerror(rc), value);
581
printf("invalid credential file name specified\n");
585
} else if (strncmp(data, "uid", 3) == 0) {
586
if (value && *value) {
588
if (!isdigit(*value)) {
591
if (!(pw = getpwnam(value))) {
592
printf("bad user name \"%s\"\n", value);
595
snprintf(user, sizeof(user), "%u", pw->pw_uid);
597
strlcpy(user,value,sizeof(user));
601
} else if (strncmp(data, "gid", 3) == 0) {
602
if (value && *value) {
604
if (!isdigit(*value)) {
607
if (!(gr = getgrnam(value))) {
608
printf("bad group name \"%s\"\n", value);
611
snprintf(group, sizeof(group), "%u", gr->gr_gid);
613
strlcpy(group,value,sizeof(group));
617
/* fmask and dmask synonyms for people used to smbfs syntax */
618
} else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
619
if (!value || !*value) {
620
printf ("Option '%s' requires a numerical argument\n", data);
625
if (value[0] != '0') {
626
printf ("WARNING: '%s' not expressed in octal.\n", data);
629
if (strcmp (data, "fmask") == 0) {
630
printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
631
data = "file_mode"; /* BB fix this */
633
} else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
634
if (!value || !*value) {
635
printf ("Option '%s' requires a numerical argument\n", data);
640
if (value[0] != '0') {
641
printf ("WARNING: '%s' not expressed in octal.\n", data);
644
if (strcmp (data, "dmask") == 0) {
645
printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
648
/* the following eight mount options should be
649
stripped out from what is passed into the kernel
650
since these eight options are best passed as the
651
mount flags rather than redundantly to the kernel
652
and could generate spurious warnings depending on the
653
level of the corresponding cifs vfs kernel code */
654
} else if (strncmp(data, "nosuid", 6) == 0) {
655
*filesys_flags |= MS_NOSUID;
656
} else if (strncmp(data, "suid", 4) == 0) {
657
*filesys_flags &= ~MS_NOSUID;
658
} else if (strncmp(data, "nodev", 5) == 0) {
659
*filesys_flags |= MS_NODEV;
660
} else if ((strncmp(data, "nobrl", 5) == 0) ||
661
(strncmp(data, "nolock", 6) == 0)) {
662
*filesys_flags &= ~MS_MANDLOCK;
663
} else if (strncmp(data, "dev", 3) == 0) {
664
*filesys_flags &= ~MS_NODEV;
665
} else if (strncmp(data, "noexec", 6) == 0) {
666
*filesys_flags |= MS_NOEXEC;
667
} else if (strncmp(data, "exec", 4) == 0) {
668
*filesys_flags &= ~MS_NOEXEC;
669
} else if (strncmp(data, "guest", 5) == 0) {
670
user_name = (char *)calloc(1, 1);
673
} else if (strncmp(data, "ro", 2) == 0) {
674
*filesys_flags |= MS_RDONLY;
675
} else if (strncmp(data, "rw", 2) == 0) {
676
*filesys_flags &= ~MS_RDONLY;
677
} else if (strncmp(data, "remount", 7) == 0) {
678
*filesys_flags |= MS_REMOUNT;
679
} /* else if (strnicmp(data, "port", 4) == 0) {
680
if (value && *value) {
682
simple_strtoul(value, &value, 0);
684
} else if (strnicmp(data, "rsize", 5) == 0) {
685
if (value && *value) {
687
simple_strtoul(value, &value, 0);
689
} else if (strnicmp(data, "wsize", 5) == 0) {
690
if (value && *value) {
692
simple_strtoul(value, &value, 0);
694
} else if (strnicmp(data, "version", 3) == 0) {
696
printf("CIFS: Unknown mount option %s\n",data);
697
} */ /* nothing to do on those four mount options above.
698
Just pass to kernel and ignore them here */
700
/* Copy (possibly modified) option to out */
701
word_len = strlen(data);
703
word_len += 1 + strlen(value);
705
out = (char *)realloc(out, out_len + word_len + 2);
712
strlcat(out, ",", out_len + word_len + 2);
717
snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
719
snprintf(out + out_len, word_len + 1, "%s", data);
720
out_len = strlen(out);
726
/* special-case the uid and gid */
728
word_len = strlen(user);
730
out = (char *)realloc(out, out_len + word_len + 6);
737
strlcat(out, ",", out_len + word_len + 6);
740
snprintf(out + out_len, word_len + 5, "uid=%s", user);
741
out_len = strlen(out);
744
word_len = strlen(group);
746
out = (char *)realloc(out, out_len + 1 + word_len + 6);
753
strlcat(out, ",", out_len + word_len + 6);
756
snprintf(out + out_len, word_len + 5, "gid=%s", group);
757
out_len = strlen(out);
760
SAFE_FREE(*optionsp);
765
/* replace all (one or more) commas with double commas */
766
static void check_for_comma(char ** ppasswrd)
771
int number_of_commas = 0;
786
if(number_of_commas == 0)
788
if(number_of_commas > MOUNT_PASSWD_SIZE) {
789
/* would otherwise overflow the mount options buffer */
790
printf("\nInvalid password. Password contains too many commas.\n");
794
new_pass_buf = (char *)malloc(len+number_of_commas+1);
795
if(new_pass_buf == NULL)
798
for(i=0,j=0;i<len;i++,j++) {
799
new_pass_buf[j] = pass[i];
802
new_pass_buf[j] = pass[i];
805
new_pass_buf[len+number_of_commas] = 0;
807
SAFE_FREE(*ppasswrd);
808
*ppasswrd = new_pass_buf;
813
/* Usernames can not have backslash in them and we use
814
[BB check if usernames can have forward slash in them BB]
815
backslash as domain\user separator character
817
static char * check_for_domain(char **ppuser)
819
char * original_string;
829
original_string = *ppuser;
831
if (original_string == NULL)
834
original_len = strlen(original_string);
836
usernm = strchr(*ppuser,'/');
837
if (usernm == NULL) {
838
usernm = strchr(*ppuser,'\\');
844
printf("Domain name specified twice. Username probably malformed\n");
850
if (domainnm[0] != 0) {
853
printf("null domain\n");
855
len = strlen(domainnm);
856
/* reset domainm to new buffer, and copy
857
domain name into it */
858
domainnm = (char *)malloc(len+1);
862
strlcpy(domainnm,*ppuser,len+1);
864
/* move_string(*ppuser, usernm+1) */
865
len = strlen(usernm+1);
867
if(len >= original_len) {
868
/* should not happen */
872
for(i=0;i<original_len;i++) {
874
original_string[i] = usernm[i+1];
875
else /* stuff with commas to remove last parm */
876
original_string[i] = ',';
879
/* BB add check for more than one slash?
887
/* replace all occurances of "from" in a string with "to" */
888
static void replace_char(char *string, char from, char to, int maxlen)
890
char *lastchar = string + maxlen;
892
string = strchr(string, from);
895
if (string >= lastchar)
901
/* Note that caller frees the returned buffer if necessary */
902
static struct addrinfo *
903
parse_server(char ** punc_name)
905
char * unc_name = *punc_name;
906
int length = strnlen(unc_name, MAX_UNC_LEN);
908
struct addrinfo *addrlist;
911
if(length > (MAX_UNC_LEN - 1)) {
912
printf("mount error: UNC name too long");
915
if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
916
(strncasecmp("smb://", unc_name, 6) == 0)) {
917
printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
922
/* BB add code to find DFS root here */
923
printf("\nMounting the DFS root for domain not implemented yet\n");
926
if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
927
/* check for nfs syntax ie server:share */
928
share = strchr(unc_name,':');
930
*punc_name = (char *)malloc(length+3);
931
if(*punc_name == NULL) {
932
/* put the original string back if
934
*punc_name = unc_name;
938
strlcpy((*punc_name)+2,unc_name,length+1);
940
unc_name = *punc_name;
941
unc_name[length+2] = 0;
942
goto continue_unc_parsing;
944
printf("mount error: improperly formatted UNC name.");
945
printf(" %s does not begin with \\\\ or //\n",unc_name);
949
continue_unc_parsing:
954
/* allow for either delimiter between host and sharename */
955
if ((share = strpbrk(unc_name, "/\\"))) {
956
*share = 0; /* temporarily terminate the string */
959
rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
961
printf("mount error: could not resolve address for %s: %s\n",
962
unc_name, gai_strerror(rc));
966
*(share - 1) = '/'; /* put delimiter back */
968
/* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
969
if ((prefixpath = strpbrk(share, "/\\"))) {
970
*prefixpath = 0; /* permanently terminate the string */
971
if (!strlen(++prefixpath))
972
prefixpath = NULL; /* this needs to be done explicitly */
976
printf("ip address specified explicitly\n");
979
/* BB should we pass an alternate version of the share name as Unicode */
983
/* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
984
printf("Mounting the DFS root for a particular server not implemented yet\n");
991
static struct option longopts[] = {
992
{ "all", 0, NULL, 'a' },
993
{ "help",0, NULL, 'h' },
994
{ "move",0, NULL, 'm' },
995
{ "bind",0, NULL, 'b' },
996
{ "read-only", 0, NULL, 'r' },
997
{ "ro", 0, NULL, 'r' },
998
{ "verbose", 0, NULL, 'v' },
999
{ "version", 0, NULL, 'V' },
1000
{ "read-write", 0, NULL, 'w' },
1001
{ "rw", 0, NULL, 'w' },
1002
{ "options", 1, NULL, 'o' },
1003
{ "type", 1, NULL, 't' },
1004
{ "rsize",1, NULL, 'R' },
1005
{ "wsize",1, NULL, 'W' },
1006
{ "uid", 1, NULL, '1'},
1007
{ "gid", 1, NULL, '2'},
1008
{ "user",1,NULL,'u'},
1009
{ "username",1,NULL,'u'},
1010
{ "dom",1,NULL,'d'},
1011
{ "domain",1,NULL,'d'},
1012
{ "password",1,NULL,'p'},
1013
{ "pass",1,NULL,'p'},
1014
{ "credentials",1,NULL,'c'},
1015
{ "port",1,NULL,'P'},
1016
/* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1017
{ NULL, 0, NULL, 0 }
1020
/* convert a string to uppercase. return false if the string
1021
* wasn't ASCII. Return success on a NULL ptr */
1023
uppercase_string(char *string)
1029
/* check for unicode */
1030
if ((unsigned char) string[0] & 0x80)
1032
*string = toupper((unsigned char) *string);
1039
static void print_cifs_mount_version(void)
1041
printf("mount.cifs version: %s.%s%s\n",
1042
MOUNT_CIFS_VERSION_MAJOR,
1043
MOUNT_CIFS_VERSION_MINOR,
1044
MOUNT_CIFS_VENDOR_SUFFIX);
1048
* This function borrowed from fuse-utils...
1050
* glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
1051
* newlines embedded within the text fields. To make sure no one corrupts
1052
* the mtab, fail the mount if there are embedded newlines.
1054
static int check_newline(const char *progname, const char *name)
1057
for (s = "\n"; *s; s++) {
1058
if (strchr(name, *s)) {
1059
fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
1067
static int check_mtab(const char *progname, const char *devname,
1070
if (check_newline(progname, devname) == -1 ||
1071
check_newline(progname, dir) == -1)
1077
int main(int argc, char ** argv)
1080
int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1081
char * orgoptions = NULL;
1082
char * share_name = NULL;
1083
const char * ipaddr = NULL;
1085
char * mountpoint = NULL;
1086
char * mount_user = NULL;
1087
char * options = NULL;
1089
char * resolved_path = NULL;
1101
size_t options_size = 0;
1103
int retry = 0; /* set when we have to retry mount with uppercase */
1104
struct addrinfo *addrhead = NULL, *addr;
1105
struct stat statbuf;
1106
struct utsname sysinfo;
1107
struct mntent mountent;
1108
struct sockaddr_in *addr4;
1109
struct sockaddr_in6 *addr6;
1111
sigset_t mask, oldmask;
1113
/* setlocale(LC_ALL, "");
1114
bindtextdomain(PACKAGE, LOCALEDIR);
1115
textdomain(PACKAGE); */
1118
thisprogram = argv[0];
1124
if(thisprogram == NULL)
1125
thisprogram = "mount.cifs";
1128
/* BB add workstation name and domain and pass down */
1130
/* #ifdef _GNU_SOURCE
1131
printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1135
share_name = strndup(argv[1], MAX_UNC_LEN);
1136
if (share_name == NULL) {
1137
fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1140
mountpoint = argv[2];
1141
} else if (argc == 2) {
1142
if ((strcmp(argv[1], "-V") == 0) ||
1143
(strcmp(argv[1], "--version") == 0))
1145
print_cifs_mount_version();
1149
if ((strcmp(argv[1], "-h") == 0) ||
1150
(strcmp(argv[1], "-?") == 0) ||
1151
(strcmp(argv[1], "--help") == 0))
1164
/* add sharename in opts string as unc= parm */
1166
while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1167
longopts, NULL)) != -1) {
1169
/* No code to do the following options yet */
1171
list_with_volumelabel = 1;
1174
volumelabel = optarg;
1181
case 'h': /* help */
1182
mount_cifs_usage ();
1192
"option 'b' (MS_BIND) not supported\n");
1200
"option 'm' (MS_MOVE) not supported\n");
1204
orgoptions = strdup(optarg);
1206
case 'r': /* mount readonly */
1216
print_cifs_mount_version();
1219
flags &= ~MS_RDONLY;
1222
rsize = atoi(optarg) ;
1225
wsize = atoi(optarg);
1228
if (isdigit(*optarg)) {
1231
uid = strtoul(optarg, &ep, 10);
1233
printf("bad uid value \"%s\"\n", optarg);
1239
if (!(pw = getpwnam(optarg))) {
1240
printf("bad user name \"%s\"\n", optarg);
1248
if (isdigit(*optarg)) {
1251
gid = strtoul(optarg, &ep, 10);
1253
printf("bad gid value \"%s\"\n", optarg);
1259
if (!(gr = getgrnam(optarg))) {
1260
printf("bad user name \"%s\"\n", optarg);
1272
domain_name = optarg; /* BB fix this - currently ignored */
1276
if(mountpassword == NULL)
1277
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1280
strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1284
get_password_from_file(0 /* stdin */,NULL);
1292
printf("unknown mount option %c\n",c);
1298
if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1303
if (getenv("PASSWD")) {
1304
if(mountpassword == NULL)
1305
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1307
strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1310
} else if (getenv("PASSWD_FD")) {
1311
get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1312
} else if (getenv("PASSWD_FILE")) {
1313
get_password_from_file(0, getenv("PASSWD_FILE"));
1316
if (orgoptions && parse_options(&orgoptions, &flags)) {
1320
addrhead = addr = parse_server(&share_name);
1321
if((addrhead == NULL) && (got_ip == 0)) {
1322
printf("No ip address specified and hostname not found\n");
1327
/* BB save off path and pop after mount returns? */
1328
resolved_path = (char *)malloc(PATH_MAX+1);
1330
/* Note that if we can not canonicalize the name, we get
1331
another chance to see if it is valid when we chdir to it */
1332
if (realpath(mountpoint, resolved_path)) {
1333
mountpoint = resolved_path;
1336
if(chdir(mountpoint)) {
1337
printf("mount error: can not change directory into mount target %s\n",mountpoint);
1342
if(stat (".", &statbuf)) {
1343
printf("mount error: mount point %s does not exist\n",mountpoint);
1348
if (S_ISDIR(statbuf.st_mode) == 0) {
1349
printf("mount error: mount point %s is not a directory\n",mountpoint);
1354
if((getuid() != 0) && (geteuid() == 0)) {
1355
if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1356
#ifndef CIFS_ALLOW_USR_SUID
1357
/* Do not allow user mounts to control suid flag
1358
for mount unless explicitly built that way */
1359
flags |= MS_NOSUID | MS_NODEV;
1362
printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1368
/* Note that the password will not be retrieved from the
1369
USER env variable (ie user%password form) as there is
1370
already a PASSWD environment varaible */
1372
user_name = strdup(getenv("USER"));
1373
if (user_name == NULL)
1374
user_name = getusername(getuid());
1378
if(got_password == 0) {
1379
char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1380
no good replacement yet. */
1381
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1382
if (!tmp_pass || !mountpassword) {
1383
printf("Password not entered, exiting\n");
1386
strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1389
/* FIXME launch daemon (handles dfs name resolution and credential change)
1390
remember to clear parms and overwrite password field before launching */
1392
optlen = strlen(orgoptions);
1397
optlen += strlen(share_name) + 4;
1399
printf("No server share name specified\n");
1400
printf("\nMounting the DFS root for server not implemented yet\n");
1404
optlen += strlen(user_name) + 6;
1405
optlen += MAX_ADDRESS_LEN + 4;
1407
optlen += strlen(mountpassword) + 6;
1410
options_size = optlen + 10 + DOMAIN_SIZE;
1411
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 */);
1413
if(options == NULL) {
1414
printf("Could not allocate memory for mount options\n");
1418
strlcpy(options, "unc=", options_size);
1419
strlcat(options,share_name,options_size);
1420
/* scan backwards and reverse direction of slash */
1421
temp = strrchr(options, '/');
1422
if(temp > options + 6)
1425
/* check for syntax like user=domain\user */
1427
domain_name = check_for_domain(&user_name);
1428
strlcat(options,",user=",options_size);
1429
strlcat(options,user_name,options_size);
1433
/* extra length accounted for in option string above */
1434
strlcat(options,",domain=",options_size);
1435
strlcat(options,domain_name,options_size);
1439
strlcat(options,",ver=",options_size);
1440
strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1443
strlcat(options,",",options_size);
1444
strlcat(options,orgoptions,options_size);
1447
strlcat(options,",prefixpath=",options_size);
1448
strlcat(options,prefixpath,options_size); /* no need to cat the / */
1451
/* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1452
replace_char(dev_name, '\\', '/', strlen(share_name));
1454
if (!got_ip && addr) {
1455
strlcat(options, ",ip=", options_size);
1456
current_len = strnlen(options, options_size);
1457
optionstail = options + current_len;
1458
switch (addr->ai_addr->sa_family) {
1460
addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1461
ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1462
options_size - current_len);
1465
addr4 = (struct sockaddr_in *) addr->ai_addr;
1466
ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1467
options_size - current_len);
1471
/* if the address looks bogus, try the next one */
1473
addr = addr->ai_next;
1482
fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1484
if (mountpassword) {
1486
* Commas have to be doubled, or else they will
1487
* look like the parameter separator
1490
check_for_comma(&mountpassword);
1491
strlcat(options,",pass=",options_size);
1492
strlcat(options,mountpassword,options_size);
1494
fprintf(stderr, ",pass=********");
1498
fprintf(stderr, "\n");
1500
rc = check_mtab(thisprogram, dev_name, mountpoint);
1504
if (!fakemnt && mount(dev_name, ".", "cifs", flags, options)) {
1509
addr = addr->ai_next;
1515
printf("mount error: cifs filesystem not supported by the system\n");
1520
if (uppercase_string(dev_name) &&
1521
uppercase_string(share_name) &&
1522
uppercase_string(prefixpath)) {
1523
printf("retrying with upper case share name\n");
1528
printf("mount error(%d): %s\n", errno, strerror(errno));
1529
printf("Refer to the mount.cifs(8) manual page (e.g. man "
1540
mount_user = getusername(uid);
1543
* Set the real uid to the effective uid. This prevents unprivileged
1544
* users from sending signals to this process, though ^c on controlling
1545
* terminal should still work.
1547
rc = setreuid(geteuid(), -1);
1549
fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1555
rc = sigfillset(&mask);
1557
fprintf(stderr, "Unable to set filled signal mask\n");
1562
rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1564
fprintf(stderr, "Unable to make process ignore signals\n");
1569
atexit(unlock_mtab);
1572
printf("cannot lock mtab");
1575
pmntfile = setmntent(MOUNTED, "a+");
1577
printf("could not update mount table\n");
1583
fd = fileno(pmntfile);
1585
fprintf(stderr, "mntent does not appear to be valid\n");
1590
rc = fstat(fd, &statbuf);
1592
fprintf(stderr, "unable to fstat open mtab\n");
1593
endmntent(pmntfile);
1599
mountent.mnt_fsname = dev_name;
1600
mountent.mnt_dir = mountpoint;
1601
mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1602
mountent.mnt_opts = (char *)malloc(220);
1603
if(mountent.mnt_opts) {
1604
memset(mountent.mnt_opts,0,200);
1605
if(flags & MS_RDONLY)
1606
strlcat(mountent.mnt_opts,"ro",220);
1608
strlcat(mountent.mnt_opts,"rw",220);
1609
if(flags & MS_MANDLOCK)
1610
strlcat(mountent.mnt_opts,",mand",220);
1611
if(flags & MS_NOEXEC)
1612
strlcat(mountent.mnt_opts,",noexec",220);
1613
if(flags & MS_NOSUID)
1614
strlcat(mountent.mnt_opts,",nosuid",220);
1615
if(flags & MS_NODEV)
1616
strlcat(mountent.mnt_opts,",nodev",220);
1617
if(flags & MS_SYNCHRONOUS)
1618
strlcat(mountent.mnt_opts,",sync",220);
1621
strlcat(mountent.mnt_opts,
1623
strlcat(mountent.mnt_opts,
1628
mountent.mnt_freq = 0;
1629
mountent.mnt_passno = 0;
1630
rc = addmntent(pmntfile, &mountent);
1632
fprintf(stderr, "unable to add mount entry to mtab\n");
1633
ftruncate(fd, statbuf.st_size);
1636
tmprc = my_endmntent(pmntfile, statbuf.st_size);
1638
fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
1642
SAFE_FREE(mountent.mnt_opts);
1645
sigprocmask(SIG_SETMASK, &oldmask, NULL);
1648
int len = strlen(mountpassword);
1649
memset(mountpassword,0,len);
1650
SAFE_FREE(mountpassword);
1654
freeaddrinfo(addrhead);
1656
SAFE_FREE(orgoptions);
1657
SAFE_FREE(resolved_path);
1658
SAFE_FREE(share_name);