r6505: Add missing remount flag handling
[Samba/nascimento.git] / source / client / mount.cifs.c
blob7ac4291f17de6157666f307c944fb6a5465968bc
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 "7"
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 return -ENOMEM;
133 while(fgets(line_buf,4096,fs)) {
134 /* parse line from credential file */
136 /* eat leading white space */
137 for(i=0;i<4086;i++) {
138 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
139 break;
140 /* if whitespace - skip past it */
142 if (strncasecmp("username",line_buf+i,8) == 0) {
143 temp_val = strchr(line_buf + i,'=');
144 if(temp_val) {
145 /* go past equals sign */
146 temp_val++;
147 for(length = 0;length<4087;length++) {
148 if(temp_val[length] == '\n')
149 break;
151 if(length > 4086) {
152 printf("mount.cifs failed due to malformed username in credentials file");
153 memset(line_buf,0,4096);
154 if(mountpassword) {
155 memset(mountpassword,0,64);
157 exit(1);
158 } else {
159 got_user = 1;
160 user_name = calloc(1 + length,1);
161 /* BB adding free of user_name string before exit,
162 not really necessary but would be cleaner */
163 strncpy(user_name,temp_val, length);
166 } else if (strncasecmp("password",line_buf+i,8) == 0) {
167 temp_val = strchr(line_buf+i,'=');
168 if(temp_val) {
169 /* go past equals sign */
170 temp_val++;
171 for(length = 0;length<65;length++) {
172 if(temp_val[length] == '\n')
173 break;
175 if(length > 64) {
176 printf("mount.cifs failed: password in credentials file too long\n");
177 memset(line_buf,0, 4096);
178 if(mountpassword) {
179 memset(mountpassword,0,64);
181 exit(1);
182 } else {
183 if(mountpassword == NULL) {
184 mountpassword = calloc(65,1);
185 } else
186 memset(mountpassword,0,64);
187 if(mountpassword) {
188 /* BB add handling for commas in password here */
189 strncpy(mountpassword,temp_val,length);
190 got_password = 1;
196 fclose(fs);
197 if(line_buf) {
198 memset(line_buf,0,4096);
199 free(line_buf);
201 return 0;
204 static int get_password_from_file(int file_descript, char * filename)
206 int rc = 0;
207 int i;
208 char c;
210 if(mountpassword == NULL)
211 mountpassword = calloc(65,1);
212 else
213 memset(mountpassword, 0, 64);
215 if(filename != NULL) {
216 file_descript = open(filename, O_RDONLY);
217 if(file_descript < 0) {
218 printf("mount.cifs failed. %s attempting to open password file %s\n",
219 strerror(errno),filename);
220 exit(1);
223 /* else file already open and fd provided */
225 for(i=0;i<64;i++) {
226 rc = read(file_descript,&c,1);
227 if(rc < 0) {
228 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
229 memset(mountpassword,0,64);
230 if(filename != NULL)
231 close(file_descript);
232 exit(1);
233 } else if(rc == 0) {
234 if(mountpassword[0] == 0) {
235 if(verboseflag)
236 printf("\nWarning: null password used since cifs password file empty");
238 break;
239 } else /* read valid character */ {
240 if((c == 0) || (c == '\n')) {
241 break;
242 } else
243 mountpassword[i] = c;
246 if((i == 64) && (verboseflag)) {
247 printf("\nWarning: password longer than 64 characters specified in cifs password file");
249 got_password = 1;
250 if(filename != NULL) {
251 close(file_descript);
254 return rc;
257 static int parse_options(char * options, int * filesys_flags)
259 char * data;
260 char * percent_char = NULL;
261 char * value = NULL;
262 char * next_keyword = NULL;
263 int rc = 0;
265 if (!options)
266 return 1;
267 else
268 data = options;
270 if(verboseflag)
271 printf("parsing options: %s\n", options);
273 /* BB fixme check for separator override BB */
275 /* while ((data = strsep(&options, ",")) != NULL) { */
276 while(data != NULL) {
277 /* check if ends with trailing comma */
278 if(*data == 0)
279 break;
281 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
282 /* data = next keyword */
283 /* value = next value ie stuff after equal sign */
285 next_keyword = strchr(data,','); /* BB handle sep= */
287 /* temporarily null terminate end of keyword=value pair */
288 if(next_keyword)
289 *next_keyword = 0;
291 /* temporarily null terminate keyword to make keyword and value distinct */
292 if ((value = strchr(data, '=')) != NULL) {
293 *value = '\0';
294 value++;
297 if (strncmp(data, "users",5) == 0) {
298 if(!value || !*value) {
299 strncpy(data,",,,,,",5);
301 } else if (strncmp(data, "user_xattr",10) == 0) {
302 /* do nothing - need to skip so not parsed as user name */
303 } else if (strncmp(data, "user", 4) == 0) {
305 if (!value || !*value) {
306 if(data[4] == '\0') {
307 if(verboseflag)
308 printf("\nskipping empty user mount parameter\n");
309 /* remove the parm since it would otherwise be confusing
310 to the kernel code which would think it was a real username */
311 data[0] = ',';
312 data[1] = ',';
313 data[2] = ',';
314 data[3] = ',';
315 } else {
316 printf("username specified with no parameter\n");
317 return 1; /* needs_arg; */
319 } else {
320 if (strnlen(value, 260) < 260) {
321 got_user=1;
322 percent_char = strchr(value,'%');
323 if(percent_char) {
324 *percent_char = ',';
325 if(mountpassword == NULL)
326 mountpassword = calloc(65,1);
327 if(mountpassword) {
328 if(got_password)
329 printf("\nmount.cifs warning - password specified twice\n");
330 got_password = 1;
331 percent_char++;
332 strncpy(mountpassword, percent_char,64);
333 /* remove password from username */
334 while(*percent_char != 0) {
335 *percent_char = ',';
336 percent_char++;
340 /* this is only case in which the user
341 name buf is not malloc - so we have to
342 check for domain name embedded within
343 the user name here since the later
344 call to check_for_domain will not be
345 invoked */
346 domain_name = check_for_domain(&value);
347 } else {
348 printf("username too long\n");
349 return 1;
352 } else if (strncmp(data, "pass", 4) == 0) {
353 if (!value || !*value) {
354 if(got_password) {
355 printf("\npassword specified twice, ignoring second\n");
356 } else
357 got_password = 1;
358 } else if (strnlen(value, 17) < 17) {
359 if(got_password)
360 printf("\nmount.cifs warning - password specified twice\n");
361 got_password = 1;
362 } else {
363 printf("password too long\n");
364 return 1;
366 } else if (strncmp(data, "ip", 2) == 0) {
367 if (!value || !*value) {
368 printf("target ip address argument missing");
369 } else if (strnlen(value, 35) < 35) {
370 if(verboseflag)
371 printf("ip address %s override specified\n",value);
372 got_ip = 1;
373 } else {
374 printf("ip address too long\n");
375 return 1;
377 } else if ((strncmp(data, "unc", 3) == 0)
378 || (strncmp(data, "target", 6) == 0)
379 || (strncmp(data, "path", 4) == 0)) {
380 if (!value || !*value) {
381 printf("invalid path to network resource\n");
382 return 1; /* needs_arg; */
383 } else if(strnlen(value,5) < 5) {
384 printf("UNC name too short");
387 if (strnlen(value, 300) < 300) {
388 got_unc = 1;
389 if (strncmp(value, "//", 2) == 0) {
390 if(got_unc)
391 printf("unc name specified twice, ignoring second\n");
392 else
393 got_unc = 1;
394 } else if (strncmp(value, "\\\\", 2) != 0) {
395 printf("UNC Path does not begin with // or \\\\ \n");
396 return 1;
397 } else {
398 if(got_unc)
399 printf("unc name specified twice, ignoring second\n");
400 else
401 got_unc = 1;
403 } else {
404 printf("CIFS: UNC name too long\n");
405 return 1;
407 } else if ((strncmp(data, "domain", 3) == 0)
408 || (strncmp(data, "workgroup", 5) == 0)) {
409 if (!value || !*value) {
410 printf("CIFS: invalid domain name\n");
411 return 1; /* needs_arg; */
413 if (strnlen(value, 65) < 65) {
414 got_domain = 1;
415 } else {
416 printf("domain name too long\n");
417 return 1;
419 } else if (strncmp(data, "cred", 4) == 0) {
420 if (value && *value) {
421 rc = open_cred_file(value);
422 if(rc) {
423 printf("error %d opening credential file %s\n",rc, value);
424 return 1;
426 } else {
427 printf("invalid credential file name specified\n");
428 return 1;
430 } else if (strncmp(data, "uid", 3) == 0) {
431 if (value && *value) {
432 got_uid = 1;
434 } else if (strncmp(data, "gid", 3) == 0) {
435 if (value && *value) {
436 got_gid = 1;
438 /* fmask and dmask synonyms for people used to smbfs syntax */
439 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
440 if (!value || !*value) {
441 printf ("Option '%s' requires a numerical argument\n", data);
442 return 1;
445 if (value[0] != '0') {
446 printf ("WARNING: '%s' not expressed in octal.\n", data);
449 if (strcmp (data, "fmask") == 0) {
450 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
451 data = "file_mode"; /* BB fix this */
453 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
454 if (!value || !*value) {
455 printf ("Option '%s' requires a numerical argument\n", data);
456 return 1;
459 if (value[0] != '0') {
460 printf ("WARNING: '%s' not expressed in octal.\n", data);
463 if (strcmp (data, "dmask") == 0) {
464 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
465 data = "dir_mode";
467 /* the following eight mount options should be
468 stripped out from what is passed into the kernel
469 since these eight options are best passed as the
470 mount flags rather than redundantly to the kernel
471 and could generate spurious warnings depending on the
472 level of the corresponding cifs vfs kernel code */
473 } else if (strncmp(data, "nosuid", 6) == 0) {
474 *filesys_flags |= MS_NOSUID;
475 } else if (strncmp(data, "suid", 4) == 0) {
476 *filesys_flags &= ~MS_NOSUID;
477 } else if (strncmp(data, "nodev", 5) == 0) {
478 *filesys_flags |= MS_NODEV;
479 } else if (strncmp(data, "dev", 3) == 0) {
480 *filesys_flags &= ~MS_NODEV;
481 } else if (strncmp(data, "noexec", 6) == 0) {
482 *filesys_flags |= MS_NOEXEC;
483 } else if (strncmp(data, "exec", 4) == 0) {
484 *filesys_flags &= ~MS_NOEXEC;
485 } else if (strncmp(data, "guest", 5) == 0) {
486 got_password=1;
487 /* remove the parm since it would otherwise be logged by kern */
488 data[0] = ',';
489 data[1] = ',';
490 data[2] = ',';
491 data[3] = ',';
492 data[4] = ',';
493 } else if (strncmp(data, "ro", 2) == 0) {
494 *filesys_flags |= MS_RDONLY;
495 } else if (strncmp(data, "rw", 2) == 0) {
496 *filesys_flags &= ~MS_RDONLY;
497 } else if (strncmp(data, "remount", 7) == 0) {
498 *filesys_flags |= MS_REMOUNT;
499 } /* else if (strnicmp(data, "port", 4) == 0) {
500 if (value && *value) {
501 vol->port =
502 simple_strtoul(value, &value, 0);
504 } else if (strnicmp(data, "rsize", 5) == 0) {
505 if (value && *value) {
506 vol->rsize =
507 simple_strtoul(value, &value, 0);
509 } else if (strnicmp(data, "wsize", 5) == 0) {
510 if (value && *value) {
511 vol->wsize =
512 simple_strtoul(value, &value, 0);
514 } else if (strnicmp(data, "version", 3) == 0) {
515 } else {
516 printf("CIFS: Unknown mount option %s\n",data);
517 } */ /* nothing to do on those four mount options above.
518 Just pass to kernel and ignore them here */
520 /* move to next option */
521 data = next_keyword+1;
523 /* put overwritten equals sign back */
524 if(value) {
525 value--;
526 *value = '=';
529 /* put previous overwritten comma back */
530 if(next_keyword)
531 *next_keyword = ','; /* BB handle sep= */
532 else
533 data = NULL;
535 return 0;
538 /* replace all (one or more) commas with double commas */
539 static void check_for_comma(char ** ppasswrd)
541 char *new_pass_buf;
542 char *pass;
543 int i,j;
544 int number_of_commas = 0;
545 int len = strlen(*ppasswrd);
547 if(ppasswrd == NULL)
548 return;
549 else
550 (pass = *ppasswrd);
552 for(i=0;i<len;i++) {
553 if(pass[i] == ',')
554 number_of_commas++;
557 if(number_of_commas == 0)
558 return;
559 if(number_of_commas > 64) {
560 /* would otherwise overflow the mount options buffer */
561 printf("\nInvalid password. Password contains too many commas.\n");
562 return;
565 new_pass_buf = malloc(len+number_of_commas+1);
566 if(new_pass_buf == NULL)
567 return;
569 for(i=0,j=0;i<len;i++,j++) {
570 new_pass_buf[j] = pass[i];
571 if(pass[i] == ',') {
572 j++;
573 new_pass_buf[j] = pass[i];
576 new_pass_buf[len+number_of_commas] = 0;
578 free(*ppasswrd);
579 *ppasswrd = new_pass_buf;
581 return;
584 /* Usernames can not have backslash in them and we use
585 [BB check if usernames can have forward slash in them BB]
586 backslash as domain\user separator character
588 static char * check_for_domain(char **ppuser)
590 char * original_string;
591 char * usernm;
592 char * domainnm;
593 int original_len;
594 int len;
595 int i;
597 if(ppuser == NULL)
598 return NULL;
600 original_string = *ppuser;
602 if (original_string == NULL)
603 return NULL;
605 original_len = strlen(original_string);
607 usernm = strchr(*ppuser,'/');
608 if (usernm == NULL) {
609 usernm = strchr(*ppuser,'\\');
610 if (usernm == NULL)
611 return NULL;
614 if(got_domain) {
615 printf("Domain name specified twice. Username probably malformed\n");
616 return NULL;
619 usernm[0] = 0;
620 domainnm = *ppuser;
621 if (domainnm[0] != 0) {
622 got_domain = 1;
623 } else {
624 printf("null domain\n");
626 len = strlen(domainnm);
627 /* reset domainm to new buffer, and copy
628 domain name into it */
629 domainnm = malloc(len+1);
630 if(domainnm == NULL)
631 return NULL;
633 strcpy(domainnm,*ppuser);
635 /* move_string(*ppuser, usernm+1) */
636 len = strlen(usernm+1);
638 if(len >= original_len) {
639 /* should not happen */
640 return domainnm;
643 for(i=0;i<original_len;i++) {
644 if(i<len)
645 original_string[i] = usernm[i+1];
646 else /* stuff with commas to remove last parm */
647 original_string[i] = ',';
650 /* BB add check for more than one slash?
651 strchr(*ppuser,'/');
652 strchr(*ppuser,'\\')
655 return domainnm;
658 /* Note that caller frees the returned buffer if necessary */
659 static char * parse_server(char ** punc_name)
661 char * unc_name = *punc_name;
662 int length = strnlen(unc_name,1024);
663 char * share;
664 char * ipaddress_string = NULL;
665 struct hostent * host_entry;
666 struct in_addr server_ipaddr;
667 int rc;
669 if(length > 1023) {
670 printf("mount error: UNC name too long");
671 return NULL;
673 if (strncasecmp("cifs://",unc_name,7) == 0)
674 return parse_cifs_url(unc_name+7);
675 if (strncasecmp("smb://",unc_name,6) == 0) {
676 return parse_cifs_url(unc_name+6);
679 if(length < 3) {
680 /* BB add code to find DFS root here */
681 printf("\nMounting the DFS root for domain not implemented yet");
682 return NULL;
683 } else {
684 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
685 /* check for nfs syntax ie server:share */
686 share = strchr(unc_name,':');
687 if(share) {
688 free_share_name = 1;
689 *punc_name = malloc(length+3);
690 *share = '/';
691 strncpy((*punc_name)+2,unc_name,length);
692 unc_name = *punc_name;
693 unc_name[length+2] = 0;
694 goto continue_unc_parsing;
695 } else {
696 printf("mount error: improperly formatted UNC name.");
697 printf(" %s does not begin with \\\\ or //\n",unc_name);
698 return NULL;
700 } else {
701 continue_unc_parsing:
702 unc_name[0] = '/';
703 unc_name[1] = '/';
704 unc_name += 2;
705 if ((share = strchr(unc_name, '/')) ||
706 (share = strchr(unc_name,'\\'))) {
707 *share = 0; /* temporarily terminate the string */
708 share += 1;
709 if(got_ip == 0) {
710 host_entry = gethostbyname(unc_name);
712 *(share - 1) = '/'; /* put the slash back */
713 if(got_ip) {
714 if(verboseflag)
715 printf("ip address specified explicitly\n");
716 return NULL;
718 if(host_entry == NULL) {
719 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
720 printf(" rc = %d\n",rc);
721 return NULL;
722 } else {
723 /* BB should we pass an alternate version of the share name as Unicode */
724 /* BB what about ipv6? BB */
725 /* BB add retries with alternate servers in list */
727 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
729 ipaddress_string = inet_ntoa(server_ipaddr);
730 if(ipaddress_string == NULL) {
731 printf("mount error: could not get valid ip address for target server\n");
732 return NULL;
734 return ipaddress_string;
736 } else {
737 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
738 printf("Mounting the DFS root for a particular server not implemented yet\n");
739 return NULL;
745 static struct option longopts[] = {
746 { "all", 0, NULL, 'a' },
747 { "help",0, NULL, 'h' },
748 { "move",0, NULL, 'm' },
749 { "bind",0, NULL, 'b' },
750 { "read-only", 0, NULL, 'r' },
751 { "ro", 0, NULL, 'r' },
752 { "verbose", 0, NULL, 'v' },
753 { "version", 0, NULL, 'V' },
754 { "read-write", 0, NULL, 'w' },
755 { "rw", 0, NULL, 'w' },
756 { "options", 1, NULL, 'o' },
757 { "type", 1, NULL, 't' },
758 { "rsize",1, NULL, 'R' },
759 { "wsize",1, NULL, 'W' },
760 { "uid", 1, NULL, '1'},
761 { "gid", 1, NULL, '2'},
762 { "user",1,NULL,'u'},
763 { "username",1,NULL,'u'},
764 { "dom",1,NULL,'d'},
765 { "domain",1,NULL,'d'},
766 { "password",1,NULL,'p'},
767 { "pass",1,NULL,'p'},
768 { "credentials",1,NULL,'c'},
769 { "port",1,NULL,'P'},
770 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
771 { NULL, 0, NULL, 0 }
774 int main(int argc, char ** argv)
776 int c;
777 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
778 char * orgoptions = NULL;
779 char * share_name = NULL;
780 char * ipaddr = NULL;
781 char * uuid = NULL;
782 char * mountpoint;
783 char * options;
784 char * resolved_path;
785 char * temp;
786 int rc;
787 int rsize = 0;
788 int wsize = 0;
789 int nomtab = 0;
790 int uid = 0;
791 int gid = 0;
792 int optlen = 0;
793 int orgoptlen = 0;
794 int retry = 0; /* set when we have to retry mount with uppercase */
795 struct stat statbuf;
796 struct utsname sysinfo;
797 struct mntent mountent;
798 FILE * pmntfile;
800 /* setlocale(LC_ALL, "");
801 bindtextdomain(PACKAGE, LOCALEDIR);
802 textdomain(PACKAGE); */
804 if(argc && argv) {
805 thisprogram = argv[0];
807 if(thisprogram == NULL)
808 thisprogram = "mount.cifs";
810 uname(&sysinfo);
811 /* BB add workstation name and domain and pass down */
813 /* #ifdef _GNU_SOURCE
814 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
815 #endif */
817 share_name = argv[1];
818 mountpoint = argv[2];
820 /* add sharename in opts string as unc= parm */
822 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
823 longopts, NULL)) != -1) {
824 switch (c) {
825 /* No code to do the following options yet */
826 /* case 'l':
827 list_with_volumelabel = 1;
828 break;
829 case 'L':
830 volumelabel = optarg;
831 break; */
832 /* case 'a':
833 ++mount_all;
834 break; */
836 case '?':
837 case 'h': /* help */
838 mount_cifs_usage ();
839 exit(1);
840 case 'n':
841 ++nomtab;
842 break;
843 case 'b':
844 flags |= MS_BIND;
845 break;
846 case 'm':
847 flags |= MS_MOVE;
848 break;
849 case 'o':
850 orgoptions = strdup(optarg);
851 break;
852 case 'r': /* mount readonly */
853 flags |= MS_RDONLY;
854 break;
855 case 'U':
856 uuid = optarg;
857 break;
858 case 'v':
859 ++verboseflag;
860 break;
861 case 'V':
862 printf ("mount.cifs version: %s.%s%s\n",
863 MOUNT_CIFS_VERSION_MAJOR,
864 MOUNT_CIFS_VERSION_MINOR,
865 MOUNT_CIFS_VENDOR_SUFFIX);
866 if(mountpassword) {
867 memset(mountpassword,0,64);
869 exit (0);
870 case 'w':
871 flags &= ~MS_RDONLY;
872 break;
873 case 'R':
874 rsize = atoi(optarg) ;
875 break;
876 case 'W':
877 wsize = atoi(optarg);
878 break;
879 case '1':
880 uid = atoi(optarg);
881 break;
882 case '2':
883 gid = atoi(optarg);
884 break;
885 case 'u':
886 got_user = 1;
887 user_name = optarg;
888 break;
889 case 'd':
890 domain_name = optarg; /* BB fix this - currently ignored */
891 break;
892 case 'p':
893 if(mountpassword == NULL)
894 mountpassword = calloc(65,1);
895 if(mountpassword) {
896 got_password = 1;
897 strncpy(mountpassword,optarg,64);
899 break;
900 case 'S':
901 get_password_from_file(0 /* stdin */,NULL);
902 break;
903 case 't':
904 break;
905 default:
906 printf("unknown mount option %c\n",c);
907 mount_cifs_usage();
908 exit(1);
912 if(argc < 3)
913 mount_cifs_usage();
915 if (getenv("PASSWD")) {
916 if(mountpassword == NULL)
917 mountpassword = calloc(65,1);
918 if(mountpassword) {
919 strncpy(mountpassword,getenv("PASSWD"),64);
920 got_password = 1;
922 } else if (getenv("PASSWD_FD")) {
923 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
924 } else if (getenv("PASSWD_FILE")) {
925 get_password_from_file(0, getenv("PASSWD_FILE"));
928 if (orgoptions && parse_options(orgoptions, &flags))
929 return -1;
930 ipaddr = parse_server(&share_name);
931 if((ipaddr == NULL) && (got_ip == 0)) {
932 printf("No ip address specified and hostname not found\n");
933 return -1;
936 /* BB save off path and pop after mount returns? */
937 resolved_path = malloc(PATH_MAX+1);
938 if(resolved_path) {
939 /* Note that if we can not canonicalize the name, we get
940 another chance to see if it is valid when we chdir to it */
941 if (realpath(mountpoint, resolved_path)) {
942 mountpoint = resolved_path;
945 if(chdir(mountpoint)) {
946 printf("mount error: can not change directory into mount target %s\n",mountpoint);
947 return -1;
950 if(stat (".", &statbuf)) {
951 printf("mount error: mount point %s does not exist\n",mountpoint);
952 return -1;
955 if (S_ISDIR(statbuf.st_mode) == 0) {
956 printf("mount error: mount point %s is not a directory\n",mountpoint);
957 return -1;
960 if((getuid() != 0) && (geteuid() == 0)) {
961 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
962 #ifndef CIFS_ALLOW_USR_SUID
963 /* Do not allow user mounts to control suid flag
964 for mount unless explicitly built that way */
965 flags |= MS_NOSUID | MS_NODEV;
966 #endif
967 } else {
968 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
969 return -1;
973 if(got_user == 0) {
974 user_name = getusername();
975 got_user = 1;
978 if(got_password == 0) {
979 mountpassword = getpass("Password: "); /* BB obsolete */
980 got_password = 1;
982 /* FIXME launch daemon (handles dfs name resolution and credential change)
983 remember to clear parms and overwrite password field before launching */
984 mount_retry:
985 if(orgoptions) {
986 optlen = strlen(orgoptions);
987 orgoptlen = optlen;
988 } else
989 optlen = 0;
990 if(share_name)
991 optlen += strlen(share_name) + 4;
992 if(user_name)
993 optlen += strlen(user_name) + 6;
994 if(ipaddr)
995 optlen += strlen(ipaddr) + 4;
996 if(mountpassword)
997 optlen += strlen(mountpassword) + 6;
998 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 */);
1000 if(options == NULL) {
1001 printf("Could not allocate memory for mount options\n");
1002 return -1;
1006 options[0] = 0;
1007 strncat(options,"unc=",4);
1008 strcat(options,share_name);
1009 /* scan backwards and reverse direction of slash */
1010 temp = strrchr(options, '/');
1011 if(temp > options + 6)
1012 *temp = '\\';
1013 if(ipaddr) {
1014 strncat(options,",ip=",4);
1015 strcat(options,ipaddr);
1018 if(user_name) {
1019 /* check for syntax like user=domain\user */
1020 domain_name = check_for_domain(&user_name);
1021 strncat(options,",user=",6);
1022 strcat(options,user_name);
1024 if(retry == 0) {
1025 if(domain_name) {
1026 /* extra length accounted for in option string above */
1027 strncat(options,",domain=",8);
1028 strcat(options,domain_name);
1031 if(mountpassword) {
1032 /* Commas have to be doubled, or else they will
1033 look like the parameter separator */
1034 /* if(sep is not set)*/
1035 if(retry == 0)
1036 check_for_comma(&mountpassword);
1037 strncat(options,",pass=",6);
1038 strcat(options,mountpassword);
1041 strncat(options,",ver=",5);
1042 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1044 if(orgoptions) {
1045 strcat(options,",");
1046 strcat(options,orgoptions);
1048 if(verboseflag)
1049 printf("\nmount.cifs kernel mount options %s \n",options);
1050 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1051 /* remember to kill daemon on error */
1052 char * tmp;
1054 switch (errno) {
1055 case 0:
1056 printf("mount failed but no error number set\n");
1057 break;
1058 case ENODEV:
1059 printf("mount error: cifs filesystem not supported by the system\n");
1060 break;
1061 case ENXIO:
1062 if(retry == 0) {
1063 retry = 1;
1064 tmp = share_name;
1065 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1066 *tmp = toupper((unsigned char)*tmp);
1067 tmp++;
1069 if(!*tmp) {
1070 printf("retrying with upper case share name\n");
1071 goto mount_retry;
1074 default:
1076 printf("mount error %d = %s\n",errno,strerror(errno));
1078 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1079 if(mountpassword) {
1080 memset(mountpassword,0,64);
1082 return -1;
1083 } else {
1084 pmntfile = setmntent(MOUNTED, "a+");
1085 if(pmntfile) {
1086 mountent.mnt_fsname = share_name;
1087 mountent.mnt_dir = mountpoint;
1088 mountent.mnt_type = "cifs";
1089 mountent.mnt_opts = malloc(220);
1090 if(mountent.mnt_opts) {
1091 char * mount_user = getusername();
1092 memset(mountent.mnt_opts,0,200);
1093 if(flags & MS_RDONLY)
1094 strcat(mountent.mnt_opts,"ro");
1095 else
1096 strcat(mountent.mnt_opts,"rw");
1097 if(flags & MS_MANDLOCK)
1098 strcat(mountent.mnt_opts,",mand");
1099 else
1100 strcat(mountent.mnt_opts,",nomand");
1101 if(flags & MS_NOEXEC)
1102 strcat(mountent.mnt_opts,",noexec");
1103 if(flags & MS_NOSUID)
1104 strcat(mountent.mnt_opts,",nosuid");
1105 if(flags & MS_NODEV)
1106 strcat(mountent.mnt_opts,",nodev");
1107 if(flags & MS_SYNCHRONOUS)
1108 strcat(mountent.mnt_opts,",synch");
1109 if(mount_user) {
1110 if(getuid() != 0) {
1111 strcat(mountent.mnt_opts,",user=");
1112 strcat(mountent.mnt_opts,mount_user);
1114 free(mount_user);
1117 mountent.mnt_freq = 0;
1118 mountent.mnt_passno = 0;
1119 rc = addmntent(pmntfile,&mountent);
1120 endmntent(pmntfile);
1121 if(mountent.mnt_opts)
1122 free(mountent.mnt_opts);
1123 } else {
1124 printf("could not update mount table\n");
1127 if(mountpassword) {
1128 int len = strlen(mountpassword);
1129 memset(mountpassword,0,len);
1130 free(mountpassword);
1133 if(options) {
1134 memset(options,0,optlen);
1135 free(options);
1138 if(orgoptions) {
1139 memset(orgoptions,0,orgoptlen);
1140 free(orgoptions);
1142 if(resolved_path) {
1143 free(resolved_path);
1146 if(free_share_name) {
1147 free(share_name);
1149 return 0;