Fix Windows 2008 (Longhorn) join.
[Samba/bb.git] / source3 / client / mount.cifs.c
blob79f402a7d486cc6ae2d09a0cd81dbbfba459d553
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>
41 #define MOUNT_CIFS_VERSION_MAJOR "1"
42 #define MOUNT_CIFS_VERSION_MINOR "11"
44 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
45 #ifdef _SAMBA_BUILD_
46 #include "include/version.h"
47 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
48 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
49 #else
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
51 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
52 #else
53 #define MOUNT_CIFS_VENDOR_SUFFIX ""
54 #endif /* _SAMBA_BUILD_ */
55 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
57 #ifndef MS_MOVE
58 #define MS_MOVE 8192
59 #endif
61 #ifndef MS_BIND
62 #define MS_BIND 4096
63 #endif
65 #define MAX_UNC_LEN 1024
67 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
69 const char *thisprogram;
70 int verboseflag = 0;
71 static int got_password = 0;
72 static int got_user = 0;
73 static int got_domain = 0;
74 static int got_ip = 0;
75 static int got_unc = 0;
76 static int got_uid = 0;
77 static int got_gid = 0;
78 static char * user_name = NULL;
79 static char * mountpassword = NULL;
80 char * domain_name = NULL;
81 char * prefixpath = NULL;
83 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
84 * don't link to libreplace so need them here. */
86 /* like strncpy but does not 0 fill the buffer and always null
87 * terminates. bufsize is the size of the destination buffer */
88 size_t strlcpy(char *d, const char *s, size_t bufsize)
90 size_t len = strlen(s);
91 size_t ret = len;
92 if (bufsize <= 0) return 0;
93 if (len >= bufsize) len = bufsize-1;
94 memcpy(d, s, len);
95 d[len] = 0;
96 return ret;
99 /* like strncat but does not 0 fill the buffer and always null
100 * terminates. bufsize is the length of the buffer, which should
101 * be one more than the maximum resulting string length */
102 size_t strlcat(char *d, const char *s, size_t bufsize)
104 size_t len1 = strlen(d);
105 size_t len2 = strlen(s);
106 size_t ret = len1 + len2;
108 if (len1+len2 >= bufsize) {
109 len2 = bufsize - (len1+1);
111 if (len2 > 0) {
112 memcpy(d+len1, s, len2);
113 d[len1+len2] = 0;
115 return ret;
118 /* BB finish BB
120 cifs_umount
121 open nofollow - avoid symlink exposure?
122 get owner of dir see if matches self or if root
123 call system(umount argv) etc.
125 BB end finish BB */
127 static char * check_for_domain(char **);
130 static void mount_cifs_usage(void)
132 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
133 printf("\nMount the remote target, specified as a UNC name,");
134 printf(" to a local directory.\n\nOptions:\n");
135 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
136 printf("\nLess commonly used options:");
137 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
138 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
139 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
140 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
141 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
142 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
143 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
144 printf("\n\nRarely used options:");
145 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
146 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
147 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
148 printf("\n\tin6_addr");
149 printf("\n\nOptions are described in more detail in the manual page");
150 printf("\n\tman 8 mount.cifs\n");
151 printf("\nTo display the version number of the mount helper:");
152 printf("\n\t%s -V\n",thisprogram);
154 if(mountpassword) {
155 memset(mountpassword,0,64);
156 free(mountpassword);
158 exit(1);
161 /* caller frees username if necessary */
162 static char * getusername(void) {
163 char *username = NULL;
164 struct passwd *password = getpwuid(getuid());
166 if (password) {
167 username = password->pw_name;
169 return username;
172 static char * parse_cifs_url(char * unc_name)
174 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
175 return NULL;
178 static int open_cred_file(char * file_name)
180 char * line_buf;
181 char * temp_val;
182 FILE * fs;
183 int i, length;
184 fs = fopen(file_name,"r");
185 if(fs == NULL)
186 return errno;
187 line_buf = (char *)malloc(4096);
188 if(line_buf == NULL) {
189 fclose(fs);
190 return -ENOMEM;
193 while(fgets(line_buf,4096,fs)) {
194 /* parse line from credential file */
196 /* eat leading white space */
197 for(i=0;i<4086;i++) {
198 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
199 break;
200 /* if whitespace - skip past it */
202 if (strncasecmp("username",line_buf+i,8) == 0) {
203 temp_val = strchr(line_buf + i,'=');
204 if(temp_val) {
205 /* go past equals sign */
206 temp_val++;
207 for(length = 0;length<4087;length++) {
208 if(temp_val[length] == '\n')
209 break;
211 if(length > 4086) {
212 printf("mount.cifs failed due to malformed username in credentials file");
213 memset(line_buf,0,4096);
214 if(mountpassword) {
215 memset(mountpassword,0,64);
217 exit(1);
218 } else {
219 got_user = 1;
220 user_name = (char *)calloc(1 + length,1);
221 /* BB adding free of user_name string before exit,
222 not really necessary but would be cleaner */
223 strncpy(user_name,temp_val, length);
226 } else if (strncasecmp("password",line_buf+i,8) == 0) {
227 temp_val = strchr(line_buf+i,'=');
228 if(temp_val) {
229 /* go past equals sign */
230 temp_val++;
231 for(length = 0;length<65;length++) {
232 if(temp_val[length] == '\n')
233 break;
235 if(length > 64) {
236 printf("mount.cifs failed: password in credentials file too long\n");
237 memset(line_buf,0, 4096);
238 if(mountpassword) {
239 memset(mountpassword,0,64);
241 exit(1);
242 } else {
243 if(mountpassword == NULL) {
244 mountpassword = (char *)calloc(65,1);
245 } else
246 memset(mountpassword,0,64);
247 if(mountpassword) {
248 strncpy(mountpassword,temp_val,length);
249 got_password = 1;
253 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
254 temp_val = strchr(line_buf+i,'=');
255 if(temp_val) {
256 /* go past equals sign */
257 temp_val++;
258 if(verboseflag)
259 printf("\nDomain %s\n",temp_val);
260 for(length = 0;length<65;length++) {
261 if(temp_val[length] == '\n')
262 break;
264 if(length > 64) {
265 printf("mount.cifs failed: domain in credentials file too long\n");
266 if(mountpassword) {
267 memset(mountpassword,0,64);
269 exit(1);
270 } else {
271 if(domain_name == NULL) {
272 domain_name = (char *)calloc(65,1);
273 } else
274 memset(domain_name,0,64);
275 if(domain_name) {
276 strncpy(domain_name,temp_val,length);
277 got_domain = 1;
284 fclose(fs);
285 if(line_buf) {
286 memset(line_buf,0,4096);
287 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(65,1);
300 else
301 memset(mountpassword, 0, 64);
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<64;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 memset(mountpassword,0,64);
323 if(filename != NULL)
324 close(file_descript);
325 exit(1);
326 } else if(rc == 0) {
327 if(mountpassword[0] == 0) {
328 if(verboseflag)
329 printf("\nWarning: null password used since cifs password file empty");
331 break;
332 } else /* read valid character */ {
333 if((c == 0) || (c == '\n')) {
334 break;
335 } else
336 mountpassword[i] = c;
339 if((i == 64) && (verboseflag)) {
340 printf("\nWarning: password longer than 64 characters specified in cifs password file");
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(65,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 strncpy(mountpassword, percent_char,64);
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 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 > 64) {
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 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)
837 while (string) {
838 string = strchr(string, from);
839 if (string)
840 *string = to;
844 /* Note that caller frees the returned buffer if necessary */
845 static char * parse_server(char ** punc_name)
847 char * unc_name = *punc_name;
848 int length = strnlen(unc_name, MAX_UNC_LEN);
849 char * share;
850 char * ipaddress_string = NULL;
851 struct hostent * host_entry = NULL;
852 struct in_addr server_ipaddr;
854 if(length > (MAX_UNC_LEN - 1)) {
855 printf("mount error: UNC name too long");
856 return NULL;
858 if (strncasecmp("cifs://",unc_name,7) == 0)
859 return parse_cifs_url(unc_name+7);
860 if (strncasecmp("smb://",unc_name,6) == 0) {
861 return parse_cifs_url(unc_name+6);
864 if(length < 3) {
865 /* BB add code to find DFS root here */
866 printf("\nMounting the DFS root for domain not implemented yet\n");
867 return NULL;
868 } else {
869 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
870 /* check for nfs syntax ie server:share */
871 share = strchr(unc_name,':');
872 if(share) {
873 *punc_name = (char *)malloc(length+3);
874 if(*punc_name == NULL) {
875 /* put the original string back if
876 no memory left */
877 *punc_name = unc_name;
878 return NULL;
880 *share = '/';
881 strncpy((*punc_name)+2,unc_name,length);
882 free(unc_name);
883 unc_name = *punc_name;
884 unc_name[length+2] = 0;
885 goto continue_unc_parsing;
886 } else {
887 printf("mount error: improperly formatted UNC name.");
888 printf(" %s does not begin with \\\\ or //\n",unc_name);
889 return NULL;
891 } else {
892 continue_unc_parsing:
893 unc_name[0] = '\\';
894 unc_name[1] = '\\';
895 unc_name += 2;
897 /* convert any '/' in unc to '\\' */
898 replace_char(unc_name, '/', '\\');
900 if ((share = strchr(unc_name,'\\'))) {
901 *share = 0; /* temporarily terminate the string */
902 share += 1;
903 if(got_ip == 0) {
904 host_entry = gethostbyname(unc_name);
906 *(share - 1) = '\\'; /* put delimiter back */
907 if ((prefixpath = strchr(share, '\\'))) {
908 *prefixpath = 0; /* permanently terminate the string */
909 if (!strlen(++prefixpath))
910 prefixpath = NULL; /* this needs to be done explicitly */
912 if(got_ip) {
913 if(verboseflag)
914 printf("ip address specified explicitly\n");
915 return NULL;
917 if(host_entry == NULL) {
918 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
919 return NULL;
920 } else {
921 /* BB should we pass an alternate version of the share name as Unicode */
922 /* BB what about ipv6? BB */
923 /* BB add retries with alternate servers in list */
925 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
927 ipaddress_string = inet_ntoa(server_ipaddr);
928 if(ipaddress_string == NULL) {
929 printf("mount error: could not get valid ip address for target server\n");
930 return NULL;
932 return ipaddress_string;
934 } else {
935 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
936 printf("Mounting the DFS root for a particular server not implemented yet\n");
937 return NULL;
943 static struct option longopts[] = {
944 { "all", 0, NULL, 'a' },
945 { "help",0, NULL, 'h' },
946 { "move",0, NULL, 'm' },
947 { "bind",0, NULL, 'b' },
948 { "read-only", 0, NULL, 'r' },
949 { "ro", 0, NULL, 'r' },
950 { "verbose", 0, NULL, 'v' },
951 { "version", 0, NULL, 'V' },
952 { "read-write", 0, NULL, 'w' },
953 { "rw", 0, NULL, 'w' },
954 { "options", 1, NULL, 'o' },
955 { "type", 1, NULL, 't' },
956 { "rsize",1, NULL, 'R' },
957 { "wsize",1, NULL, 'W' },
958 { "uid", 1, NULL, '1'},
959 { "gid", 1, NULL, '2'},
960 { "user",1,NULL,'u'},
961 { "username",1,NULL,'u'},
962 { "dom",1,NULL,'d'},
963 { "domain",1,NULL,'d'},
964 { "password",1,NULL,'p'},
965 { "pass",1,NULL,'p'},
966 { "credentials",1,NULL,'c'},
967 { "port",1,NULL,'P'},
968 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
969 { NULL, 0, NULL, 0 }
972 /* convert a string to uppercase. return false if the string
973 * wasn't ASCII or was a NULL ptr */
974 static int
975 uppercase_string(char *string)
977 if (!string)
978 return 0;
980 while (*string) {
981 /* check for unicode */
982 if ((unsigned char) string[0] & 0x80)
983 return 0;
984 *string = toupper((unsigned char) *string);
985 string++;
988 return 1;
991 int main(int argc, char ** argv)
993 int c;
994 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
995 char * orgoptions = NULL;
996 char * share_name = NULL;
997 char * ipaddr = NULL;
998 char * uuid = NULL;
999 char * mountpoint = NULL;
1000 char * options = NULL;
1001 char * resolved_path = NULL;
1002 char * temp;
1003 char * dev_name;
1004 int rc;
1005 int rsize = 0;
1006 int wsize = 0;
1007 int nomtab = 0;
1008 int uid = 0;
1009 int gid = 0;
1010 int optlen = 0;
1011 int orgoptlen = 0;
1012 size_t options_size = 0;
1013 int retry = 0; /* set when we have to retry mount with uppercase */
1014 struct stat statbuf;
1015 struct utsname sysinfo;
1016 struct mntent mountent;
1017 FILE * pmntfile;
1019 /* setlocale(LC_ALL, "");
1020 bindtextdomain(PACKAGE, LOCALEDIR);
1021 textdomain(PACKAGE); */
1023 if(argc && argv) {
1024 thisprogram = argv[0];
1025 } else {
1026 mount_cifs_usage();
1027 exit(1);
1030 if(thisprogram == NULL)
1031 thisprogram = "mount.cifs";
1033 uname(&sysinfo);
1034 /* BB add workstation name and domain and pass down */
1036 /* #ifdef _GNU_SOURCE
1037 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1038 #endif */
1039 if(argc > 2) {
1040 dev_name = argv[1];
1041 share_name = strndup(argv[1], MAX_UNC_LEN);
1042 if (share_name == NULL) {
1043 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1044 exit(1);
1046 mountpoint = argv[2];
1047 } else {
1048 mount_cifs_usage();
1049 exit(1);
1052 /* add sharename in opts string as unc= parm */
1054 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1055 longopts, NULL)) != -1) {
1056 switch (c) {
1057 /* No code to do the following options yet */
1058 /* case 'l':
1059 list_with_volumelabel = 1;
1060 break;
1061 case 'L':
1062 volumelabel = optarg;
1063 break; */
1064 /* case 'a':
1065 ++mount_all;
1066 break; */
1068 case '?':
1069 case 'h': /* help */
1070 mount_cifs_usage ();
1071 exit(1);
1072 case 'n':
1073 ++nomtab;
1074 break;
1075 case 'b':
1076 #ifdef MS_BIND
1077 flags |= MS_BIND;
1078 #else
1079 fprintf(stderr,
1080 "option 'b' (MS_BIND) not supported\n");
1081 #endif
1082 break;
1083 case 'm':
1084 #ifdef MS_MOVE
1085 flags |= MS_MOVE;
1086 #else
1087 fprintf(stderr,
1088 "option 'm' (MS_MOVE) not supported\n");
1089 #endif
1090 break;
1091 case 'o':
1092 orgoptions = strdup(optarg);
1093 break;
1094 case 'r': /* mount readonly */
1095 flags |= MS_RDONLY;
1096 break;
1097 case 'U':
1098 uuid = optarg;
1099 break;
1100 case 'v':
1101 ++verboseflag;
1102 break;
1103 case 'V':
1104 printf ("mount.cifs version: %s.%s%s\n",
1105 MOUNT_CIFS_VERSION_MAJOR,
1106 MOUNT_CIFS_VERSION_MINOR,
1107 MOUNT_CIFS_VENDOR_SUFFIX);
1108 if(mountpassword) {
1109 memset(mountpassword,0,64);
1111 exit (0);
1112 case 'w':
1113 flags &= ~MS_RDONLY;
1114 break;
1115 case 'R':
1116 rsize = atoi(optarg) ;
1117 break;
1118 case 'W':
1119 wsize = atoi(optarg);
1120 break;
1121 case '1':
1122 if (isdigit(*optarg)) {
1123 char *ep;
1125 uid = strtoul(optarg, &ep, 10);
1126 if (*ep) {
1127 printf("bad uid value \"%s\"\n", optarg);
1128 exit(1);
1130 } else {
1131 struct passwd *pw;
1133 if (!(pw = getpwnam(optarg))) {
1134 printf("bad user name \"%s\"\n", optarg);
1135 exit(1);
1137 uid = pw->pw_uid;
1138 endpwent();
1140 break;
1141 case '2':
1142 if (isdigit(*optarg)) {
1143 char *ep;
1145 gid = strtoul(optarg, &ep, 10);
1146 if (*ep) {
1147 printf("bad gid value \"%s\"\n", optarg);
1148 exit(1);
1150 } else {
1151 struct group *gr;
1153 if (!(gr = getgrnam(optarg))) {
1154 printf("bad user name \"%s\"\n", optarg);
1155 exit(1);
1157 gid = gr->gr_gid;
1158 endpwent();
1160 break;
1161 case 'u':
1162 got_user = 1;
1163 user_name = optarg;
1164 break;
1165 case 'd':
1166 domain_name = optarg; /* BB fix this - currently ignored */
1167 got_domain = 1;
1168 break;
1169 case 'p':
1170 if(mountpassword == NULL)
1171 mountpassword = (char *)calloc(65,1);
1172 if(mountpassword) {
1173 got_password = 1;
1174 strncpy(mountpassword,optarg,64);
1176 break;
1177 case 'S':
1178 get_password_from_file(0 /* stdin */,NULL);
1179 break;
1180 case 't':
1181 break;
1182 default:
1183 printf("unknown mount option %c\n",c);
1184 mount_cifs_usage();
1185 exit(1);
1189 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1190 mount_cifs_usage();
1191 exit(1);
1194 if (getenv("PASSWD")) {
1195 if(mountpassword == NULL)
1196 mountpassword = (char *)calloc(65,1);
1197 if(mountpassword) {
1198 strncpy(mountpassword,getenv("PASSWD"),64);
1199 got_password = 1;
1201 } else if (getenv("PASSWD_FD")) {
1202 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1203 } else if (getenv("PASSWD_FILE")) {
1204 get_password_from_file(0, getenv("PASSWD_FILE"));
1207 if (orgoptions && parse_options(&orgoptions, &flags)) {
1208 rc = -1;
1209 goto mount_exit;
1211 ipaddr = parse_server(&share_name);
1212 if((ipaddr == NULL) && (got_ip == 0)) {
1213 printf("No ip address specified and hostname not found\n");
1214 rc = -1;
1215 goto mount_exit;
1218 /* BB save off path and pop after mount returns? */
1219 resolved_path = (char *)malloc(PATH_MAX+1);
1220 if(resolved_path) {
1221 /* Note that if we can not canonicalize the name, we get
1222 another chance to see if it is valid when we chdir to it */
1223 if (realpath(mountpoint, resolved_path)) {
1224 mountpoint = resolved_path;
1227 if(chdir(mountpoint)) {
1228 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1229 rc = -1;
1230 goto mount_exit;
1233 if(stat (".", &statbuf)) {
1234 printf("mount error: mount point %s does not exist\n",mountpoint);
1235 rc = -1;
1236 goto mount_exit;
1239 if (S_ISDIR(statbuf.st_mode) == 0) {
1240 printf("mount error: mount point %s is not a directory\n",mountpoint);
1241 rc = -1;
1242 goto mount_exit;
1245 if((getuid() != 0) && (geteuid() == 0)) {
1246 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1247 #ifndef CIFS_ALLOW_USR_SUID
1248 /* Do not allow user mounts to control suid flag
1249 for mount unless explicitly built that way */
1250 flags |= MS_NOSUID | MS_NODEV;
1251 #endif
1252 } else {
1253 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1254 return -1;
1258 if(got_user == 0) {
1259 user_name = getusername();
1260 got_user = 1;
1263 if(got_password == 0) {
1264 mountpassword = getpass("Password: "); /* BB obsolete */
1265 got_password = 1;
1267 /* FIXME launch daemon (handles dfs name resolution and credential change)
1268 remember to clear parms and overwrite password field before launching */
1269 mount_retry:
1270 if(orgoptions) {
1271 optlen = strlen(orgoptions);
1272 orgoptlen = optlen;
1273 } else
1274 optlen = 0;
1275 if(share_name)
1276 optlen += strlen(share_name) + 4;
1277 else {
1278 printf("No server share name specified\n");
1279 printf("\nMounting the DFS root for server not implemented yet\n");
1280 exit(1);
1282 if(user_name)
1283 optlen += strlen(user_name) + 6;
1284 if(ipaddr)
1285 optlen += strlen(ipaddr) + 4;
1286 if(mountpassword)
1287 optlen += strlen(mountpassword) + 6;
1288 if(options)
1289 free(options);
1290 options_size = optlen + 10 + 64;
1291 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 */);
1293 if(options == NULL) {
1294 printf("Could not allocate memory for mount options\n");
1295 return -1;
1298 options[0] = 0;
1299 strlcpy(options,"unc=",options_size);
1300 strlcat(options,share_name,options_size);
1301 /* scan backwards and reverse direction of slash */
1302 temp = strrchr(options, '/');
1303 if(temp > options + 6)
1304 *temp = '\\';
1305 if(ipaddr) {
1306 strlcat(options,",ip=",options_size);
1307 strlcat(options,ipaddr,options_size);
1310 if(user_name) {
1311 /* check for syntax like user=domain\user */
1312 if(got_domain == 0)
1313 domain_name = check_for_domain(&user_name);
1314 strlcat(options,",user=",options_size);
1315 strlcat(options,user_name,options_size);
1317 if(retry == 0) {
1318 if(domain_name) {
1319 /* extra length accounted for in option string above */
1320 strlcat(options,",domain=",options_size);
1321 strlcat(options,domain_name,options_size);
1324 if(mountpassword) {
1325 /* Commas have to be doubled, or else they will
1326 look like the parameter separator */
1327 /* if(sep is not set)*/
1328 if(retry == 0)
1329 check_for_comma(&mountpassword);
1330 strlcat(options,",pass=",options_size);
1331 strlcat(options,mountpassword,options_size);
1334 strlcat(options,",ver=",options_size);
1335 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1337 if(orgoptions) {
1338 strlcat(options,",",options_size);
1339 strlcat(options,orgoptions,options_size);
1341 if(prefixpath) {
1342 strlcat(options,",prefixpath=",options_size);
1343 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1345 if(verboseflag)
1346 printf("\nmount.cifs kernel mount options %s \n",options);
1348 /* convert all '\\' to '/' so that /proc/mounts looks pretty */
1349 replace_char(dev_name, '\\', '/');
1351 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1352 /* remember to kill daemon on error */
1353 switch (errno) {
1354 case 0:
1355 printf("mount failed but no error number set\n");
1356 break;
1357 case ENODEV:
1358 printf("mount error: cifs filesystem not supported by the system\n");
1359 break;
1360 case ENXIO:
1361 if(retry == 0) {
1362 retry = 1;
1363 if (uppercase_string(dev_name) &&
1364 uppercase_string(share_name) &&
1365 uppercase_string(prefixpath)) {
1366 printf("retrying with upper case share name\n");
1367 goto mount_retry;
1370 default:
1371 printf("mount error %d = %s\n",errno,strerror(errno));
1373 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1374 rc = -1;
1375 goto mount_exit;
1376 } else {
1377 pmntfile = setmntent(MOUNTED, "a+");
1378 if(pmntfile) {
1379 mountent.mnt_fsname = dev_name;
1380 mountent.mnt_dir = mountpoint;
1381 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1382 mountent.mnt_opts = (char *)malloc(220);
1383 if(mountent.mnt_opts) {
1384 char * mount_user = getusername();
1385 memset(mountent.mnt_opts,0,200);
1386 if(flags & MS_RDONLY)
1387 strlcat(mountent.mnt_opts,"ro",220);
1388 else
1389 strlcat(mountent.mnt_opts,"rw",220);
1390 if(flags & MS_MANDLOCK)
1391 strlcat(mountent.mnt_opts,",mand",220);
1392 if(flags & MS_NOEXEC)
1393 strlcat(mountent.mnt_opts,",noexec",220);
1394 if(flags & MS_NOSUID)
1395 strlcat(mountent.mnt_opts,",nosuid",220);
1396 if(flags & MS_NODEV)
1397 strlcat(mountent.mnt_opts,",nodev",220);
1398 if(flags & MS_SYNCHRONOUS)
1399 strlcat(mountent.mnt_opts,",synch",220);
1400 if(mount_user) {
1401 if(getuid() != 0) {
1402 strlcat(mountent.mnt_opts,",user=",220);
1403 strlcat(mountent.mnt_opts,mount_user,220);
1405 /* free(mount_user); do not free static mem */
1408 mountent.mnt_freq = 0;
1409 mountent.mnt_passno = 0;
1410 rc = addmntent(pmntfile,&mountent);
1411 endmntent(pmntfile);
1412 if(mountent.mnt_opts)
1413 free(mountent.mnt_opts);
1414 } else {
1415 printf("could not update mount table\n");
1418 rc = 0;
1419 mount_exit:
1420 if(mountpassword) {
1421 int len = strlen(mountpassword);
1422 memset(mountpassword,0,len);
1423 free(mountpassword);
1426 if(options) {
1427 memset(options,0,optlen);
1428 free(options);
1431 if(orgoptions) {
1432 memset(orgoptions,0,orgoptlen);
1433 free(orgoptions);
1435 if(resolved_path) {
1436 free(resolved_path);
1439 free(share_name);
1440 return rc;