r20124: clean up nested extern declaration warnings
[Samba/gebeck_regimport.git] / source3 / client / mount.cifs.c
blob076001ccc076ff22e9cb7e7b0b93a69b2f48ef72
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 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
64 const char *thisprogram;
65 int verboseflag = 0;
66 static int got_password = 0;
67 static int got_user = 0;
68 static int got_domain = 0;
69 static int got_ip = 0;
70 static int got_unc = 0;
71 static int got_uid = 0;
72 static int got_gid = 0;
73 static int free_share_name = 0;
74 static char * user_name = NULL;
75 static char * mountpassword = NULL;
76 char * domain_name = NULL;
79 /* BB finish BB
81 cifs_umount
82 open nofollow - avoid symlink exposure?
83 get owner of dir see if matches self or if root
84 call system(umount argv) etc.
86 BB end finish BB */
88 static char * check_for_domain(char **);
91 static void mount_cifs_usage(void)
93 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
94 printf("\nMount the remote target, specified as a UNC name,");
95 printf(" to a local directory.\n\nOptions:\n");
96 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
97 printf("\nLess commonly used options:");
98 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
99 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
100 printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
101 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
102 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
103 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
104 printf("\n\nRarely used options:");
105 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
106 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
107 printf("\n\tnointr,ignorecase,noposixpaths,noacl");
108 printf("\n\nOptions are described in more detail in the manual page");
109 printf("\n\tman 8 mount.cifs\n");
110 printf("\nTo display the version number of the mount helper:");
111 printf("\n\t%s -V\n",thisprogram);
113 if(mountpassword) {
114 memset(mountpassword,0,64);
115 free(mountpassword);
117 exit(1);
120 /* caller frees username if necessary */
121 static char * getusername(void) {
122 char *username = NULL;
123 struct passwd *password = getpwuid(getuid());
125 if (password) {
126 username = password->pw_name;
128 return username;
131 static char * parse_cifs_url(char * unc_name)
133 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
134 return NULL;
137 static int open_cred_file(char * file_name)
139 char * line_buf;
140 char * temp_val;
141 FILE * fs;
142 int i, length;
143 fs = fopen(file_name,"r");
144 if(fs == NULL)
145 return errno;
146 line_buf = (char *)malloc(4096);
147 if(line_buf == NULL) {
148 fclose(fs);
149 return -ENOMEM;
152 while(fgets(line_buf,4096,fs)) {
153 /* parse line from credential file */
155 /* eat leading white space */
156 for(i=0;i<4086;i++) {
157 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
158 break;
159 /* if whitespace - skip past it */
161 if (strncasecmp("username",line_buf+i,8) == 0) {
162 temp_val = strchr(line_buf + i,'=');
163 if(temp_val) {
164 /* go past equals sign */
165 temp_val++;
166 for(length = 0;length<4087;length++) {
167 if(temp_val[length] == '\n')
168 break;
170 if(length > 4086) {
171 printf("mount.cifs failed due to malformed username in credentials file");
172 memset(line_buf,0,4096);
173 if(mountpassword) {
174 memset(mountpassword,0,64);
176 exit(1);
177 } else {
178 got_user = 1;
179 user_name = (char *)calloc(1 + length,1);
180 /* BB adding free of user_name string before exit,
181 not really necessary but would be cleaner */
182 strncpy(user_name,temp_val, length);
185 } else if (strncasecmp("password",line_buf+i,8) == 0) {
186 temp_val = strchr(line_buf+i,'=');
187 if(temp_val) {
188 /* go past equals sign */
189 temp_val++;
190 for(length = 0;length<65;length++) {
191 if(temp_val[length] == '\n')
192 break;
194 if(length > 64) {
195 printf("mount.cifs failed: password in credentials file too long\n");
196 memset(line_buf,0, 4096);
197 if(mountpassword) {
198 memset(mountpassword,0,64);
200 exit(1);
201 } else {
202 if(mountpassword == NULL) {
203 mountpassword = (char *)calloc(65,1);
204 } else
205 memset(mountpassword,0,64);
206 if(mountpassword) {
207 strncpy(mountpassword,temp_val,length);
208 got_password = 1;
212 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
213 temp_val = strchr(line_buf+i,'=');
214 if(temp_val) {
215 /* go past equals sign */
216 temp_val++;
217 if(verboseflag)
218 printf("\nDomain %s\n",temp_val);
219 for(length = 0;length<65;length++) {
220 if(temp_val[length] == '\n')
221 break;
223 if(length > 64) {
224 printf("mount.cifs failed: domain in credentials file too long\n");
225 if(mountpassword) {
226 memset(mountpassword,0,64);
228 exit(1);
229 } else {
230 if(domain_name == NULL) {
231 domain_name = (char *)calloc(65,1);
232 } else
233 memset(domain_name,0,64);
234 if(domain_name) {
235 strncpy(domain_name,temp_val,length);
236 got_domain = 1;
243 fclose(fs);
244 if(line_buf) {
245 memset(line_buf,0,4096);
246 free(line_buf);
248 return 0;
251 static int get_password_from_file(int file_descript, char * filename)
253 int rc = 0;
254 int i;
255 char c;
257 if(mountpassword == NULL)
258 mountpassword = (char *)calloc(65,1);
259 else
260 memset(mountpassword, 0, 64);
262 if (mountpassword == NULL) {
263 printf("malloc failed\n");
264 exit(1);
267 if(filename != NULL) {
268 file_descript = open(filename, O_RDONLY);
269 if(file_descript < 0) {
270 printf("mount.cifs failed. %s attempting to open password file %s\n",
271 strerror(errno),filename);
272 exit(1);
275 /* else file already open and fd provided */
277 for(i=0;i<64;i++) {
278 rc = read(file_descript,&c,1);
279 if(rc < 0) {
280 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
281 memset(mountpassword,0,64);
282 if(filename != NULL)
283 close(file_descript);
284 exit(1);
285 } else if(rc == 0) {
286 if(mountpassword[0] == 0) {
287 if(verboseflag)
288 printf("\nWarning: null password used since cifs password file empty");
290 break;
291 } else /* read valid character */ {
292 if((c == 0) || (c == '\n')) {
293 break;
294 } else
295 mountpassword[i] = c;
298 if((i == 64) && (verboseflag)) {
299 printf("\nWarning: password longer than 64 characters specified in cifs password file");
301 got_password = 1;
302 if(filename != NULL) {
303 close(file_descript);
306 return rc;
309 static int parse_options(char ** optionsp, int * filesys_flags)
311 const char * data;
312 char * percent_char = NULL;
313 char * value = NULL;
314 char * next_keyword = NULL;
315 char * out = NULL;
316 int out_len = 0;
317 int word_len;
318 int rc = 0;
320 if (!optionsp || !*optionsp)
321 return 1;
322 data = *optionsp;
324 if(verboseflag)
325 printf("parsing options: %s\n", data);
327 /* BB fixme check for separator override BB */
329 /* while ((data = strsep(&options, ",")) != NULL) { */
330 while(data != NULL) {
331 /* check if ends with trailing comma */
332 if(*data == 0)
333 break;
335 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
336 /* data = next keyword */
337 /* value = next value ie stuff after equal sign */
339 next_keyword = strchr(data,','); /* BB handle sep= */
341 /* temporarily null terminate end of keyword=value pair */
342 if(next_keyword)
343 *next_keyword++ = 0;
345 /* temporarily null terminate keyword to make keyword and value distinct */
346 if ((value = strchr(data, '=')) != NULL) {
347 *value = '\0';
348 value++;
351 if (strncmp(data, "users",5) == 0) {
352 if(!value || !*value) {
353 goto nocopy;
355 } else if (strncmp(data, "user_xattr",10) == 0) {
356 /* do nothing - need to skip so not parsed as user name */
357 } else if (strncmp(data, "user", 4) == 0) {
359 if (!value || !*value) {
360 if(data[4] == '\0') {
361 if(verboseflag)
362 printf("\nskipping empty user mount parameter\n");
363 /* remove the parm since it would otherwise be confusing
364 to the kernel code which would think it was a real username */
365 goto nocopy;
366 } else {
367 printf("username specified with no parameter\n");
368 return 1; /* needs_arg; */
370 } else {
371 if (strnlen(value, 260) < 260) {
372 got_user=1;
373 percent_char = strchr(value,'%');
374 if(percent_char) {
375 *percent_char = ',';
376 if(mountpassword == NULL)
377 mountpassword = (char *)calloc(65,1);
378 if(mountpassword) {
379 if(got_password)
380 printf("\nmount.cifs warning - password specified twice\n");
381 got_password = 1;
382 percent_char++;
383 strncpy(mountpassword, percent_char,64);
384 /* remove password from username */
385 while(*percent_char != 0) {
386 *percent_char = ',';
387 percent_char++;
391 /* this is only case in which the user
392 name buf is not malloc - so we have to
393 check for domain name embedded within
394 the user name here since the later
395 call to check_for_domain will not be
396 invoked */
397 domain_name = check_for_domain(&value);
398 } else {
399 printf("username too long\n");
400 return 1;
403 } else if (strncmp(data, "pass", 4) == 0) {
404 if (!value || !*value) {
405 if(got_password) {
406 printf("\npassword specified twice, ignoring second\n");
407 } else
408 got_password = 1;
409 } else if (strnlen(value, 17) < 17) {
410 if(got_password)
411 printf("\nmount.cifs warning - password specified twice\n");
412 got_password = 1;
413 } else {
414 printf("password too long\n");
415 return 1;
417 } else if (strncmp(data, "ip", 2) == 0) {
418 if (!value || !*value) {
419 printf("target ip address argument missing");
420 } else if (strnlen(value, 35) < 35) {
421 if(verboseflag)
422 printf("ip address %s override specified\n",value);
423 got_ip = 1;
424 } else {
425 printf("ip address too long\n");
426 return 1;
428 } else if ((strncmp(data, "unc", 3) == 0)
429 || (strncmp(data, "target", 6) == 0)
430 || (strncmp(data, "path", 4) == 0)) {
431 if (!value || !*value) {
432 printf("invalid path to network resource\n");
433 return 1; /* needs_arg; */
434 } else if(strnlen(value,5) < 5) {
435 printf("UNC name too short");
438 if (strnlen(value, 300) < 300) {
439 got_unc = 1;
440 if (strncmp(value, "//", 2) == 0) {
441 if(got_unc)
442 printf("unc name specified twice, ignoring second\n");
443 else
444 got_unc = 1;
445 } else if (strncmp(value, "\\\\", 2) != 0) {
446 printf("UNC Path does not begin with // or \\\\ \n");
447 return 1;
448 } else {
449 if(got_unc)
450 printf("unc name specified twice, ignoring second\n");
451 else
452 got_unc = 1;
454 } else {
455 printf("CIFS: UNC name too long\n");
456 return 1;
458 } else if ((strncmp(data, "domain", 3) == 0)
459 || (strncmp(data, "workgroup", 5) == 0)) {
460 if (!value || !*value) {
461 printf("CIFS: invalid domain name\n");
462 return 1; /* needs_arg; */
464 if (strnlen(value, 65) < 65) {
465 got_domain = 1;
466 } else {
467 printf("domain name too long\n");
468 return 1;
470 } else if (strncmp(data, "cred", 4) == 0) {
471 if (value && *value) {
472 rc = open_cred_file(value);
473 if(rc) {
474 printf("error %d opening credential file %s\n",rc, value);
475 return 1;
477 } else {
478 printf("invalid credential file name specified\n");
479 return 1;
481 } else if (strncmp(data, "uid", 3) == 0) {
482 if (value && *value) {
483 got_uid = 1;
484 if (!isdigit(*value)) {
485 struct passwd *pw;
486 static char temp[32];
488 if (!(pw = getpwnam(value))) {
489 printf("bad user name \"%s\"\n", value);
490 exit(1);
492 sprintf(temp, "%u", pw->pw_uid);
493 value = temp;
494 endpwent();
497 } else if (strncmp(data, "gid", 3) == 0) {
498 if (value && *value) {
499 got_gid = 1;
500 if (!isdigit(*value)) {
501 struct group *gr;
502 static char temp[32];
504 if (!(gr = getgrnam(value))) {
505 printf("bad group name \"%s\"\n", value);
506 exit(1);
508 sprintf(temp, "%u", gr->gr_gid);
509 value = temp;
510 endpwent();
513 /* fmask and dmask synonyms for people used to smbfs syntax */
514 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
515 if (!value || !*value) {
516 printf ("Option '%s' requires a numerical argument\n", data);
517 return 1;
520 if (value[0] != '0') {
521 printf ("WARNING: '%s' not expressed in octal.\n", data);
524 if (strcmp (data, "fmask") == 0) {
525 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
526 data = "file_mode"; /* BB fix this */
528 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
529 if (!value || !*value) {
530 printf ("Option '%s' requires a numerical argument\n", data);
531 return 1;
534 if (value[0] != '0') {
535 printf ("WARNING: '%s' not expressed in octal.\n", data);
538 if (strcmp (data, "dmask") == 0) {
539 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
540 data = "dir_mode";
542 /* the following eight mount options should be
543 stripped out from what is passed into the kernel
544 since these eight options are best passed as the
545 mount flags rather than redundantly to the kernel
546 and could generate spurious warnings depending on the
547 level of the corresponding cifs vfs kernel code */
548 } else if (strncmp(data, "nosuid", 6) == 0) {
549 *filesys_flags |= MS_NOSUID;
550 } else if (strncmp(data, "suid", 4) == 0) {
551 *filesys_flags &= ~MS_NOSUID;
552 } else if (strncmp(data, "nodev", 5) == 0) {
553 *filesys_flags |= MS_NODEV;
554 } else if ((strncmp(data, "nobrl", 5) == 0) ||
555 (strncmp(data, "nolock", 6) == 0)) {
556 *filesys_flags &= ~MS_MANDLOCK;
557 } else if (strncmp(data, "dev", 3) == 0) {
558 *filesys_flags &= ~MS_NODEV;
559 } else if (strncmp(data, "noexec", 6) == 0) {
560 *filesys_flags |= MS_NOEXEC;
561 } else if (strncmp(data, "exec", 4) == 0) {
562 *filesys_flags &= ~MS_NOEXEC;
563 } else if (strncmp(data, "guest", 5) == 0) {
564 got_password=1;
565 /* remove the parm since it would otherwise be logged by kern */
566 goto nocopy;
567 } else if (strncmp(data, "ro", 2) == 0) {
568 *filesys_flags |= MS_RDONLY;
569 } else if (strncmp(data, "rw", 2) == 0) {
570 *filesys_flags &= ~MS_RDONLY;
571 } else if (strncmp(data, "remount", 7) == 0) {
572 *filesys_flags |= MS_REMOUNT;
573 } /* else if (strnicmp(data, "port", 4) == 0) {
574 if (value && *value) {
575 vol->port =
576 simple_strtoul(value, &value, 0);
578 } else if (strnicmp(data, "rsize", 5) == 0) {
579 if (value && *value) {
580 vol->rsize =
581 simple_strtoul(value, &value, 0);
583 } else if (strnicmp(data, "wsize", 5) == 0) {
584 if (value && *value) {
585 vol->wsize =
586 simple_strtoul(value, &value, 0);
588 } else if (strnicmp(data, "version", 3) == 0) {
589 } else {
590 printf("CIFS: Unknown mount option %s\n",data);
591 } */ /* nothing to do on those four mount options above.
592 Just pass to kernel and ignore them here */
594 /* Copy (possibly modified) option to out */
595 word_len = strlen(data);
596 if (value)
597 word_len += 1 + strlen(value);
599 out = (char *)realloc(out, out_len + word_len + 2);
600 if (out == NULL) {
601 perror("malloc");
602 exit(1);
605 if (out_len)
606 out[out_len++] = ',';
607 if (value)
608 sprintf(out + out_len, "%s=%s", data, value);
609 else
610 sprintf(out + out_len, "%s", data);
611 out_len = strlen(out);
613 nocopy:
614 data = next_keyword;
616 *optionsp = out;
617 return 0;
620 /* replace all (one or more) commas with double commas */
621 static void check_for_comma(char ** ppasswrd)
623 char *new_pass_buf;
624 char *pass;
625 int i,j;
626 int number_of_commas = 0;
627 int len;
629 if(ppasswrd == NULL)
630 return;
631 else
632 (pass = *ppasswrd);
634 len = strlen(pass);
636 for(i=0;i<len;i++) {
637 if(pass[i] == ',')
638 number_of_commas++;
641 if(number_of_commas == 0)
642 return;
643 if(number_of_commas > 64) {
644 /* would otherwise overflow the mount options buffer */
645 printf("\nInvalid password. Password contains too many commas.\n");
646 return;
649 new_pass_buf = (char *)malloc(len+number_of_commas+1);
650 if(new_pass_buf == NULL)
651 return;
653 for(i=0,j=0;i<len;i++,j++) {
654 new_pass_buf[j] = pass[i];
655 if(pass[i] == ',') {
656 j++;
657 new_pass_buf[j] = pass[i];
660 new_pass_buf[len+number_of_commas] = 0;
662 free(*ppasswrd);
663 *ppasswrd = new_pass_buf;
665 return;
668 /* Usernames can not have backslash in them and we use
669 [BB check if usernames can have forward slash in them BB]
670 backslash as domain\user separator character
672 static char * check_for_domain(char **ppuser)
674 char * original_string;
675 char * usernm;
676 char * domainnm;
677 int original_len;
678 int len;
679 int i;
681 if(ppuser == NULL)
682 return NULL;
684 original_string = *ppuser;
686 if (original_string == NULL)
687 return NULL;
689 original_len = strlen(original_string);
691 usernm = strchr(*ppuser,'/');
692 if (usernm == NULL) {
693 usernm = strchr(*ppuser,'\\');
694 if (usernm == NULL)
695 return NULL;
698 if(got_domain) {
699 printf("Domain name specified twice. Username probably malformed\n");
700 return NULL;
703 usernm[0] = 0;
704 domainnm = *ppuser;
705 if (domainnm[0] != 0) {
706 got_domain = 1;
707 } else {
708 printf("null domain\n");
710 len = strlen(domainnm);
711 /* reset domainm to new buffer, and copy
712 domain name into it */
713 domainnm = (char *)malloc(len+1);
714 if(domainnm == NULL)
715 return NULL;
717 strcpy(domainnm,*ppuser);
719 /* move_string(*ppuser, usernm+1) */
720 len = strlen(usernm+1);
722 if(len >= original_len) {
723 /* should not happen */
724 return domainnm;
727 for(i=0;i<original_len;i++) {
728 if(i<len)
729 original_string[i] = usernm[i+1];
730 else /* stuff with commas to remove last parm */
731 original_string[i] = ',';
734 /* BB add check for more than one slash?
735 strchr(*ppuser,'/');
736 strchr(*ppuser,'\\')
739 return domainnm;
742 /* Note that caller frees the returned buffer if necessary */
743 static char * parse_server(char ** punc_name)
745 char * unc_name = *punc_name;
746 int length = strnlen(unc_name,1024);
747 char * share;
748 char * ipaddress_string = NULL;
749 struct hostent * host_entry = NULL;
750 struct in_addr server_ipaddr;
752 if(length > 1023) {
753 printf("mount error: UNC name too long");
754 return NULL;
756 if (strncasecmp("cifs://",unc_name,7) == 0)
757 return parse_cifs_url(unc_name+7);
758 if (strncasecmp("smb://",unc_name,6) == 0) {
759 return parse_cifs_url(unc_name+6);
762 if(length < 3) {
763 /* BB add code to find DFS root here */
764 printf("\nMounting the DFS root for domain not implemented yet\n");
765 return NULL;
766 } else {
767 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
768 /* check for nfs syntax ie server:share */
769 share = strchr(unc_name,':');
770 if(share) {
771 free_share_name = 1;
772 *punc_name = (char *)malloc(length+3);
773 if(*punc_name == NULL) {
774 /* put the original string back if
775 no memory left */
776 *punc_name = unc_name;
777 return NULL;
780 *share = '/';
781 strncpy((*punc_name)+2,unc_name,length);
782 unc_name = *punc_name;
783 unc_name[length+2] = 0;
784 goto continue_unc_parsing;
785 } else {
786 printf("mount error: improperly formatted UNC name.");
787 printf(" %s does not begin with \\\\ or //\n",unc_name);
788 return NULL;
790 } else {
791 continue_unc_parsing:
792 unc_name[0] = '/';
793 unc_name[1] = '/';
794 unc_name += 2;
795 if ((share = strchr(unc_name, '/')) ||
796 (share = strchr(unc_name,'\\'))) {
797 *share = 0; /* temporarily terminate the string */
798 share += 1;
799 if(got_ip == 0) {
800 host_entry = gethostbyname(unc_name);
802 *(share - 1) = '/'; /* put the slash back */
803 if(got_ip) {
804 if(verboseflag)
805 printf("ip address specified explicitly\n");
806 return NULL;
808 if(host_entry == NULL) {
809 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
810 return NULL;
811 } else {
812 /* BB should we pass an alternate version of the share name as Unicode */
813 /* BB what about ipv6? BB */
814 /* BB add retries with alternate servers in list */
816 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
818 ipaddress_string = inet_ntoa(server_ipaddr);
819 if(ipaddress_string == NULL) {
820 printf("mount error: could not get valid ip address for target server\n");
821 return NULL;
823 return ipaddress_string;
825 } else {
826 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
827 printf("Mounting the DFS root for a particular server not implemented yet\n");
828 return NULL;
834 static struct option longopts[] = {
835 { "all", 0, NULL, 'a' },
836 { "help",0, NULL, 'h' },
837 { "move",0, NULL, 'm' },
838 { "bind",0, NULL, 'b' },
839 { "read-only", 0, NULL, 'r' },
840 { "ro", 0, NULL, 'r' },
841 { "verbose", 0, NULL, 'v' },
842 { "version", 0, NULL, 'V' },
843 { "read-write", 0, NULL, 'w' },
844 { "rw", 0, NULL, 'w' },
845 { "options", 1, NULL, 'o' },
846 { "type", 1, NULL, 't' },
847 { "rsize",1, NULL, 'R' },
848 { "wsize",1, NULL, 'W' },
849 { "uid", 1, NULL, '1'},
850 { "gid", 1, NULL, '2'},
851 { "user",1,NULL,'u'},
852 { "username",1,NULL,'u'},
853 { "dom",1,NULL,'d'},
854 { "domain",1,NULL,'d'},
855 { "password",1,NULL,'p'},
856 { "pass",1,NULL,'p'},
857 { "credentials",1,NULL,'c'},
858 { "port",1,NULL,'P'},
859 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
860 { NULL, 0, NULL, 0 }
863 int main(int argc, char ** argv)
865 int c;
866 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
867 char * orgoptions = NULL;
868 char * share_name = NULL;
869 char * ipaddr = NULL;
870 char * uuid = NULL;
871 char * mountpoint = NULL;
872 char * options;
873 char * resolved_path;
874 char * temp;
875 int rc;
876 int rsize = 0;
877 int wsize = 0;
878 int nomtab = 0;
879 int uid = 0;
880 int gid = 0;
881 int optlen = 0;
882 int orgoptlen = 0;
883 int retry = 0; /* set when we have to retry mount with uppercase */
884 struct stat statbuf;
885 struct utsname sysinfo;
886 struct mntent mountent;
887 FILE * pmntfile;
889 /* setlocale(LC_ALL, "");
890 bindtextdomain(PACKAGE, LOCALEDIR);
891 textdomain(PACKAGE); */
893 if(argc && argv) {
894 thisprogram = argv[0];
895 } else {
896 mount_cifs_usage();
897 exit(1);
900 if(thisprogram == NULL)
901 thisprogram = "mount.cifs";
903 uname(&sysinfo);
904 /* BB add workstation name and domain and pass down */
906 /* #ifdef _GNU_SOURCE
907 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
908 #endif */
909 if(argc > 2) {
910 share_name = argv[1];
911 mountpoint = argv[2];
914 /* add sharename in opts string as unc= parm */
916 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
917 longopts, NULL)) != -1) {
918 switch (c) {
919 /* No code to do the following options yet */
920 /* case 'l':
921 list_with_volumelabel = 1;
922 break;
923 case 'L':
924 volumelabel = optarg;
925 break; */
926 /* case 'a':
927 ++mount_all;
928 break; */
930 case '?':
931 case 'h': /* help */
932 mount_cifs_usage ();
933 exit(1);
934 case 'n':
935 ++nomtab;
936 break;
937 case 'b':
938 #ifdef MS_BIND
939 flags |= MS_BIND;
940 #else
941 fprintf(stderr,
942 "option 'b' (MS_BIND) not supported\n");
943 #endif
944 break;
945 case 'm':
946 #ifdef MS_MOVE
947 flags |= MS_MOVE;
948 #else
949 fprintf(stderr,
950 "option 'm' (MS_MOVE) not supported\n");
951 #endif
952 break;
953 case 'o':
954 orgoptions = strdup(optarg);
955 break;
956 case 'r': /* mount readonly */
957 flags |= MS_RDONLY;
958 break;
959 case 'U':
960 uuid = optarg;
961 break;
962 case 'v':
963 ++verboseflag;
964 break;
965 case 'V':
966 printf ("mount.cifs version: %s.%s%s\n",
967 MOUNT_CIFS_VERSION_MAJOR,
968 MOUNT_CIFS_VERSION_MINOR,
969 MOUNT_CIFS_VENDOR_SUFFIX);
970 if(mountpassword) {
971 memset(mountpassword,0,64);
973 exit (0);
974 case 'w':
975 flags &= ~MS_RDONLY;
976 break;
977 case 'R':
978 rsize = atoi(optarg) ;
979 break;
980 case 'W':
981 wsize = atoi(optarg);
982 break;
983 case '1':
984 if (isdigit(*optarg)) {
985 char *ep;
987 uid = strtoul(optarg, &ep, 10);
988 if (*ep) {
989 printf("bad uid value \"%s\"\n", optarg);
990 exit(1);
992 } else {
993 struct passwd *pw;
995 if (!(pw = getpwnam(optarg))) {
996 printf("bad user name \"%s\"\n", optarg);
997 exit(1);
999 uid = pw->pw_uid;
1000 endpwent();
1002 break;
1003 case '2':
1004 if (isdigit(*optarg)) {
1005 char *ep;
1007 gid = strtoul(optarg, &ep, 10);
1008 if (*ep) {
1009 printf("bad gid value \"%s\"\n", optarg);
1010 exit(1);
1012 } else {
1013 struct group *gr;
1015 if (!(gr = getgrnam(optarg))) {
1016 printf("bad user name \"%s\"\n", optarg);
1017 exit(1);
1019 gid = gr->gr_gid;
1020 endpwent();
1022 break;
1023 case 'u':
1024 got_user = 1;
1025 user_name = optarg;
1026 break;
1027 case 'd':
1028 domain_name = optarg; /* BB fix this - currently ignored */
1029 got_domain = 1;
1030 break;
1031 case 'p':
1032 if(mountpassword == NULL)
1033 mountpassword = (char *)calloc(65,1);
1034 if(mountpassword) {
1035 got_password = 1;
1036 strncpy(mountpassword,optarg,64);
1038 break;
1039 case 'S':
1040 get_password_from_file(0 /* stdin */,NULL);
1041 break;
1042 case 't':
1043 break;
1044 default:
1045 printf("unknown mount option %c\n",c);
1046 mount_cifs_usage();
1047 exit(1);
1051 if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1052 mount_cifs_usage();
1053 exit(1);
1056 if (getenv("PASSWD")) {
1057 if(mountpassword == NULL)
1058 mountpassword = (char *)calloc(65,1);
1059 if(mountpassword) {
1060 strncpy(mountpassword,getenv("PASSWD"),64);
1061 got_password = 1;
1063 } else if (getenv("PASSWD_FD")) {
1064 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1065 } else if (getenv("PASSWD_FILE")) {
1066 get_password_from_file(0, getenv("PASSWD_FILE"));
1069 if (orgoptions && parse_options(&orgoptions, &flags))
1070 return -1;
1071 ipaddr = parse_server(&share_name);
1072 if((ipaddr == NULL) && (got_ip == 0)) {
1073 printf("No ip address specified and hostname not found\n");
1074 return -1;
1077 /* BB save off path and pop after mount returns? */
1078 resolved_path = (char *)malloc(PATH_MAX+1);
1079 if(resolved_path) {
1080 /* Note that if we can not canonicalize the name, we get
1081 another chance to see if it is valid when we chdir to it */
1082 if (realpath(mountpoint, resolved_path)) {
1083 mountpoint = resolved_path;
1086 if(chdir(mountpoint)) {
1087 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1088 return -1;
1091 if(stat (".", &statbuf)) {
1092 printf("mount error: mount point %s does not exist\n",mountpoint);
1093 return -1;
1096 if (S_ISDIR(statbuf.st_mode) == 0) {
1097 printf("mount error: mount point %s is not a directory\n",mountpoint);
1098 return -1;
1101 if((getuid() != 0) && (geteuid() == 0)) {
1102 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1103 #ifndef CIFS_ALLOW_USR_SUID
1104 /* Do not allow user mounts to control suid flag
1105 for mount unless explicitly built that way */
1106 flags |= MS_NOSUID | MS_NODEV;
1107 #endif
1108 } else {
1109 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1110 return -1;
1114 if(got_user == 0) {
1115 user_name = getusername();
1116 got_user = 1;
1119 if(got_password == 0) {
1120 mountpassword = getpass("Password: "); /* BB obsolete */
1121 got_password = 1;
1123 /* FIXME launch daemon (handles dfs name resolution and credential change)
1124 remember to clear parms and overwrite password field before launching */
1125 mount_retry:
1126 if(orgoptions) {
1127 optlen = strlen(orgoptions);
1128 orgoptlen = optlen;
1129 } else
1130 optlen = 0;
1131 if(share_name)
1132 optlen += strlen(share_name) + 4;
1133 else {
1134 printf("No server share name specified\n");
1135 printf("\nMounting the DFS root for server not implemented yet\n");
1136 exit(1);
1138 if(user_name)
1139 optlen += strlen(user_name) + 6;
1140 if(ipaddr)
1141 optlen += strlen(ipaddr) + 4;
1142 if(mountpassword)
1143 optlen += strlen(mountpassword) + 6;
1144 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 */);
1146 if(options == NULL) {
1147 printf("Could not allocate memory for mount options\n");
1148 return -1;
1152 options[0] = 0;
1153 strncat(options,"unc=",4);
1154 strcat(options,share_name);
1155 /* scan backwards and reverse direction of slash */
1156 temp = strrchr(options, '/');
1157 if(temp > options + 6)
1158 *temp = '\\';
1159 if(ipaddr) {
1160 strncat(options,",ip=",4);
1161 strcat(options,ipaddr);
1164 if(user_name) {
1165 /* check for syntax like user=domain\user */
1166 if(got_domain == 0)
1167 domain_name = check_for_domain(&user_name);
1168 strncat(options,",user=",6);
1169 strcat(options,user_name);
1171 if(retry == 0) {
1172 if(domain_name) {
1173 /* extra length accounted for in option string above */
1174 strncat(options,",domain=",8);
1175 strcat(options,domain_name);
1178 if(mountpassword) {
1179 /* Commas have to be doubled, or else they will
1180 look like the parameter separator */
1181 /* if(sep is not set)*/
1182 if(retry == 0)
1183 check_for_comma(&mountpassword);
1184 strncat(options,",pass=",6);
1185 strcat(options,mountpassword);
1188 strncat(options,",ver=",5);
1189 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1191 if(orgoptions) {
1192 strcat(options,",");
1193 strcat(options,orgoptions);
1195 if(verboseflag)
1196 printf("\nmount.cifs kernel mount options %s \n",options);
1197 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1198 /* remember to kill daemon on error */
1199 char * tmp;
1201 switch (errno) {
1202 case 0:
1203 printf("mount failed but no error number set\n");
1204 break;
1205 case ENODEV:
1206 printf("mount error: cifs filesystem not supported by the system\n");
1207 break;
1208 case ENXIO:
1209 if(retry == 0) {
1210 retry = 1;
1211 tmp = share_name;
1212 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1213 *tmp = toupper((unsigned char)*tmp);
1214 tmp++;
1216 if(!*tmp) {
1217 printf("retrying with upper case share name\n");
1218 goto mount_retry;
1221 default:
1223 printf("mount error %d = %s\n",errno,strerror(errno));
1225 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1226 if(mountpassword) {
1227 memset(mountpassword,0,64);
1229 return -1;
1230 } else {
1231 pmntfile = setmntent(MOUNTED, "a+");
1232 if(pmntfile) {
1233 mountent.mnt_fsname = share_name;
1234 mountent.mnt_dir = mountpoint;
1235 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1236 mountent.mnt_opts = (char *)malloc(220);
1237 if(mountent.mnt_opts) {
1238 char * mount_user = getusername();
1239 memset(mountent.mnt_opts,0,200);
1240 if(flags & MS_RDONLY)
1241 strcat(mountent.mnt_opts,"ro");
1242 else
1243 strcat(mountent.mnt_opts,"rw");
1244 if(flags & MS_MANDLOCK)
1245 strcat(mountent.mnt_opts,",mand");
1246 if(flags & MS_NOEXEC)
1247 strcat(mountent.mnt_opts,",noexec");
1248 if(flags & MS_NOSUID)
1249 strcat(mountent.mnt_opts,",nosuid");
1250 if(flags & MS_NODEV)
1251 strcat(mountent.mnt_opts,",nodev");
1252 if(flags & MS_SYNCHRONOUS)
1253 strcat(mountent.mnt_opts,",synch");
1254 if(mount_user) {
1255 if(getuid() != 0) {
1256 strcat(mountent.mnt_opts,",user=");
1257 strcat(mountent.mnt_opts,mount_user);
1259 free(mount_user);
1262 mountent.mnt_freq = 0;
1263 mountent.mnt_passno = 0;
1264 rc = addmntent(pmntfile,&mountent);
1265 endmntent(pmntfile);
1266 if(mountent.mnt_opts)
1267 free(mountent.mnt_opts);
1268 } else {
1269 printf("could not update mount table\n");
1272 if(mountpassword) {
1273 int len = strlen(mountpassword);
1274 memset(mountpassword,0,len);
1275 free(mountpassword);
1278 if(options) {
1279 memset(options,0,optlen);
1280 free(options);
1283 if(orgoptions) {
1284 memset(orgoptions,0,orgoptlen);
1285 free(orgoptions);
1287 if(resolved_path) {
1288 free(resolved_path);
1291 if(free_share_name) {
1292 free(share_name);
1294 return 0;