Corrected default settings for "ldap timeout".
[Samba/gebeck_regimport.git] / source3 / client / mount.cifs.c
blobfdee87ae274cc018b790ecc1e7de0d304a0300e3
1 /*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2005 Steve French (sfrench@us.ibm.com)
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <sys/stat.h>
31 #include <sys/utsname.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <getopt.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <string.h>
38 #include <mntent.h>
39 #include <fcntl.h>
40 #include <limits.h>
42 #define MOUNT_CIFS_VERSION_MAJOR "1"
43 #define MOUNT_CIFS_VERSION_MINOR "11"
45 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
46 #ifdef _SAMBA_BUILD_
47 #include "include/version.h"
48 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
49 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
50 #else
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
52 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
53 #else
54 #define MOUNT_CIFS_VENDOR_SUFFIX ""
55 #endif /* _SAMBA_BUILD_ */
56 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
58 #ifndef MS_MOVE
59 #define MS_MOVE 8192
60 #endif
62 #ifndef MS_BIND
63 #define MS_BIND 4096
64 #endif
66 #define MAX_UNC_LEN 1024
68 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
70 const char *thisprogram;
71 int verboseflag = 0;
72 static int got_password = 0;
73 static int got_user = 0;
74 static int got_domain = 0;
75 static int got_ip = 0;
76 static int got_unc = 0;
77 static int got_uid = 0;
78 static int got_gid = 0;
79 static char * user_name = NULL;
80 static char * mountpassword = NULL;
81 char * domain_name = NULL;
82 char * prefixpath = NULL;
84 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
85 * don't link to libreplace so need them here. */
87 /* like strncpy but does not 0 fill the buffer and always null
88 * terminates. bufsize is the size of the destination buffer */
89 size_t strlcpy(char *d, const char *s, size_t bufsize)
91 size_t len = strlen(s);
92 size_t ret = len;
93 if (bufsize <= 0) return 0;
94 if (len >= bufsize) len = bufsize-1;
95 memcpy(d, s, len);
96 d[len] = 0;
97 return ret;
100 /* like strncat but does not 0 fill the buffer and always null
101 * terminates. bufsize is the length of the buffer, which should
102 * be one more than the maximum resulting string length */
103 size_t strlcat(char *d, const char *s, size_t bufsize)
105 size_t len1 = strlen(d);
106 size_t len2 = strlen(s);
107 size_t ret = len1 + len2;
109 if (len1+len2 >= bufsize) {
110 len2 = bufsize - (len1+1);
112 if (len2 > 0) {
113 memcpy(d+len1, s, len2);
114 d[len1+len2] = 0;
116 return ret;
119 /* BB finish BB
121 cifs_umount
122 open nofollow - avoid symlink exposure?
123 get owner of dir see if matches self or if root
124 call system(umount argv) etc.
126 BB end finish BB */
128 static char * check_for_domain(char **);
131 static void mount_cifs_usage(void)
133 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
134 printf("\nMount the remote target, specified as a UNC name,");
135 printf(" to a local directory.\n\nOptions:\n");
136 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
137 printf("\nLess commonly used options:");
138 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
139 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
140 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
141 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
142 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
143 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
144 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
145 printf("\n\nRarely used options:");
146 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
147 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
148 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
149 printf("\n\tin6_addr");
150 printf("\n\nOptions are described in more detail in the manual page");
151 printf("\n\tman 8 mount.cifs\n");
152 printf("\nTo display the version number of the mount helper:");
153 printf("\n\t%s -V\n",thisprogram);
155 if(mountpassword) {
156 memset(mountpassword,0,64);
157 free(mountpassword);
159 exit(1);
162 /* caller frees username if necessary */
163 static char * getusername(void) {
164 char *username = NULL;
165 struct passwd *password = getpwuid(getuid());
167 if (password) {
168 username = password->pw_name;
170 return username;
173 static char * parse_cifs_url(char * unc_name)
175 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
176 return NULL;
179 static int open_cred_file(char * file_name)
181 char * line_buf;
182 char * temp_val;
183 FILE * fs;
184 int i, length;
185 fs = fopen(file_name,"r");
186 if(fs == NULL)
187 return errno;
188 line_buf = (char *)malloc(4096);
189 if(line_buf == NULL) {
190 fclose(fs);
191 return -ENOMEM;
194 while(fgets(line_buf,4096,fs)) {
195 /* parse line from credential file */
197 /* eat leading white space */
198 for(i=0;i<4086;i++) {
199 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
200 break;
201 /* if whitespace - skip past it */
203 if (strncasecmp("username",line_buf+i,8) == 0) {
204 temp_val = strchr(line_buf + i,'=');
205 if(temp_val) {
206 /* go past equals sign */
207 temp_val++;
208 for(length = 0;length<4087;length++) {
209 if ((temp_val[length] == '\n')
210 || (temp_val[length] == '\0')) {
211 break;
214 if(length > 4086) {
215 printf("mount.cifs failed due to malformed username in credentials file");
216 memset(line_buf,0,4096);
217 if(mountpassword) {
218 memset(mountpassword,0,64);
220 exit(1);
221 } else {
222 got_user = 1;
223 user_name = (char *)calloc(1 + length,1);
224 /* BB adding free of user_name string before exit,
225 not really necessary but would be cleaner */
226 strncpy(user_name,temp_val, length);
229 } else if (strncasecmp("password",line_buf+i,8) == 0) {
230 temp_val = strchr(line_buf+i,'=');
231 if(temp_val) {
232 /* go past equals sign */
233 temp_val++;
234 for(length = 0;length<65;length++) {
235 if ((temp_val[length] == '\n')
236 || (temp_val[length] == '\0')) {
237 break;
240 if(length > 64) {
241 printf("mount.cifs failed: password in credentials file too long\n");
242 memset(line_buf,0, 4096);
243 if(mountpassword) {
244 memset(mountpassword,0,64);
246 exit(1);
247 } else {
248 if(mountpassword == NULL) {
249 mountpassword = (char *)calloc(65,1);
250 } else
251 memset(mountpassword,0,64);
252 if(mountpassword) {
253 strncpy(mountpassword,temp_val,length);
254 got_password = 1;
258 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
259 temp_val = strchr(line_buf+i,'=');
260 if(temp_val) {
261 /* go past equals sign */
262 temp_val++;
263 if(verboseflag)
264 printf("\nDomain %s\n",temp_val);
265 for(length = 0;length<65;length++) {
266 if ((temp_val[length] == '\n')
267 || (temp_val[length] == '\0')) {
268 break;
271 if(length > 64) {
272 printf("mount.cifs failed: domain in credentials file too long\n");
273 if(mountpassword) {
274 memset(mountpassword,0,64);
276 exit(1);
277 } else {
278 if(domain_name == NULL) {
279 domain_name = (char *)calloc(65,1);
280 } else
281 memset(domain_name,0,64);
282 if(domain_name) {
283 strncpy(domain_name,temp_val,length);
284 got_domain = 1;
291 fclose(fs);
292 if(line_buf) {
293 memset(line_buf,0,4096);
294 free(line_buf);
296 return 0;
299 static int get_password_from_file(int file_descript, char * filename)
301 int rc = 0;
302 int i;
303 char c;
305 if(mountpassword == NULL)
306 mountpassword = (char *)calloc(65,1);
307 else
308 memset(mountpassword, 0, 64);
310 if (mountpassword == NULL) {
311 printf("malloc failed\n");
312 exit(1);
315 if(filename != NULL) {
316 file_descript = open(filename, O_RDONLY);
317 if(file_descript < 0) {
318 printf("mount.cifs failed. %s attempting to open password file %s\n",
319 strerror(errno),filename);
320 exit(1);
323 /* else file already open and fd provided */
325 for(i=0;i<64;i++) {
326 rc = read(file_descript,&c,1);
327 if(rc < 0) {
328 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
329 memset(mountpassword,0,64);
330 if(filename != NULL)
331 close(file_descript);
332 exit(1);
333 } else if(rc == 0) {
334 if(mountpassword[0] == 0) {
335 if(verboseflag)
336 printf("\nWarning: null password used since cifs password file empty");
338 break;
339 } else /* read valid character */ {
340 if((c == 0) || (c == '\n')) {
341 break;
342 } else
343 mountpassword[i] = c;
346 if((i == 64) && (verboseflag)) {
347 printf("\nWarning: password longer than 64 characters specified in cifs password file");
349 got_password = 1;
350 if(filename != NULL) {
351 close(file_descript);
354 return rc;
357 static int parse_options(char ** optionsp, int * filesys_flags)
359 const char * data;
360 char * percent_char = NULL;
361 char * value = NULL;
362 char * next_keyword = NULL;
363 char * out = NULL;
364 int out_len = 0;
365 int word_len;
366 int rc = 0;
367 char user[32];
368 char group[32];
370 if (!optionsp || !*optionsp)
371 return 1;
372 data = *optionsp;
374 if(verboseflag)
375 printf("parsing options: %s\n", data);
377 /* BB fixme check for separator override BB */
379 if (getuid()) {
380 got_uid = 1;
381 snprintf(user,sizeof(user),"%u",getuid());
382 got_gid = 1;
383 snprintf(group,sizeof(group),"%u",getgid());
386 /* while ((data = strsep(&options, ",")) != NULL) { */
387 while(data != NULL) {
388 /* check if ends with trailing comma */
389 if(*data == 0)
390 break;
392 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
393 /* data = next keyword */
394 /* value = next value ie stuff after equal sign */
396 next_keyword = strchr(data,','); /* BB handle sep= */
398 /* temporarily null terminate end of keyword=value pair */
399 if(next_keyword)
400 *next_keyword++ = 0;
402 /* temporarily null terminate keyword to make keyword and value distinct */
403 if ((value = strchr(data, '=')) != NULL) {
404 *value = '\0';
405 value++;
408 if (strncmp(data, "users",5) == 0) {
409 if(!value || !*value) {
410 goto nocopy;
412 } else if (strncmp(data, "user_xattr",10) == 0) {
413 /* do nothing - need to skip so not parsed as user name */
414 } else if (strncmp(data, "user", 4) == 0) {
416 if (!value || !*value) {
417 if(data[4] == '\0') {
418 if(verboseflag)
419 printf("\nskipping empty user mount parameter\n");
420 /* remove the parm since it would otherwise be confusing
421 to the kernel code which would think it was a real username */
422 goto nocopy;
423 } else {
424 printf("username specified with no parameter\n");
425 return 1; /* needs_arg; */
427 } else {
428 if (strnlen(value, 260) < 260) {
429 got_user=1;
430 percent_char = strchr(value,'%');
431 if(percent_char) {
432 *percent_char = ',';
433 if(mountpassword == NULL)
434 mountpassword = (char *)calloc(65,1);
435 if(mountpassword) {
436 if(got_password)
437 printf("\nmount.cifs warning - password specified twice\n");
438 got_password = 1;
439 percent_char++;
440 strncpy(mountpassword, percent_char,64);
441 /* remove password from username */
442 while(*percent_char != 0) {
443 *percent_char = ',';
444 percent_char++;
448 /* this is only case in which the user
449 name buf is not malloc - so we have to
450 check for domain name embedded within
451 the user name here since the later
452 call to check_for_domain will not be
453 invoked */
454 domain_name = check_for_domain(&value);
455 } else {
456 printf("username too long\n");
457 return 1;
460 } else if (strncmp(data, "pass", 4) == 0) {
461 if (!value || !*value) {
462 if(got_password) {
463 printf("\npassword specified twice, ignoring second\n");
464 } else
465 got_password = 1;
466 } else if (strnlen(value, 17) < 17) {
467 if(got_password)
468 printf("\nmount.cifs warning - password specified twice\n");
469 got_password = 1;
470 } else {
471 printf("password too long\n");
472 return 1;
474 } else if (strncmp(data, "sec", 3) == 0) {
475 if (value) {
476 if (!strcmp(value, "none"))
477 got_password = 1;
479 } else if (strncmp(data, "ip", 2) == 0) {
480 if (!value || !*value) {
481 printf("target ip address argument missing");
482 } else if (strnlen(value, 35) < 35) {
483 if(verboseflag)
484 printf("ip address %s override specified\n",value);
485 got_ip = 1;
486 } else {
487 printf("ip address too long\n");
488 return 1;
490 } else if ((strncmp(data, "unc", 3) == 0)
491 || (strncmp(data, "target", 6) == 0)
492 || (strncmp(data, "path", 4) == 0)) {
493 if (!value || !*value) {
494 printf("invalid path to network resource\n");
495 return 1; /* needs_arg; */
496 } else if(strnlen(value,5) < 5) {
497 printf("UNC name too short");
500 if (strnlen(value, 300) < 300) {
501 got_unc = 1;
502 if (strncmp(value, "//", 2) == 0) {
503 if(got_unc)
504 printf("unc name specified twice, ignoring second\n");
505 else
506 got_unc = 1;
507 } else if (strncmp(value, "\\\\", 2) != 0) {
508 printf("UNC Path does not begin with // or \\\\ \n");
509 return 1;
510 } else {
511 if(got_unc)
512 printf("unc name specified twice, ignoring second\n");
513 else
514 got_unc = 1;
516 } else {
517 printf("CIFS: UNC name too long\n");
518 return 1;
520 } else if ((strncmp(data, "domain", 3) == 0)
521 || (strncmp(data, "workgroup", 5) == 0)) {
522 if (!value || !*value) {
523 printf("CIFS: invalid domain name\n");
524 return 1; /* needs_arg; */
526 if (strnlen(value, 65) < 65) {
527 got_domain = 1;
528 } else {
529 printf("domain name too long\n");
530 return 1;
532 } else if (strncmp(data, "cred", 4) == 0) {
533 if (value && *value) {
534 rc = open_cred_file(value);
535 if(rc) {
536 printf("error %d opening credential file %s\n",rc, value);
537 return 1;
539 } else {
540 printf("invalid credential file name specified\n");
541 return 1;
543 } else if (strncmp(data, "uid", 3) == 0) {
544 if (value && *value) {
545 got_uid = 1;
546 if (!isdigit(*value)) {
547 struct passwd *pw;
549 if (!(pw = getpwnam(value))) {
550 printf("bad user name \"%s\"\n", value);
551 exit(1);
553 snprintf(user, sizeof(user), "%u", pw->pw_uid);
554 } else {
555 strlcpy(user,value,sizeof(user));
558 goto nocopy;
559 } else if (strncmp(data, "gid", 3) == 0) {
560 if (value && *value) {
561 got_gid = 1;
562 if (!isdigit(*value)) {
563 struct group *gr;
565 if (!(gr = getgrnam(value))) {
566 printf("bad group name \"%s\"\n", value);
567 exit(1);
569 snprintf(group, sizeof(group), "%u", gr->gr_gid);
570 } else {
571 strlcpy(group,value,sizeof(group));
574 goto nocopy;
575 /* fmask and dmask synonyms for people used to smbfs syntax */
576 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
577 if (!value || !*value) {
578 printf ("Option '%s' requires a numerical argument\n", data);
579 return 1;
582 if (value[0] != '0') {
583 printf ("WARNING: '%s' not expressed in octal.\n", data);
586 if (strcmp (data, "fmask") == 0) {
587 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
588 data = "file_mode"; /* BB fix this */
590 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
591 if (!value || !*value) {
592 printf ("Option '%s' requires a numerical argument\n", data);
593 return 1;
596 if (value[0] != '0') {
597 printf ("WARNING: '%s' not expressed in octal.\n", data);
600 if (strcmp (data, "dmask") == 0) {
601 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
602 data = "dir_mode";
604 /* the following eight mount options should be
605 stripped out from what is passed into the kernel
606 since these eight options are best passed as the
607 mount flags rather than redundantly to the kernel
608 and could generate spurious warnings depending on the
609 level of the corresponding cifs vfs kernel code */
610 } else if (strncmp(data, "nosuid", 6) == 0) {
611 *filesys_flags |= MS_NOSUID;
612 } else if (strncmp(data, "suid", 4) == 0) {
613 *filesys_flags &= ~MS_NOSUID;
614 } else if (strncmp(data, "nodev", 5) == 0) {
615 *filesys_flags |= MS_NODEV;
616 } else if ((strncmp(data, "nobrl", 5) == 0) ||
617 (strncmp(data, "nolock", 6) == 0)) {
618 *filesys_flags &= ~MS_MANDLOCK;
619 } else if (strncmp(data, "dev", 3) == 0) {
620 *filesys_flags &= ~MS_NODEV;
621 } else if (strncmp(data, "noexec", 6) == 0) {
622 *filesys_flags |= MS_NOEXEC;
623 } else if (strncmp(data, "exec", 4) == 0) {
624 *filesys_flags &= ~MS_NOEXEC;
625 } else if (strncmp(data, "guest", 5) == 0) {
626 got_password=1;
627 } else if (strncmp(data, "ro", 2) == 0) {
628 *filesys_flags |= MS_RDONLY;
629 } else if (strncmp(data, "rw", 2) == 0) {
630 *filesys_flags &= ~MS_RDONLY;
631 } else if (strncmp(data, "remount", 7) == 0) {
632 *filesys_flags |= MS_REMOUNT;
633 } /* else if (strnicmp(data, "port", 4) == 0) {
634 if (value && *value) {
635 vol->port =
636 simple_strtoul(value, &value, 0);
638 } else if (strnicmp(data, "rsize", 5) == 0) {
639 if (value && *value) {
640 vol->rsize =
641 simple_strtoul(value, &value, 0);
643 } else if (strnicmp(data, "wsize", 5) == 0) {
644 if (value && *value) {
645 vol->wsize =
646 simple_strtoul(value, &value, 0);
648 } else if (strnicmp(data, "version", 3) == 0) {
649 } else {
650 printf("CIFS: Unknown mount option %s\n",data);
651 } */ /* nothing to do on those four mount options above.
652 Just pass to kernel and ignore them here */
654 /* Copy (possibly modified) option to out */
655 word_len = strlen(data);
656 if (value)
657 word_len += 1 + strlen(value);
659 out = (char *)realloc(out, out_len + word_len + 2);
660 if (out == NULL) {
661 perror("malloc");
662 exit(1);
665 if (out_len) {
666 strlcat(out, ",", out_len + word_len + 2);
667 out_len++;
670 if (value)
671 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
672 else
673 snprintf(out + out_len, word_len + 1, "%s", data);
674 out_len = strlen(out);
676 nocopy:
677 data = next_keyword;
680 /* special-case the uid and gid */
681 if (got_uid) {
682 word_len = strlen(user);
684 out = (char *)realloc(out, out_len + word_len + 6);
685 if (out == NULL) {
686 perror("malloc");
687 exit(1);
690 if (out_len) {
691 strlcat(out, ",", out_len + word_len + 6);
692 out_len++;
694 snprintf(out + out_len, word_len + 5, "uid=%s", user);
695 out_len = strlen(out);
697 if (got_gid) {
698 word_len = strlen(group);
700 out = (char *)realloc(out, out_len + 1 + word_len + 6);
701 if (out == NULL) {
702 perror("malloc");
703 exit(1);
706 if (out_len) {
707 strlcat(out, ",", out_len + word_len + 6);
708 out_len++;
710 snprintf(out + out_len, word_len + 5, "gid=%s", group);
711 out_len = strlen(out);
714 free(*optionsp);
715 *optionsp = out;
716 return 0;
719 /* replace all (one or more) commas with double commas */
720 static void check_for_comma(char ** ppasswrd)
722 char *new_pass_buf;
723 char *pass;
724 int i,j;
725 int number_of_commas = 0;
726 int len;
728 if(ppasswrd == NULL)
729 return;
730 else
731 (pass = *ppasswrd);
733 len = strlen(pass);
735 for(i=0;i<len;i++) {
736 if(pass[i] == ',')
737 number_of_commas++;
740 if(number_of_commas == 0)
741 return;
742 if(number_of_commas > 64) {
743 /* would otherwise overflow the mount options buffer */
744 printf("\nInvalid password. Password contains too many commas.\n");
745 return;
748 new_pass_buf = (char *)malloc(len+number_of_commas+1);
749 if(new_pass_buf == NULL)
750 return;
752 for(i=0,j=0;i<len;i++,j++) {
753 new_pass_buf[j] = pass[i];
754 if(pass[i] == ',') {
755 j++;
756 new_pass_buf[j] = pass[i];
759 new_pass_buf[len+number_of_commas] = 0;
761 free(*ppasswrd);
762 *ppasswrd = new_pass_buf;
764 return;
767 /* Usernames can not have backslash in them and we use
768 [BB check if usernames can have forward slash in them BB]
769 backslash as domain\user separator character
771 static char * check_for_domain(char **ppuser)
773 char * original_string;
774 char * usernm;
775 char * domainnm;
776 int original_len;
777 int len;
778 int i;
780 if(ppuser == NULL)
781 return NULL;
783 original_string = *ppuser;
785 if (original_string == NULL)
786 return NULL;
788 original_len = strlen(original_string);
790 usernm = strchr(*ppuser,'/');
791 if (usernm == NULL) {
792 usernm = strchr(*ppuser,'\\');
793 if (usernm == NULL)
794 return NULL;
797 if(got_domain) {
798 printf("Domain name specified twice. Username probably malformed\n");
799 return NULL;
802 usernm[0] = 0;
803 domainnm = *ppuser;
804 if (domainnm[0] != 0) {
805 got_domain = 1;
806 } else {
807 printf("null domain\n");
809 len = strlen(domainnm);
810 /* reset domainm to new buffer, and copy
811 domain name into it */
812 domainnm = (char *)malloc(len+1);
813 if(domainnm == NULL)
814 return NULL;
816 strlcpy(domainnm,*ppuser,len+1);
818 /* move_string(*ppuser, usernm+1) */
819 len = strlen(usernm+1);
821 if(len >= original_len) {
822 /* should not happen */
823 return domainnm;
826 for(i=0;i<original_len;i++) {
827 if(i<len)
828 original_string[i] = usernm[i+1];
829 else /* stuff with commas to remove last parm */
830 original_string[i] = ',';
833 /* BB add check for more than one slash?
834 strchr(*ppuser,'/');
835 strchr(*ppuser,'\\')
838 return domainnm;
841 /* replace all occurances of "from" in a string with "to" */
842 static void replace_char(char *string, char from, char to, int maxlen)
844 char *lastchar = string + maxlen;
845 while (string) {
846 string = strchr(string, from);
847 if (string) {
848 *string = to;
849 if (string >= lastchar)
850 return;
855 /* Note that caller frees the returned buffer if necessary */
856 static char * parse_server(char ** punc_name)
858 char * unc_name = *punc_name;
859 int length = strnlen(unc_name, MAX_UNC_LEN);
860 char * share;
861 char * ipaddress_string = NULL;
862 struct hostent * host_entry = NULL;
863 struct in_addr server_ipaddr;
865 if(length > (MAX_UNC_LEN - 1)) {
866 printf("mount error: UNC name too long");
867 return NULL;
869 if (strncasecmp("cifs://",unc_name,7) == 0)
870 return parse_cifs_url(unc_name+7);
871 if (strncasecmp("smb://",unc_name,6) == 0) {
872 return parse_cifs_url(unc_name+6);
875 if(length < 3) {
876 /* BB add code to find DFS root here */
877 printf("\nMounting the DFS root for domain not implemented yet\n");
878 return NULL;
879 } else {
880 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
881 /* check for nfs syntax ie server:share */
882 share = strchr(unc_name,':');
883 if(share) {
884 *punc_name = (char *)malloc(length+3);
885 if(*punc_name == NULL) {
886 /* put the original string back if
887 no memory left */
888 *punc_name = unc_name;
889 return NULL;
891 *share = '/';
892 strncpy((*punc_name)+2,unc_name,length);
893 free(unc_name);
894 unc_name = *punc_name;
895 unc_name[length+2] = 0;
896 goto continue_unc_parsing;
897 } else {
898 printf("mount error: improperly formatted UNC name.");
899 printf(" %s does not begin with \\\\ or //\n",unc_name);
900 return NULL;
902 } else {
903 continue_unc_parsing:
904 unc_name[0] = '/';
905 unc_name[1] = '/';
906 unc_name += 2;
908 /* allow for either delimiter between host and sharename */
909 if ((share = strpbrk(unc_name, "/\\"))) {
910 *share = 0; /* temporarily terminate the string */
911 share += 1;
912 if(got_ip == 0) {
913 host_entry = gethostbyname(unc_name);
915 *(share - 1) = '/'; /* put delimiter back */
917 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
918 if ((prefixpath = strpbrk(share, "/\\"))) {
919 *prefixpath = 0; /* permanently terminate the string */
920 if (!strlen(++prefixpath))
921 prefixpath = NULL; /* this needs to be done explicitly */
923 if(got_ip) {
924 if(verboseflag)
925 printf("ip address specified explicitly\n");
926 return NULL;
928 if(host_entry == NULL) {
929 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
930 return NULL;
931 } else {
932 /* BB should we pass an alternate version of the share name as Unicode */
933 /* BB what about ipv6? BB */
934 /* BB add retries with alternate servers in list */
936 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
938 ipaddress_string = inet_ntoa(server_ipaddr);
939 if(ipaddress_string == NULL) {
940 printf("mount error: could not get valid ip address for target server\n");
941 return NULL;
943 return ipaddress_string;
945 } else {
946 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
947 printf("Mounting the DFS root for a particular server not implemented yet\n");
948 return NULL;
954 static struct option longopts[] = {
955 { "all", 0, NULL, 'a' },
956 { "help",0, NULL, 'h' },
957 { "move",0, NULL, 'm' },
958 { "bind",0, NULL, 'b' },
959 { "read-only", 0, NULL, 'r' },
960 { "ro", 0, NULL, 'r' },
961 { "verbose", 0, NULL, 'v' },
962 { "version", 0, NULL, 'V' },
963 { "read-write", 0, NULL, 'w' },
964 { "rw", 0, NULL, 'w' },
965 { "options", 1, NULL, 'o' },
966 { "type", 1, NULL, 't' },
967 { "rsize",1, NULL, 'R' },
968 { "wsize",1, NULL, 'W' },
969 { "uid", 1, NULL, '1'},
970 { "gid", 1, NULL, '2'},
971 { "user",1,NULL,'u'},
972 { "username",1,NULL,'u'},
973 { "dom",1,NULL,'d'},
974 { "domain",1,NULL,'d'},
975 { "password",1,NULL,'p'},
976 { "pass",1,NULL,'p'},
977 { "credentials",1,NULL,'c'},
978 { "port",1,NULL,'P'},
979 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
980 { NULL, 0, NULL, 0 }
983 /* convert a string to uppercase. return false if the string
984 * wasn't ASCII or was a NULL ptr */
985 static int
986 uppercase_string(char *string)
988 if (!string)
989 return 0;
991 while (*string) {
992 /* check for unicode */
993 if ((unsigned char) string[0] & 0x80)
994 return 0;
995 *string = toupper((unsigned char) *string);
996 string++;
999 return 1;
1002 int main(int argc, char ** argv)
1004 int c;
1005 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1006 char * orgoptions = NULL;
1007 char * share_name = NULL;
1008 char * ipaddr = NULL;
1009 char * uuid = NULL;
1010 char * mountpoint = NULL;
1011 char * options = NULL;
1012 char * resolved_path = NULL;
1013 char * temp;
1014 char * dev_name;
1015 int rc;
1016 int rsize = 0;
1017 int wsize = 0;
1018 int nomtab = 0;
1019 int uid = 0;
1020 int gid = 0;
1021 int optlen = 0;
1022 int orgoptlen = 0;
1023 size_t options_size = 0;
1024 int retry = 0; /* set when we have to retry mount with uppercase */
1025 struct stat statbuf;
1026 struct utsname sysinfo;
1027 struct mntent mountent;
1028 FILE * pmntfile;
1030 /* setlocale(LC_ALL, "");
1031 bindtextdomain(PACKAGE, LOCALEDIR);
1032 textdomain(PACKAGE); */
1034 if(argc && argv) {
1035 thisprogram = argv[0];
1036 } else {
1037 mount_cifs_usage();
1038 exit(1);
1041 if(thisprogram == NULL)
1042 thisprogram = "mount.cifs";
1044 uname(&sysinfo);
1045 /* BB add workstation name and domain and pass down */
1047 /* #ifdef _GNU_SOURCE
1048 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1049 #endif */
1050 if(argc > 2) {
1051 dev_name = argv[1];
1052 share_name = strndup(argv[1], MAX_UNC_LEN);
1053 if (share_name == NULL) {
1054 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1055 exit(1);
1057 mountpoint = argv[2];
1058 } else {
1059 mount_cifs_usage();
1060 exit(1);
1063 /* add sharename in opts string as unc= parm */
1065 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1066 longopts, NULL)) != -1) {
1067 switch (c) {
1068 /* No code to do the following options yet */
1069 /* case 'l':
1070 list_with_volumelabel = 1;
1071 break;
1072 case 'L':
1073 volumelabel = optarg;
1074 break; */
1075 /* case 'a':
1076 ++mount_all;
1077 break; */
1079 case '?':
1080 case 'h': /* help */
1081 mount_cifs_usage ();
1082 exit(1);
1083 case 'n':
1084 ++nomtab;
1085 break;
1086 case 'b':
1087 #ifdef MS_BIND
1088 flags |= MS_BIND;
1089 #else
1090 fprintf(stderr,
1091 "option 'b' (MS_BIND) not supported\n");
1092 #endif
1093 break;
1094 case 'm':
1095 #ifdef MS_MOVE
1096 flags |= MS_MOVE;
1097 #else
1098 fprintf(stderr,
1099 "option 'm' (MS_MOVE) not supported\n");
1100 #endif
1101 break;
1102 case 'o':
1103 orgoptions = strdup(optarg);
1104 break;
1105 case 'r': /* mount readonly */
1106 flags |= MS_RDONLY;
1107 break;
1108 case 'U':
1109 uuid = optarg;
1110 break;
1111 case 'v':
1112 ++verboseflag;
1113 break;
1114 case 'V':
1115 printf ("mount.cifs version: %s.%s%s\n",
1116 MOUNT_CIFS_VERSION_MAJOR,
1117 MOUNT_CIFS_VERSION_MINOR,
1118 MOUNT_CIFS_VENDOR_SUFFIX);
1119 if(mountpassword) {
1120 memset(mountpassword,0,64);
1122 exit (0);
1123 case 'w':
1124 flags &= ~MS_RDONLY;
1125 break;
1126 case 'R':
1127 rsize = atoi(optarg) ;
1128 break;
1129 case 'W':
1130 wsize = atoi(optarg);
1131 break;
1132 case '1':
1133 if (isdigit(*optarg)) {
1134 char *ep;
1136 uid = strtoul(optarg, &ep, 10);
1137 if (*ep) {
1138 printf("bad uid value \"%s\"\n", optarg);
1139 exit(1);
1141 } else {
1142 struct passwd *pw;
1144 if (!(pw = getpwnam(optarg))) {
1145 printf("bad user name \"%s\"\n", optarg);
1146 exit(1);
1148 uid = pw->pw_uid;
1149 endpwent();
1151 break;
1152 case '2':
1153 if (isdigit(*optarg)) {
1154 char *ep;
1156 gid = strtoul(optarg, &ep, 10);
1157 if (*ep) {
1158 printf("bad gid value \"%s\"\n", optarg);
1159 exit(1);
1161 } else {
1162 struct group *gr;
1164 if (!(gr = getgrnam(optarg))) {
1165 printf("bad user name \"%s\"\n", optarg);
1166 exit(1);
1168 gid = gr->gr_gid;
1169 endpwent();
1171 break;
1172 case 'u':
1173 got_user = 1;
1174 user_name = optarg;
1175 break;
1176 case 'd':
1177 domain_name = optarg; /* BB fix this - currently ignored */
1178 got_domain = 1;
1179 break;
1180 case 'p':
1181 if(mountpassword == NULL)
1182 mountpassword = (char *)calloc(65,1);
1183 if(mountpassword) {
1184 got_password = 1;
1185 strncpy(mountpassword,optarg,64);
1187 break;
1188 case 'S':
1189 get_password_from_file(0 /* stdin */,NULL);
1190 break;
1191 case 't':
1192 break;
1193 default:
1194 printf("unknown mount option %c\n",c);
1195 mount_cifs_usage();
1196 exit(1);
1200 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1201 mount_cifs_usage();
1202 exit(1);
1205 if (getenv("PASSWD")) {
1206 if(mountpassword == NULL)
1207 mountpassword = (char *)calloc(65,1);
1208 if(mountpassword) {
1209 strncpy(mountpassword,getenv("PASSWD"),64);
1210 got_password = 1;
1212 } else if (getenv("PASSWD_FD")) {
1213 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1214 } else if (getenv("PASSWD_FILE")) {
1215 get_password_from_file(0, getenv("PASSWD_FILE"));
1218 if (orgoptions && parse_options(&orgoptions, &flags)) {
1219 rc = -1;
1220 goto mount_exit;
1222 ipaddr = parse_server(&share_name);
1223 if((ipaddr == NULL) && (got_ip == 0)) {
1224 printf("No ip address specified and hostname not found\n");
1225 rc = -1;
1226 goto mount_exit;
1229 /* BB save off path and pop after mount returns? */
1230 resolved_path = (char *)malloc(PATH_MAX+1);
1231 if(resolved_path) {
1232 /* Note that if we can not canonicalize the name, we get
1233 another chance to see if it is valid when we chdir to it */
1234 if (realpath(mountpoint, resolved_path)) {
1235 mountpoint = resolved_path;
1238 if(chdir(mountpoint)) {
1239 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1240 rc = -1;
1241 goto mount_exit;
1244 if(stat (".", &statbuf)) {
1245 printf("mount error: mount point %s does not exist\n",mountpoint);
1246 rc = -1;
1247 goto mount_exit;
1250 if (S_ISDIR(statbuf.st_mode) == 0) {
1251 printf("mount error: mount point %s is not a directory\n",mountpoint);
1252 rc = -1;
1253 goto mount_exit;
1256 if((getuid() != 0) && (geteuid() == 0)) {
1257 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1258 #ifndef CIFS_ALLOW_USR_SUID
1259 /* Do not allow user mounts to control suid flag
1260 for mount unless explicitly built that way */
1261 flags |= MS_NOSUID | MS_NODEV;
1262 #endif
1263 } else {
1264 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1265 return -1;
1269 if(got_user == 0) {
1270 user_name = getusername();
1271 got_user = 1;
1274 if(got_password == 0) {
1275 mountpassword = getpass("Password: "); /* BB obsolete */
1276 got_password = 1;
1278 /* FIXME launch daemon (handles dfs name resolution and credential change)
1279 remember to clear parms and overwrite password field before launching */
1280 mount_retry:
1281 if(orgoptions) {
1282 optlen = strlen(orgoptions);
1283 orgoptlen = optlen;
1284 } else
1285 optlen = 0;
1286 if(share_name)
1287 optlen += strlen(share_name) + 4;
1288 else {
1289 printf("No server share name specified\n");
1290 printf("\nMounting the DFS root for server not implemented yet\n");
1291 exit(1);
1293 if(user_name)
1294 optlen += strlen(user_name) + 6;
1295 if(ipaddr)
1296 optlen += strlen(ipaddr) + 4;
1297 if(mountpassword)
1298 optlen += strlen(mountpassword) + 6;
1299 if(options)
1300 free(options);
1301 options_size = optlen + 10 + 64;
1302 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 */);
1304 if(options == NULL) {
1305 printf("Could not allocate memory for mount options\n");
1306 return -1;
1309 options[0] = 0;
1310 strlcpy(options,"unc=",options_size);
1311 strlcat(options,share_name,options_size);
1312 /* scan backwards and reverse direction of slash */
1313 temp = strrchr(options, '/');
1314 if(temp > options + 6)
1315 *temp = '\\';
1316 if(ipaddr) {
1317 strlcat(options,",ip=",options_size);
1318 strlcat(options,ipaddr,options_size);
1321 if(user_name) {
1322 /* check for syntax like user=domain\user */
1323 if(got_domain == 0)
1324 domain_name = check_for_domain(&user_name);
1325 strlcat(options,",user=",options_size);
1326 strlcat(options,user_name,options_size);
1328 if(retry == 0) {
1329 if(domain_name) {
1330 /* extra length accounted for in option string above */
1331 strlcat(options,",domain=",options_size);
1332 strlcat(options,domain_name,options_size);
1335 if(mountpassword) {
1336 /* Commas have to be doubled, or else they will
1337 look like the parameter separator */
1338 /* if(sep is not set)*/
1339 if(retry == 0)
1340 check_for_comma(&mountpassword);
1341 strlcat(options,",pass=",options_size);
1342 strlcat(options,mountpassword,options_size);
1345 strlcat(options,",ver=",options_size);
1346 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1348 if(orgoptions) {
1349 strlcat(options,",",options_size);
1350 strlcat(options,orgoptions,options_size);
1352 if(prefixpath) {
1353 strlcat(options,",prefixpath=",options_size);
1354 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1356 if(verboseflag)
1357 printf("\nmount.cifs kernel mount options %s \n",options);
1359 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1360 replace_char(dev_name, '\\', '/', strlen(share_name));
1362 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1363 /* remember to kill daemon on error */
1364 switch (errno) {
1365 case 0:
1366 printf("mount failed but no error number set\n");
1367 break;
1368 case ENODEV:
1369 printf("mount error: cifs filesystem not supported by the system\n");
1370 break;
1371 case ENXIO:
1372 if(retry == 0) {
1373 retry = 1;
1374 if (uppercase_string(dev_name) &&
1375 uppercase_string(share_name) &&
1376 uppercase_string(prefixpath)) {
1377 printf("retrying with upper case share name\n");
1378 goto mount_retry;
1381 default:
1382 printf("mount error %d = %s\n",errno,strerror(errno));
1384 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1385 rc = -1;
1386 goto mount_exit;
1387 } else {
1388 pmntfile = setmntent(MOUNTED, "a+");
1389 if(pmntfile) {
1390 mountent.mnt_fsname = dev_name;
1391 mountent.mnt_dir = mountpoint;
1392 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1393 mountent.mnt_opts = (char *)malloc(220);
1394 if(mountent.mnt_opts) {
1395 char * mount_user = getusername();
1396 memset(mountent.mnt_opts,0,200);
1397 if(flags & MS_RDONLY)
1398 strlcat(mountent.mnt_opts,"ro",220);
1399 else
1400 strlcat(mountent.mnt_opts,"rw",220);
1401 if(flags & MS_MANDLOCK)
1402 strlcat(mountent.mnt_opts,",mand",220);
1403 if(flags & MS_NOEXEC)
1404 strlcat(mountent.mnt_opts,",noexec",220);
1405 if(flags & MS_NOSUID)
1406 strlcat(mountent.mnt_opts,",nosuid",220);
1407 if(flags & MS_NODEV)
1408 strlcat(mountent.mnt_opts,",nodev",220);
1409 if(flags & MS_SYNCHRONOUS)
1410 strlcat(mountent.mnt_opts,",synch",220);
1411 if(mount_user) {
1412 if(getuid() != 0) {
1413 strlcat(mountent.mnt_opts,",user=",220);
1414 strlcat(mountent.mnt_opts,mount_user,220);
1416 /* free(mount_user); do not free static mem */
1419 mountent.mnt_freq = 0;
1420 mountent.mnt_passno = 0;
1421 rc = addmntent(pmntfile,&mountent);
1422 endmntent(pmntfile);
1423 if(mountent.mnt_opts)
1424 free(mountent.mnt_opts);
1425 } else {
1426 printf("could not update mount table\n");
1429 rc = 0;
1430 mount_exit:
1431 if(mountpassword) {
1432 int len = strlen(mountpassword);
1433 memset(mountpassword,0,len);
1434 free(mountpassword);
1437 if(options) {
1438 memset(options,0,optlen);
1439 free(options);
1442 if(orgoptions) {
1443 memset(orgoptions,0,orgoptlen);
1444 free(orgoptions);
1446 if(resolved_path) {
1447 free(resolved_path);
1450 free(share_name);
1451 return rc;