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;
1100
size_t options_size = 0;
1102
int retry = 0; /* set when we have to retry mount with uppercase */
1103
struct addrinfo *addrhead = NULL, *addr;
1104
struct stat statbuf;
1105
struct utsname sysinfo;
1106
struct mntent mountent;
1107
struct sockaddr_in *addr4;
1108
struct sockaddr_in6 *addr6;
1110
sigset_t mask, oldmask;
1112
/* setlocale(LC_ALL, "");
1113
bindtextdomain(PACKAGE, LOCALEDIR);
1114
textdomain(PACKAGE); */
1117
thisprogram = argv[0];
1123
if(thisprogram == NULL)
1124
thisprogram = "mount.cifs";
1127
/* BB add workstation name and domain and pass down */
1129
/* #ifdef _GNU_SOURCE
1130
printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1134
share_name = strndup(argv[1], MAX_UNC_LEN);
1135
if (share_name == NULL) {
1136
fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1139
mountpoint = argv[2];
1140
} else if (argc == 2) {
1141
if ((strcmp(argv[1], "-V") == 0) ||
1142
(strcmp(argv[1], "--version") == 0))
1144
print_cifs_mount_version();
1148
if ((strcmp(argv[1], "-h") == 0) ||
1149
(strcmp(argv[1], "-?") == 0) ||
1150
(strcmp(argv[1], "--help") == 0))
1163
/* add sharename in opts string as unc= parm */
1165
while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1166
longopts, NULL)) != -1) {
1168
/* No code to do the following options yet */
1170
list_with_volumelabel = 1;
1173
volumelabel = optarg;
1180
case 'h': /* help */
1181
mount_cifs_usage ();
1191
"option 'b' (MS_BIND) not supported\n");
1199
"option 'm' (MS_MOVE) not supported\n");
1203
orgoptions = strdup(optarg);
1205
case 'r': /* mount readonly */
1215
print_cifs_mount_version();
1218
flags &= ~MS_RDONLY;
1221
rsize = atoi(optarg) ;
1224
wsize = atoi(optarg);
1227
if (isdigit(*optarg)) {
1230
uid = strtoul(optarg, &ep, 10);
1232
printf("bad uid value \"%s\"\n", optarg);
1238
if (!(pw = getpwnam(optarg))) {
1239
printf("bad user name \"%s\"\n", optarg);
1247
if (isdigit(*optarg)) {
1250
gid = strtoul(optarg, &ep, 10);
1252
printf("bad gid value \"%s\"\n", optarg);
1258
if (!(gr = getgrnam(optarg))) {
1259
printf("bad user name \"%s\"\n", optarg);
1271
domain_name = optarg; /* BB fix this - currently ignored */
1275
if(mountpassword == NULL)
1276
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1279
strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1283
get_password_from_file(0 /* stdin */,NULL);
1291
printf("unknown mount option %c\n",c);
1297
if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1302
if (getenv("PASSWD")) {
1303
if(mountpassword == NULL)
1304
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1306
strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1309
} else if (getenv("PASSWD_FD")) {
1310
get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1311
} else if (getenv("PASSWD_FILE")) {
1312
get_password_from_file(0, getenv("PASSWD_FILE"));
1315
if (orgoptions && parse_options(&orgoptions, &flags)) {
1319
addrhead = addr = parse_server(&share_name);
1320
if((addrhead == NULL) && (got_ip == 0)) {
1321
printf("No ip address specified and hostname not found\n");
1326
/* BB save off path and pop after mount returns? */
1327
resolved_path = (char *)malloc(PATH_MAX+1);
1329
/* Note that if we can not canonicalize the name, we get
1330
another chance to see if it is valid when we chdir to it */
1331
if (realpath(mountpoint, resolved_path)) {
1332
mountpoint = resolved_path;
1335
if(chdir(mountpoint)) {
1336
printf("mount error: can not change directory into mount target %s\n",mountpoint);
1341
if(stat (".", &statbuf)) {
1342
printf("mount error: mount point %s does not exist\n",mountpoint);
1347
if (S_ISDIR(statbuf.st_mode) == 0) {
1348
printf("mount error: mount point %s is not a directory\n",mountpoint);
1353
if((getuid() != 0) && (geteuid() == 0)) {
1354
if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1355
#ifndef CIFS_ALLOW_USR_SUID
1356
/* Do not allow user mounts to control suid flag
1357
for mount unless explicitly built that way */
1358
flags |= MS_NOSUID | MS_NODEV;
1361
printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1367
/* Note that the password will not be retrieved from the
1368
USER env variable (ie user%password form) as there is
1369
already a PASSWD environment varaible */
1371
user_name = strdup(getenv("USER"));
1372
if (user_name == NULL)
1373
user_name = getusername(getuid());
1377
if(got_password == 0) {
1378
char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1379
no good replacement yet. */
1380
mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1381
if (!tmp_pass || !mountpassword) {
1382
printf("Password not entered, exiting\n");
1385
strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1388
/* FIXME launch daemon (handles dfs name resolution and credential change)
1389
remember to clear parms and overwrite password field before launching */
1391
optlen = strlen(orgoptions);
1396
optlen += strlen(share_name) + 4;
1398
printf("No server share name specified\n");
1399
printf("\nMounting the DFS root for server not implemented yet\n");
1403
optlen += strlen(user_name) + 6;
1404
optlen += MAX_ADDRESS_LEN + 4;
1406
optlen += strlen(mountpassword) + 6;
1409
options_size = optlen + 10 + DOMAIN_SIZE;
1410
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 */);
1412
if(options == NULL) {
1413
printf("Could not allocate memory for mount options\n");
1417
strlcpy(options, "unc=", options_size);
1418
strlcat(options,share_name,options_size);
1419
/* scan backwards and reverse direction of slash */
1420
temp = strrchr(options, '/');
1421
if(temp > options + 6)
1424
/* check for syntax like user=domain\user */
1426
domain_name = check_for_domain(&user_name);
1427
strlcat(options,",user=",options_size);
1428
strlcat(options,user_name,options_size);
1432
/* extra length accounted for in option string above */
1433
strlcat(options,",domain=",options_size);
1434
strlcat(options,domain_name,options_size);
1438
strlcat(options,",ver=",options_size);
1439
strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1442
strlcat(options,",",options_size);
1443
strlcat(options,orgoptions,options_size);
1446
strlcat(options,",prefixpath=",options_size);
1447
strlcat(options,prefixpath,options_size); /* no need to cat the / */
1450
/* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1451
replace_char(dev_name, '\\', '/', strlen(share_name));
1453
if (!got_ip && addr) {
1454
strlcat(options, ",ip=", options_size);
1455
current_len = strnlen(options, options_size);
1456
optionstail = options + current_len;
1457
switch (addr->ai_addr->sa_family) {
1459
addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1460
ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1461
options_size - current_len);
1464
addr4 = (struct sockaddr_in *) addr->ai_addr;
1465
ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1466
options_size - current_len);
1470
/* if the address looks bogus, try the next one */
1472
addr = addr->ai_next;
1481
fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1483
if (mountpassword) {
1485
* Commas have to be doubled, or else they will
1486
* look like the parameter separator
1489
check_for_comma(&mountpassword);
1490
strlcat(options,",pass=",options_size);
1491
strlcat(options,mountpassword,options_size);
1493
fprintf(stderr, ",pass=********");
1497
fprintf(stderr, "\n");
1499
rc = check_mtab(thisprogram, dev_name, mountpoint);
1503
if (!fakemnt && mount(dev_name, ".", "cifs", flags, options)) {
1508
addr = addr->ai_next;
1514
printf("mount error: cifs filesystem not supported by the system\n");
1519
if (uppercase_string(dev_name) &&
1520
uppercase_string(share_name) &&
1521
uppercase_string(prefixpath)) {
1522
printf("retrying with upper case share name\n");
1527
printf("mount error(%d): %s\n", errno, strerror(errno));
1528
printf("Refer to the mount.cifs(8) manual page (e.g. man "
1539
mount_user = getusername(uid);
1542
* Set the real uid to the effective uid. This prevents unprivileged
1543
* users from sending signals to this process, though ^c on controlling
1544
* terminal should still work.
1546
rc = setreuid(geteuid(), -1);
1548
fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
1554
rc = sigfillset(&mask);
1556
fprintf(stderr, "Unable to set filled signal mask\n");
1561
rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
1563
fprintf(stderr, "Unable to make process ignore signals\n");
1568
atexit(unlock_mtab);
1571
printf("cannot lock mtab");
1574
pmntfile = setmntent(MOUNTED, "a+");
1576
printf("could not update mount table\n");
1581
mountent.mnt_fsname = dev_name;
1582
mountent.mnt_dir = mountpoint;
1583
mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1584
mountent.mnt_opts = (char *)malloc(220);
1585
if(mountent.mnt_opts) {
1586
memset(mountent.mnt_opts,0,200);
1587
if(flags & MS_RDONLY)
1588
strlcat(mountent.mnt_opts,"ro",220);
1590
strlcat(mountent.mnt_opts,"rw",220);
1591
if(flags & MS_MANDLOCK)
1592
strlcat(mountent.mnt_opts,",mand",220);
1593
if(flags & MS_NOEXEC)
1594
strlcat(mountent.mnt_opts,",noexec",220);
1595
if(flags & MS_NOSUID)
1596
strlcat(mountent.mnt_opts,",nosuid",220);
1597
if(flags & MS_NODEV)
1598
strlcat(mountent.mnt_opts,",nodev",220);
1599
if(flags & MS_SYNCHRONOUS)
1600
strlcat(mountent.mnt_opts,",sync",220);
1603
strlcat(mountent.mnt_opts,
1605
strlcat(mountent.mnt_opts,
1610
mountent.mnt_freq = 0;
1611
mountent.mnt_passno = 0;
1612
rc = addmntent(pmntfile,&mountent);
1613
endmntent(pmntfile);
1615
SAFE_FREE(mountent.mnt_opts);
1618
sigprocmask(SIG_SETMASK, &oldmask, NULL);
1621
int len = strlen(mountpassword);
1622
memset(mountpassword,0,len);
1623
SAFE_FREE(mountpassword);
1627
freeaddrinfo(addrhead);
1629
SAFE_FREE(orgoptions);
1630
SAFE_FREE(resolved_path);
1631
SAFE_FREE(share_name);