Make mount.cifs.c consistent with other Samba code usage w.r.t
[Samba.git] / source / client / mount.cifs.c
blob1b67a5e8e6dc4319009099269b1fafa9af03d5e4
1 /*
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/>. */
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/mount.h>
31 #include <sys/stat.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <getopt.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <mntent.h>
40 #include <fcntl.h>
41 #include <limits.h>
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
47 #ifdef _SAMBA_BUILD_
48 #include "include/version.h"
49 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
51 #else
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
54 #else
55 #define MOUNT_CIFS_VENDOR_SUFFIX ""
56 #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
59 #ifndef MS_MOVE
60 #define MS_MOVE 8192
61 #endif
63 #ifndef MS_BIND
64 #define MS_BIND 4096
65 #endif
67 #define MAX_UNC_LEN 1024
69 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
71 #ifndef SAFE_FREE
72 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
73 #endif
75 #define MOUNT_PASSWD_SIZE 64
76 #define DOMAIN_SIZE 64
78 const char *thisprogram;
79 int verboseflag = 0;
80 static int got_password = 0;
81 static int got_user = 0;
82 static int got_domain = 0;
83 static int got_ip = 0;
84 static int got_unc = 0;
85 static int got_uid = 0;
86 static int got_gid = 0;
87 static char * user_name = NULL;
88 static char * mountpassword = NULL;
89 char * domain_name = NULL;
90 char * prefixpath = NULL;
92 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93 * don't link to libreplace so need them here. */
95 /* like strncpy but does not 0 fill the buffer and always null
96 * terminates. bufsize is the size of the destination buffer */
97 size_t strlcpy(char *d, const char *s, size_t bufsize)
99 size_t len = strlen(s);
100 size_t ret = len;
101 if (bufsize <= 0) return 0;
102 if (len >= bufsize) len = bufsize-1;
103 memcpy(d, s, len);
104 d[len] = 0;
105 return ret;
108 /* like strncat but does not 0 fill the buffer and always null
109 * terminates. bufsize is the length of the buffer, which should
110 * be one more than the maximum resulting string length */
111 size_t strlcat(char *d, const char *s, size_t bufsize)
113 size_t len1 = strlen(d);
114 size_t len2 = strlen(s);
115 size_t ret = len1 + len2;
117 if (len1+len2 >= bufsize) {
118 len2 = bufsize - (len1+1);
120 if (len2 > 0) {
121 memcpy(d+len1, s, len2);
122 d[len1+len2] = 0;
124 return ret;
127 /* BB finish BB
129 cifs_umount
130 open nofollow - avoid symlink exposure?
131 get owner of dir see if matches self or if root
132 call system(umount argv) etc.
134 BB end finish BB */
136 static char * check_for_domain(char **);
139 static void mount_cifs_usage(void)
141 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
142 printf("\nMount the remote target, specified as a UNC name,");
143 printf(" to a local directory.\n\nOptions:\n");
144 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
145 printf("\nLess commonly used options:");
146 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
147 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
148 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
149 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
150 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
151 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
152 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
153 printf("\n\nRarely used options:");
154 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
155 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
156 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
157 printf("\n\tin6_addr");
158 printf("\n\nOptions are described in more detail in the manual page");
159 printf("\n\tman 8 mount.cifs\n");
160 printf("\nTo display the version number of the mount helper:");
161 printf("\n\t%s -V\n",thisprogram);
163 SAFE_FREE(mountpassword);
164 exit(1);
167 /* caller frees username if necessary */
168 static char * getusername(void) {
169 char *username = NULL;
170 struct passwd *password = getpwuid(getuid());
172 if (password) {
173 username = password->pw_name;
175 return username;
178 static char * parse_cifs_url(char * unc_name)
180 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
181 return NULL;
184 static int open_cred_file(char * file_name)
186 char * line_buf;
187 char * temp_val;
188 FILE * fs;
189 int i, length;
190 fs = fopen(file_name,"r");
191 if(fs == NULL)
192 return errno;
193 line_buf = (char *)malloc(4096);
194 if(line_buf == NULL) {
195 fclose(fs);
196 return -ENOMEM;
199 while(fgets(line_buf,4096,fs)) {
200 /* parse line from credential file */
202 /* eat leading white space */
203 for(i=0;i<4086;i++) {
204 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
205 break;
206 /* if whitespace - skip past it */
208 if (strncasecmp("username",line_buf+i,8) == 0) {
209 temp_val = strchr(line_buf + i,'=');
210 if(temp_val) {
211 /* go past equals sign */
212 temp_val++;
213 for(length = 0;length<4087;length++) {
214 if ((temp_val[length] == '\n')
215 || (temp_val[length] == '\0')) {
216 break;
219 if(length > 4086) {
220 printf("mount.cifs failed due to malformed username in credentials file");
221 memset(line_buf,0,4096);
222 exit(1);
223 } else {
224 got_user = 1;
225 user_name = (char *)calloc(1 + length,1);
226 /* BB adding free of user_name string before exit,
227 not really necessary but would be cleaner */
228 strlcpy(user_name,temp_val, length+1);
231 } else if (strncasecmp("password",line_buf+i,8) == 0) {
232 temp_val = strchr(line_buf+i,'=');
233 if(temp_val) {
234 /* go past equals sign */
235 temp_val++;
236 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
237 if ((temp_val[length] == '\n')
238 || (temp_val[length] == '\0')) {
239 break;
242 if(length > MOUNT_PASSWD_SIZE) {
243 printf("mount.cifs failed: password in credentials file too long\n");
244 memset(line_buf,0, 4096);
245 exit(1);
246 } else {
247 if(mountpassword == NULL) {
248 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
249 } else
250 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
251 if(mountpassword) {
252 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
253 got_password = 1;
257 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
258 temp_val = strchr(line_buf+i,'=');
259 if(temp_val) {
260 /* go past equals sign */
261 temp_val++;
262 if(verboseflag)
263 printf("\nDomain %s\n",temp_val);
264 for(length = 0;length<DOMAIN_SIZE+1;length++) {
265 if ((temp_val[length] == '\n')
266 || (temp_val[length] == '\0')) {
267 break;
270 if(length > DOMAIN_SIZE) {
271 printf("mount.cifs failed: domain in credentials file too long\n");
272 exit(1);
273 } else {
274 if(domain_name == NULL) {
275 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
276 } else
277 memset(domain_name,0,DOMAIN_SIZE);
278 if(domain_name) {
279 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
280 got_domain = 1;
287 fclose(fs);
288 SAFE_FREE(line_buf);
289 return 0;
292 static int get_password_from_file(int file_descript, char * filename)
294 int rc = 0;
295 int i;
296 char c;
298 if(mountpassword == NULL)
299 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
300 else
301 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
303 if (mountpassword == NULL) {
304 printf("malloc failed\n");
305 exit(1);
308 if(filename != NULL) {
309 file_descript = open(filename, O_RDONLY);
310 if(file_descript < 0) {
311 printf("mount.cifs failed. %s attempting to open password file %s\n",
312 strerror(errno),filename);
313 exit(1);
316 /* else file already open and fd provided */
318 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
319 rc = read(file_descript,&c,1);
320 if(rc < 0) {
321 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
322 if(filename != NULL)
323 close(file_descript);
324 exit(1);
325 } else if(rc == 0) {
326 if(mountpassword[0] == 0) {
327 if(verboseflag)
328 printf("\nWarning: null password used since cifs password file empty");
330 break;
331 } else /* read valid character */ {
332 if((c == 0) || (c == '\n')) {
333 break;
334 } else
335 mountpassword[i] = c;
338 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
339 printf("\nWarning: password longer than %d characters specified in cifs password file",
340 MOUNT_PASSWD_SIZE);
342 got_password = 1;
343 if(filename != NULL) {
344 close(file_descript);
347 return rc;
350 static int parse_options(char ** optionsp, int * filesys_flags)
352 const char * data;
353 char * percent_char = NULL;
354 char * value = NULL;
355 char * next_keyword = NULL;
356 char * out = NULL;
357 int out_len = 0;
358 int word_len;
359 int rc = 0;
360 char user[32];
361 char group[32];
363 if (!optionsp || !*optionsp)
364 return 1;
365 data = *optionsp;
367 if(verboseflag)
368 printf("parsing options: %s\n", data);
370 /* BB fixme check for separator override BB */
372 if (getuid()) {
373 got_uid = 1;
374 snprintf(user,sizeof(user),"%u",getuid());
375 got_gid = 1;
376 snprintf(group,sizeof(group),"%u",getgid());
379 /* while ((data = strsep(&options, ",")) != NULL) { */
380 while(data != NULL) {
381 /* check if ends with trailing comma */
382 if(*data == 0)
383 break;
385 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
386 /* data = next keyword */
387 /* value = next value ie stuff after equal sign */
389 next_keyword = strchr(data,','); /* BB handle sep= */
391 /* temporarily null terminate end of keyword=value pair */
392 if(next_keyword)
393 *next_keyword++ = 0;
395 /* temporarily null terminate keyword to make keyword and value distinct */
396 if ((value = strchr(data, '=')) != NULL) {
397 *value = '\0';
398 value++;
401 if (strncmp(data, "users",5) == 0) {
402 if(!value || !*value) {
403 goto nocopy;
405 } else if (strncmp(data, "user_xattr",10) == 0) {
406 /* do nothing - need to skip so not parsed as user name */
407 } else if (strncmp(data, "user", 4) == 0) {
409 if (!value || !*value) {
410 if(data[4] == '\0') {
411 if(verboseflag)
412 printf("\nskipping empty user mount parameter\n");
413 /* remove the parm since it would otherwise be confusing
414 to the kernel code which would think it was a real username */
415 goto nocopy;
416 } else {
417 printf("username specified with no parameter\n");
418 return 1; /* needs_arg; */
420 } else {
421 if (strnlen(value, 260) < 260) {
422 got_user=1;
423 percent_char = strchr(value,'%');
424 if(percent_char) {
425 *percent_char = ',';
426 if(mountpassword == NULL)
427 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
428 if(mountpassword) {
429 if(got_password)
430 printf("\nmount.cifs warning - password specified twice\n");
431 got_password = 1;
432 percent_char++;
433 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
434 /* remove password from username */
435 while(*percent_char != 0) {
436 *percent_char = ',';
437 percent_char++;
441 /* this is only case in which the user
442 name buf is not malloc - so we have to
443 check for domain name embedded within
444 the user name here since the later
445 call to check_for_domain will not be
446 invoked */
447 domain_name = check_for_domain(&value);
448 } else {
449 printf("username too long\n");
450 return 1;
453 } else if (strncmp(data, "pass", 4) == 0) {
454 if (!value || !*value) {
455 if(got_password) {
456 printf("\npassword specified twice, ignoring second\n");
457 } else
458 got_password = 1;
459 } else if (strnlen(value, 17) < 17) {
460 if(got_password)
461 printf("\nmount.cifs warning - password specified twice\n");
462 got_password = 1;
463 } else {
464 printf("password too long\n");
465 return 1;
467 } else if (strncmp(data, "sec", 3) == 0) {
468 if (value) {
469 if (!strcmp(value, "none"))
470 got_password = 1;
472 } else if (strncmp(data, "ip", 2) == 0) {
473 if (!value || !*value) {
474 printf("target ip address argument missing");
475 } else if (strnlen(value, 35) < 35) {
476 if(verboseflag)
477 printf("ip address %s override specified\n",value);
478 got_ip = 1;
479 } else {
480 printf("ip address too long\n");
481 return 1;
483 } else if ((strncmp(data, "unc", 3) == 0)
484 || (strncmp(data, "target", 6) == 0)
485 || (strncmp(data, "path", 4) == 0)) {
486 if (!value || !*value) {
487 printf("invalid path to network resource\n");
488 return 1; /* needs_arg; */
489 } else if(strnlen(value,5) < 5) {
490 printf("UNC name too short");
493 if (strnlen(value, 300) < 300) {
494 got_unc = 1;
495 if (strncmp(value, "//", 2) == 0) {
496 if(got_unc)
497 printf("unc name specified twice, ignoring second\n");
498 else
499 got_unc = 1;
500 } else if (strncmp(value, "\\\\", 2) != 0) {
501 printf("UNC Path does not begin with // or \\\\ \n");
502 return 1;
503 } else {
504 if(got_unc)
505 printf("unc name specified twice, ignoring second\n");
506 else
507 got_unc = 1;
509 } else {
510 printf("CIFS: UNC name too long\n");
511 return 1;
513 } else if ((strncmp(data, "domain", 3) == 0)
514 || (strncmp(data, "workgroup", 5) == 0)) {
515 if (!value || !*value) {
516 printf("CIFS: invalid domain name\n");
517 return 1; /* needs_arg; */
519 if (strnlen(value, 65) < 65) {
520 got_domain = 1;
521 } else {
522 printf("domain name too long\n");
523 return 1;
525 } else if (strncmp(data, "cred", 4) == 0) {
526 if (value && *value) {
527 rc = open_cred_file(value);
528 if(rc) {
529 printf("error %d opening credential file %s\n",rc, value);
530 return 1;
532 } else {
533 printf("invalid credential file name specified\n");
534 return 1;
536 } else if (strncmp(data, "uid", 3) == 0) {
537 if (value && *value) {
538 got_uid = 1;
539 if (!isdigit(*value)) {
540 struct passwd *pw;
542 if (!(pw = getpwnam(value))) {
543 printf("bad user name \"%s\"\n", value);
544 exit(1);
546 snprintf(user, sizeof(user), "%u", pw->pw_uid);
547 } else {
548 strlcpy(user,value,sizeof(user));
551 goto nocopy;
552 } else if (strncmp(data, "gid", 3) == 0) {
553 if (value && *value) {
554 got_gid = 1;
555 if (!isdigit(*value)) {
556 struct group *gr;
558 if (!(gr = getgrnam(value))) {
559 printf("bad group name \"%s\"\n", value);
560 exit(1);
562 snprintf(group, sizeof(group), "%u", gr->gr_gid);
563 } else {
564 strlcpy(group,value,sizeof(group));
567 goto nocopy;
568 /* fmask and dmask synonyms for people used to smbfs syntax */
569 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
570 if (!value || !*value) {
571 printf ("Option '%s' requires a numerical argument\n", data);
572 return 1;
575 if (value[0] != '0') {
576 printf ("WARNING: '%s' not expressed in octal.\n", data);
579 if (strcmp (data, "fmask") == 0) {
580 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
581 data = "file_mode"; /* BB fix this */
583 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
584 if (!value || !*value) {
585 printf ("Option '%s' requires a numerical argument\n", data);
586 return 1;
589 if (value[0] != '0') {
590 printf ("WARNING: '%s' not expressed in octal.\n", data);
593 if (strcmp (data, "dmask") == 0) {
594 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
595 data = "dir_mode";
597 /* the following eight mount options should be
598 stripped out from what is passed into the kernel
599 since these eight options are best passed as the
600 mount flags rather than redundantly to the kernel
601 and could generate spurious warnings depending on the
602 level of the corresponding cifs vfs kernel code */
603 } else if (strncmp(data, "nosuid", 6) == 0) {
604 *filesys_flags |= MS_NOSUID;
605 } else if (strncmp(data, "suid", 4) == 0) {
606 *filesys_flags &= ~MS_NOSUID;
607 } else if (strncmp(data, "nodev", 5) == 0) {
608 *filesys_flags |= MS_NODEV;
609 } else if ((strncmp(data, "nobrl", 5) == 0) ||
610 (strncmp(data, "nolock", 6) == 0)) {
611 *filesys_flags &= ~MS_MANDLOCK;
612 } else if (strncmp(data, "dev", 3) == 0) {
613 *filesys_flags &= ~MS_NODEV;
614 } else if (strncmp(data, "noexec", 6) == 0) {
615 *filesys_flags |= MS_NOEXEC;
616 } else if (strncmp(data, "exec", 4) == 0) {
617 *filesys_flags &= ~MS_NOEXEC;
618 } else if (strncmp(data, "guest", 5) == 0) {
619 got_password=1;
620 } else if (strncmp(data, "ro", 2) == 0) {
621 *filesys_flags |= MS_RDONLY;
622 } else if (strncmp(data, "rw", 2) == 0) {
623 *filesys_flags &= ~MS_RDONLY;
624 } else if (strncmp(data, "remount", 7) == 0) {
625 *filesys_flags |= MS_REMOUNT;
626 } /* else if (strnicmp(data, "port", 4) == 0) {
627 if (value && *value) {
628 vol->port =
629 simple_strtoul(value, &value, 0);
631 } else if (strnicmp(data, "rsize", 5) == 0) {
632 if (value && *value) {
633 vol->rsize =
634 simple_strtoul(value, &value, 0);
636 } else if (strnicmp(data, "wsize", 5) == 0) {
637 if (value && *value) {
638 vol->wsize =
639 simple_strtoul(value, &value, 0);
641 } else if (strnicmp(data, "version", 3) == 0) {
642 } else {
643 printf("CIFS: Unknown mount option %s\n",data);
644 } */ /* nothing to do on those four mount options above.
645 Just pass to kernel and ignore them here */
647 /* Copy (possibly modified) option to out */
648 word_len = strlen(data);
649 if (value)
650 word_len += 1 + strlen(value);
652 out = (char *)realloc(out, out_len + word_len + 2);
653 if (out == NULL) {
654 perror("malloc");
655 exit(1);
658 if (out_len) {
659 strlcat(out, ",", out_len + word_len + 2);
660 out_len++;
663 if (value)
664 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
665 else
666 snprintf(out + out_len, word_len + 1, "%s", data);
667 out_len = strlen(out);
669 nocopy:
670 data = next_keyword;
673 /* special-case the uid and gid */
674 if (got_uid) {
675 word_len = strlen(user);
677 out = (char *)realloc(out, out_len + word_len + 6);
678 if (out == NULL) {
679 perror("malloc");
680 exit(1);
683 if (out_len) {
684 strlcat(out, ",", out_len + word_len + 6);
685 out_len++;
687 snprintf(out + out_len, word_len + 5, "uid=%s", user);
688 out_len = strlen(out);
690 if (got_gid) {
691 word_len = strlen(group);
693 out = (char *)realloc(out, out_len + 1 + word_len + 6);
694 if (out == NULL) {
695 perror("malloc");
696 exit(1);
699 if (out_len) {
700 strlcat(out, ",", out_len + word_len + 6);
701 out_len++;
703 snprintf(out + out_len, word_len + 5, "gid=%s", group);
704 out_len = strlen(out);
707 SAFE_FREE(*optionsp);
708 *optionsp = out;
709 return 0;
712 /* replace all (one or more) commas with double commas */
713 static void check_for_comma(char ** ppasswrd)
715 char *new_pass_buf;
716 char *pass;
717 int i,j;
718 int number_of_commas = 0;
719 int len;
721 if(ppasswrd == NULL)
722 return;
723 else
724 (pass = *ppasswrd);
726 len = strlen(pass);
728 for(i=0;i<len;i++) {
729 if(pass[i] == ',')
730 number_of_commas++;
733 if(number_of_commas == 0)
734 return;
735 if(number_of_commas > MOUNT_PASSWD_SIZE) {
736 /* would otherwise overflow the mount options buffer */
737 printf("\nInvalid password. Password contains too many commas.\n");
738 return;
741 new_pass_buf = (char *)malloc(len+number_of_commas+1);
742 if(new_pass_buf == NULL)
743 return;
745 for(i=0,j=0;i<len;i++,j++) {
746 new_pass_buf[j] = pass[i];
747 if(pass[i] == ',') {
748 j++;
749 new_pass_buf[j] = pass[i];
752 new_pass_buf[len+number_of_commas] = 0;
754 SAFE_FREE(*ppasswrd);
755 *ppasswrd = new_pass_buf;
757 return;
760 /* Usernames can not have backslash in them and we use
761 [BB check if usernames can have forward slash in them BB]
762 backslash as domain\user separator character
764 static char * check_for_domain(char **ppuser)
766 char * original_string;
767 char * usernm;
768 char * domainnm;
769 int original_len;
770 int len;
771 int i;
773 if(ppuser == NULL)
774 return NULL;
776 original_string = *ppuser;
778 if (original_string == NULL)
779 return NULL;
781 original_len = strlen(original_string);
783 usernm = strchr(*ppuser,'/');
784 if (usernm == NULL) {
785 usernm = strchr(*ppuser,'\\');
786 if (usernm == NULL)
787 return NULL;
790 if(got_domain) {
791 printf("Domain name specified twice. Username probably malformed\n");
792 return NULL;
795 usernm[0] = 0;
796 domainnm = *ppuser;
797 if (domainnm[0] != 0) {
798 got_domain = 1;
799 } else {
800 printf("null domain\n");
802 len = strlen(domainnm);
803 /* reset domainm to new buffer, and copy
804 domain name into it */
805 domainnm = (char *)malloc(len+1);
806 if(domainnm == NULL)
807 return NULL;
809 strlcpy(domainnm,*ppuser,len+1);
811 /* move_string(*ppuser, usernm+1) */
812 len = strlen(usernm+1);
814 if(len >= original_len) {
815 /* should not happen */
816 return domainnm;
819 for(i=0;i<original_len;i++) {
820 if(i<len)
821 original_string[i] = usernm[i+1];
822 else /* stuff with commas to remove last parm */
823 original_string[i] = ',';
826 /* BB add check for more than one slash?
827 strchr(*ppuser,'/');
828 strchr(*ppuser,'\\')
831 return domainnm;
834 /* replace all occurances of "from" in a string with "to" */
835 static void replace_char(char *string, char from, char to, int maxlen)
837 char *lastchar = string + maxlen;
838 while (string) {
839 string = strchr(string, from);
840 if (string) {
841 *string = to;
842 if (string >= lastchar)
843 return;
848 /* Note that caller frees the returned buffer if necessary */
849 static char * parse_server(char ** punc_name)
851 char * unc_name = *punc_name;
852 int length = strnlen(unc_name, MAX_UNC_LEN);
853 char * share;
854 char * ipaddress_string = NULL;
855 struct hostent * host_entry = NULL;
856 struct in_addr server_ipaddr;
858 if(length > (MAX_UNC_LEN - 1)) {
859 printf("mount error: UNC name too long");
860 return NULL;
862 if (strncasecmp("cifs://",unc_name,7) == 0)
863 return parse_cifs_url(unc_name+7);
864 if (strncasecmp("smb://",unc_name,6) == 0) {
865 return parse_cifs_url(unc_name+6);
868 if(length < 3) {
869 /* BB add code to find DFS root here */
870 printf("\nMounting the DFS root for domain not implemented yet\n");
871 return NULL;
872 } else {
873 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
874 /* check for nfs syntax ie server:share */
875 share = strchr(unc_name,':');
876 if(share) {
877 *punc_name = (char *)malloc(length+3);
878 if(*punc_name == NULL) {
879 /* put the original string back if
880 no memory left */
881 *punc_name = unc_name;
882 return NULL;
884 *share = '/';
885 strlcpy((*punc_name)+2,unc_name,length+1);
886 SAFE_FREE(unc_name);
887 unc_name = *punc_name;
888 unc_name[length+2] = 0;
889 goto continue_unc_parsing;
890 } else {
891 printf("mount error: improperly formatted UNC name.");
892 printf(" %s does not begin with \\\\ or //\n",unc_name);
893 return NULL;
895 } else {
896 continue_unc_parsing:
897 unc_name[0] = '/';
898 unc_name[1] = '/';
899 unc_name += 2;
901 /* allow for either delimiter between host and sharename */
902 if ((share = strpbrk(unc_name, "/\\"))) {
903 *share = 0; /* temporarily terminate the string */
904 share += 1;
905 if(got_ip == 0) {
906 host_entry = gethostbyname(unc_name);
908 *(share - 1) = '/'; /* put delimiter back */
910 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
911 if ((prefixpath = strpbrk(share, "/\\"))) {
912 *prefixpath = 0; /* permanently terminate the string */
913 if (!strlen(++prefixpath))
914 prefixpath = NULL; /* this needs to be done explicitly */
916 if(got_ip) {
917 if(verboseflag)
918 printf("ip address specified explicitly\n");
919 return NULL;
921 if(host_entry == NULL) {
922 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
923 return NULL;
924 } else {
925 /* BB should we pass an alternate version of the share name as Unicode */
926 /* BB what about ipv6? BB */
927 /* BB add retries with alternate servers in list */
929 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
931 ipaddress_string = inet_ntoa(server_ipaddr);
932 if(ipaddress_string == NULL) {
933 printf("mount error: could not get valid ip address for target server\n");
934 return NULL;
936 return ipaddress_string;
938 } else {
939 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
940 printf("Mounting the DFS root for a particular server not implemented yet\n");
941 return NULL;
947 static struct option longopts[] = {
948 { "all", 0, NULL, 'a' },
949 { "help",0, NULL, 'h' },
950 { "move",0, NULL, 'm' },
951 { "bind",0, NULL, 'b' },
952 { "read-only", 0, NULL, 'r' },
953 { "ro", 0, NULL, 'r' },
954 { "verbose", 0, NULL, 'v' },
955 { "version", 0, NULL, 'V' },
956 { "read-write", 0, NULL, 'w' },
957 { "rw", 0, NULL, 'w' },
958 { "options", 1, NULL, 'o' },
959 { "type", 1, NULL, 't' },
960 { "rsize",1, NULL, 'R' },
961 { "wsize",1, NULL, 'W' },
962 { "uid", 1, NULL, '1'},
963 { "gid", 1, NULL, '2'},
964 { "user",1,NULL,'u'},
965 { "username",1,NULL,'u'},
966 { "dom",1,NULL,'d'},
967 { "domain",1,NULL,'d'},
968 { "password",1,NULL,'p'},
969 { "pass",1,NULL,'p'},
970 { "credentials",1,NULL,'c'},
971 { "port",1,NULL,'P'},
972 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
973 { NULL, 0, NULL, 0 }
976 /* convert a string to uppercase. return false if the string
977 * wasn't ASCII or was a NULL ptr */
978 static int
979 uppercase_string(char *string)
981 if (!string)
982 return 0;
984 while (*string) {
985 /* check for unicode */
986 if ((unsigned char) string[0] & 0x80)
987 return 0;
988 *string = toupper((unsigned char) *string);
989 string++;
992 return 1;
995 int main(int argc, char ** argv)
997 int c;
998 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
999 char * orgoptions = NULL;
1000 char * share_name = NULL;
1001 char * ipaddr = NULL;
1002 char * uuid = NULL;
1003 char * mountpoint = NULL;
1004 char * options = NULL;
1005 char * resolved_path = NULL;
1006 char * temp;
1007 char * dev_name;
1008 int rc;
1009 int rsize = 0;
1010 int wsize = 0;
1011 int nomtab = 0;
1012 int uid = 0;
1013 int gid = 0;
1014 int optlen = 0;
1015 int orgoptlen = 0;
1016 size_t options_size = 0;
1017 int retry = 0; /* set when we have to retry mount with uppercase */
1018 struct stat statbuf;
1019 struct utsname sysinfo;
1020 struct mntent mountent;
1021 FILE * pmntfile;
1023 /* setlocale(LC_ALL, "");
1024 bindtextdomain(PACKAGE, LOCALEDIR);
1025 textdomain(PACKAGE); */
1027 if(argc && argv) {
1028 thisprogram = argv[0];
1029 } else {
1030 mount_cifs_usage();
1031 exit(1);
1034 if(thisprogram == NULL)
1035 thisprogram = "mount.cifs";
1037 uname(&sysinfo);
1038 /* BB add workstation name and domain and pass down */
1040 /* #ifdef _GNU_SOURCE
1041 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1042 #endif */
1043 if(argc > 2) {
1044 dev_name = argv[1];
1045 share_name = strndup(argv[1], MAX_UNC_LEN);
1046 if (share_name == NULL) {
1047 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1048 exit(1);
1050 mountpoint = argv[2];
1051 } else {
1052 mount_cifs_usage();
1053 exit(1);
1056 /* add sharename in opts string as unc= parm */
1058 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1059 longopts, NULL)) != -1) {
1060 switch (c) {
1061 /* No code to do the following options yet */
1062 /* case 'l':
1063 list_with_volumelabel = 1;
1064 break;
1065 case 'L':
1066 volumelabel = optarg;
1067 break; */
1068 /* case 'a':
1069 ++mount_all;
1070 break; */
1072 case '?':
1073 case 'h': /* help */
1074 mount_cifs_usage ();
1075 exit(1);
1076 case 'n':
1077 ++nomtab;
1078 break;
1079 case 'b':
1080 #ifdef MS_BIND
1081 flags |= MS_BIND;
1082 #else
1083 fprintf(stderr,
1084 "option 'b' (MS_BIND) not supported\n");
1085 #endif
1086 break;
1087 case 'm':
1088 #ifdef MS_MOVE
1089 flags |= MS_MOVE;
1090 #else
1091 fprintf(stderr,
1092 "option 'm' (MS_MOVE) not supported\n");
1093 #endif
1094 break;
1095 case 'o':
1096 orgoptions = strdup(optarg);
1097 break;
1098 case 'r': /* mount readonly */
1099 flags |= MS_RDONLY;
1100 break;
1101 case 'U':
1102 uuid = optarg;
1103 break;
1104 case 'v':
1105 ++verboseflag;
1106 break;
1107 case 'V':
1108 printf ("mount.cifs version: %s.%s%s\n",
1109 MOUNT_CIFS_VERSION_MAJOR,
1110 MOUNT_CIFS_VERSION_MINOR,
1111 MOUNT_CIFS_VENDOR_SUFFIX);
1112 exit (0);
1113 case 'w':
1114 flags &= ~MS_RDONLY;
1115 break;
1116 case 'R':
1117 rsize = atoi(optarg) ;
1118 break;
1119 case 'W':
1120 wsize = atoi(optarg);
1121 break;
1122 case '1':
1123 if (isdigit(*optarg)) {
1124 char *ep;
1126 uid = strtoul(optarg, &ep, 10);
1127 if (*ep) {
1128 printf("bad uid value \"%s\"\n", optarg);
1129 exit(1);
1131 } else {
1132 struct passwd *pw;
1134 if (!(pw = getpwnam(optarg))) {
1135 printf("bad user name \"%s\"\n", optarg);
1136 exit(1);
1138 uid = pw->pw_uid;
1139 endpwent();
1141 break;
1142 case '2':
1143 if (isdigit(*optarg)) {
1144 char *ep;
1146 gid = strtoul(optarg, &ep, 10);
1147 if (*ep) {
1148 printf("bad gid value \"%s\"\n", optarg);
1149 exit(1);
1151 } else {
1152 struct group *gr;
1154 if (!(gr = getgrnam(optarg))) {
1155 printf("bad user name \"%s\"\n", optarg);
1156 exit(1);
1158 gid = gr->gr_gid;
1159 endpwent();
1161 break;
1162 case 'u':
1163 got_user = 1;
1164 user_name = optarg;
1165 break;
1166 case 'd':
1167 domain_name = optarg; /* BB fix this - currently ignored */
1168 got_domain = 1;
1169 break;
1170 case 'p':
1171 if(mountpassword == NULL)
1172 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1173 if(mountpassword) {
1174 got_password = 1;
1175 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1177 break;
1178 case 'S':
1179 get_password_from_file(0 /* stdin */,NULL);
1180 break;
1181 case 't':
1182 break;
1183 default:
1184 printf("unknown mount option %c\n",c);
1185 mount_cifs_usage();
1186 exit(1);
1190 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1191 mount_cifs_usage();
1192 exit(1);
1195 if (getenv("PASSWD")) {
1196 if(mountpassword == NULL)
1197 mountpassword = (char *)calloc(65,1);
1198 if(mountpassword) {
1199 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE);
1200 got_password = 1;
1202 } else if (getenv("PASSWD_FD")) {
1203 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1204 } else if (getenv("PASSWD_FILE")) {
1205 get_password_from_file(0, getenv("PASSWD_FILE"));
1208 if (orgoptions && parse_options(&orgoptions, &flags)) {
1209 rc = -1;
1210 goto mount_exit;
1212 ipaddr = parse_server(&share_name);
1213 if((ipaddr == NULL) && (got_ip == 0)) {
1214 printf("No ip address specified and hostname not found\n");
1215 rc = -1;
1216 goto mount_exit;
1219 /* BB save off path and pop after mount returns? */
1220 resolved_path = (char *)malloc(PATH_MAX+1);
1221 if(resolved_path) {
1222 /* Note that if we can not canonicalize the name, we get
1223 another chance to see if it is valid when we chdir to it */
1224 if (realpath(mountpoint, resolved_path)) {
1225 mountpoint = resolved_path;
1228 if(chdir(mountpoint)) {
1229 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1230 rc = -1;
1231 goto mount_exit;
1234 if(stat (".", &statbuf)) {
1235 printf("mount error: mount point %s does not exist\n",mountpoint);
1236 rc = -1;
1237 goto mount_exit;
1240 if (S_ISDIR(statbuf.st_mode) == 0) {
1241 printf("mount error: mount point %s is not a directory\n",mountpoint);
1242 rc = -1;
1243 goto mount_exit;
1246 if((getuid() != 0) && (geteuid() == 0)) {
1247 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1248 #ifndef CIFS_ALLOW_USR_SUID
1249 /* Do not allow user mounts to control suid flag
1250 for mount unless explicitly built that way */
1251 flags |= MS_NOSUID | MS_NODEV;
1252 #endif
1253 } else {
1254 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1255 return -1;
1259 if(got_user == 0) {
1260 user_name = getusername();
1261 got_user = 1;
1264 if(got_password == 0) {
1265 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1266 no good replacement yet. */
1267 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1268 if (!tmp_pass || !mountpassword) {
1269 printf("Password not entered, exiting\n");
1270 return -1;
1272 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1273 got_password = 1;
1275 /* FIXME launch daemon (handles dfs name resolution and credential change)
1276 remember to clear parms and overwrite password field before launching */
1277 mount_retry:
1278 if(orgoptions) {
1279 optlen = strlen(orgoptions);
1280 orgoptlen = optlen;
1281 } else
1282 optlen = 0;
1283 if(share_name)
1284 optlen += strlen(share_name) + 4;
1285 else {
1286 printf("No server share name specified\n");
1287 printf("\nMounting the DFS root for server not implemented yet\n");
1288 exit(1);
1290 if(user_name)
1291 optlen += strlen(user_name) + 6;
1292 if(ipaddr)
1293 optlen += strlen(ipaddr) + 4;
1294 if(mountpassword)
1295 optlen += strlen(mountpassword) + 6;
1296 SAFE_FREE(options);
1297 options_size = optlen + 10 + DOMAIN_SIZE;
1298 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 */);
1300 if(options == NULL) {
1301 printf("Could not allocate memory for mount options\n");
1302 return -1;
1305 options[0] = 0;
1306 strlcpy(options,"unc=",options_size);
1307 strlcat(options,share_name,options_size);
1308 /* scan backwards and reverse direction of slash */
1309 temp = strrchr(options, '/');
1310 if(temp > options + 6)
1311 *temp = '\\';
1312 if(ipaddr) {
1313 strlcat(options,",ip=",options_size);
1314 strlcat(options,ipaddr,options_size);
1317 if(user_name) {
1318 /* check for syntax like user=domain\user */
1319 if(got_domain == 0)
1320 domain_name = check_for_domain(&user_name);
1321 strlcat(options,",user=",options_size);
1322 strlcat(options,user_name,options_size);
1324 if(retry == 0) {
1325 if(domain_name) {
1326 /* extra length accounted for in option string above */
1327 strlcat(options,",domain=",options_size);
1328 strlcat(options,domain_name,options_size);
1331 if(mountpassword) {
1332 /* Commas have to be doubled, or else they will
1333 look like the parameter separator */
1334 /* if(sep is not set)*/
1335 if(retry == 0)
1336 check_for_comma(&mountpassword);
1337 strlcat(options,",pass=",options_size);
1338 strlcat(options,mountpassword,options_size);
1341 strlcat(options,",ver=",options_size);
1342 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1344 if(orgoptions) {
1345 strlcat(options,",",options_size);
1346 strlcat(options,orgoptions,options_size);
1348 if(prefixpath) {
1349 strlcat(options,",prefixpath=",options_size);
1350 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1352 if(verboseflag)
1353 printf("\nmount.cifs kernel mount options %s \n",options);
1355 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1356 replace_char(dev_name, '\\', '/', strlen(share_name));
1358 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1359 /* remember to kill daemon on error */
1360 switch (errno) {
1361 case 0:
1362 printf("mount failed but no error number set\n");
1363 break;
1364 case ENODEV:
1365 printf("mount error: cifs filesystem not supported by the system\n");
1366 break;
1367 case ENXIO:
1368 if(retry == 0) {
1369 retry = 1;
1370 if (uppercase_string(dev_name) &&
1371 uppercase_string(share_name) &&
1372 uppercase_string(prefixpath)) {
1373 printf("retrying with upper case share name\n");
1374 goto mount_retry;
1377 default:
1378 printf("mount error %d = %s\n",errno,strerror(errno));
1380 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1381 rc = -1;
1382 goto mount_exit;
1383 } else {
1384 pmntfile = setmntent(MOUNTED, "a+");
1385 if(pmntfile) {
1386 mountent.mnt_fsname = dev_name;
1387 mountent.mnt_dir = mountpoint;
1388 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1389 mountent.mnt_opts = (char *)malloc(220);
1390 if(mountent.mnt_opts) {
1391 char * mount_user = getusername();
1392 memset(mountent.mnt_opts,0,200);
1393 if(flags & MS_RDONLY)
1394 strlcat(mountent.mnt_opts,"ro",220);
1395 else
1396 strlcat(mountent.mnt_opts,"rw",220);
1397 if(flags & MS_MANDLOCK)
1398 strlcat(mountent.mnt_opts,",mand",220);
1399 if(flags & MS_NOEXEC)
1400 strlcat(mountent.mnt_opts,",noexec",220);
1401 if(flags & MS_NOSUID)
1402 strlcat(mountent.mnt_opts,",nosuid",220);
1403 if(flags & MS_NODEV)
1404 strlcat(mountent.mnt_opts,",nodev",220);
1405 if(flags & MS_SYNCHRONOUS)
1406 strlcat(mountent.mnt_opts,",synch",220);
1407 if(mount_user) {
1408 if(getuid() != 0) {
1409 strlcat(mountent.mnt_opts,",user=",220);
1410 strlcat(mountent.mnt_opts,mount_user,220);
1412 /* free(mount_user); do not free static mem */
1415 mountent.mnt_freq = 0;
1416 mountent.mnt_passno = 0;
1417 rc = addmntent(pmntfile,&mountent);
1418 endmntent(pmntfile);
1419 SAFE_FREE(mountent.mnt_opts);
1420 } else {
1421 printf("could not update mount table\n");
1424 rc = 0;
1425 mount_exit:
1426 if(mountpassword) {
1427 int len = strlen(mountpassword);
1428 memset(mountpassword,0,len);
1429 SAFE_FREE(mountpassword);
1432 SAFE_FREE(options);
1433 SAFE_FREE(orgoptions);
1434 SAFE_FREE(resolved_path);
1435 SAFE_FREE(share_name);
1436 return rc;