r6303: Setting up for 3.0.15pre1
[Samba.git] / source / client / mount.cifs.c
blob3145447cc4efe3ac4a1cc6e403011ab714c632ad
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");
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 (strnicmp(data, "port", 4) == 0) {
498 if (value && *value) {
499 vol->port =
500 simple_strtoul(value, &value, 0);
502 } else if (strnicmp(data, "rsize", 5) == 0) {
503 if (value && *value) {
504 vol->rsize =
505 simple_strtoul(value, &value, 0);
507 } else if (strnicmp(data, "wsize", 5) == 0) {
508 if (value && *value) {
509 vol->wsize =
510 simple_strtoul(value, &value, 0);
512 } else if (strnicmp(data, "version", 3) == 0) {
513 } else {
514 printf("CIFS: Unknown mount option %s\n",data);
515 } */ /* nothing to do on those four mount options above.
516 Just pass to kernel and ignore them here */
518 /* move to next option */
519 data = next_keyword+1;
521 /* put overwritten equals sign back */
522 if(value) {
523 value--;
524 *value = '=';
527 /* put previous overwritten comma back */
528 if(next_keyword)
529 *next_keyword = ','; /* BB handle sep= */
530 else
531 data = NULL;
533 return 0;
536 /* replace all (one or more) commas with double commas */
537 static void check_for_comma(char ** ppasswrd)
539 char *new_pass_buf;
540 char *pass;
541 int i,j;
542 int number_of_commas = 0;
543 int len = strlen(*ppasswrd);
545 if(ppasswrd == NULL)
546 return;
547 else
548 (pass = *ppasswrd);
550 for(i=0;i<len;i++) {
551 if(pass[i] == ',')
552 number_of_commas++;
555 if(number_of_commas == 0)
556 return;
557 if(number_of_commas > 64) {
558 /* would otherwise overflow the mount options buffer */
559 printf("\nInvalid password. Password contains too many commas.\n");
560 return;
563 new_pass_buf = malloc(len+number_of_commas+1);
564 if(new_pass_buf == NULL)
565 return;
567 for(i=0,j=0;i<len;i++,j++) {
568 new_pass_buf[j] = pass[i];
569 if(pass[i] == ',') {
570 j++;
571 new_pass_buf[j] = pass[i];
574 new_pass_buf[len+number_of_commas] = 0;
576 free(*ppasswrd);
577 *ppasswrd = new_pass_buf;
579 return;
582 /* Usernames can not have backslash in them and we use
583 [BB check if usernames can have forward slash in them BB]
584 backslash as domain\user separator character
586 static char * check_for_domain(char **ppuser)
588 char * original_string;
589 char * usernm;
590 char * domainnm;
591 int original_len;
592 int len;
593 int i;
595 if(ppuser == NULL)
596 return NULL;
598 original_string = *ppuser;
600 if (original_string == NULL)
601 return NULL;
603 original_len = strlen(original_string);
605 usernm = strchr(*ppuser,'/');
606 if (usernm == NULL) {
607 usernm = strchr(*ppuser,'\\');
608 if (usernm == NULL)
609 return NULL;
612 if(got_domain) {
613 printf("Domain name specified twice. Username probably malformed\n");
614 return NULL;
617 usernm[0] = 0;
618 domainnm = *ppuser;
619 if (domainnm[0] != 0) {
620 got_domain = 1;
621 } else {
622 printf("null domain\n");
624 len = strlen(domainnm);
625 /* reset domainm to new buffer, and copy
626 domain name into it */
627 domainnm = malloc(len+1);
628 if(domainnm == NULL)
629 return NULL;
631 strcpy(domainnm,*ppuser);
633 /* move_string(*ppuser, usernm+1) */
634 len = strlen(usernm+1);
636 if(len >= original_len) {
637 /* should not happen */
638 return domainnm;
641 for(i=0;i<original_len;i++) {
642 if(i<len)
643 original_string[i] = usernm[i+1];
644 else /* stuff with commas to remove last parm */
645 original_string[i] = ',';
648 /* BB add check for more than one slash?
649 strchr(*ppuser,'/');
650 strchr(*ppuser,'\\')
653 return domainnm;
656 /* Note that caller frees the returned buffer if necessary */
657 static char * parse_server(char ** punc_name)
659 char * unc_name = *punc_name;
660 int length = strnlen(unc_name,1024);
661 char * share;
662 char * ipaddress_string = NULL;
663 struct hostent * host_entry;
664 struct in_addr server_ipaddr;
665 int rc;
667 if(length > 1023) {
668 printf("mount error: UNC name too long");
669 return NULL;
671 if (strncasecmp("cifs://",unc_name,7) == 0)
672 return parse_cifs_url(unc_name+7);
673 if (strncasecmp("smb://",unc_name,6) == 0) {
674 return parse_cifs_url(unc_name+6);
677 if(length < 3) {
678 /* BB add code to find DFS root here */
679 printf("\nMounting the DFS root for domain not implemented yet");
680 return NULL;
681 } else {
682 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
683 /* check for nfs syntax ie server:share */
684 share = strchr(unc_name,':');
685 if(share) {
686 free_share_name = 1;
687 *punc_name = malloc(length+3);
688 *share = '/';
689 strncpy((*punc_name)+2,unc_name,length);
690 unc_name = *punc_name;
691 unc_name[length+2] = 0;
692 goto continue_unc_parsing;
693 } else {
694 printf("mount error: improperly formatted UNC name.");
695 printf(" %s does not begin with \\\\ or //\n",unc_name);
696 return NULL;
698 } else {
699 continue_unc_parsing:
700 unc_name[0] = '/';
701 unc_name[1] = '/';
702 unc_name += 2;
703 if ((share = strchr(unc_name, '/')) ||
704 (share = strchr(unc_name,'\\'))) {
705 *share = 0; /* temporarily terminate the string */
706 share += 1;
707 if(got_ip == 0) {
708 host_entry = gethostbyname(unc_name);
710 *(share - 1) = '/'; /* put the slash back */
711 if(got_ip) {
712 if(verboseflag)
713 printf("ip address specified explicitly\n");
714 return NULL;
716 if(host_entry == NULL) {
717 printf("mount error: could not find target server. TCP name %s not found ", unc_name);
718 printf(" rc = %d\n",rc);
719 return NULL;
720 } else {
721 /* BB should we pass an alternate version of the share name as Unicode */
722 /* BB what about ipv6? BB */
723 /* BB add retries with alternate servers in list */
725 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
727 ipaddress_string = inet_ntoa(server_ipaddr);
728 if(ipaddress_string == NULL) {
729 printf("mount error: could not get valid ip address for target server\n");
730 return NULL;
732 return ipaddress_string;
734 } else {
735 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
736 printf("Mounting the DFS root for a particular server not implemented yet\n");
737 return NULL;
743 static struct option longopts[] = {
744 { "all", 0, NULL, 'a' },
745 { "help",0, NULL, 'h' },
746 { "move",0, NULL, 'm' },
747 { "bind",0, NULL, 'b' },
748 { "read-only", 0, NULL, 'r' },
749 { "ro", 0, NULL, 'r' },
750 { "verbose", 0, NULL, 'v' },
751 { "version", 0, NULL, 'V' },
752 { "read-write", 0, NULL, 'w' },
753 { "rw", 0, NULL, 'w' },
754 { "options", 1, NULL, 'o' },
755 { "type", 1, NULL, 't' },
756 { "rsize",1, NULL, 'R' },
757 { "wsize",1, NULL, 'W' },
758 { "uid", 1, NULL, '1'},
759 { "gid", 1, NULL, '2'},
760 { "user",1,NULL,'u'},
761 { "username",1,NULL,'u'},
762 { "dom",1,NULL,'d'},
763 { "domain",1,NULL,'d'},
764 { "password",1,NULL,'p'},
765 { "pass",1,NULL,'p'},
766 { "credentials",1,NULL,'c'},
767 { "port",1,NULL,'P'},
768 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
769 { NULL, 0, NULL, 0 }
772 int main(int argc, char ** argv)
774 int c;
775 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
776 char * orgoptions = NULL;
777 char * share_name = NULL;
778 char * ipaddr = NULL;
779 char * uuid = NULL;
780 char * mountpoint;
781 char * options;
782 char * resolved_path;
783 char * temp;
784 int rc;
785 int rsize = 0;
786 int wsize = 0;
787 int nomtab = 0;
788 int uid = 0;
789 int gid = 0;
790 int optlen = 0;
791 int orgoptlen = 0;
792 int retry = 0; /* set when we have to retry mount with uppercase */
793 struct stat statbuf;
794 struct utsname sysinfo;
795 struct mntent mountent;
796 FILE * pmntfile;
798 /* setlocale(LC_ALL, "");
799 bindtextdomain(PACKAGE, LOCALEDIR);
800 textdomain(PACKAGE); */
802 if(argc && argv) {
803 thisprogram = argv[0];
805 if(thisprogram == NULL)
806 thisprogram = "mount.cifs";
808 uname(&sysinfo);
809 /* BB add workstation name and domain and pass down */
811 /* #ifdef _GNU_SOURCE
812 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
813 #endif */
815 share_name = argv[1];
816 mountpoint = argv[2];
818 /* add sharename in opts string as unc= parm */
820 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
821 longopts, NULL)) != -1) {
822 switch (c) {
823 /* No code to do the following options yet */
824 /* case 'l':
825 list_with_volumelabel = 1;
826 break;
827 case 'L':
828 volumelabel = optarg;
829 break; */
830 /* case 'a':
831 ++mount_all;
832 break; */
834 case '?':
835 case 'h': /* help */
836 mount_cifs_usage ();
837 exit(1);
838 case 'n':
839 ++nomtab;
840 break;
841 case 'b':
842 flags |= MS_BIND;
843 break;
844 case 'm':
845 flags |= MS_MOVE;
846 break;
847 case 'o':
848 orgoptions = strdup(optarg);
849 break;
850 case 'r': /* mount readonly */
851 flags |= MS_RDONLY;
852 break;
853 case 'U':
854 uuid = optarg;
855 break;
856 case 'v':
857 ++verboseflag;
858 break;
859 case 'V':
860 printf ("mount.cifs version: %s.%s%s\n",
861 MOUNT_CIFS_VERSION_MAJOR,
862 MOUNT_CIFS_VERSION_MINOR,
863 MOUNT_CIFS_VENDOR_SUFFIX);
864 if(mountpassword) {
865 memset(mountpassword,0,64);
867 exit (0);
868 case 'w':
869 flags &= ~MS_RDONLY;
870 break;
871 case 'R':
872 rsize = atoi(optarg) ;
873 break;
874 case 'W':
875 wsize = atoi(optarg);
876 break;
877 case '1':
878 uid = atoi(optarg);
879 break;
880 case '2':
881 gid = atoi(optarg);
882 break;
883 case 'u':
884 got_user = 1;
885 user_name = optarg;
886 break;
887 case 'd':
888 domain_name = optarg; /* BB fix this - currently ignored */
889 break;
890 case 'p':
891 if(mountpassword == NULL)
892 mountpassword = calloc(65,1);
893 if(mountpassword) {
894 got_password = 1;
895 strncpy(mountpassword,optarg,64);
897 break;
898 case 'S':
899 get_password_from_file(0 /* stdin */,NULL);
900 break;
901 case 't':
902 break;
903 default:
904 printf("unknown mount option %c\n",c);
905 mount_cifs_usage();
906 exit(1);
910 if(argc < 3)
911 mount_cifs_usage();
913 if (getenv("PASSWD")) {
914 if(mountpassword == NULL)
915 mountpassword = calloc(65,1);
916 if(mountpassword) {
917 strncpy(mountpassword,getenv("PASSWD"),64);
918 got_password = 1;
920 } else if (getenv("PASSWD_FD")) {
921 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
922 } else if (getenv("PASSWD_FILE")) {
923 get_password_from_file(0, getenv("PASSWD_FILE"));
926 if (orgoptions && parse_options(orgoptions, &flags))
927 return -1;
928 ipaddr = parse_server(&share_name);
929 if((ipaddr == NULL) && (got_ip == 0)) {
930 printf("No ip address specified and hostname not found\n");
931 return -1;
934 /* BB save off path and pop after mount returns? */
935 resolved_path = malloc(PATH_MAX+1);
936 if(resolved_path) {
937 /* Note that if we can not canonicalize the name, we get
938 another chance to see if it is valid when we chdir to it */
939 if (realpath(mountpoint, resolved_path)) {
940 mountpoint = resolved_path;
943 if(chdir(mountpoint)) {
944 printf("mount error: can not change directory into mount target %s\n",mountpoint);
945 return -1;
948 if(stat (".", &statbuf)) {
949 printf("mount error: mount point %s does not exist\n",mountpoint);
950 return -1;
953 if (S_ISDIR(statbuf.st_mode) == 0) {
954 printf("mount error: mount point %s is not a directory\n",mountpoint);
955 return -1;
958 if((getuid() != 0) && (geteuid() == 0)) {
959 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
960 #ifndef CIFS_ALLOW_USR_SUID
961 /* Do not allow user mounts to control suid flag
962 for mount unless explicitly built that way */
963 flags |= MS_NOSUID | MS_NODEV;
964 #endif
965 } else {
966 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
967 return -1;
971 if(got_user == 0) {
972 user_name = getusername();
973 got_user = 1;
976 if(got_password == 0) {
977 mountpassword = getpass("Password: "); /* BB obsolete */
978 got_password = 1;
980 /* FIXME launch daemon (handles dfs name resolution and credential change)
981 remember to clear parms and overwrite password field before launching */
982 mount_retry:
983 if(orgoptions) {
984 optlen = strlen(orgoptions);
985 orgoptlen = optlen;
986 } else
987 optlen = 0;
988 if(share_name)
989 optlen += strlen(share_name) + 4;
990 if(user_name)
991 optlen += strlen(user_name) + 6;
992 if(ipaddr)
993 optlen += strlen(ipaddr) + 4;
994 if(mountpassword)
995 optlen += strlen(mountpassword) + 6;
996 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 */);
998 if(options == NULL) {
999 printf("Could not allocate memory for mount options\n");
1000 return -1;
1004 options[0] = 0;
1005 strncat(options,"unc=",4);
1006 strcat(options,share_name);
1007 /* scan backwards and reverse direction of slash */
1008 temp = strrchr(options, '/');
1009 if(temp > options + 6)
1010 *temp = '\\';
1011 if(ipaddr) {
1012 strncat(options,",ip=",4);
1013 strcat(options,ipaddr);
1016 if(user_name) {
1017 /* check for syntax like user=domain\user */
1018 domain_name = check_for_domain(&user_name);
1019 strncat(options,",user=",6);
1020 strcat(options,user_name);
1022 if(retry == 0) {
1023 if(domain_name) {
1024 /* extra length accounted for in option string above */
1025 strncat(options,",domain=",8);
1026 strcat(options,domain_name);
1029 if(mountpassword) {
1030 /* Commas have to be doubled, or else they will
1031 look like the parameter separator */
1032 /* if(sep is not set)*/
1033 if(retry == 0)
1034 check_for_comma(&mountpassword);
1035 strncat(options,",pass=",6);
1036 strcat(options,mountpassword);
1039 strncat(options,",ver=",5);
1040 strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1042 if(orgoptions) {
1043 strcat(options,",");
1044 strcat(options,orgoptions);
1046 if(verboseflag)
1047 printf("\nmount.cifs kernel mount options %s \n",options);
1048 if(mount(share_name, mountpoint, "cifs", flags, options)) {
1049 /* remember to kill daemon on error */
1050 char * tmp;
1052 switch (errno) {
1053 case 0:
1054 printf("mount failed but no error number set\n");
1055 break;
1056 case ENODEV:
1057 printf("mount error: cifs filesystem not supported by the system\n");
1058 break;
1059 case ENXIO:
1060 if(retry == 0) {
1061 retry = 1;
1062 tmp = share_name;
1063 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1064 *tmp = toupper((unsigned char)*tmp);
1065 tmp++;
1067 if(!*tmp) {
1068 printf("retrying with upper case share name\n");
1069 goto mount_retry;
1072 default:
1074 printf("mount error %d = %s\n",errno,strerror(errno));
1076 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1077 if(mountpassword) {
1078 memset(mountpassword,0,64);
1080 return -1;
1081 } else {
1082 pmntfile = setmntent(MOUNTED, "a+");
1083 if(pmntfile) {
1084 mountent.mnt_fsname = share_name;
1085 mountent.mnt_dir = mountpoint;
1086 mountent.mnt_type = "cifs";
1087 mountent.mnt_opts = malloc(220);
1088 if(mountent.mnt_opts) {
1089 char * mount_user = getusername();
1090 memset(mountent.mnt_opts,0,200);
1091 if(flags & MS_RDONLY)
1092 strcat(mountent.mnt_opts,"ro");
1093 else
1094 strcat(mountent.mnt_opts,"rw");
1095 if(flags & MS_MANDLOCK)
1096 strcat(mountent.mnt_opts,",mand");
1097 else
1098 strcat(mountent.mnt_opts,",nomand");
1099 if(flags & MS_NOEXEC)
1100 strcat(mountent.mnt_opts,",noexec");
1101 if(flags & MS_NOSUID)
1102 strcat(mountent.mnt_opts,",nosuid");
1103 if(flags & MS_NODEV)
1104 strcat(mountent.mnt_opts,",nodev");
1105 if(flags & MS_SYNCHRONOUS)
1106 strcat(mountent.mnt_opts,",synch");
1107 if(mount_user) {
1108 if(getuid() != 0) {
1109 strcat(mountent.mnt_opts,",user=");
1110 strcat(mountent.mnt_opts,mount_user);
1112 free(mount_user);
1115 mountent.mnt_freq = 0;
1116 mountent.mnt_passno = 0;
1117 rc = addmntent(pmntfile,&mountent);
1118 endmntent(pmntfile);
1119 if(mountent.mnt_opts)
1120 free(mountent.mnt_opts);
1121 } else {
1122 printf("could not update mount table\n");
1125 if(mountpassword) {
1126 int len = strlen(mountpassword);
1127 memset(mountpassword,0,len);
1128 free(mountpassword);
1131 if(options) {
1132 memset(options,0,optlen);
1133 free(options);
1136 if(orgoptions) {
1137 memset(orgoptions,0,orgoptlen);
1138 free(orgoptions);
1140 if(resolved_path) {
1141 free(resolved_path);
1144 if(free_share_name) {
1145 free(share_name);
1147 return 0;