r22781: grab Steve's patch for mount.cifs and sec=none
[Samba.git] / source / client / mount.cifs.c
blob009d2a6e73f46eec8d5bc350f9051712c1f7986e
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 2 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, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
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>
42 #define MOUNT_CIFS_VERSION_MAJOR "1"
43 #define MOUNT_CIFS_VERSION_MINOR "10"
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 CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
68 const char *thisprogram;
69 int verboseflag = 0;
70 static int got_password = 0;
71 static int got_user = 0;
72 static int got_domain = 0;
73 static int got_ip = 0;
74 static int got_unc = 0;
75 static int got_uid = 0;
76 static int got_gid = 0;
77 static int free_share_name = 0;
78 static char * user_name = NULL;
79 static char * mountpassword = NULL;
80 char * domain_name = NULL;
81 char * prefixpath = NULL;
84 /* BB finish BB
86 cifs_umount
87 open nofollow - avoid symlink exposure?
88 get owner of dir see if matches self or if root
89 call system(umount argv) etc.
91 BB end finish BB */
93 static char * check_for_domain(char **);
96 static void mount_cifs_usage(void)
98 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
99 printf("\nMount the remote target, specified as a UNC name,");
100 printf(" to a local directory.\n\nOptions:\n");
101 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
102 printf("\nLess commonly used options:");
103 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
104 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
105 printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
106 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
107 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
108 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
109 printf("\n\nRarely used options:");
110 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
111 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
112 printf("\n\tnointr,ignorecase,noposixpaths,noacl");
113 printf("\n\nOptions are described in more detail in the manual page");
114 printf("\n\tman 8 mount.cifs\n");
115 printf("\nTo display the version number of the mount helper:");
116 printf("\n\t%s -V\n",thisprogram);
118 if(mountpassword) {
119 memset(mountpassword,0,64);
120 free(mountpassword);
122 exit(1);
125 /* caller frees username if necessary */
126 static char * getusername(void) {
127 char *username = NULL;
128 struct passwd *password = getpwuid(getuid());
130 if (password) {
131 username = password->pw_name;
133 return username;
136 static char * parse_cifs_url(char * unc_name)
138 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
139 return NULL;
142 static int open_cred_file(char * file_name)
144 char * line_buf;
145 char * temp_val;
146 FILE * fs;
147 int i, length;
148 fs = fopen(file_name,"r");
149 if(fs == NULL)
150 return errno;
151 line_buf = (char *)malloc(4096);
152 if(line_buf == NULL) {
153 fclose(fs);
154 return -ENOMEM;
157 while(fgets(line_buf,4096,fs)) {
158 /* parse line from credential file */
160 /* eat leading white space */
161 for(i=0;i<4086;i++) {
162 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
163 break;
164 /* if whitespace - skip past it */
166 if (strncasecmp("username",line_buf+i,8) == 0) {
167 temp_val = strchr(line_buf + i,'=');
168 if(temp_val) {
169 /* go past equals sign */
170 temp_val++;
171 for(length = 0;length<4087;length++) {
172 if(temp_val[length] == '\n')
173 break;
175 if(length > 4086) {
176 printf("mount.cifs failed due to malformed username in credentials file");
177 memset(line_buf,0,4096);
178 if(mountpassword) {
179 memset(mountpassword,0,64);
181 exit(1);
182 } else {
183 got_user = 1;
184 user_name = (char *)calloc(1 + length,1);
185 /* BB adding free of user_name string before exit,
186 not really necessary but would be cleaner */
187 strncpy(user_name,temp_val, length);
190 } else if (strncasecmp("password",line_buf+i,8) == 0) {
191 temp_val = strchr(line_buf+i,'=');
192 if(temp_val) {
193 /* go past equals sign */
194 temp_val++;
195 for(length = 0;length<65;length++) {
196 if(temp_val[length] == '\n')
197 break;
199 if(length > 64) {
200 printf("mount.cifs failed: password in credentials file too long\n");
201 memset(line_buf,0, 4096);
202 if(mountpassword) {
203 memset(mountpassword,0,64);
205 exit(1);
206 } else {
207 if(mountpassword == NULL) {
208 mountpassword = (char *)calloc(65,1);
209 } else
210 memset(mountpassword,0,64);
211 if(mountpassword) {
212 strncpy(mountpassword,temp_val,length);
213 got_password = 1;
217 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
218 temp_val = strchr(line_buf+i,'=');
219 if(temp_val) {
220 /* go past equals sign */
221 temp_val++;
222 if(verboseflag)
223 printf("\nDomain %s\n",temp_val);
224 for(length = 0;length<65;length++) {
225 if(temp_val[length] == '\n')
226 break;
228 if(length > 64) {
229 printf("mount.cifs failed: domain in credentials file too long\n");
230 if(mountpassword) {
231 memset(mountpassword,0,64);
233 exit(1);
234 } else {
235 if(domain_name == NULL) {
236 domain_name = (char *)calloc(65,1);
237 } else
238 memset(domain_name,0,64);
239 if(domain_name) {
240 strncpy(domain_name,temp_val,length);
241 got_domain = 1;
248 fclose(fs);
249 if(line_buf) {
250 memset(line_buf,0,4096);
251 free(line_buf);
253 return 0;
256 static int get_password_from_file(int file_descript, char * filename)
258 int rc = 0;
259 int i;
260 char c;
262 if(mountpassword == NULL)
263 mountpassword = (char *)calloc(65,1);
264 else
265 memset(mountpassword, 0, 64);
267 if (mountpassword == NULL) {
268 printf("malloc failed\n");
269 exit(1);
272 if(filename != NULL) {
273 file_descript = open(filename, O_RDONLY);
274 if(file_descript < 0) {
275 printf("mount.cifs failed. %s attempting to open password file %s\n",
276 strerror(errno),filename);
277 exit(1);
280 /* else file already open and fd provided */
282 for(i=0;i<64;i++) {
283 rc = read(file_descript,&c,1);
284 if(rc < 0) {
285 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
286 memset(mountpassword,0,64);
287 if(filename != NULL)
288 close(file_descript);
289 exit(1);
290 } else if(rc == 0) {
291 if(mountpassword[0] == 0) {
292 if(verboseflag)
293 printf("\nWarning: null password used since cifs password file empty");
295 break;
296 } else /* read valid character */ {
297 if((c == 0) || (c == '\n')) {
298 break;
299 } else
300 mountpassword[i] = c;
303 if((i == 64) && (verboseflag)) {
304 printf("\nWarning: password longer than 64 characters specified in cifs password file");
306 got_password = 1;
307 if(filename != NULL) {
308 close(file_descript);
311 return rc;
314 static int parse_options(char ** optionsp, int * filesys_flags)
316 const char * data;
317 char * percent_char = NULL;
318 char * value = NULL;
319 char * next_keyword = NULL;
320 char * out = NULL;
321 int out_len = 0;
322 int word_len;
323 int rc = 0;
325 if (!optionsp || !*optionsp)
326 return 1;
327 data = *optionsp;
329 if(verboseflag)
330 printf("parsing options: %s\n", data);
332 /* BB fixme check for separator override BB */
334 /* while ((data = strsep(&options, ",")) != NULL) { */
335 while(data != NULL) {
336 /* check if ends with trailing comma */
337 if(*data == 0)
338 break;
340 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
341 /* data = next keyword */
342 /* value = next value ie stuff after equal sign */
344 next_keyword = strchr(data,','); /* BB handle sep= */
346 /* temporarily null terminate end of keyword=value pair */
347 if(next_keyword)
348 *next_keyword++ = 0;
350 /* temporarily null terminate keyword to make keyword and value distinct */
351 if ((value = strchr(data, '=')) != NULL) {
352 *value = '\0';
353 value++;
356 if (strncmp(data, "users",5) == 0) {
357 if(!value || !*value) {
358 goto nocopy;
360 } else if (strncmp(data, "user_xattr",10) == 0) {
361 /* do nothing - need to skip so not parsed as user name */
362 } else if (strncmp(data, "user", 4) == 0) {
364 if (!value || !*value) {
365 if(data[4] == '\0') {
366 if(verboseflag)
367 printf("\nskipping empty user mount parameter\n");
368 /* remove the parm since it would otherwise be confusing
369 to the kernel code which would think it was a real username */
370 goto nocopy;
371 } else {
372 printf("username specified with no parameter\n");
373 return 1; /* needs_arg; */
375 } else {
376 if (strnlen(value, 260) < 260) {
377 got_user=1;
378 percent_char = strchr(value,'%');
379 if(percent_char) {
380 *percent_char = ',';
381 if(mountpassword == NULL)
382 mountpassword = (char *)calloc(65,1);
383 if(mountpassword) {
384 if(got_password)
385 printf("\nmount.cifs warning - password specified twice\n");
386 got_password = 1;
387 percent_char++;
388 strncpy(mountpassword, percent_char,64);
389 /* remove password from username */
390 while(*percent_char != 0) {
391 *percent_char = ',';
392 percent_char++;
396 /* this is only case in which the user
397 name buf is not malloc - so we have to
398 check for domain name embedded within
399 the user name here since the later
400 call to check_for_domain will not be
401 invoked */
402 domain_name = check_for_domain(&value);
403 } else {
404 printf("username too long\n");
405 return 1;
408 } else if (strncmp(data, "pass", 4) == 0) {
409 if (!value || !*value) {
410 if(got_password) {
411 printf("\npassword specified twice, ignoring second\n");
412 } else
413 got_password = 1;
414 } else if (strnlen(value, 17) < 17) {
415 if(got_password)
416 printf("\nmount.cifs warning - password specified twice\n");
417 got_password = 1;
418 } else {
419 printf("password too long\n");
420 return 1;
422 } else if (strncmp(data, "sec", 3) == 0) {
423 if (value) {
424 if (!strcmp(value, "none"))
425 got_password = 1;
427 } else if (strncmp(data, "ip", 2) == 0) {
428 if (!value || !*value) {
429 printf("target ip address argument missing");
430 } else if (strnlen(value, 35) < 35) {
431 if(verboseflag)
432 printf("ip address %s override specified\n",value);
433 got_ip = 1;
434 } else {
435 printf("ip address too long\n");
436 return 1;
438 } else if ((strncmp(data, "unc", 3) == 0)
439 || (strncmp(data, "target", 6) == 0)
440 || (strncmp(data, "path", 4) == 0)) {
441 if (!value || !*value) {
442 printf("invalid path to network resource\n");
443 return 1; /* needs_arg; */
444 } else if(strnlen(value,5) < 5) {
445 printf("UNC name too short");
448 if (strnlen(value, 300) < 300) {
449 got_unc = 1;
450 if (strncmp(value, "//", 2) == 0) {
451 if(got_unc)
452 printf("unc name specified twice, ignoring second\n");
453 else
454 got_unc = 1;
455 } else if (strncmp(value, "\\\\", 2) != 0) {
456 printf("UNC Path does not begin with // or \\\\ \n");
457 return 1;
458 } else {
459 if(got_unc)
460 printf("unc name specified twice, ignoring second\n");
461 else
462 got_unc = 1;
464 } else {
465 printf("CIFS: UNC name too long\n");
466 return 1;
468 } else if ((strncmp(data, "domain", 3) == 0)
469 || (strncmp(data, "workgroup", 5) == 0)) {
470 if (!value || !*value) {
471 printf("CIFS: invalid domain name\n");
472 return 1; /* needs_arg; */
474 if (strnlen(value, 65) < 65) {
475 got_domain = 1;
476 } else {
477 printf("domain name too long\n");
478 return 1;
480 } else if (strncmp(data, "cred", 4) == 0) {
481 if (value && *value) {
482 rc = open_cred_file(value);
483 if(rc) {
484 printf("error %d opening credential file %s\n",rc, value);
485 return 1;
487 } else {
488 printf("invalid credential file name specified\n");
489 return 1;
491 } else if (strncmp(data, "uid", 3) == 0) {
492 if (value && *value) {
493 got_uid = 1;
494 if (!isdigit(*value)) {
495 struct passwd *pw;
496 static char temp[32];
498 if (!(pw = getpwnam(value))) {
499 printf("bad user name \"%s\"\n", value);
500 exit(1);
502 sprintf(temp, "%u", pw->pw_uid);
503 value = temp;
504 endpwent();
507 } else if (strncmp(data, "gid", 3) == 0) {
508 if (value && *value) {
509 got_gid = 1;
510 if (!isdigit(*value)) {
511 struct group *gr;
512 static char temp[32];
514 if (!(gr = getgrnam(value))) {
515 printf("bad group name \"%s\"\n", value);
516 exit(1);
518 sprintf(temp, "%u", gr->gr_gid);
519 value = temp;
520 endpwent();
523 /* fmask and dmask synonyms for people used to smbfs syntax */
524 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
525 if (!value || !*value) {
526 printf ("Option '%s' requires a numerical argument\n", data);
527 return 1;
530 if (value[0] != '0') {
531 printf ("WARNING: '%s' not expressed in octal.\n", data);
534 if (strcmp (data, "fmask") == 0) {
535 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
536 data = "file_mode"; /* BB fix this */
538 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
539 if (!value || !*value) {
540 printf ("Option '%s' requires a numerical argument\n", data);
541 return 1;
544 if (value[0] != '0') {
545 printf ("WARNING: '%s' not expressed in octal.\n", data);
548 if (strcmp (data, "dmask") == 0) {
549 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
550 data = "dir_mode";
552 /* the following eight mount options should be
553 stripped out from what is passed into the kernel
554 since these eight options are best passed as the
555 mount flags rather than redundantly to the kernel
556 and could generate spurious warnings depending on the
557 level of the corresponding cifs vfs kernel code */
558 } else if (strncmp(data, "nosuid", 6) == 0) {
559 *filesys_flags |= MS_NOSUID;
560 } else if (strncmp(data, "suid", 4) == 0) {
561 *filesys_flags &= ~MS_NOSUID;
562 } else if (strncmp(data, "nodev", 5) == 0) {
563 *filesys_flags |= MS_NODEV;
564 } else if ((strncmp(data, "nobrl", 5) == 0) ||
565 (strncmp(data, "nolock", 6) == 0)) {
566 *filesys_flags &= ~MS_MANDLOCK;
567 } else if (strncmp(data, "dev", 3) == 0) {
568 *filesys_flags &= ~MS_NODEV;
569 } else if (strncmp(data, "noexec", 6) == 0) {
570 *filesys_flags |= MS_NOEXEC;
571 } else if (strncmp(data, "exec", 4) == 0) {
572 *filesys_flags &= ~MS_NOEXEC;
573 } else if (strncmp(data, "guest", 5) == 0) {
574 got_password=1;
575 } else if (strncmp(data, "ro", 2) == 0) {
576 *filesys_flags |= MS_RDONLY;
577 } else if (strncmp(data, "rw", 2) == 0) {
578 *filesys_flags &= ~MS_RDONLY;
579 } else if (strncmp(data, "remount", 7) == 0) {
580 *filesys_flags |= MS_REMOUNT;
581 } /* else if (strnicmp(data, "port", 4) == 0) {
582 if (value && *value) {
583 vol->port =
584 simple_strtoul(value, &value, 0);
586 } else if (strnicmp(data, "rsize", 5) == 0) {
587 if (value && *value) {
588 vol->rsize =
589 simple_strtoul(value, &value, 0);
591 } else if (strnicmp(data, "wsize", 5) == 0) {
592 if (value && *value) {
593 vol->wsize =
594 simple_strtoul(value, &value, 0);
596 } else if (strnicmp(data, "version", 3) == 0) {
597 } else {
598 printf("CIFS: Unknown mount option %s\n",data);
599 } */ /* nothing to do on those four mount options above.
600 Just pass to kernel and ignore them here */
602 /* Copy (possibly modified) option to out */
603 word_len = strlen(data);
604 if (value)
605 word_len += 1 + strlen(value);
607 out = (char *)realloc(out, out_len + word_len + 2);
608 if (out == NULL) {
609 perror("malloc");
610 exit(1);
613 if (out_len)
614 out[out_len++] = ',';
615 if (value)
616 sprintf(out + out_len, "%s=%s", data, value);
617 else
618 sprintf(out + out_len, "%s", data);
619 out_len = strlen(out);
621 nocopy:
622 data = next_keyword;
624 *optionsp = out;
625 return 0;
628 /* replace all (one or more) commas with double commas */
629 static void check_for_comma(char ** ppasswrd)
631 char *new_pass_buf;
632 char *pass;
633 int i,j;
634 int number_of_commas = 0;
635 int len;
637 if(ppasswrd == NULL)
638 return;
639 else
640 (pass = *ppasswrd);
642 len = strlen(pass);
644 for(i=0;i<len;i++) {
645 if(pass[i] == ',')
646 number_of_commas++;
649 if(number_of_commas == 0)
650 return;
651 if(number_of_commas > 64) {
652 /* would otherwise overflow the mount options buffer */
653 printf("\nInvalid password. Password contains too many commas.\n");
654 return;
657 new_pass_buf = (char *)malloc(len+number_of_commas+1);
658 if(new_pass_buf == NULL)
659 return;
661 for(i=0,j=0;i<len;i++,j++) {
662 new_pass_buf[j] = pass[i];
663 if(pass[i] == ',') {
664 j++;
665 new_pass_buf[j] = pass[i];
668 new_pass_buf[len+number_of_commas] = 0;
670 free(*ppasswrd);
671 *ppasswrd = new_pass_buf;
673 return;
676 /* Usernames can not have backslash in them and we use
677 [BB check if usernames can have forward slash in them BB]
678 backslash as domain\user separator character
680 static char * check_for_domain(char **ppuser)
682 char * original_string;
683 char * usernm;
684 char * domainnm;
685 int original_len;
686 int len;
687 int i;
689 if(ppuser == NULL)
690 return NULL;
692 original_string = *ppuser;
694 if (original_string == NULL)
695 return NULL;
697 original_len = strlen(original_string);
699 usernm = strchr(*ppuser,'/');
700 if (usernm == NULL) {
701 usernm = strchr(*ppuser,'\\');
702 if (usernm == NULL)
703 return NULL;
706 if(got_domain) {
707 printf("Domain name specified twice. Username probably malformed\n");
708 return NULL;
711 usernm[0] = 0;
712 domainnm = *ppuser;
713 if (domainnm[0] != 0) {
714 got_domain = 1;
715 } else {
716 printf("null domain\n");
718 len = strlen(domainnm);
719 /* reset domainm to new buffer, and copy
720 domain name into it */
721 domainnm = (char *)malloc(len+1);
722 if(domainnm == NULL)
723 return NULL;
725 strcpy(domainnm,*ppuser);
727 /* move_string(*ppuser, usernm+1) */
728 len = strlen(usernm+1);
730 if(len >= original_len) {
731 /* should not happen */
732 return domainnm;
735 for(i=0;i<original_len;i++) {
736 if(i<len)
737 original_string[i] = usernm[i+1];
738 else /* stuff with commas to remove last parm */
739 original_string[i] = ',';
742 /* BB add check for more than one slash?
743 strchr(*ppuser,'/');
744 strchr(*ppuser,'\\')
747 return domainnm;
750 /* Note that caller frees the returned buffer if necessary */
751 static char * parse_server(char ** punc_name)
753 char * unc_name = *punc_name;
754 int length = strnlen(unc_name,1024);
755 char * share;
756 char * ipaddress_string = NULL;
757 struct hostent * host_entry = NULL;
758 struct in_addr server_ipaddr;
760 if(length > 1023) {
761 printf("mount error: UNC name too long");
762 return NULL;
764 if (strncasecmp("cifs://",unc_name,7) == 0)
765 return parse_cifs_url(unc_name+7);
766 if (strncasecmp("smb://",unc_name,6) == 0) {
767 return parse_cifs_url(unc_name+6);
770 if(length < 3) {
771 /* BB add code to find DFS root here */
772 printf("\nMounting the DFS root for domain not implemented yet\n");
773 return NULL;
774 } else {
775 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
776 /* check for nfs syntax ie server:share */
777 share = strchr(unc_name,':');
778 if(share) {
779 free_share_name = 1;
780 *punc_name = (char *)malloc(length+3);
781 if(*punc_name == NULL) {
782 /* put the original string back if
783 no memory left */
784 *punc_name = unc_name;
785 return NULL;
788 *share = '/';
789 strncpy((*punc_name)+2,unc_name,length);
790 unc_name = *punc_name;
791 unc_name[length+2] = 0;
792 goto continue_unc_parsing;
793 } else {
794 printf("mount error: improperly formatted UNC name.");
795 printf(" %s does not begin with \\\\ or //\n",unc_name);
796 return NULL;
798 } else {
799 continue_unc_parsing:
800 unc_name[0] = '/';
801 unc_name[1] = '/';
802 unc_name += 2;
803 if ((share = strchr(unc_name, '/')) ||
804 (share = strchr(unc_name,'\\'))) {
805 *share = 0; /* temporarily terminate the string */
806 share += 1;
807 if(got_ip == 0) {
808 host_entry = gethostbyname(unc_name);
810 *(share - 1) = '/'; /* put the slash back */
811 if ((prefixpath = strchr(share, '/'))) {
812 *prefixpath = 0; /* permanently terminate the string */
813 if (!strlen(++prefixpath))
814 prefixpath = NULL; /* this needs to be done explicitly */
816 if(got_ip) {
817 if(verboseflag)
818 printf("ip address specified explicitly\n");
819 return NULL;
821 if(host_entry == NULL) {
822 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
823 return NULL;
824 } else {
825 /* BB should we pass an alternate version of the share name as Unicode */
826 /* BB what about ipv6? BB */
827 /* BB add retries with alternate servers in list */
829 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
831 ipaddress_string = inet_ntoa(server_ipaddr);
832 if(ipaddress_string == NULL) {
833 printf("mount error: could not get valid ip address for target server\n");
834 return NULL;
836 return ipaddress_string;
838 } else {
839 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
840 printf("Mounting the DFS root for a particular server not implemented yet\n");
841 return NULL;
847 static struct option longopts[] = {
848 { "all", 0, NULL, 'a' },
849 { "help",0, NULL, 'h' },
850 { "move",0, NULL, 'm' },
851 { "bind",0, NULL, 'b' },
852 { "read-only", 0, NULL, 'r' },
853 { "ro", 0, NULL, 'r' },
854 { "verbose", 0, NULL, 'v' },
855 { "version", 0, NULL, 'V' },
856 { "read-write", 0, NULL, 'w' },
857 { "rw", 0, NULL, 'w' },
858 { "options", 1, NULL, 'o' },
859 { "type", 1, NULL, 't' },
860 { "rsize",1, NULL, 'R' },
861 { "wsize",1, NULL, 'W' },
862 { "uid", 1, NULL, '1'},
863 { "gid", 1, NULL, '2'},
864 { "user",1,NULL,'u'},
865 { "username",1,NULL,'u'},
866 { "dom",1,NULL,'d'},
867 { "domain",1,NULL,'d'},
868 { "password",1,NULL,'p'},
869 { "pass",1,NULL,'p'},
870 { "credentials",1,NULL,'c'},
871 { "port",1,NULL,'P'},
872 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
873 { NULL, 0, NULL, 0 }
876 int main(int argc, char ** argv)
878 int c;
879 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
880 char * orgoptions = NULL;
881 char * share_name = NULL;
882 char * ipaddr = NULL;
883 char * uuid = NULL;
884 char * mountpoint = NULL;
885 char * options;
886 char * resolved_path;
887 char * temp;
888 int rc;
889 int rsize = 0;
890 int wsize = 0;
891 int nomtab = 0;
892 int uid = 0;
893 int gid = 0;
894 int optlen = 0;
895 int orgoptlen = 0;
896 int retry = 0; /* set when we have to retry mount with uppercase */
897 struct stat statbuf;
898 struct utsname sysinfo;
899 struct mntent mountent;
900 FILE * pmntfile;
902 /* setlocale(LC_ALL, "");
903 bindtextdomain(PACKAGE, LOCALEDIR);
904 textdomain(PACKAGE); */
906 if(argc && argv) {
907 thisprogram = argv[0];
908 } else {
909 mount_cifs_usage();
910 exit(1);
913 if(thisprogram == NULL)
914 thisprogram = "mount.cifs";
916 uname(&sysinfo);
917 /* BB add workstation name and domain and pass down */
919 /* #ifdef _GNU_SOURCE
920 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
921 #endif */
922 if(argc > 2) {
923 share_name = argv[1];
924 mountpoint = argv[2];
927 /* add sharename in opts string as unc= parm */
929 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
930 longopts, NULL)) != -1) {
931 switch (c) {
932 /* No code to do the following options yet */
933 /* case 'l':
934 list_with_volumelabel = 1;
935 break;
936 case 'L':
937 volumelabel = optarg;
938 break; */
939 /* case 'a':
940 ++mount_all;
941 break; */
943 case '?':
944 case 'h': /* help */
945 mount_cifs_usage ();
946 exit(1);
947 case 'n':
948 ++nomtab;
949 break;
950 case 'b':
951 #ifdef MS_BIND
952 flags |= MS_BIND;
953 #else
954 fprintf(stderr,
955 "option 'b' (MS_BIND) not supported\n");
956 #endif
957 break;
958 case 'm':
959 #ifdef MS_MOVE
960 flags |= MS_MOVE;
961 #else
962 fprintf(stderr,
963 "option 'm' (MS_MOVE) not supported\n");
964 #endif
965 break;
966 case 'o':
967 orgoptions = strdup(optarg);
968 break;
969 case 'r': /* mount readonly */
970 flags |= MS_RDONLY;
971 break;
972 case 'U':
973 uuid = optarg;
974 break;
975 case 'v':
976 ++verboseflag;
977 break;
978 case 'V':
979 printf ("mount.cifs version: %s.%s%s\n",
980 MOUNT_CIFS_VERSION_MAJOR,
981 MOUNT_CIFS_VERSION_MINOR,
982 MOUNT_CIFS_VENDOR_SUFFIX);
983 if(mountpassword) {
984 memset(mountpassword,0,64);
986 exit (0);
987 case 'w':
988 flags &= ~MS_RDONLY;
989 break;
990 case 'R':
991 rsize = atoi(optarg) ;
992 break;
993 case 'W':
994 wsize = atoi(optarg);
995 break;
996 case '1':
997 if (isdigit(*optarg)) {
998 char *ep;
1000 uid = strtoul(optarg, &ep, 10);
1001 if (*ep) {
1002 printf("bad uid value \"%s\"\n", optarg);
1003 exit(1);
1005 } else {
1006 struct passwd *pw;
1008 if (!(pw = getpwnam(optarg))) {
1009 printf("bad user name \"%s\"\n", optarg);
1010 exit(1);
1012 uid = pw->pw_uid;
1013 endpwent();
1015 break;
1016 case '2':
1017 if (isdigit(*optarg)) {
1018 char *ep;
1020 gid = strtoul(optarg, &ep, 10);
1021 if (*ep) {
1022 printf("bad gid value \"%s\"\n", optarg);
1023 exit(1);
1025 } else {
1026 struct group *gr;
1028 if (!(gr = getgrnam(optarg))) {
1029 printf("bad user name \"%s\"\n", optarg);
1030 exit(1);
1032 gid = gr->gr_gid;
1033 endpwent();
1035 break;
1036 case 'u':
1037 got_user = 1;
1038 user_name = optarg;
1039 break;
1040 case 'd':
1041 domain_name = optarg; /* BB fix this - currently ignored */
1042 got_domain = 1;
1043 break;
1044 case 'p':
1045 if(mountpassword == NULL)
1046 mountpassword = (char *)calloc(65,1);
1047 if(mountpassword) {
1048 got_password = 1;
1049 strncpy(mountpassword,optarg,64);
1051 break;
1052 case 'S':
1053 get_password_from_file(0 /* stdin */,NULL);
1054 break;
1055 case 't':
1056 break;
1057 default:
1058 printf("unknown mount option %c\n",c);
1059 mount_cifs_usage();
1060 exit(1);
1064 if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1065 mount_cifs_usage();
1066 exit(1);
1069 if (getenv("PASSWD")) {
1070 if(mountpassword == NULL)
1071 mountpassword = (char *)calloc(65,1);
1072 if(mountpassword) {
1073 strncpy(mountpassword,getenv("PASSWD"),64);
1074 got_password = 1;
1076 } else if (getenv("PASSWD_FD")) {
1077 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1078 } else if (getenv("PASSWD_FILE")) {
1079 get_password_from_file(0, getenv("PASSWD_FILE"));
1082 if (orgoptions && parse_options(&orgoptions, &flags))
1083 return -1;
1084 ipaddr = parse_server(&share_name);
1085 if((ipaddr == NULL) && (got_ip == 0)) {
1086 printf("No ip address specified and hostname not found\n");
1087 return -1;
1090 /* BB save off path and pop after mount returns? */
1091 resolved_path = (char *)malloc(PATH_MAX+1);
1092 if(resolved_path) {
1093 /* Note that if we can not canonicalize the name, we get
1094 another chance to see if it is valid when we chdir to it */
1095 if (realpath(mountpoint, resolved_path)) {
1096 mountpoint = resolved_path;
1099 if(chdir(mountpoint)) {
1100 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1101 return -1;
1104 if(stat (".", &statbuf)) {
1105 printf("mount error: mount point %s does not exist\n",mountpoint);
1106 return -1;
1109 if (S_ISDIR(statbuf.st_mode) == 0) {
1110 printf("mount error: mount point %s is not a directory\n",mountpoint);
1111 return -1;
1114 if((getuid() != 0) && (geteuid() == 0)) {
1115 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1116 #ifndef CIFS_ALLOW_USR_SUID
1117 /* Do not allow user mounts to control suid flag
1118 for mount unless explicitly built that way */
1119 flags |= MS_NOSUID | MS_NODEV;
1120 #endif
1121 } else {
1122 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1123 return -1;
1127 if(got_user == 0) {
1128 user_name = getusername();
1129 got_user = 1;
1132 if(got_password == 0) {
1133 mountpassword = getpass("Password: "); /* BB obsolete */
1134 got_password = 1;
1136 /* FIXME launch daemon (handles dfs name resolution and credential change)
1137 remember to clear parms and overwrite password field before launching */
1138 mount_retry:
1139 if(orgoptions) {
1140 optlen = strlen(orgoptions);
1141 orgoptlen = optlen;
1142 } else
1143 optlen = 0;
1144 if(share_name)
1145 optlen += strlen(share_name) + 4;
1146 else {
1147 printf("No server share name specified\n");
1148 printf("\nMounting the DFS root for server not implemented yet\n");
1149 exit(1);
1151 if(user_name)
1152 optlen += strlen(user_name) + 6;
1153 if(ipaddr)
1154 optlen += strlen(ipaddr) + 4;
1155 if(mountpassword)
1156 optlen += strlen(mountpassword) + 6;
1157 options = (char *)malloc(optlen + 10 + 64 /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1159 if(options == NULL) {
1160 printf("Could not allocate memory for mount options\n");
1161 return -1;
1165 options[0] = 0;
1166 strncat(options,"unc=",4);
1167 strcat(options,share_name);
1168 /* scan backwards and reverse direction of slash */
1169 temp = strrchr(options, '/');
1170 if(temp > options + 6)
1171 *temp = '\\';
1172 if(ipaddr) {
1173 strncat(options,",ip=",4);
1174 strcat(options,ipaddr);
1177 if(user_name) {
1178 /* check for syntax like user=domain\user */
1179 if(got_domain == 0)
1180 domain_name = check_for_domain(&user_name);
1181 strncat(options,",user=",6);
1182 strcat(options,user_name);
1184 if(retry == 0) {
1185 if(domain_name) {
1186 /* extra length accounted for in option string above */
1187 strncat(options,",domain=",8);
1188 strcat(options,domain_name);
1191 if(mountpassword) {
1192 /* Commas have to be doubled, or else they will
1193 look like the parameter separator */
1194 /* if(sep is not set)*/
1195 if(retry == 0)
1196 check_for_comma(&mountpassword);
1197 strncat(options,",pass=",6);
1198 strcat(options,mountpassword);
1201 strncat(options,",ver=",5);
1202 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1204 if(orgoptions) {
1205 strcat(options,",");
1206 strcat(options,orgoptions);
1208 if(prefixpath) {
1209 strncat(options,",prefixpath=",12);
1210 strcat(options,prefixpath); /* no need to cat the / */
1212 if(verboseflag)
1213 printf("\nmount.cifs kernel mount options %s \n",options);
1214 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1215 /* remember to kill daemon on error */
1216 char * tmp;
1218 switch (errno) {
1219 case 0:
1220 printf("mount failed but no error number set\n");
1221 break;
1222 case ENODEV:
1223 printf("mount error: cifs filesystem not supported by the system\n");
1224 break;
1225 case ENXIO:
1226 if(retry == 0) {
1227 retry = 1;
1228 tmp = share_name;
1229 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1230 *tmp = toupper((unsigned char)*tmp);
1231 tmp++;
1233 if(!*tmp) {
1234 printf("retrying with upper case share name\n");
1235 goto mount_retry;
1238 default:
1240 printf("mount error %d = %s\n",errno,strerror(errno));
1242 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1243 if(mountpassword) {
1244 memset(mountpassword,0,64);
1246 return -1;
1247 } else {
1248 pmntfile = setmntent(MOUNTED, "a+");
1249 if(pmntfile) {
1250 mountent.mnt_fsname = share_name;
1251 mountent.mnt_dir = mountpoint;
1252 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1253 mountent.mnt_opts = (char *)malloc(220);
1254 if(mountent.mnt_opts) {
1255 char * mount_user = getusername();
1256 memset(mountent.mnt_opts,0,200);
1257 if(flags & MS_RDONLY)
1258 strcat(mountent.mnt_opts,"ro");
1259 else
1260 strcat(mountent.mnt_opts,"rw");
1261 if(flags & MS_MANDLOCK)
1262 strcat(mountent.mnt_opts,",mand");
1263 if(flags & MS_NOEXEC)
1264 strcat(mountent.mnt_opts,",noexec");
1265 if(flags & MS_NOSUID)
1266 strcat(mountent.mnt_opts,",nosuid");
1267 if(flags & MS_NODEV)
1268 strcat(mountent.mnt_opts,",nodev");
1269 if(flags & MS_SYNCHRONOUS)
1270 strcat(mountent.mnt_opts,",synch");
1271 if(mount_user) {
1272 if(getuid() != 0) {
1273 strcat(mountent.mnt_opts,",user=");
1274 strcat(mountent.mnt_opts,mount_user);
1276 free(mount_user);
1279 mountent.mnt_freq = 0;
1280 mountent.mnt_passno = 0;
1281 rc = addmntent(pmntfile,&mountent);
1282 endmntent(pmntfile);
1283 if(mountent.mnt_opts)
1284 free(mountent.mnt_opts);
1285 } else {
1286 printf("could not update mount table\n");
1289 if(mountpassword) {
1290 int len = strlen(mountpassword);
1291 memset(mountpassword,0,len);
1292 free(mountpassword);
1295 if(options) {
1296 memset(options,0,optlen);
1297 free(options);
1300 if(orgoptions) {
1301 memset(orgoptions,0,orgoptlen);
1302 free(orgoptions);
1304 if(resolved_path) {
1305 free(resolved_path);
1308 if(free_share_name) {
1309 free(share_name);
1311 return 0;