r22979: Revert previous checkin (which had some debug junk) and fix misc frees that
[Samba/bjacke.git] / source / client / mount.cifs.c
blob77074644e050e50d0bbc1705c728173a40e09375
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, "ip", 2) == 0) {
423 if (!value || !*value) {
424 printf("target ip address argument missing");
425 } else if (strnlen(value, 35) < 35) {
426 if(verboseflag)
427 printf("ip address %s override specified\n",value);
428 got_ip = 1;
429 } else {
430 printf("ip address too long\n");
431 return 1;
433 } else if ((strncmp(data, "unc", 3) == 0)
434 || (strncmp(data, "target", 6) == 0)
435 || (strncmp(data, "path", 4) == 0)) {
436 if (!value || !*value) {
437 printf("invalid path to network resource\n");
438 return 1; /* needs_arg; */
439 } else if(strnlen(value,5) < 5) {
440 printf("UNC name too short");
443 if (strnlen(value, 300) < 300) {
444 got_unc = 1;
445 if (strncmp(value, "//", 2) == 0) {
446 if(got_unc)
447 printf("unc name specified twice, ignoring second\n");
448 else
449 got_unc = 1;
450 } else if (strncmp(value, "\\\\", 2) != 0) {
451 printf("UNC Path does not begin with // or \\\\ \n");
452 return 1;
453 } else {
454 if(got_unc)
455 printf("unc name specified twice, ignoring second\n");
456 else
457 got_unc = 1;
459 } else {
460 printf("CIFS: UNC name too long\n");
461 return 1;
463 } else if ((strncmp(data, "domain", 3) == 0)
464 || (strncmp(data, "workgroup", 5) == 0)) {
465 if (!value || !*value) {
466 printf("CIFS: invalid domain name\n");
467 return 1; /* needs_arg; */
469 if (strnlen(value, 65) < 65) {
470 got_domain = 1;
471 } else {
472 printf("domain name too long\n");
473 return 1;
475 } else if (strncmp(data, "cred", 4) == 0) {
476 if (value && *value) {
477 rc = open_cred_file(value);
478 if(rc) {
479 printf("error %d opening credential file %s\n",rc, value);
480 return 1;
482 } else {
483 printf("invalid credential file name specified\n");
484 return 1;
486 } else if (strncmp(data, "uid", 3) == 0) {
487 if (value && *value) {
488 got_uid = 1;
489 if (!isdigit(*value)) {
490 struct passwd *pw;
491 static char temp[32];
493 if (!(pw = getpwnam(value))) {
494 printf("bad user name \"%s\"\n", value);
495 exit(1);
497 sprintf(temp, "%u", pw->pw_uid);
498 value = temp;
499 endpwent();
502 } else if (strncmp(data, "gid", 3) == 0) {
503 if (value && *value) {
504 got_gid = 1;
505 if (!isdigit(*value)) {
506 struct group *gr;
507 static char temp[32];
509 if (!(gr = getgrnam(value))) {
510 printf("bad group name \"%s\"\n", value);
511 exit(1);
513 sprintf(temp, "%u", gr->gr_gid);
514 value = temp;
515 endpwent();
518 /* fmask and dmask synonyms for people used to smbfs syntax */
519 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
520 if (!value || !*value) {
521 printf ("Option '%s' requires a numerical argument\n", data);
522 return 1;
525 if (value[0] != '0') {
526 printf ("WARNING: '%s' not expressed in octal.\n", data);
529 if (strcmp (data, "fmask") == 0) {
530 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
531 data = "file_mode"; /* BB fix this */
533 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
534 if (!value || !*value) {
535 printf ("Option '%s' requires a numerical argument\n", data);
536 return 1;
539 if (value[0] != '0') {
540 printf ("WARNING: '%s' not expressed in octal.\n", data);
543 if (strcmp (data, "dmask") == 0) {
544 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
545 data = "dir_mode";
547 /* the following eight mount options should be
548 stripped out from what is passed into the kernel
549 since these eight options are best passed as the
550 mount flags rather than redundantly to the kernel
551 and could generate spurious warnings depending on the
552 level of the corresponding cifs vfs kernel code */
553 } else if (strncmp(data, "nosuid", 6) == 0) {
554 *filesys_flags |= MS_NOSUID;
555 } else if (strncmp(data, "suid", 4) == 0) {
556 *filesys_flags &= ~MS_NOSUID;
557 } else if (strncmp(data, "nodev", 5) == 0) {
558 *filesys_flags |= MS_NODEV;
559 } else if ((strncmp(data, "nobrl", 5) == 0) ||
560 (strncmp(data, "nolock", 6) == 0)) {
561 *filesys_flags &= ~MS_MANDLOCK;
562 } else if (strncmp(data, "dev", 3) == 0) {
563 *filesys_flags &= ~MS_NODEV;
564 } else if (strncmp(data, "noexec", 6) == 0) {
565 *filesys_flags |= MS_NOEXEC;
566 } else if (strncmp(data, "exec", 4) == 0) {
567 *filesys_flags &= ~MS_NOEXEC;
568 } else if (strncmp(data, "guest", 5) == 0) {
569 got_password=1;
570 /* remove the parm since it would otherwise be logged by kern */
571 goto nocopy;
572 } else if (strncmp(data, "ro", 2) == 0) {
573 *filesys_flags |= MS_RDONLY;
574 } else if (strncmp(data, "rw", 2) == 0) {
575 *filesys_flags &= ~MS_RDONLY;
576 } else if (strncmp(data, "remount", 7) == 0) {
577 *filesys_flags |= MS_REMOUNT;
578 } /* else if (strnicmp(data, "port", 4) == 0) {
579 if (value && *value) {
580 vol->port =
581 simple_strtoul(value, &value, 0);
583 } else if (strnicmp(data, "rsize", 5) == 0) {
584 if (value && *value) {
585 vol->rsize =
586 simple_strtoul(value, &value, 0);
588 } else if (strnicmp(data, "wsize", 5) == 0) {
589 if (value && *value) {
590 vol->wsize =
591 simple_strtoul(value, &value, 0);
593 } else if (strnicmp(data, "version", 3) == 0) {
594 } else {
595 printf("CIFS: Unknown mount option %s\n",data);
596 } */ /* nothing to do on those four mount options above.
597 Just pass to kernel and ignore them here */
599 /* Copy (possibly modified) option to out */
600 word_len = strlen(data);
601 if (value)
602 word_len += 1 + strlen(value);
604 out = (char *)realloc(out, out_len + word_len + 2);
605 if (out == NULL) {
606 perror("malloc");
607 exit(1);
610 if (out_len)
611 out[out_len++] = ',';
612 if (value)
613 sprintf(out + out_len, "%s=%s", data, value);
614 else
615 sprintf(out + out_len, "%s", data);
616 out_len = strlen(out);
618 nocopy:
619 data = next_keyword;
621 free(*optionsp);
622 *optionsp = out;
623 return 0;
626 /* replace all (one or more) commas with double commas */
627 static void check_for_comma(char ** ppasswrd)
629 char *new_pass_buf;
630 char *pass;
631 int i,j;
632 int number_of_commas = 0;
633 int len;
635 if(ppasswrd == NULL)
636 return;
637 else
638 (pass = *ppasswrd);
640 len = strlen(pass);
642 for(i=0;i<len;i++) {
643 if(pass[i] == ',')
644 number_of_commas++;
647 if(number_of_commas == 0)
648 return;
649 if(number_of_commas > 64) {
650 /* would otherwise overflow the mount options buffer */
651 printf("\nInvalid password. Password contains too many commas.\n");
652 return;
655 new_pass_buf = (char *)malloc(len+number_of_commas+1);
656 if(new_pass_buf == NULL)
657 return;
659 for(i=0,j=0;i<len;i++,j++) {
660 new_pass_buf[j] = pass[i];
661 if(pass[i] == ',') {
662 j++;
663 new_pass_buf[j] = pass[i];
666 new_pass_buf[len+number_of_commas] = 0;
668 free(*ppasswrd);
669 *ppasswrd = new_pass_buf;
671 return;
674 /* Usernames can not have backslash in them and we use
675 [BB check if usernames can have forward slash in them BB]
676 backslash as domain\user separator character
678 static char * check_for_domain(char **ppuser)
680 char * original_string;
681 char * usernm;
682 char * domainnm;
683 int original_len;
684 int len;
685 int i;
687 if(ppuser == NULL)
688 return NULL;
690 original_string = *ppuser;
692 if (original_string == NULL)
693 return NULL;
695 original_len = strlen(original_string);
697 usernm = strchr(*ppuser,'/');
698 if (usernm == NULL) {
699 usernm = strchr(*ppuser,'\\');
700 if (usernm == NULL)
701 return NULL;
704 if(got_domain) {
705 printf("Domain name specified twice. Username probably malformed\n");
706 return NULL;
709 usernm[0] = 0;
710 domainnm = *ppuser;
711 if (domainnm[0] != 0) {
712 got_domain = 1;
713 } else {
714 printf("null domain\n");
716 len = strlen(domainnm);
717 /* reset domainm to new buffer, and copy
718 domain name into it */
719 domainnm = (char *)malloc(len+1);
720 if(domainnm == NULL)
721 return NULL;
723 strcpy(domainnm,*ppuser);
725 /* move_string(*ppuser, usernm+1) */
726 len = strlen(usernm+1);
728 if(len >= original_len) {
729 /* should not happen */
730 return domainnm;
733 for(i=0;i<original_len;i++) {
734 if(i<len)
735 original_string[i] = usernm[i+1];
736 else /* stuff with commas to remove last parm */
737 original_string[i] = ',';
740 /* BB add check for more than one slash?
741 strchr(*ppuser,'/');
742 strchr(*ppuser,'\\')
745 return domainnm;
748 /* Note that caller frees the returned buffer if necessary */
749 static char * parse_server(char ** punc_name)
751 char * unc_name = *punc_name;
752 int length = strnlen(unc_name,1024);
753 char * share;
754 char * ipaddress_string = NULL;
755 struct hostent * host_entry = NULL;
756 struct in_addr server_ipaddr;
758 if(length > 1023) {
759 printf("mount error: UNC name too long");
760 return NULL;
762 if (strncasecmp("cifs://",unc_name,7) == 0)
763 return parse_cifs_url(unc_name+7);
764 if (strncasecmp("smb://",unc_name,6) == 0) {
765 return parse_cifs_url(unc_name+6);
768 if(length < 3) {
769 /* BB add code to find DFS root here */
770 printf("\nMounting the DFS root for domain not implemented yet\n");
771 return NULL;
772 } else {
773 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
774 /* check for nfs syntax ie server:share */
775 share = strchr(unc_name,':');
776 if(share) {
777 free_share_name = 1;
778 *punc_name = (char *)malloc(length+3);
779 if(*punc_name == NULL) {
780 /* put the original string back if
781 no memory left */
782 *punc_name = unc_name;
783 return NULL;
786 *share = '/';
787 strncpy((*punc_name)+2,unc_name,length);
788 unc_name = *punc_name;
789 unc_name[length+2] = 0;
790 goto continue_unc_parsing;
791 } else {
792 printf("mount error: improperly formatted UNC name.");
793 printf(" %s does not begin with \\\\ or //\n",unc_name);
794 return NULL;
796 } else {
797 continue_unc_parsing:
798 unc_name[0] = '/';
799 unc_name[1] = '/';
800 unc_name += 2;
801 if ((share = strchr(unc_name, '/')) ||
802 (share = strchr(unc_name,'\\'))) {
803 *share = 0; /* temporarily terminate the string */
804 share += 1;
805 if(got_ip == 0) {
806 host_entry = gethostbyname(unc_name);
808 *(share - 1) = '/'; /* put the slash back */
809 if ((prefixpath = strchr(share, '/'))) {
810 *prefixpath = 0; /* permanently terminate the string */
811 if (!strlen(++prefixpath))
812 prefixpath = NULL; /* this needs to be done explicitly */
814 if(got_ip) {
815 if(verboseflag)
816 printf("ip address specified explicitly\n");
817 return NULL;
819 if(host_entry == NULL) {
820 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
821 return NULL;
822 } else {
823 /* BB should we pass an alternate version of the share name as Unicode */
824 /* BB what about ipv6? BB */
825 /* BB add retries with alternate servers in list */
827 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
829 ipaddress_string = inet_ntoa(server_ipaddr);
830 if(ipaddress_string == NULL) {
831 printf("mount error: could not get valid ip address for target server\n");
832 return NULL;
834 return ipaddress_string;
836 } else {
837 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
838 printf("Mounting the DFS root for a particular server not implemented yet\n");
839 return NULL;
845 static struct option longopts[] = {
846 { "all", 0, NULL, 'a' },
847 { "help",0, NULL, 'h' },
848 { "move",0, NULL, 'm' },
849 { "bind",0, NULL, 'b' },
850 { "read-only", 0, NULL, 'r' },
851 { "ro", 0, NULL, 'r' },
852 { "verbose", 0, NULL, 'v' },
853 { "version", 0, NULL, 'V' },
854 { "read-write", 0, NULL, 'w' },
855 { "rw", 0, NULL, 'w' },
856 { "options", 1, NULL, 'o' },
857 { "type", 1, NULL, 't' },
858 { "rsize",1, NULL, 'R' },
859 { "wsize",1, NULL, 'W' },
860 { "uid", 1, NULL, '1'},
861 { "gid", 1, NULL, '2'},
862 { "user",1,NULL,'u'},
863 { "username",1,NULL,'u'},
864 { "dom",1,NULL,'d'},
865 { "domain",1,NULL,'d'},
866 { "password",1,NULL,'p'},
867 { "pass",1,NULL,'p'},
868 { "credentials",1,NULL,'c'},
869 { "port",1,NULL,'P'},
870 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
871 { NULL, 0, NULL, 0 }
874 int main(int argc, char ** argv)
876 int c;
877 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
878 char * orgoptions = NULL;
879 char * share_name = NULL;
880 char * ipaddr = NULL;
881 char * uuid = NULL;
882 char * mountpoint = NULL;
883 char * options = NULL;
884 char * resolved_path = NULL;
885 char * temp;
886 int rc;
887 int rsize = 0;
888 int wsize = 0;
889 int nomtab = 0;
890 int uid = 0;
891 int gid = 0;
892 int optlen = 0;
893 int orgoptlen = 0;
894 int retry = 0; /* set when we have to retry mount with uppercase */
895 struct stat statbuf;
896 struct utsname sysinfo;
897 struct mntent mountent;
898 FILE * pmntfile;
900 /* setlocale(LC_ALL, "");
901 bindtextdomain(PACKAGE, LOCALEDIR);
902 textdomain(PACKAGE); */
904 if(argc && argv) {
905 thisprogram = argv[0];
906 } else {
907 mount_cifs_usage();
908 exit(1);
911 if(thisprogram == NULL)
912 thisprogram = "mount.cifs";
914 uname(&sysinfo);
915 /* BB add workstation name and domain and pass down */
917 /* #ifdef _GNU_SOURCE
918 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
919 #endif */
920 if(argc > 2) {
921 share_name = argv[1];
922 mountpoint = argv[2];
925 /* add sharename in opts string as unc= parm */
927 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
928 longopts, NULL)) != -1) {
929 switch (c) {
930 /* No code to do the following options yet */
931 /* case 'l':
932 list_with_volumelabel = 1;
933 break;
934 case 'L':
935 volumelabel = optarg;
936 break; */
937 /* case 'a':
938 ++mount_all;
939 break; */
941 case '?':
942 case 'h': /* help */
943 mount_cifs_usage ();
944 exit(1);
945 case 'n':
946 ++nomtab;
947 break;
948 case 'b':
949 #ifdef MS_BIND
950 flags |= MS_BIND;
951 #else
952 fprintf(stderr,
953 "option 'b' (MS_BIND) not supported\n");
954 #endif
955 break;
956 case 'm':
957 #ifdef MS_MOVE
958 flags |= MS_MOVE;
959 #else
960 fprintf(stderr,
961 "option 'm' (MS_MOVE) not supported\n");
962 #endif
963 break;
964 case 'o':
965 orgoptions = strdup(optarg);
966 break;
967 case 'r': /* mount readonly */
968 flags |= MS_RDONLY;
969 break;
970 case 'U':
971 uuid = optarg;
972 break;
973 case 'v':
974 ++verboseflag;
975 break;
976 case 'V':
977 printf ("mount.cifs version: %s.%s%s\n",
978 MOUNT_CIFS_VERSION_MAJOR,
979 MOUNT_CIFS_VERSION_MINOR,
980 MOUNT_CIFS_VENDOR_SUFFIX);
981 if(mountpassword) {
982 memset(mountpassword,0,64);
984 exit (0);
985 case 'w':
986 flags &= ~MS_RDONLY;
987 break;
988 case 'R':
989 rsize = atoi(optarg) ;
990 break;
991 case 'W':
992 wsize = atoi(optarg);
993 break;
994 case '1':
995 if (isdigit(*optarg)) {
996 char *ep;
998 uid = strtoul(optarg, &ep, 10);
999 if (*ep) {
1000 printf("bad uid value \"%s\"\n", optarg);
1001 exit(1);
1003 } else {
1004 struct passwd *pw;
1006 if (!(pw = getpwnam(optarg))) {
1007 printf("bad user name \"%s\"\n", optarg);
1008 exit(1);
1010 uid = pw->pw_uid;
1011 endpwent();
1013 break;
1014 case '2':
1015 if (isdigit(*optarg)) {
1016 char *ep;
1018 gid = strtoul(optarg, &ep, 10);
1019 if (*ep) {
1020 printf("bad gid value \"%s\"\n", optarg);
1021 exit(1);
1023 } else {
1024 struct group *gr;
1026 if (!(gr = getgrnam(optarg))) {
1027 printf("bad user name \"%s\"\n", optarg);
1028 exit(1);
1030 gid = gr->gr_gid;
1031 endpwent();
1033 break;
1034 case 'u':
1035 got_user = 1;
1036 user_name = optarg;
1037 break;
1038 case 'd':
1039 domain_name = optarg; /* BB fix this - currently ignored */
1040 got_domain = 1;
1041 break;
1042 case 'p':
1043 if(mountpassword == NULL)
1044 mountpassword = (char *)calloc(65,1);
1045 if(mountpassword) {
1046 got_password = 1;
1047 strncpy(mountpassword,optarg,64);
1049 break;
1050 case 'S':
1051 get_password_from_file(0 /* stdin */,NULL);
1052 break;
1053 case 't':
1054 break;
1055 default:
1056 printf("unknown mount option %c\n",c);
1057 mount_cifs_usage();
1058 exit(1);
1062 if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1063 mount_cifs_usage();
1064 exit(1);
1067 if (getenv("PASSWD")) {
1068 if(mountpassword == NULL)
1069 mountpassword = (char *)calloc(65,1);
1070 if(mountpassword) {
1071 strncpy(mountpassword,getenv("PASSWD"),64);
1072 got_password = 1;
1074 } else if (getenv("PASSWD_FD")) {
1075 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1076 } else if (getenv("PASSWD_FILE")) {
1077 get_password_from_file(0, getenv("PASSWD_FILE"));
1080 if (orgoptions && parse_options(&orgoptions, &flags)) {
1081 rc = -1;
1082 goto mount_exit;
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 rc = -1;
1088 goto mount_exit;
1091 /* BB save off path and pop after mount returns? */
1092 resolved_path = (char *)malloc(PATH_MAX+1);
1093 if(resolved_path) {
1094 /* Note that if we can not canonicalize the name, we get
1095 another chance to see if it is valid when we chdir to it */
1096 if (realpath(mountpoint, resolved_path)) {
1097 mountpoint = resolved_path;
1100 if(chdir(mountpoint)) {
1101 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1102 rc = -1;
1103 goto mount_exit;
1106 if(stat (".", &statbuf)) {
1107 printf("mount error: mount point %s does not exist\n",mountpoint);
1108 rc = -1;
1109 goto mount_exit;
1112 if (S_ISDIR(statbuf.st_mode) == 0) {
1113 printf("mount error: mount point %s is not a directory\n",mountpoint);
1114 rc = -1;
1115 goto mount_exit;
1118 if((getuid() != 0) && (geteuid() == 0)) {
1119 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1120 #ifndef CIFS_ALLOW_USR_SUID
1121 /* Do not allow user mounts to control suid flag
1122 for mount unless explicitly built that way */
1123 flags |= MS_NOSUID | MS_NODEV;
1124 #endif
1125 } else {
1126 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1127 return -1;
1131 if(got_user == 0) {
1132 user_name = getusername();
1133 got_user = 1;
1136 if(got_password == 0) {
1137 mountpassword = getpass("Password: "); /* BB obsolete */
1138 got_password = 1;
1140 /* FIXME launch daemon (handles dfs name resolution and credential change)
1141 remember to clear parms and overwrite password field before launching */
1142 mount_retry:
1143 if(orgoptions) {
1144 optlen = strlen(orgoptions);
1145 orgoptlen = optlen;
1146 } else
1147 optlen = 0;
1148 if(share_name)
1149 optlen += strlen(share_name) + 4;
1150 else {
1151 printf("No server share name specified\n");
1152 printf("\nMounting the DFS root for server not implemented yet\n");
1153 exit(1);
1155 if(user_name)
1156 optlen += strlen(user_name) + 6;
1157 if(ipaddr)
1158 optlen += strlen(ipaddr) + 4;
1159 if(mountpassword)
1160 optlen += strlen(mountpassword) + 6;
1161 if(options)
1162 free(options);
1163 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 */);
1165 if(options == NULL) {
1166 printf("Could not allocate memory for mount options\n");
1167 return -1;
1171 options[0] = 0;
1172 strncat(options,"unc=",4);
1173 strcat(options,share_name);
1174 /* scan backwards and reverse direction of slash */
1175 temp = strrchr(options, '/');
1176 if(temp > options + 6)
1177 *temp = '\\';
1178 if(ipaddr) {
1179 strncat(options,",ip=",4);
1180 strcat(options,ipaddr);
1183 if(user_name) {
1184 /* check for syntax like user=domain\user */
1185 if(got_domain == 0)
1186 domain_name = check_for_domain(&user_name);
1187 strncat(options,",user=",6);
1188 strcat(options,user_name);
1190 if(retry == 0) {
1191 if(domain_name) {
1192 /* extra length accounted for in option string above */
1193 strncat(options,",domain=",8);
1194 strcat(options,domain_name);
1197 if(mountpassword) {
1198 /* Commas have to be doubled, or else they will
1199 look like the parameter separator */
1200 /* if(sep is not set)*/
1201 if(retry == 0)
1202 check_for_comma(&mountpassword);
1203 strncat(options,",pass=",6);
1204 strcat(options,mountpassword);
1207 strncat(options,",ver=",5);
1208 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1210 if(orgoptions) {
1211 strcat(options,",");
1212 strcat(options,orgoptions);
1214 if(prefixpath) {
1215 strncat(options,",prefixpath=",12);
1216 strcat(options,prefixpath); /* no need to cat the / */
1218 if(verboseflag)
1219 printf("\nmount.cifs kernel mount options %s \n",options);
1220 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1221 /* remember to kill daemon on error */
1222 char * tmp;
1224 switch (errno) {
1225 case 0:
1226 printf("mount failed but no error number set\n");
1227 break;
1228 case ENODEV:
1229 printf("mount error: cifs filesystem not supported by the system\n");
1230 break;
1231 case ENXIO:
1232 if(retry == 0) {
1233 retry = 1;
1234 tmp = share_name;
1235 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1236 *tmp = toupper((unsigned char)*tmp);
1237 tmp++;
1239 if(!*tmp) {
1240 printf("retrying with upper case share name\n");
1241 goto mount_retry;
1244 default:
1245 printf("mount error %d = %s\n",errno,strerror(errno));
1247 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1248 rc = -1;
1249 goto mount_exit;
1250 } else {
1251 pmntfile = setmntent(MOUNTED, "a+");
1252 if(pmntfile) {
1253 mountent.mnt_fsname = share_name;
1254 mountent.mnt_dir = mountpoint;
1255 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1256 mountent.mnt_opts = (char *)malloc(220);
1257 if(mountent.mnt_opts) {
1258 char * mount_user = getusername();
1259 memset(mountent.mnt_opts,0,200);
1260 if(flags & MS_RDONLY)
1261 strcat(mountent.mnt_opts,"ro");
1262 else
1263 strcat(mountent.mnt_opts,"rw");
1264 if(flags & MS_MANDLOCK)
1265 strcat(mountent.mnt_opts,",mand");
1266 if(flags & MS_NOEXEC)
1267 strcat(mountent.mnt_opts,",noexec");
1268 if(flags & MS_NOSUID)
1269 strcat(mountent.mnt_opts,",nosuid");
1270 if(flags & MS_NODEV)
1271 strcat(mountent.mnt_opts,",nodev");
1272 if(flags & MS_SYNCHRONOUS)
1273 strcat(mountent.mnt_opts,",synch");
1274 if(mount_user) {
1275 if(getuid() != 0) {
1276 strcat(mountent.mnt_opts,",user=");
1277 strcat(mountent.mnt_opts,mount_user);
1279 /* free(mount_user); do not free static mem */
1282 mountent.mnt_freq = 0;
1283 mountent.mnt_passno = 0;
1284 rc = addmntent(pmntfile,&mountent);
1285 endmntent(pmntfile);
1286 if(mountent.mnt_opts)
1287 free(mountent.mnt_opts);
1288 } else {
1289 printf("could not update mount table\n");
1292 rc = 0;
1293 mount_exit:
1294 if(mountpassword) {
1295 int len = strlen(mountpassword);
1296 memset(mountpassword,0,len);
1297 free(mountpassword);
1300 if(options) {
1301 memset(options,0,optlen);
1302 free(options);
1305 if(orgoptions) {
1306 memset(orgoptions,0,orgoptlen);
1307 free(orgoptions);
1309 if(resolved_path) {
1310 free(resolved_path);
1313 if(free_share_name) {
1314 free(share_name);
1316 return rc;