r12018: more 3.0.21 changes. This is a full sync except for changes to rpc-server...
[Samba.git] / source / client / mount.cifs.c
blobed88c92c626bc7ed2174c05f485ac9169d0ee6d4
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 #define MOUNT_CIFS_VENDOR_SUFFIX ""
47 #endif
49 #ifndef MS_MOVE
50 #define MS_MOVE 8192
51 #endif
53 char * thisprogram;
54 int verboseflag = 0;
55 static int got_password = 0;
56 static int got_user = 0;
57 static int got_domain = 0;
58 static int got_ip = 0;
59 static int got_unc = 0;
60 static int got_uid = 0;
61 static int got_gid = 0;
62 static int free_share_name = 0;
63 static char * user_name = NULL;
64 static char * mountpassword = NULL;
65 char * domain_name = NULL;
68 /* BB finish BB
70 cifs_umount
71 open nofollow - avoid symlink exposure?
72 get owner of dir see if matches self or if root
73 call system(umount argv) etc.
75 BB end finish BB */
77 static char * check_for_domain(char **);
80 static void mount_cifs_usage(void)
82 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
83 printf("\nMount the remote target, specified as a UNC name,");
84 printf(" to a local directory.\n\nOptions:\n");
85 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
86 printf("\nLess commonly used options:");
87 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
88 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
89 printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
90 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
91 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
92 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
93 printf("\n\nRarely used options:");
94 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
95 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
96 printf("\n\tnointr,ignorecase,noposixpaths,noacl");
97 printf("\n\nOptions are described in more detail in the manual page");
98 printf("\n\tman 8 mount.cifs\n");
99 printf("\nTo display the version number of the mount helper:");
100 printf("\n\t%s -V\n",thisprogram);
102 if(mountpassword) {
103 memset(mountpassword,0,64);
104 free(mountpassword);
106 exit(1);
109 /* caller frees username if necessary */
110 static char * getusername(void) {
111 char *username = NULL;
112 struct passwd *password = getpwuid(getuid());
114 if (password) {
115 username = password->pw_name;
117 return username;
120 static char * parse_cifs_url(char * unc_name)
122 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
123 return NULL;
126 static int open_cred_file(char * file_name)
128 char * line_buf;
129 char * temp_val;
130 FILE * fs;
131 int i, length;
132 fs = fopen(file_name,"r");
133 if(fs == NULL)
134 return errno;
135 line_buf = malloc(4096);
136 if(line_buf == NULL) {
137 fclose(fs);
138 return -ENOMEM;
141 while(fgets(line_buf,4096,fs)) {
142 /* parse line from credential file */
144 /* eat leading white space */
145 for(i=0;i<4086;i++) {
146 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
147 break;
148 /* if whitespace - skip past it */
150 if (strncasecmp("username",line_buf+i,8) == 0) {
151 temp_val = strchr(line_buf + i,'=');
152 if(temp_val) {
153 /* go past equals sign */
154 temp_val++;
155 for(length = 0;length<4087;length++) {
156 if(temp_val[length] == '\n')
157 break;
159 if(length > 4086) {
160 printf("mount.cifs failed due to malformed username in credentials file");
161 memset(line_buf,0,4096);
162 if(mountpassword) {
163 memset(mountpassword,0,64);
165 exit(1);
166 } else {
167 got_user = 1;
168 user_name = calloc(1 + length,1);
169 /* BB adding free of user_name string before exit,
170 not really necessary but would be cleaner */
171 strncpy(user_name,temp_val, length);
174 } else if (strncasecmp("password",line_buf+i,8) == 0) {
175 temp_val = strchr(line_buf+i,'=');
176 if(temp_val) {
177 /* go past equals sign */
178 temp_val++;
179 for(length = 0;length<65;length++) {
180 if(temp_val[length] == '\n')
181 break;
183 if(length > 64) {
184 printf("mount.cifs failed: password in credentials file too long\n");
185 memset(line_buf,0, 4096);
186 if(mountpassword) {
187 memset(mountpassword,0,64);
189 exit(1);
190 } else {
191 if(mountpassword == NULL) {
192 mountpassword = calloc(65,1);
193 } else
194 memset(mountpassword,0,64);
195 if(mountpassword) {
196 strncpy(mountpassword,temp_val,length);
197 got_password = 1;
201 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
202 temp_val = strchr(line_buf+i,'=');
203 if(temp_val) {
204 /* go past equals sign */
205 temp_val++;
206 if(verboseflag)
207 printf("\nDomain %s\n",temp_val);
208 for(length = 0;length<65;length++) {
209 if(temp_val[length] == '\n')
210 break;
212 if(length > 64) {
213 printf("mount.cifs failed: domain in credentials file too long\n");
214 if(mountpassword) {
215 memset(mountpassword,0,64);
217 exit(1);
218 } else {
219 if(domain_name == NULL) {
220 domain_name = calloc(65,1);
221 } else
222 memset(domain_name,0,64);
223 if(domain_name) {
224 strncpy(domain_name,temp_val,length);
225 got_domain = 1;
232 fclose(fs);
233 if(line_buf) {
234 memset(line_buf,0,4096);
235 free(line_buf);
237 return 0;
240 static int get_password_from_file(int file_descript, char * filename)
242 int rc = 0;
243 int i;
244 char c;
246 if(mountpassword == NULL)
247 mountpassword = calloc(65,1);
248 else
249 memset(mountpassword, 0, 64);
251 if(filename != NULL) {
252 file_descript = open(filename, O_RDONLY);
253 if(file_descript < 0) {
254 printf("mount.cifs failed. %s attempting to open password file %s\n",
255 strerror(errno),filename);
256 exit(1);
259 /* else file already open and fd provided */
261 for(i=0;i<64;i++) {
262 rc = read(file_descript,&c,1);
263 if(rc < 0) {
264 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
265 memset(mountpassword,0,64);
266 if(filename != NULL)
267 close(file_descript);
268 exit(1);
269 } else if(rc == 0) {
270 if(mountpassword[0] == 0) {
271 if(verboseflag)
272 printf("\nWarning: null password used since cifs password file empty");
274 break;
275 } else /* read valid character */ {
276 if((c == 0) || (c == '\n')) {
277 break;
278 } else
279 mountpassword[i] = c;
282 if((i == 64) && (verboseflag)) {
283 printf("\nWarning: password longer than 64 characters specified in cifs password file");
285 got_password = 1;
286 if(filename != NULL) {
287 close(file_descript);
290 return rc;
293 static int parse_options(char ** optionsp, int * filesys_flags)
295 char * data;
296 char * percent_char = NULL;
297 char * value = NULL;
298 char * next_keyword = NULL;
299 char * out = NULL;
300 int out_len = 0;
301 int word_len;
302 int rc = 0;
304 if (!optionsp || !*optionsp)
305 return 1;
306 data = *optionsp;
308 if(verboseflag)
309 printf("parsing options: %s\n", data);
311 /* BB fixme check for separator override BB */
313 /* while ((data = strsep(&options, ",")) != NULL) { */
314 while(data != NULL) {
315 /* check if ends with trailing comma */
316 if(*data == 0)
317 break;
319 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
320 /* data = next keyword */
321 /* value = next value ie stuff after equal sign */
323 next_keyword = strchr(data,','); /* BB handle sep= */
325 /* temporarily null terminate end of keyword=value pair */
326 if(next_keyword)
327 *next_keyword++ = 0;
329 /* temporarily null terminate keyword to make keyword and value distinct */
330 if ((value = strchr(data, '=')) != NULL) {
331 *value = '\0';
332 value++;
335 if (strncmp(data, "users",5) == 0) {
336 if(!value || !*value) {
337 goto nocopy;
339 } else if (strncmp(data, "user_xattr",10) == 0) {
340 /* do nothing - need to skip so not parsed as user name */
341 } else if (strncmp(data, "user", 4) == 0) {
343 if (!value || !*value) {
344 if(data[4] == '\0') {
345 if(verboseflag)
346 printf("\nskipping empty user mount parameter\n");
347 /* remove the parm since it would otherwise be confusing
348 to the kernel code which would think it was a real username */
349 goto nocopy;
350 } else {
351 printf("username specified with no parameter\n");
352 return 1; /* needs_arg; */
354 } else {
355 if (strnlen(value, 260) < 260) {
356 got_user=1;
357 percent_char = strchr(value,'%');
358 if(percent_char) {
359 *percent_char = ',';
360 if(mountpassword == NULL)
361 mountpassword = calloc(65,1);
362 if(mountpassword) {
363 if(got_password)
364 printf("\nmount.cifs warning - password specified twice\n");
365 got_password = 1;
366 percent_char++;
367 strncpy(mountpassword, percent_char,64);
368 /* remove password from username */
369 while(*percent_char != 0) {
370 *percent_char = ',';
371 percent_char++;
375 /* this is only case in which the user
376 name buf is not malloc - so we have to
377 check for domain name embedded within
378 the user name here since the later
379 call to check_for_domain will not be
380 invoked */
381 domain_name = check_for_domain(&value);
382 } else {
383 printf("username too long\n");
384 return 1;
387 } else if (strncmp(data, "pass", 4) == 0) {
388 if (!value || !*value) {
389 if(got_password) {
390 printf("\npassword specified twice, ignoring second\n");
391 } else
392 got_password = 1;
393 } else if (strnlen(value, 17) < 17) {
394 if(got_password)
395 printf("\nmount.cifs warning - password specified twice\n");
396 got_password = 1;
397 } else {
398 printf("password too long\n");
399 return 1;
401 } else if (strncmp(data, "ip", 2) == 0) {
402 if (!value || !*value) {
403 printf("target ip address argument missing");
404 } else if (strnlen(value, 35) < 35) {
405 if(verboseflag)
406 printf("ip address %s override specified\n",value);
407 got_ip = 1;
408 } else {
409 printf("ip address too long\n");
410 return 1;
412 } else if ((strncmp(data, "unc", 3) == 0)
413 || (strncmp(data, "target", 6) == 0)
414 || (strncmp(data, "path", 4) == 0)) {
415 if (!value || !*value) {
416 printf("invalid path to network resource\n");
417 return 1; /* needs_arg; */
418 } else if(strnlen(value,5) < 5) {
419 printf("UNC name too short");
422 if (strnlen(value, 300) < 300) {
423 got_unc = 1;
424 if (strncmp(value, "//", 2) == 0) {
425 if(got_unc)
426 printf("unc name specified twice, ignoring second\n");
427 else
428 got_unc = 1;
429 } else if (strncmp(value, "\\\\", 2) != 0) {
430 printf("UNC Path does not begin with // or \\\\ \n");
431 return 1;
432 } else {
433 if(got_unc)
434 printf("unc name specified twice, ignoring second\n");
435 else
436 got_unc = 1;
438 } else {
439 printf("CIFS: UNC name too long\n");
440 return 1;
442 } else if ((strncmp(data, "domain", 3) == 0)
443 || (strncmp(data, "workgroup", 5) == 0)) {
444 if (!value || !*value) {
445 printf("CIFS: invalid domain name\n");
446 return 1; /* needs_arg; */
448 if (strnlen(value, 65) < 65) {
449 got_domain = 1;
450 } else {
451 printf("domain name too long\n");
452 return 1;
454 } else if (strncmp(data, "cred", 4) == 0) {
455 if (value && *value) {
456 rc = open_cred_file(value);
457 if(rc) {
458 printf("error %d opening credential file %s\n",rc, value);
459 return 1;
461 } else {
462 printf("invalid credential file name specified\n");
463 return 1;
465 } else if (strncmp(data, "uid", 3) == 0) {
466 if (value && *value) {
467 got_uid = 1;
468 if (!isdigit(*value)) {
469 struct passwd *pw;
470 static char temp[32];
472 if (!(pw = getpwnam(value))) {
473 printf("bad user name \"%s\"\n", value);
474 exit(1);
476 sprintf(temp, "%u", pw->pw_uid);
477 value = temp;
478 endpwent();
481 } else if (strncmp(data, "gid", 3) == 0) {
482 if (value && *value) {
483 got_gid = 1;
484 if (!isdigit(*value)) {
485 struct group *gr;
486 static char temp[32];
488 if (!(gr = getgrnam(value))) {
489 printf("bad group name \"%s\"\n", value);
490 exit(1);
492 sprintf(temp, "%u", gr->gr_gid);
493 value = temp;
494 endpwent();
497 /* fmask and dmask synonyms for people used to smbfs syntax */
498 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
499 if (!value || !*value) {
500 printf ("Option '%s' requires a numerical argument\n", data);
501 return 1;
504 if (value[0] != '0') {
505 printf ("WARNING: '%s' not expressed in octal.\n", data);
508 if (strcmp (data, "fmask") == 0) {
509 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
510 data = "file_mode"; /* BB fix this */
512 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
513 if (!value || !*value) {
514 printf ("Option '%s' requires a numerical argument\n", data);
515 return 1;
518 if (value[0] != '0') {
519 printf ("WARNING: '%s' not expressed in octal.\n", data);
522 if (strcmp (data, "dmask") == 0) {
523 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
524 data = "dir_mode";
526 /* the following eight mount options should be
527 stripped out from what is passed into the kernel
528 since these eight options are best passed as the
529 mount flags rather than redundantly to the kernel
530 and could generate spurious warnings depending on the
531 level of the corresponding cifs vfs kernel code */
532 } else if (strncmp(data, "nosuid", 6) == 0) {
533 *filesys_flags |= MS_NOSUID;
534 } else if (strncmp(data, "suid", 4) == 0) {
535 *filesys_flags &= ~MS_NOSUID;
536 } else if (strncmp(data, "nodev", 5) == 0) {
537 *filesys_flags |= MS_NODEV;
538 } else if ((strncmp(data, "nobrl", 5) == 0) ||
539 (strncmp(data, "nolock", 6) == 0)) {
540 *filesys_flags &= ~MS_MANDLOCK;
541 } else if (strncmp(data, "dev", 3) == 0) {
542 *filesys_flags &= ~MS_NODEV;
543 } else if (strncmp(data, "noexec", 6) == 0) {
544 *filesys_flags |= MS_NOEXEC;
545 } else if (strncmp(data, "exec", 4) == 0) {
546 *filesys_flags &= ~MS_NOEXEC;
547 } else if (strncmp(data, "guest", 5) == 0) {
548 got_password=1;
549 /* remove the parm since it would otherwise be logged by kern */
550 goto nocopy;
551 } else if (strncmp(data, "ro", 2) == 0) {
552 *filesys_flags |= MS_RDONLY;
553 } else if (strncmp(data, "rw", 2) == 0) {
554 *filesys_flags &= ~MS_RDONLY;
555 } else if (strncmp(data, "remount", 7) == 0) {
556 *filesys_flags |= MS_REMOUNT;
557 } /* else if (strnicmp(data, "port", 4) == 0) {
558 if (value && *value) {
559 vol->port =
560 simple_strtoul(value, &value, 0);
562 } else if (strnicmp(data, "rsize", 5) == 0) {
563 if (value && *value) {
564 vol->rsize =
565 simple_strtoul(value, &value, 0);
567 } else if (strnicmp(data, "wsize", 5) == 0) {
568 if (value && *value) {
569 vol->wsize =
570 simple_strtoul(value, &value, 0);
572 } else if (strnicmp(data, "version", 3) == 0) {
573 } else {
574 printf("CIFS: Unknown mount option %s\n",data);
575 } */ /* nothing to do on those four mount options above.
576 Just pass to kernel and ignore them here */
578 /* Copy (possibly modified) option to out */
579 word_len = strlen(data);
580 if (value)
581 word_len += 1 + strlen(value);
583 out = realloc(out, out_len + word_len + 2);
584 if (out == NULL) {
585 perror("malloc");
586 exit(1);
589 if (out_len)
590 out[out_len++] = ',';
591 if (value)
592 sprintf(out + out_len, "%s=%s", data, value);
593 else
594 sprintf(out + out_len, "%s", data);
595 out_len = strlen(out);
597 nocopy:
598 data = next_keyword;
600 *optionsp = out;
601 return 0;
604 /* replace all (one or more) commas with double commas */
605 static void check_for_comma(char ** ppasswrd)
607 char *new_pass_buf;
608 char *pass;
609 int i,j;
610 int number_of_commas = 0;
611 int len;
613 if(ppasswrd == NULL)
614 return;
615 else
616 (pass = *ppasswrd);
618 len = strlen(pass);
620 for(i=0;i<len;i++) {
621 if(pass[i] == ',')
622 number_of_commas++;
625 if(number_of_commas == 0)
626 return;
627 if(number_of_commas > 64) {
628 /* would otherwise overflow the mount options buffer */
629 printf("\nInvalid password. Password contains too many commas.\n");
630 return;
633 new_pass_buf = malloc(len+number_of_commas+1);
634 if(new_pass_buf == NULL)
635 return;
637 for(i=0,j=0;i<len;i++,j++) {
638 new_pass_buf[j] = pass[i];
639 if(pass[i] == ',') {
640 j++;
641 new_pass_buf[j] = pass[i];
644 new_pass_buf[len+number_of_commas] = 0;
646 free(*ppasswrd);
647 *ppasswrd = new_pass_buf;
649 return;
652 /* Usernames can not have backslash in them and we use
653 [BB check if usernames can have forward slash in them BB]
654 backslash as domain\user separator character
656 static char * check_for_domain(char **ppuser)
658 char * original_string;
659 char * usernm;
660 char * domainnm;
661 int original_len;
662 int len;
663 int i;
665 if(ppuser == NULL)
666 return NULL;
668 original_string = *ppuser;
670 if (original_string == NULL)
671 return NULL;
673 original_len = strlen(original_string);
675 usernm = strchr(*ppuser,'/');
676 if (usernm == NULL) {
677 usernm = strchr(*ppuser,'\\');
678 if (usernm == NULL)
679 return NULL;
682 if(got_domain) {
683 printf("Domain name specified twice. Username probably malformed\n");
684 return NULL;
687 usernm[0] = 0;
688 domainnm = *ppuser;
689 if (domainnm[0] != 0) {
690 got_domain = 1;
691 } else {
692 printf("null domain\n");
694 len = strlen(domainnm);
695 /* reset domainm to new buffer, and copy
696 domain name into it */
697 domainnm = malloc(len+1);
698 if(domainnm == NULL)
699 return NULL;
701 strcpy(domainnm,*ppuser);
703 /* move_string(*ppuser, usernm+1) */
704 len = strlen(usernm+1);
706 if(len >= original_len) {
707 /* should not happen */
708 return domainnm;
711 for(i=0;i<original_len;i++) {
712 if(i<len)
713 original_string[i] = usernm[i+1];
714 else /* stuff with commas to remove last parm */
715 original_string[i] = ',';
718 /* BB add check for more than one slash?
719 strchr(*ppuser,'/');
720 strchr(*ppuser,'\\')
723 return domainnm;
726 /* Note that caller frees the returned buffer if necessary */
727 static char * parse_server(char ** punc_name)
729 char * unc_name = *punc_name;
730 int length = strnlen(unc_name,1024);
731 char * share;
732 char * ipaddress_string = NULL;
733 struct hostent * host_entry = NULL;
734 struct in_addr server_ipaddr;
736 if(length > 1023) {
737 printf("mount error: UNC name too long");
738 return NULL;
740 if (strncasecmp("cifs://",unc_name,7) == 0)
741 return parse_cifs_url(unc_name+7);
742 if (strncasecmp("smb://",unc_name,6) == 0) {
743 return parse_cifs_url(unc_name+6);
746 if(length < 3) {
747 /* BB add code to find DFS root here */
748 printf("\nMounting the DFS root for domain not implemented yet");
749 return NULL;
750 } else {
751 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
752 /* check for nfs syntax ie server:share */
753 share = strchr(unc_name,':');
754 if(share) {
755 free_share_name = 1;
756 *punc_name = malloc(length+3);
757 if(*punc_name == NULL) {
758 /* put the original string back if
759 no memory left */
760 *punc_name = unc_name;
761 return NULL;
764 *share = '/';
765 strncpy((*punc_name)+2,unc_name,length);
766 unc_name = *punc_name;
767 unc_name[length+2] = 0;
768 goto continue_unc_parsing;
769 } else {
770 printf("mount error: improperly formatted UNC name.");
771 printf(" %s does not begin with \\\\ or //\n",unc_name);
772 return NULL;
774 } else {
775 continue_unc_parsing:
776 unc_name[0] = '/';
777 unc_name[1] = '/';
778 unc_name += 2;
779 if ((share = strchr(unc_name, '/')) ||
780 (share = strchr(unc_name,'\\'))) {
781 *share = 0; /* temporarily terminate the string */
782 share += 1;
783 if(got_ip == 0) {
784 host_entry = gethostbyname(unc_name);
786 *(share - 1) = '/'; /* put the slash back */
787 if(got_ip) {
788 if(verboseflag)
789 printf("ip address specified explicitly\n");
790 return NULL;
792 if(host_entry == NULL) {
793 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
794 return NULL;
795 } else {
796 /* BB should we pass an alternate version of the share name as Unicode */
797 /* BB what about ipv6? BB */
798 /* BB add retries with alternate servers in list */
800 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
802 ipaddress_string = inet_ntoa(server_ipaddr);
803 if(ipaddress_string == NULL) {
804 printf("mount error: could not get valid ip address for target server\n");
805 return NULL;
807 return ipaddress_string;
809 } else {
810 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
811 printf("Mounting the DFS root for a particular server not implemented yet\n");
812 return NULL;
818 static struct option longopts[] = {
819 { "all", 0, NULL, 'a' },
820 { "help",0, NULL, 'h' },
821 { "move",0, NULL, 'm' },
822 { "bind",0, NULL, 'b' },
823 { "read-only", 0, NULL, 'r' },
824 { "ro", 0, NULL, 'r' },
825 { "verbose", 0, NULL, 'v' },
826 { "version", 0, NULL, 'V' },
827 { "read-write", 0, NULL, 'w' },
828 { "rw", 0, NULL, 'w' },
829 { "options", 1, NULL, 'o' },
830 { "type", 1, NULL, 't' },
831 { "rsize",1, NULL, 'R' },
832 { "wsize",1, NULL, 'W' },
833 { "uid", 1, NULL, '1'},
834 { "gid", 1, NULL, '2'},
835 { "user",1,NULL,'u'},
836 { "username",1,NULL,'u'},
837 { "dom",1,NULL,'d'},
838 { "domain",1,NULL,'d'},
839 { "password",1,NULL,'p'},
840 { "pass",1,NULL,'p'},
841 { "credentials",1,NULL,'c'},
842 { "port",1,NULL,'P'},
843 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
844 { NULL, 0, NULL, 0 }
847 int main(int argc, char ** argv)
849 int c;
850 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
851 char * orgoptions = NULL;
852 char * share_name = NULL;
853 char * ipaddr = NULL;
854 char * uuid = NULL;
855 char * mountpoint;
856 char * options;
857 char * resolved_path;
858 char * temp;
859 int rc;
860 int rsize = 0;
861 int wsize = 0;
862 int nomtab = 0;
863 int uid = 0;
864 int gid = 0;
865 int optlen = 0;
866 int orgoptlen = 0;
867 int retry = 0; /* set when we have to retry mount with uppercase */
868 struct stat statbuf;
869 struct utsname sysinfo;
870 struct mntent mountent;
871 FILE * pmntfile;
873 /* setlocale(LC_ALL, "");
874 bindtextdomain(PACKAGE, LOCALEDIR);
875 textdomain(PACKAGE); */
877 if(argc && argv) {
878 thisprogram = argv[0];
880 if(thisprogram == NULL)
881 thisprogram = "mount.cifs";
883 uname(&sysinfo);
884 /* BB add workstation name and domain and pass down */
886 /* #ifdef _GNU_SOURCE
887 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
888 #endif */
890 share_name = argv[1];
891 mountpoint = argv[2];
893 /* add sharename in opts string as unc= parm */
895 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
896 longopts, NULL)) != -1) {
897 switch (c) {
898 /* No code to do the following options yet */
899 /* case 'l':
900 list_with_volumelabel = 1;
901 break;
902 case 'L':
903 volumelabel = optarg;
904 break; */
905 /* case 'a':
906 ++mount_all;
907 break; */
909 case '?':
910 case 'h': /* help */
911 mount_cifs_usage ();
912 exit(1);
913 case 'n':
914 ++nomtab;
915 break;
916 case 'b':
917 flags |= MS_BIND;
918 break;
919 case 'm':
920 flags |= MS_MOVE;
921 break;
922 case 'o':
923 orgoptions = strdup(optarg);
924 break;
925 case 'r': /* mount readonly */
926 flags |= MS_RDONLY;
927 break;
928 case 'U':
929 uuid = optarg;
930 break;
931 case 'v':
932 ++verboseflag;
933 break;
934 case 'V':
935 printf ("mount.cifs version: %s.%s%s\n",
936 MOUNT_CIFS_VERSION_MAJOR,
937 MOUNT_CIFS_VERSION_MINOR,
938 MOUNT_CIFS_VENDOR_SUFFIX);
939 if(mountpassword) {
940 memset(mountpassword,0,64);
942 exit (0);
943 case 'w':
944 flags &= ~MS_RDONLY;
945 break;
946 case 'R':
947 rsize = atoi(optarg) ;
948 break;
949 case 'W':
950 wsize = atoi(optarg);
951 break;
952 case '1':
953 if (isdigit(*optarg)) {
954 char *ep;
956 uid = strtoul(optarg, &ep, 10);
957 if (*ep) {
958 printf("bad uid value \"%s\"\n", optarg);
959 exit(1);
961 } else {
962 struct passwd *pw;
964 if (!(pw = getpwnam(optarg))) {
965 printf("bad user name \"%s\"\n", optarg);
966 exit(1);
968 uid = pw->pw_uid;
969 endpwent();
971 break;
972 case '2':
973 if (isdigit(*optarg)) {
974 char *ep;
976 gid = strtoul(optarg, &ep, 10);
977 if (*ep) {
978 printf("bad gid value \"%s\"\n", optarg);
979 exit(1);
981 } else {
982 struct group *gr;
984 if (!(gr = getgrnam(optarg))) {
985 printf("bad user name \"%s\"\n", optarg);
986 exit(1);
988 gid = gr->gr_gid;
989 endpwent();
991 break;
992 case 'u':
993 got_user = 1;
994 user_name = optarg;
995 break;
996 case 'd':
997 domain_name = optarg; /* BB fix this - currently ignored */
998 got_domain = 1;
999 break;
1000 case 'p':
1001 if(mountpassword == NULL)
1002 mountpassword = calloc(65,1);
1003 if(mountpassword) {
1004 got_password = 1;
1005 strncpy(mountpassword,optarg,64);
1007 break;
1008 case 'S':
1009 get_password_from_file(0 /* stdin */,NULL);
1010 break;
1011 case 't':
1012 break;
1013 default:
1014 printf("unknown mount option %c\n",c);
1015 mount_cifs_usage();
1016 exit(1);
1020 if(argc < 3)
1021 mount_cifs_usage();
1023 if (getenv("PASSWD")) {
1024 if(mountpassword == NULL)
1025 mountpassword = calloc(65,1);
1026 if(mountpassword) {
1027 strncpy(mountpassword,getenv("PASSWD"),64);
1028 got_password = 1;
1030 } else if (getenv("PASSWD_FD")) {
1031 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1032 } else if (getenv("PASSWD_FILE")) {
1033 get_password_from_file(0, getenv("PASSWD_FILE"));
1036 if (orgoptions && parse_options(&orgoptions, &flags))
1037 return -1;
1038 ipaddr = parse_server(&share_name);
1039 if((ipaddr == NULL) && (got_ip == 0)) {
1040 printf("No ip address specified and hostname not found\n");
1041 return -1;
1044 /* BB save off path and pop after mount returns? */
1045 resolved_path = malloc(PATH_MAX+1);
1046 if(resolved_path) {
1047 /* Note that if we can not canonicalize the name, we get
1048 another chance to see if it is valid when we chdir to it */
1049 if (realpath(mountpoint, resolved_path)) {
1050 mountpoint = resolved_path;
1053 if(chdir(mountpoint)) {
1054 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1055 return -1;
1058 if(stat (".", &statbuf)) {
1059 printf("mount error: mount point %s does not exist\n",mountpoint);
1060 return -1;
1063 if (S_ISDIR(statbuf.st_mode) == 0) {
1064 printf("mount error: mount point %s is not a directory\n",mountpoint);
1065 return -1;
1068 if((getuid() != 0) && (geteuid() == 0)) {
1069 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1070 #ifndef CIFS_ALLOW_USR_SUID
1071 /* Do not allow user mounts to control suid flag
1072 for mount unless explicitly built that way */
1073 flags |= MS_NOSUID | MS_NODEV;
1074 #endif
1075 } else {
1076 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1077 return -1;
1081 if(got_user == 0) {
1082 user_name = getusername();
1083 got_user = 1;
1086 if(got_password == 0) {
1087 mountpassword = getpass("Password: "); /* BB obsolete */
1088 got_password = 1;
1090 /* FIXME launch daemon (handles dfs name resolution and credential change)
1091 remember to clear parms and overwrite password field before launching */
1092 mount_retry:
1093 if(orgoptions) {
1094 optlen = strlen(orgoptions);
1095 orgoptlen = optlen;
1096 } else
1097 optlen = 0;
1098 if(share_name)
1099 optlen += strlen(share_name) + 4;
1100 else {
1101 printf("No server share name specified\n");
1103 if(user_name)
1104 optlen += strlen(user_name) + 6;
1105 if(ipaddr)
1106 optlen += strlen(ipaddr) + 4;
1107 if(mountpassword)
1108 optlen += strlen(mountpassword) + 6;
1109 options = 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 */);
1111 if(options == NULL) {
1112 printf("Could not allocate memory for mount options\n");
1113 return -1;
1117 options[0] = 0;
1118 strncat(options,"unc=",4);
1119 strcat(options,share_name);
1120 /* scan backwards and reverse direction of slash */
1121 temp = strrchr(options, '/');
1122 if(temp > options + 6)
1123 *temp = '\\';
1124 if(ipaddr) {
1125 strncat(options,",ip=",4);
1126 strcat(options,ipaddr);
1129 if(user_name) {
1130 /* check for syntax like user=domain\user */
1131 if(got_domain == 0)
1132 domain_name = check_for_domain(&user_name);
1133 strncat(options,",user=",6);
1134 strcat(options,user_name);
1136 if(retry == 0) {
1137 if(domain_name) {
1138 /* extra length accounted for in option string above */
1139 strncat(options,",domain=",8);
1140 strcat(options,domain_name);
1143 if(mountpassword) {
1144 /* Commas have to be doubled, or else they will
1145 look like the parameter separator */
1146 /* if(sep is not set)*/
1147 if(retry == 0)
1148 check_for_comma(&mountpassword);
1149 strncat(options,",pass=",6);
1150 strcat(options,mountpassword);
1153 strncat(options,",ver=",5);
1154 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1156 if(orgoptions) {
1157 strcat(options,",");
1158 strcat(options,orgoptions);
1160 if(verboseflag)
1161 printf("\nmount.cifs kernel mount options %s \n",options);
1162 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1163 /* remember to kill daemon on error */
1164 char * tmp;
1166 switch (errno) {
1167 case 0:
1168 printf("mount failed but no error number set\n");
1169 break;
1170 case ENODEV:
1171 printf("mount error: cifs filesystem not supported by the system\n");
1172 break;
1173 case ENXIO:
1174 if(retry == 0) {
1175 retry = 1;
1176 tmp = share_name;
1177 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1178 *tmp = toupper((unsigned char)*tmp);
1179 tmp++;
1181 if(!*tmp) {
1182 printf("retrying with upper case share name\n");
1183 goto mount_retry;
1186 default:
1188 printf("mount error %d = %s\n",errno,strerror(errno));
1190 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1191 if(mountpassword) {
1192 memset(mountpassword,0,64);
1194 return -1;
1195 } else {
1196 pmntfile = setmntent(MOUNTED, "a+");
1197 if(pmntfile) {
1198 mountent.mnt_fsname = share_name;
1199 mountent.mnt_dir = mountpoint;
1200 mountent.mnt_type = "cifs";
1201 mountent.mnt_opts = malloc(220);
1202 if(mountent.mnt_opts) {
1203 char * mount_user = getusername();
1204 memset(mountent.mnt_opts,0,200);
1205 if(flags & MS_RDONLY)
1206 strcat(mountent.mnt_opts,"ro");
1207 else
1208 strcat(mountent.mnt_opts,"rw");
1209 if(flags & MS_MANDLOCK)
1210 strcat(mountent.mnt_opts,",mand");
1211 if(flags & MS_NOEXEC)
1212 strcat(mountent.mnt_opts,",noexec");
1213 if(flags & MS_NOSUID)
1214 strcat(mountent.mnt_opts,",nosuid");
1215 if(flags & MS_NODEV)
1216 strcat(mountent.mnt_opts,",nodev");
1217 if(flags & MS_SYNCHRONOUS)
1218 strcat(mountent.mnt_opts,",synch");
1219 if(mount_user) {
1220 if(getuid() != 0) {
1221 strcat(mountent.mnt_opts,",user=");
1222 strcat(mountent.mnt_opts,mount_user);
1224 free(mount_user);
1227 mountent.mnt_freq = 0;
1228 mountent.mnt_passno = 0;
1229 rc = addmntent(pmntfile,&mountent);
1230 endmntent(pmntfile);
1231 if(mountent.mnt_opts)
1232 free(mountent.mnt_opts);
1233 } else {
1234 printf("could not update mount table\n");
1237 if(mountpassword) {
1238 int len = strlen(mountpassword);
1239 memset(mountpassword,0,len);
1240 free(mountpassword);
1243 if(options) {
1244 memset(options,0,optlen);
1245 free(options);
1248 if(orgoptions) {
1249 memset(orgoptions,0,orgoptlen);
1250 free(orgoptions);
1252 if(resolved_path) {
1253 free(resolved_path);
1256 if(free_share_name) {
1257 free(share_name);
1259 return 0;