r10400: commit merge patch from jra
[Samba.git] / source / client / mount.cifs.c
bloba6136a9e2c26cfb7f558db36f0f361c093a6180b
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 <ctype.h>
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <sys/stat.h>
31 #include <sys/utsname.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <getopt.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <string.h>
38 #include <mntent.h>
39 #include <fcntl.h>
41 #define MOUNT_CIFS_VERSION_MAJOR "1"
42 #define MOUNT_CIFS_VERSION_MINOR "9"
44 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
45 #define MOUNT_CIFS_VENDOR_SUFFIX ""
46 #endif
48 #ifndef MS_MOVE
49 #define MS_MOVE 8192
50 #endif
52 char * thisprogram;
53 int verboseflag = 0;
54 static int got_password = 0;
55 static int got_user = 0;
56 static int got_domain = 0;
57 static int got_ip = 0;
58 static int got_unc = 0;
59 static int got_uid = 0;
60 static int got_gid = 0;
61 static int free_share_name = 0;
62 static char * user_name = NULL;
63 static char * mountpassword = NULL;
64 char * domain_name = NULL;
67 /* BB finish BB
69 cifs_umount
70 open nofollow - avoid symlink exposure?
71 get owner of dir see if matches self or if root
72 call system(umount argv) etc.
74 BB end finish BB */
76 static char * check_for_domain(char **);
79 static void mount_cifs_usage(void)
81 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
82 printf("\nMount the remote target, specified as a UNC name,");
83 printf(" to a local directory.\n\nOptions:\n");
84 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
85 printf("\nLess commonly used options:");
86 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,\n\tdirectio,mapchars,nomapchars");
87 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions\n\t(e.g. most Samba versions):");
88 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
89 printf("\n\nRarely used options:");
90 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,\n\tdev,nodev,nouser_xattr,netbiosname,hard,soft,intr,nointr,noacl");
91 printf("\n\nOptions are described in more detail in the manual page");
92 printf("\n\tman 8 mount.cifs\n");
93 printf("\nTo display the version number of the mount helper:");
94 printf("\n\t%s -V\n",thisprogram);
96 if(mountpassword) {
97 memset(mountpassword,0,64);
98 free(mountpassword);
100 exit(1);
103 /* caller frees username if necessary */
104 static char * getusername(void) {
105 char *username = NULL;
106 struct passwd *password = getpwuid(getuid());
108 if (password) {
109 username = password->pw_name;
111 return username;
114 static char * parse_cifs_url(char * unc_name)
116 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
117 return NULL;
120 static int open_cred_file(char * file_name)
122 char * line_buf;
123 char * temp_val;
124 FILE * fs;
125 int i, length;
126 fs = fopen(file_name,"r");
127 if(fs == NULL)
128 return errno;
129 line_buf = malloc(4096);
130 if(line_buf == NULL) {
131 fclose(fs);
132 return -ENOMEM;
135 while(fgets(line_buf,4096,fs)) {
136 /* parse line from credential file */
138 /* eat leading white space */
139 for(i=0;i<4086;i++) {
140 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
141 break;
142 /* if whitespace - skip past it */
144 if (strncasecmp("username",line_buf+i,8) == 0) {
145 temp_val = strchr(line_buf + i,'=');
146 if(temp_val) {
147 /* go past equals sign */
148 temp_val++;
149 for(length = 0;length<4087;length++) {
150 if(temp_val[length] == '\n')
151 break;
153 if(length > 4086) {
154 printf("mount.cifs failed due to malformed username in credentials file");
155 memset(line_buf,0,4096);
156 if(mountpassword) {
157 memset(mountpassword,0,64);
159 exit(1);
160 } else {
161 got_user = 1;
162 user_name = calloc(1 + length,1);
163 /* BB adding free of user_name string before exit,
164 not really necessary but would be cleaner */
165 strncpy(user_name,temp_val, length);
168 } else if (strncasecmp("password",line_buf+i,8) == 0) {
169 temp_val = strchr(line_buf+i,'=');
170 if(temp_val) {
171 /* go past equals sign */
172 temp_val++;
173 for(length = 0;length<65;length++) {
174 if(temp_val[length] == '\n')
175 break;
177 if(length > 64) {
178 printf("mount.cifs failed: password in credentials file too long\n");
179 memset(line_buf,0, 4096);
180 if(mountpassword) {
181 memset(mountpassword,0,64);
183 exit(1);
184 } else {
185 if(mountpassword == NULL) {
186 mountpassword = calloc(65,1);
187 } else
188 memset(mountpassword,0,64);
189 if(mountpassword) {
190 strncpy(mountpassword,temp_val,length);
191 got_password = 1;
195 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
196 temp_val = strchr(line_buf+i,'=');
197 if(temp_val) {
198 /* go past equals sign */
199 temp_val++;
200 if(verboseflag)
201 printf("\nDomain %s\n",temp_val);
202 for(length = 0;length<65;length++) {
203 if(temp_val[length] == '\n')
204 break;
206 if(length > 64) {
207 printf("mount.cifs failed: domain in credentials file too long\n");
208 if(mountpassword) {
209 memset(mountpassword,0,64);
211 exit(1);
212 } else {
213 if(domain_name == NULL) {
214 domain_name = calloc(65,1);
215 } else
216 memset(domain_name,0,64);
217 if(domain_name) {
218 strncpy(domain_name,temp_val,length);
219 got_domain = 1;
226 fclose(fs);
227 if(line_buf) {
228 memset(line_buf,0,4096);
229 free(line_buf);
231 return 0;
234 static int get_password_from_file(int file_descript, char * filename)
236 int rc = 0;
237 int i;
238 char c;
240 if(mountpassword == NULL)
241 mountpassword = calloc(65,1);
242 else
243 memset(mountpassword, 0, 64);
245 if(filename != NULL) {
246 file_descript = open(filename, O_RDONLY);
247 if(file_descript < 0) {
248 printf("mount.cifs failed. %s attempting to open password file %s\n",
249 strerror(errno),filename);
250 exit(1);
253 /* else file already open and fd provided */
255 for(i=0;i<64;i++) {
256 rc = read(file_descript,&c,1);
257 if(rc < 0) {
258 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
259 memset(mountpassword,0,64);
260 if(filename != NULL)
261 close(file_descript);
262 exit(1);
263 } else if(rc == 0) {
264 if(mountpassword[0] == 0) {
265 if(verboseflag)
266 printf("\nWarning: null password used since cifs password file empty");
268 break;
269 } else /* read valid character */ {
270 if((c == 0) || (c == '\n')) {
271 break;
272 } else
273 mountpassword[i] = c;
276 if((i == 64) && (verboseflag)) {
277 printf("\nWarning: password longer than 64 characters specified in cifs password file");
279 got_password = 1;
280 if(filename != NULL) {
281 close(file_descript);
284 return rc;
287 static int parse_options(char * options, int * filesys_flags)
289 char * data;
290 char * percent_char = NULL;
291 char * value = NULL;
292 char * next_keyword = NULL;
293 int rc = 0;
295 if (!options)
296 return 1;
297 else
298 data = options;
300 if(verboseflag)
301 printf("parsing options: %s\n", options);
303 /* BB fixme check for separator override BB */
305 /* while ((data = strsep(&options, ",")) != NULL) { */
306 while(data != NULL) {
307 /* check if ends with trailing comma */
308 if(*data == 0)
309 break;
311 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
312 /* data = next keyword */
313 /* value = next value ie stuff after equal sign */
315 next_keyword = strchr(data,','); /* BB handle sep= */
317 /* temporarily null terminate end of keyword=value pair */
318 if(next_keyword)
319 *next_keyword = 0;
321 /* temporarily null terminate keyword to make keyword and value distinct */
322 if ((value = strchr(data, '=')) != NULL) {
323 *value = '\0';
324 value++;
327 if (strncmp(data, "users",5) == 0) {
328 if(!value || !*value) {
329 strncpy(data,",,,,,",5);
331 } else if (strncmp(data, "user_xattr",10) == 0) {
332 /* do nothing - need to skip so not parsed as user name */
333 } else if (strncmp(data, "user", 4) == 0) {
335 if (!value || !*value) {
336 if(data[4] == '\0') {
337 if(verboseflag)
338 printf("\nskipping empty user mount parameter\n");
339 /* remove the parm since it would otherwise be confusing
340 to the kernel code which would think it was a real username */
341 data[0] = ',';
342 data[1] = ',';
343 data[2] = ',';
344 data[3] = ',';
345 } else {
346 printf("username specified with no parameter\n");
347 return 1; /* needs_arg; */
349 } else {
350 if (strnlen(value, 260) < 260) {
351 got_user=1;
352 percent_char = strchr(value,'%');
353 if(percent_char) {
354 *percent_char = ',';
355 if(mountpassword == NULL)
356 mountpassword = calloc(65,1);
357 if(mountpassword) {
358 if(got_password)
359 printf("\nmount.cifs warning - password specified twice\n");
360 got_password = 1;
361 percent_char++;
362 strncpy(mountpassword, percent_char,64);
363 /* remove password from username */
364 while(*percent_char != 0) {
365 *percent_char = ',';
366 percent_char++;
370 /* this is only case in which the user
371 name buf is not malloc - so we have to
372 check for domain name embedded within
373 the user name here since the later
374 call to check_for_domain will not be
375 invoked */
376 domain_name = check_for_domain(&value);
377 } else {
378 printf("username too long\n");
379 return 1;
382 } else if (strncmp(data, "pass", 4) == 0) {
383 if (!value || !*value) {
384 if(got_password) {
385 printf("\npassword specified twice, ignoring second\n");
386 } else
387 got_password = 1;
388 } else if (strnlen(value, 17) < 17) {
389 if(got_password)
390 printf("\nmount.cifs warning - password specified twice\n");
391 got_password = 1;
392 } else {
393 printf("password too long\n");
394 return 1;
396 } else if (strncmp(data, "ip", 2) == 0) {
397 if (!value || !*value) {
398 printf("target ip address argument missing");
399 } else if (strnlen(value, 35) < 35) {
400 if(verboseflag)
401 printf("ip address %s override specified\n",value);
402 got_ip = 1;
403 } else {
404 printf("ip address too long\n");
405 return 1;
407 } else if ((strncmp(data, "unc", 3) == 0)
408 || (strncmp(data, "target", 6) == 0)
409 || (strncmp(data, "path", 4) == 0)) {
410 if (!value || !*value) {
411 printf("invalid path to network resource\n");
412 return 1; /* needs_arg; */
413 } else if(strnlen(value,5) < 5) {
414 printf("UNC name too short");
417 if (strnlen(value, 300) < 300) {
418 got_unc = 1;
419 if (strncmp(value, "//", 2) == 0) {
420 if(got_unc)
421 printf("unc name specified twice, ignoring second\n");
422 else
423 got_unc = 1;
424 } else if (strncmp(value, "\\\\", 2) != 0) {
425 printf("UNC Path does not begin with // or \\\\ \n");
426 return 1;
427 } else {
428 if(got_unc)
429 printf("unc name specified twice, ignoring second\n");
430 else
431 got_unc = 1;
433 } else {
434 printf("CIFS: UNC name too long\n");
435 return 1;
437 } else if ((strncmp(data, "domain", 3) == 0)
438 || (strncmp(data, "workgroup", 5) == 0)) {
439 if (!value || !*value) {
440 printf("CIFS: invalid domain name\n");
441 return 1; /* needs_arg; */
443 if (strnlen(value, 65) < 65) {
444 got_domain = 1;
445 } else {
446 printf("domain name too long\n");
447 return 1;
449 } else if (strncmp(data, "cred", 4) == 0) {
450 if (value && *value) {
451 rc = open_cred_file(value);
452 if(rc) {
453 printf("error %d opening credential file %s\n",rc, value);
454 return 1;
456 } else {
457 printf("invalid credential file name specified\n");
458 return 1;
460 } else if (strncmp(data, "uid", 3) == 0) {
461 if (value && *value) {
462 got_uid = 1;
464 } else if (strncmp(data, "gid", 3) == 0) {
465 if (value && *value) {
466 got_gid = 1;
468 /* fmask and dmask synonyms for people used to smbfs syntax */
469 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
470 if (!value || !*value) {
471 printf ("Option '%s' requires a numerical argument\n", data);
472 return 1;
475 if (value[0] != '0') {
476 printf ("WARNING: '%s' not expressed in octal.\n", data);
479 if (strcmp (data, "fmask") == 0) {
480 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
481 data = "file_mode"; /* BB fix this */
483 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
484 if (!value || !*value) {
485 printf ("Option '%s' requires a numerical argument\n", data);
486 return 1;
489 if (value[0] != '0') {
490 printf ("WARNING: '%s' not expressed in octal.\n", data);
493 if (strcmp (data, "dmask") == 0) {
494 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
495 data = "dir_mode";
497 /* the following eight mount options should be
498 stripped out from what is passed into the kernel
499 since these eight options are best passed as the
500 mount flags rather than redundantly to the kernel
501 and could generate spurious warnings depending on the
502 level of the corresponding cifs vfs kernel code */
503 } else if (strncmp(data, "nosuid", 6) == 0) {
504 *filesys_flags |= MS_NOSUID;
505 } else if (strncmp(data, "suid", 4) == 0) {
506 *filesys_flags &= ~MS_NOSUID;
507 } else if (strncmp(data, "nodev", 5) == 0) {
508 *filesys_flags |= MS_NODEV;
509 } else if (strncmp(data, "nobrl", 5) == 0) {
510 *filesys_flags &= ~MS_MANDLOCK;
511 } else if (strncmp(data, "dev", 3) == 0) {
512 *filesys_flags &= ~MS_NODEV;
513 } else if (strncmp(data, "noexec", 6) == 0) {
514 *filesys_flags |= MS_NOEXEC;
515 } else if (strncmp(data, "exec", 4) == 0) {
516 *filesys_flags &= ~MS_NOEXEC;
517 } else if (strncmp(data, "guest", 5) == 0) {
518 got_password=1;
519 /* remove the parm since it would otherwise be logged by kern */
520 data[0] = ',';
521 data[1] = ',';
522 data[2] = ',';
523 data[3] = ',';
524 data[4] = ',';
525 } else if (strncmp(data, "ro", 2) == 0) {
526 *filesys_flags |= MS_RDONLY;
527 } else if (strncmp(data, "rw", 2) == 0) {
528 *filesys_flags &= ~MS_RDONLY;
529 } else if (strncmp(data, "remount", 7) == 0) {
530 *filesys_flags |= MS_REMOUNT;
531 } /* else if (strnicmp(data, "port", 4) == 0) {
532 if (value && *value) {
533 vol->port =
534 simple_strtoul(value, &value, 0);
536 } else if (strnicmp(data, "rsize", 5) == 0) {
537 if (value && *value) {
538 vol->rsize =
539 simple_strtoul(value, &value, 0);
541 } else if (strnicmp(data, "wsize", 5) == 0) {
542 if (value && *value) {
543 vol->wsize =
544 simple_strtoul(value, &value, 0);
546 } else if (strnicmp(data, "version", 3) == 0) {
547 } else {
548 printf("CIFS: Unknown mount option %s\n",data);
549 } */ /* nothing to do on those four mount options above.
550 Just pass to kernel and ignore them here */
552 /* move to next option */
553 data = next_keyword+1;
555 /* put overwritten equals sign back */
556 if(value) {
557 value--;
558 *value = '=';
561 /* put previous overwritten comma back */
562 if(next_keyword)
563 *next_keyword = ','; /* BB handle sep= */
564 else
565 data = NULL;
567 return 0;
570 /* replace all (one or more) commas with double commas */
571 static void check_for_comma(char ** ppasswrd)
573 char *new_pass_buf;
574 char *pass;
575 int i,j;
576 int number_of_commas = 0;
577 int len;
579 if(ppasswrd == NULL)
580 return;
581 else
582 (pass = *ppasswrd);
584 len = strlen(pass);
586 for(i=0;i<len;i++) {
587 if(pass[i] == ',')
588 number_of_commas++;
591 if(number_of_commas == 0)
592 return;
593 if(number_of_commas > 64) {
594 /* would otherwise overflow the mount options buffer */
595 printf("\nInvalid password. Password contains too many commas.\n");
596 return;
599 new_pass_buf = malloc(len+number_of_commas+1);
600 if(new_pass_buf == NULL)
601 return;
603 for(i=0,j=0;i<len;i++,j++) {
604 new_pass_buf[j] = pass[i];
605 if(pass[i] == ',') {
606 j++;
607 new_pass_buf[j] = pass[i];
610 new_pass_buf[len+number_of_commas] = 0;
612 free(*ppasswrd);
613 *ppasswrd = new_pass_buf;
615 return;
618 /* Usernames can not have backslash in them and we use
619 [BB check if usernames can have forward slash in them BB]
620 backslash as domain\user separator character
622 static char * check_for_domain(char **ppuser)
624 char * original_string;
625 char * usernm;
626 char * domainnm;
627 int original_len;
628 int len;
629 int i;
631 if(ppuser == NULL)
632 return NULL;
634 original_string = *ppuser;
636 if (original_string == NULL)
637 return NULL;
639 original_len = strlen(original_string);
641 usernm = strchr(*ppuser,'/');
642 if (usernm == NULL) {
643 usernm = strchr(*ppuser,'\\');
644 if (usernm == NULL)
645 return NULL;
648 if(got_domain) {
649 printf("Domain name specified twice. Username probably malformed\n");
650 return NULL;
653 usernm[0] = 0;
654 domainnm = *ppuser;
655 if (domainnm[0] != 0) {
656 got_domain = 1;
657 } else {
658 printf("null domain\n");
660 len = strlen(domainnm);
661 /* reset domainm to new buffer, and copy
662 domain name into it */
663 domainnm = malloc(len+1);
664 if(domainnm == NULL)
665 return NULL;
667 strcpy(domainnm,*ppuser);
669 /* move_string(*ppuser, usernm+1) */
670 len = strlen(usernm+1);
672 if(len >= original_len) {
673 /* should not happen */
674 return domainnm;
677 for(i=0;i<original_len;i++) {
678 if(i<len)
679 original_string[i] = usernm[i+1];
680 else /* stuff with commas to remove last parm */
681 original_string[i] = ',';
684 /* BB add check for more than one slash?
685 strchr(*ppuser,'/');
686 strchr(*ppuser,'\\')
689 return domainnm;
692 /* Note that caller frees the returned buffer if necessary */
693 static char * parse_server(char ** punc_name)
695 char * unc_name = *punc_name;
696 int length = strnlen(unc_name,1024);
697 char * share;
698 char * ipaddress_string = NULL;
699 struct hostent * host_entry;
700 struct in_addr server_ipaddr;
702 if(length > 1023) {
703 printf("mount error: UNC name too long");
704 return NULL;
706 if (strncasecmp("cifs://",unc_name,7) == 0)
707 return parse_cifs_url(unc_name+7);
708 if (strncasecmp("smb://",unc_name,6) == 0) {
709 return parse_cifs_url(unc_name+6);
712 if(length < 3) {
713 /* BB add code to find DFS root here */
714 printf("\nMounting the DFS root for domain not implemented yet");
715 return NULL;
716 } else {
717 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
718 /* check for nfs syntax ie server:share */
719 share = strchr(unc_name,':');
720 if(share) {
721 free_share_name = 1;
722 *punc_name = malloc(length+3);
723 if(*punc_name == NULL) {
724 /* put the original string back if
725 no memory left */
726 *punc_name = unc_name;
727 return NULL;
730 *share = '/';
731 strncpy((*punc_name)+2,unc_name,length);
732 unc_name = *punc_name;
733 unc_name[length+2] = 0;
734 goto continue_unc_parsing;
735 } else {
736 printf("mount error: improperly formatted UNC name.");
737 printf(" %s does not begin with \\\\ or //\n",unc_name);
738 return NULL;
740 } else {
741 continue_unc_parsing:
742 unc_name[0] = '/';
743 unc_name[1] = '/';
744 unc_name += 2;
745 if ((share = strchr(unc_name, '/')) ||
746 (share = strchr(unc_name,'\\'))) {
747 *share = 0; /* temporarily terminate the string */
748 share += 1;
749 if(got_ip == 0) {
750 host_entry = gethostbyname(unc_name);
752 *(share - 1) = '/'; /* put the slash back */
753 if(got_ip) {
754 if(verboseflag)
755 printf("ip address specified explicitly\n");
756 return NULL;
758 if(host_entry == NULL) {
759 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
760 return NULL;
761 } else {
762 /* BB should we pass an alternate version of the share name as Unicode */
763 /* BB what about ipv6? BB */
764 /* BB add retries with alternate servers in list */
766 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
768 ipaddress_string = inet_ntoa(server_ipaddr);
769 if(ipaddress_string == NULL) {
770 printf("mount error: could not get valid ip address for target server\n");
771 return NULL;
773 return ipaddress_string;
775 } else {
776 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
777 printf("Mounting the DFS root for a particular server not implemented yet\n");
778 return NULL;
784 static struct option longopts[] = {
785 { "all", 0, NULL, 'a' },
786 { "help",0, NULL, 'h' },
787 { "move",0, NULL, 'm' },
788 { "bind",0, NULL, 'b' },
789 { "read-only", 0, NULL, 'r' },
790 { "ro", 0, NULL, 'r' },
791 { "verbose", 0, NULL, 'v' },
792 { "version", 0, NULL, 'V' },
793 { "read-write", 0, NULL, 'w' },
794 { "rw", 0, NULL, 'w' },
795 { "options", 1, NULL, 'o' },
796 { "type", 1, NULL, 't' },
797 { "rsize",1, NULL, 'R' },
798 { "wsize",1, NULL, 'W' },
799 { "uid", 1, NULL, '1'},
800 { "gid", 1, NULL, '2'},
801 { "user",1,NULL,'u'},
802 { "username",1,NULL,'u'},
803 { "dom",1,NULL,'d'},
804 { "domain",1,NULL,'d'},
805 { "password",1,NULL,'p'},
806 { "pass",1,NULL,'p'},
807 { "credentials",1,NULL,'c'},
808 { "port",1,NULL,'P'},
809 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
810 { NULL, 0, NULL, 0 }
813 int main(int argc, char ** argv)
815 int c;
816 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
817 char * orgoptions = NULL;
818 char * share_name = NULL;
819 char * ipaddr = NULL;
820 char * uuid = NULL;
821 char * mountpoint;
822 char * options;
823 char * resolved_path;
824 char * temp;
825 int rc;
826 int rsize = 0;
827 int wsize = 0;
828 int nomtab = 0;
829 int uid = 0;
830 int gid = 0;
831 int optlen = 0;
832 int orgoptlen = 0;
833 int retry = 0; /* set when we have to retry mount with uppercase */
834 struct stat statbuf;
835 struct utsname sysinfo;
836 struct mntent mountent;
837 FILE * pmntfile;
839 /* setlocale(LC_ALL, "");
840 bindtextdomain(PACKAGE, LOCALEDIR);
841 textdomain(PACKAGE); */
843 if(argc && argv) {
844 thisprogram = argv[0];
846 if(thisprogram == NULL)
847 thisprogram = "mount.cifs";
849 uname(&sysinfo);
850 /* BB add workstation name and domain and pass down */
852 /* #ifdef _GNU_SOURCE
853 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
854 #endif */
856 share_name = argv[1];
857 mountpoint = argv[2];
859 /* add sharename in opts string as unc= parm */
861 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
862 longopts, NULL)) != -1) {
863 switch (c) {
864 /* No code to do the following options yet */
865 /* case 'l':
866 list_with_volumelabel = 1;
867 break;
868 case 'L':
869 volumelabel = optarg;
870 break; */
871 /* case 'a':
872 ++mount_all;
873 break; */
875 case '?':
876 case 'h': /* help */
877 mount_cifs_usage ();
878 exit(1);
879 case 'n':
880 ++nomtab;
881 break;
882 case 'b':
883 flags |= MS_BIND;
884 break;
885 case 'm':
886 flags |= MS_MOVE;
887 break;
888 case 'o':
889 orgoptions = strdup(optarg);
890 break;
891 case 'r': /* mount readonly */
892 flags |= MS_RDONLY;
893 break;
894 case 'U':
895 uuid = optarg;
896 break;
897 case 'v':
898 ++verboseflag;
899 break;
900 case 'V':
901 printf ("mount.cifs version: %s.%s%s\n",
902 MOUNT_CIFS_VERSION_MAJOR,
903 MOUNT_CIFS_VERSION_MINOR,
904 MOUNT_CIFS_VENDOR_SUFFIX);
905 if(mountpassword) {
906 memset(mountpassword,0,64);
908 exit (0);
909 case 'w':
910 flags &= ~MS_RDONLY;
911 break;
912 case 'R':
913 rsize = atoi(optarg) ;
914 break;
915 case 'W':
916 wsize = atoi(optarg);
917 break;
918 case '1':
919 uid = atoi(optarg);
920 break;
921 case '2':
922 gid = atoi(optarg);
923 break;
924 case 'u':
925 got_user = 1;
926 user_name = optarg;
927 break;
928 case 'd':
929 domain_name = optarg; /* BB fix this - currently ignored */
930 got_domain = 1;
931 break;
932 case 'p':
933 if(mountpassword == NULL)
934 mountpassword = calloc(65,1);
935 if(mountpassword) {
936 got_password = 1;
937 strncpy(mountpassword,optarg,64);
939 break;
940 case 'S':
941 get_password_from_file(0 /* stdin */,NULL);
942 break;
943 case 't':
944 break;
945 default:
946 printf("unknown mount option %c\n",c);
947 mount_cifs_usage();
948 exit(1);
952 if(argc < 3)
953 mount_cifs_usage();
955 if (getenv("PASSWD")) {
956 if(mountpassword == NULL)
957 mountpassword = calloc(65,1);
958 if(mountpassword) {
959 strncpy(mountpassword,getenv("PASSWD"),64);
960 got_password = 1;
962 } else if (getenv("PASSWD_FD")) {
963 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
964 } else if (getenv("PASSWD_FILE")) {
965 get_password_from_file(0, getenv("PASSWD_FILE"));
968 if (orgoptions && parse_options(orgoptions, &flags))
969 return -1;
970 ipaddr = parse_server(&share_name);
971 if((ipaddr == NULL) && (got_ip == 0)) {
972 printf("No ip address specified and hostname not found\n");
973 return -1;
976 /* BB save off path and pop after mount returns? */
977 resolved_path = malloc(PATH_MAX+1);
978 if(resolved_path) {
979 /* Note that if we can not canonicalize the name, we get
980 another chance to see if it is valid when we chdir to it */
981 if (realpath(mountpoint, resolved_path)) {
982 mountpoint = resolved_path;
985 if(chdir(mountpoint)) {
986 printf("mount error: can not change directory into mount target %s\n",mountpoint);
987 return -1;
990 if(stat (".", &statbuf)) {
991 printf("mount error: mount point %s does not exist\n",mountpoint);
992 return -1;
995 if (S_ISDIR(statbuf.st_mode) == 0) {
996 printf("mount error: mount point %s is not a directory\n",mountpoint);
997 return -1;
1000 if((getuid() != 0) && (geteuid() == 0)) {
1001 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1002 #ifndef CIFS_ALLOW_USR_SUID
1003 /* Do not allow user mounts to control suid flag
1004 for mount unless explicitly built that way */
1005 flags |= MS_NOSUID | MS_NODEV;
1006 #endif
1007 } else {
1008 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1009 return -1;
1013 if(got_user == 0) {
1014 user_name = getusername();
1015 got_user = 1;
1018 if(got_password == 0) {
1019 mountpassword = getpass("Password: "); /* BB obsolete */
1020 got_password = 1;
1022 /* FIXME launch daemon (handles dfs name resolution and credential change)
1023 remember to clear parms and overwrite password field before launching */
1024 mount_retry:
1025 if(orgoptions) {
1026 optlen = strlen(orgoptions);
1027 orgoptlen = optlen;
1028 } else
1029 optlen = 0;
1030 if(share_name)
1031 optlen += strlen(share_name) + 4;
1032 else {
1033 printf("No server share name specified\n");
1035 if(user_name)
1036 optlen += strlen(user_name) + 6;
1037 if(ipaddr)
1038 optlen += strlen(ipaddr) + 4;
1039 if(mountpassword)
1040 optlen += strlen(mountpassword) + 6;
1041 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 */);
1043 if(options == NULL) {
1044 printf("Could not allocate memory for mount options\n");
1045 return -1;
1049 options[0] = 0;
1050 strncat(options,"unc=",4);
1051 strcat(options,share_name);
1052 /* scan backwards and reverse direction of slash */
1053 temp = strrchr(options, '/');
1054 if(temp > options + 6)
1055 *temp = '\\';
1056 if(ipaddr) {
1057 strncat(options,",ip=",4);
1058 strcat(options,ipaddr);
1061 if(user_name) {
1062 /* check for syntax like user=domain\user */
1063 if(got_domain == 0)
1064 domain_name = check_for_domain(&user_name);
1065 strncat(options,",user=",6);
1066 strcat(options,user_name);
1068 if(retry == 0) {
1069 if(domain_name) {
1070 /* extra length accounted for in option string above */
1071 strncat(options,",domain=",8);
1072 strcat(options,domain_name);
1075 if(mountpassword) {
1076 /* Commas have to be doubled, or else they will
1077 look like the parameter separator */
1078 /* if(sep is not set)*/
1079 if(retry == 0)
1080 check_for_comma(&mountpassword);
1081 strncat(options,",pass=",6);
1082 strcat(options,mountpassword);
1085 strncat(options,",ver=",5);
1086 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1088 if(orgoptions) {
1089 strcat(options,",");
1090 strcat(options,orgoptions);
1092 if(verboseflag)
1093 printf("\nmount.cifs kernel mount options %s \n",options);
1094 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1095 /* remember to kill daemon on error */
1096 char * tmp;
1098 switch (errno) {
1099 case 0:
1100 printf("mount failed but no error number set\n");
1101 break;
1102 case ENODEV:
1103 printf("mount error: cifs filesystem not supported by the system\n");
1104 break;
1105 case ENXIO:
1106 if(retry == 0) {
1107 retry = 1;
1108 tmp = share_name;
1109 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1110 *tmp = toupper((unsigned char)*tmp);
1111 tmp++;
1113 if(!*tmp) {
1114 printf("retrying with upper case share name\n");
1115 goto mount_retry;
1118 default:
1120 printf("mount error %d = %s\n",errno,strerror(errno));
1122 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1123 if(mountpassword) {
1124 memset(mountpassword,0,64);
1126 return -1;
1127 } else {
1128 pmntfile = setmntent(MOUNTED, "a+");
1129 if(pmntfile) {
1130 mountent.mnt_fsname = share_name;
1131 mountent.mnt_dir = mountpoint;
1132 mountent.mnt_type = "cifs";
1133 mountent.mnt_opts = malloc(220);
1134 if(mountent.mnt_opts) {
1135 char * mount_user = getusername();
1136 memset(mountent.mnt_opts,0,200);
1137 if(flags & MS_RDONLY)
1138 strcat(mountent.mnt_opts,"ro");
1139 else
1140 strcat(mountent.mnt_opts,"rw");
1141 if(flags & MS_MANDLOCK)
1142 strcat(mountent.mnt_opts,",mand");
1143 if(flags & MS_NOEXEC)
1144 strcat(mountent.mnt_opts,",noexec");
1145 if(flags & MS_NOSUID)
1146 strcat(mountent.mnt_opts,",nosuid");
1147 if(flags & MS_NODEV)
1148 strcat(mountent.mnt_opts,",nodev");
1149 if(flags & MS_SYNCHRONOUS)
1150 strcat(mountent.mnt_opts,",synch");
1151 if(mount_user) {
1152 if(getuid() != 0) {
1153 strcat(mountent.mnt_opts,",user=");
1154 strcat(mountent.mnt_opts,mount_user);
1156 free(mount_user);
1159 mountent.mnt_freq = 0;
1160 mountent.mnt_passno = 0;
1161 rc = addmntent(pmntfile,&mountent);
1162 endmntent(pmntfile);
1163 if(mountent.mnt_opts)
1164 free(mountent.mnt_opts);
1165 } else {
1166 printf("could not update mount table\n");
1169 if(mountpassword) {
1170 int len = strlen(mountpassword);
1171 memset(mountpassword,0,len);
1172 free(mountpassword);
1175 if(options) {
1176 memset(options,0,optlen);
1177 free(options);
1180 if(orgoptions) {
1181 memset(orgoptions,0,orgoptlen);
1182 free(orgoptions);
1184 if(resolved_path) {
1185 free(resolved_path);
1188 if(free_share_name) {
1189 free(share_name);
1191 return 0;