Merge in J.Layton patch and resolve conflict.
[Samba/bb.git] / source / client / mount.cifs.c
bloba25ccc54de5cb1ab856df912d973cd1e2a4fe7b4
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 3 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, see <http://www.gnu.org/licenses/>. */
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <grp.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 "10"
44 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
45 #ifdef _SAMBA_BUILD_
46 #include "include/version.h"
47 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
48 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
49 #else
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
51 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
52 #else
53 #define MOUNT_CIFS_VENDOR_SUFFIX ""
54 #endif /* _SAMBA_BUILD_ */
55 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
57 #ifndef MS_MOVE
58 #define MS_MOVE 8192
59 #endif
61 #ifndef MS_BIND
62 #define MS_BIND 4096
63 #endif
65 #define MAX_UNC_LEN 1024
67 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
69 const char *thisprogram;
70 int verboseflag = 0;
71 static int got_password = 0;
72 static int got_user = 0;
73 static int got_domain = 0;
74 static int got_ip = 0;
75 static int got_unc = 0;
76 static int got_uid = 0;
77 static int got_gid = 0;
78 static char * user_name = NULL;
79 static char * mountpassword = NULL;
80 char * domain_name = NULL;
81 char * prefixpath = NULL;
83 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
84 * don't link to libreplace so need them here. */
86 /* like strncpy but does not 0 fill the buffer and always null
87 * terminates. bufsize is the size of the destination buffer */
88 size_t strlcpy(char *d, const char *s, size_t bufsize)
90 size_t len = strlen(s);
91 size_t ret = len;
92 if (bufsize <= 0) return 0;
93 if (len >= bufsize) len = bufsize-1;
94 memcpy(d, s, len);
95 d[len] = 0;
96 return ret;
99 /* like strncat but does not 0 fill the buffer and always null
100 * terminates. bufsize is the length of the buffer, which should
101 * be one more than the maximum resulting string length */
102 size_t strlcat(char *d, const char *s, size_t bufsize)
104 size_t len1 = strlen(d);
105 size_t len2 = strlen(s);
106 size_t ret = len1 + len2;
108 if (len1+len2 >= bufsize) {
109 len2 = bufsize - (len1+1);
111 if (len2 > 0) {
112 memcpy(d+len1, s, len2);
113 d[len1+len2] = 0;
115 return ret;
118 /* BB finish BB
120 cifs_umount
121 open nofollow - avoid symlink exposure?
122 get owner of dir see if matches self or if root
123 call system(umount argv) etc.
125 BB end finish BB */
127 static char * check_for_domain(char **);
130 static void mount_cifs_usage(void)
132 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
133 printf("\nMount the remote target, specified as a UNC name,");
134 printf(" to a local directory.\n\nOptions:\n");
135 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
136 printf("\nLess commonly used options:");
137 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
138 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
139 printf("\n\tdirectio,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
140 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
141 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
142 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
143 printf("\n\nRarely used options:");
144 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
145 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
146 printf("\n\tnointr,ignorecase,noposixpaths,noacl");
147 printf("\n\nOptions are described in more detail in the manual page");
148 printf("\n\tman 8 mount.cifs\n");
149 printf("\nTo display the version number of the mount helper:");
150 printf("\n\t%s -V\n",thisprogram);
152 if(mountpassword) {
153 memset(mountpassword,0,64);
154 free(mountpassword);
156 exit(1);
159 /* caller frees username if necessary */
160 static char * getusername(void) {
161 char *username = NULL;
162 struct passwd *password = getpwuid(getuid());
164 if (password) {
165 username = password->pw_name;
167 return username;
170 static char * parse_cifs_url(char * unc_name)
172 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
173 return NULL;
176 static int open_cred_file(char * file_name)
178 char * line_buf;
179 char * temp_val;
180 FILE * fs;
181 int i, length;
182 fs = fopen(file_name,"r");
183 if(fs == NULL)
184 return errno;
185 line_buf = (char *)malloc(4096);
186 if(line_buf == NULL) {
187 fclose(fs);
188 return -ENOMEM;
191 while(fgets(line_buf,4096,fs)) {
192 /* parse line from credential file */
194 /* eat leading white space */
195 for(i=0;i<4086;i++) {
196 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
197 break;
198 /* if whitespace - skip past it */
200 if (strncasecmp("username",line_buf+i,8) == 0) {
201 temp_val = strchr(line_buf + i,'=');
202 if(temp_val) {
203 /* go past equals sign */
204 temp_val++;
205 for(length = 0;length<4087;length++) {
206 if(temp_val[length] == '\n')
207 break;
209 if(length > 4086) {
210 printf("mount.cifs failed due to malformed username in credentials file");
211 memset(line_buf,0,4096);
212 if(mountpassword) {
213 memset(mountpassword,0,64);
215 exit(1);
216 } else {
217 got_user = 1;
218 user_name = (char *)calloc(1 + length,1);
219 /* BB adding free of user_name string before exit,
220 not really necessary but would be cleaner */
221 strncpy(user_name,temp_val, length);
224 } else if (strncasecmp("password",line_buf+i,8) == 0) {
225 temp_val = strchr(line_buf+i,'=');
226 if(temp_val) {
227 /* go past equals sign */
228 temp_val++;
229 for(length = 0;length<65;length++) {
230 if(temp_val[length] == '\n')
231 break;
233 if(length > 64) {
234 printf("mount.cifs failed: password in credentials file too long\n");
235 memset(line_buf,0, 4096);
236 if(mountpassword) {
237 memset(mountpassword,0,64);
239 exit(1);
240 } else {
241 if(mountpassword == NULL) {
242 mountpassword = (char *)calloc(65,1);
243 } else
244 memset(mountpassword,0,64);
245 if(mountpassword) {
246 strncpy(mountpassword,temp_val,length);
247 got_password = 1;
251 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
252 temp_val = strchr(line_buf+i,'=');
253 if(temp_val) {
254 /* go past equals sign */
255 temp_val++;
256 if(verboseflag)
257 printf("\nDomain %s\n",temp_val);
258 for(length = 0;length<65;length++) {
259 if(temp_val[length] == '\n')
260 break;
262 if(length > 64) {
263 printf("mount.cifs failed: domain in credentials file too long\n");
264 if(mountpassword) {
265 memset(mountpassword,0,64);
267 exit(1);
268 } else {
269 if(domain_name == NULL) {
270 domain_name = (char *)calloc(65,1);
271 } else
272 memset(domain_name,0,64);
273 if(domain_name) {
274 strncpy(domain_name,temp_val,length);
275 got_domain = 1;
282 fclose(fs);
283 if(line_buf) {
284 memset(line_buf,0,4096);
285 free(line_buf);
287 return 0;
290 static int get_password_from_file(int file_descript, char * filename)
292 int rc = 0;
293 int i;
294 char c;
296 if(mountpassword == NULL)
297 mountpassword = (char *)calloc(65,1);
298 else
299 memset(mountpassword, 0, 64);
301 if (mountpassword == NULL) {
302 printf("malloc failed\n");
303 exit(1);
306 if(filename != NULL) {
307 file_descript = open(filename, O_RDONLY);
308 if(file_descript < 0) {
309 printf("mount.cifs failed. %s attempting to open password file %s\n",
310 strerror(errno),filename);
311 exit(1);
314 /* else file already open and fd provided */
316 for(i=0;i<64;i++) {
317 rc = read(file_descript,&c,1);
318 if(rc < 0) {
319 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
320 memset(mountpassword,0,64);
321 if(filename != NULL)
322 close(file_descript);
323 exit(1);
324 } else if(rc == 0) {
325 if(mountpassword[0] == 0) {
326 if(verboseflag)
327 printf("\nWarning: null password used since cifs password file empty");
329 break;
330 } else /* read valid character */ {
331 if((c == 0) || (c == '\n')) {
332 break;
333 } else
334 mountpassword[i] = c;
337 if((i == 64) && (verboseflag)) {
338 printf("\nWarning: password longer than 64 characters specified in cifs password file");
340 got_password = 1;
341 if(filename != NULL) {
342 close(file_descript);
345 return rc;
348 static int parse_options(char ** optionsp, int * filesys_flags)
350 const char * data;
351 char * percent_char = NULL;
352 char * value = NULL;
353 char * next_keyword = NULL;
354 char * out = NULL;
355 int out_len = 0;
356 int word_len;
357 int rc = 0;
358 char user[32];
359 char group[32];
361 if (!optionsp || !*optionsp)
362 return 1;
363 data = *optionsp;
365 if(verboseflag)
366 printf("parsing options: %s\n", data);
368 /* BB fixme check for separator override BB */
370 if (getuid()) {
371 got_uid = 1;
372 snprintf(user,sizeof(user),"%u",getuid());
373 got_gid = 1;
374 snprintf(group,sizeof(group),"%u",getgid());
377 /* while ((data = strsep(&options, ",")) != NULL) { */
378 while(data != NULL) {
379 /* check if ends with trailing comma */
380 if(*data == 0)
381 break;
383 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
384 /* data = next keyword */
385 /* value = next value ie stuff after equal sign */
387 next_keyword = strchr(data,','); /* BB handle sep= */
389 /* temporarily null terminate end of keyword=value pair */
390 if(next_keyword)
391 *next_keyword++ = 0;
393 /* temporarily null terminate keyword to make keyword and value distinct */
394 if ((value = strchr(data, '=')) != NULL) {
395 *value = '\0';
396 value++;
399 if (strncmp(data, "users",5) == 0) {
400 if(!value || !*value) {
401 goto nocopy;
403 } else if (strncmp(data, "user_xattr",10) == 0) {
404 /* do nothing - need to skip so not parsed as user name */
405 } else if (strncmp(data, "user", 4) == 0) {
407 if (!value || !*value) {
408 if(data[4] == '\0') {
409 if(verboseflag)
410 printf("\nskipping empty user mount parameter\n");
411 /* remove the parm since it would otherwise be confusing
412 to the kernel code which would think it was a real username */
413 goto nocopy;
414 } else {
415 printf("username specified with no parameter\n");
416 return 1; /* needs_arg; */
418 } else {
419 if (strnlen(value, 260) < 260) {
420 got_user=1;
421 percent_char = strchr(value,'%');
422 if(percent_char) {
423 *percent_char = ',';
424 if(mountpassword == NULL)
425 mountpassword = (char *)calloc(65,1);
426 if(mountpassword) {
427 if(got_password)
428 printf("\nmount.cifs warning - password specified twice\n");
429 got_password = 1;
430 percent_char++;
431 strncpy(mountpassword, percent_char,64);
432 /* remove password from username */
433 while(*percent_char != 0) {
434 *percent_char = ',';
435 percent_char++;
439 /* this is only case in which the user
440 name buf is not malloc - so we have to
441 check for domain name embedded within
442 the user name here since the later
443 call to check_for_domain will not be
444 invoked */
445 domain_name = check_for_domain(&value);
446 } else {
447 printf("username too long\n");
448 return 1;
451 } else if (strncmp(data, "pass", 4) == 0) {
452 if (!value || !*value) {
453 if(got_password) {
454 printf("\npassword specified twice, ignoring second\n");
455 } else
456 got_password = 1;
457 } else if (strnlen(value, 17) < 17) {
458 if(got_password)
459 printf("\nmount.cifs warning - password specified twice\n");
460 got_password = 1;
461 } else {
462 printf("password too long\n");
463 return 1;
465 } else if (strncmp(data, "sec", 3) == 0) {
466 if (value) {
467 if (!strcmp(value, "none"))
468 got_password = 1;
470 } else if (strncmp(data, "ip", 2) == 0) {
471 if (!value || !*value) {
472 printf("target ip address argument missing");
473 } else if (strnlen(value, 35) < 35) {
474 if(verboseflag)
475 printf("ip address %s override specified\n",value);
476 got_ip = 1;
477 } else {
478 printf("ip address too long\n");
479 return 1;
481 } else if ((strncmp(data, "unc", 3) == 0)
482 || (strncmp(data, "target", 6) == 0)
483 || (strncmp(data, "path", 4) == 0)) {
484 if (!value || !*value) {
485 printf("invalid path to network resource\n");
486 return 1; /* needs_arg; */
487 } else if(strnlen(value,5) < 5) {
488 printf("UNC name too short");
491 if (strnlen(value, 300) < 300) {
492 got_unc = 1;
493 if (strncmp(value, "//", 2) == 0) {
494 if(got_unc)
495 printf("unc name specified twice, ignoring second\n");
496 else
497 got_unc = 1;
498 } else if (strncmp(value, "\\\\", 2) != 0) {
499 printf("UNC Path does not begin with // or \\\\ \n");
500 return 1;
501 } else {
502 if(got_unc)
503 printf("unc name specified twice, ignoring second\n");
504 else
505 got_unc = 1;
507 } else {
508 printf("CIFS: UNC name too long\n");
509 return 1;
511 } else if ((strncmp(data, "domain", 3) == 0)
512 || (strncmp(data, "workgroup", 5) == 0)) {
513 if (!value || !*value) {
514 printf("CIFS: invalid domain name\n");
515 return 1; /* needs_arg; */
517 if (strnlen(value, 65) < 65) {
518 got_domain = 1;
519 } else {
520 printf("domain name too long\n");
521 return 1;
523 } else if (strncmp(data, "cred", 4) == 0) {
524 if (value && *value) {
525 rc = open_cred_file(value);
526 if(rc) {
527 printf("error %d opening credential file %s\n",rc, value);
528 return 1;
530 } else {
531 printf("invalid credential file name specified\n");
532 return 1;
534 } else if (strncmp(data, "uid", 3) == 0) {
535 if (value && *value) {
536 got_uid = 1;
537 if (!isdigit(*value)) {
538 struct passwd *pw;
540 if (!(pw = getpwnam(value))) {
541 printf("bad user name \"%s\"\n", value);
542 exit(1);
544 snprintf(user, sizeof(user), "%u", pw->pw_uid);
545 } else {
546 strlcpy(user,value,sizeof(user));
549 goto nocopy;
550 } else if (strncmp(data, "gid", 3) == 0) {
551 if (value && *value) {
552 got_gid = 1;
553 if (!isdigit(*value)) {
554 struct group *gr;
556 if (!(gr = getgrnam(value))) {
557 printf("bad group name \"%s\"\n", value);
558 exit(1);
560 snprintf(group, sizeof(group), "%u", gr->gr_gid);
561 } else {
562 strlcpy(group,value,sizeof(group));
565 goto nocopy;
566 /* fmask and dmask synonyms for people used to smbfs syntax */
567 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
568 if (!value || !*value) {
569 printf ("Option '%s' requires a numerical argument\n", data);
570 return 1;
573 if (value[0] != '0') {
574 printf ("WARNING: '%s' not expressed in octal.\n", data);
577 if (strcmp (data, "fmask") == 0) {
578 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
579 data = "file_mode"; /* BB fix this */
581 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
582 if (!value || !*value) {
583 printf ("Option '%s' requires a numerical argument\n", data);
584 return 1;
587 if (value[0] != '0') {
588 printf ("WARNING: '%s' not expressed in octal.\n", data);
591 if (strcmp (data, "dmask") == 0) {
592 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
593 data = "dir_mode";
595 /* the following eight mount options should be
596 stripped out from what is passed into the kernel
597 since these eight options are best passed as the
598 mount flags rather than redundantly to the kernel
599 and could generate spurious warnings depending on the
600 level of the corresponding cifs vfs kernel code */
601 } else if (strncmp(data, "nosuid", 6) == 0) {
602 *filesys_flags |= MS_NOSUID;
603 } else if (strncmp(data, "suid", 4) == 0) {
604 *filesys_flags &= ~MS_NOSUID;
605 } else if (strncmp(data, "nodev", 5) == 0) {
606 *filesys_flags |= MS_NODEV;
607 } else if ((strncmp(data, "nobrl", 5) == 0) ||
608 (strncmp(data, "nolock", 6) == 0)) {
609 *filesys_flags &= ~MS_MANDLOCK;
610 } else if (strncmp(data, "dev", 3) == 0) {
611 *filesys_flags &= ~MS_NODEV;
612 } else if (strncmp(data, "noexec", 6) == 0) {
613 *filesys_flags |= MS_NOEXEC;
614 } else if (strncmp(data, "exec", 4) == 0) {
615 *filesys_flags &= ~MS_NOEXEC;
616 } else if (strncmp(data, "guest", 5) == 0) {
617 got_password=1;
618 } else if (strncmp(data, "ro", 2) == 0) {
619 *filesys_flags |= MS_RDONLY;
620 } else if (strncmp(data, "rw", 2) == 0) {
621 *filesys_flags &= ~MS_RDONLY;
622 } else if (strncmp(data, "remount", 7) == 0) {
623 *filesys_flags |= MS_REMOUNT;
624 } /* else if (strnicmp(data, "port", 4) == 0) {
625 if (value && *value) {
626 vol->port =
627 simple_strtoul(value, &value, 0);
629 } else if (strnicmp(data, "rsize", 5) == 0) {
630 if (value && *value) {
631 vol->rsize =
632 simple_strtoul(value, &value, 0);
634 } else if (strnicmp(data, "wsize", 5) == 0) {
635 if (value && *value) {
636 vol->wsize =
637 simple_strtoul(value, &value, 0);
639 } else if (strnicmp(data, "version", 3) == 0) {
640 } else {
641 printf("CIFS: Unknown mount option %s\n",data);
642 } */ /* nothing to do on those four mount options above.
643 Just pass to kernel and ignore them here */
645 /* Copy (possibly modified) option to out */
646 word_len = strlen(data);
647 if (value)
648 word_len += 1 + strlen(value);
650 out = (char *)realloc(out, out_len + word_len + 2);
651 if (out == NULL) {
652 perror("malloc");
653 exit(1);
656 if (out_len) {
657 strlcat(out, ",", out_len + word_len + 2);
658 out_len++;
661 if (value)
662 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
663 else
664 snprintf(out + out_len, word_len + 1, "%s", data);
665 out_len = strlen(out);
667 nocopy:
668 data = next_keyword;
671 /* special-case the uid and gid */
672 if (got_uid) {
673 word_len = strlen(user);
675 out = (char *)realloc(out, out_len + word_len + 6);
676 if (out == NULL) {
677 perror("malloc");
678 exit(1);
681 if (out_len) {
682 strlcat(out, ",", out_len + word_len + 6);
683 out_len++;
685 snprintf(out + out_len, word_len + 5, "uid=%s", user);
686 out_len = strlen(out);
688 if (got_gid) {
689 word_len = strlen(group);
691 out = (char *)realloc(out, out_len + 1 + word_len + 6);
692 if (out == NULL) {
693 perror("malloc");
694 exit(1);
697 if (out_len) {
698 strlcat(out, ",", out_len + word_len + 6);
699 out_len++;
701 snprintf(out + out_len, word_len + 5, "gid=%s", group);
702 out_len = strlen(out);
705 free(*optionsp);
706 *optionsp = out;
707 return 0;
710 /* replace all (one or more) commas with double commas */
711 static void check_for_comma(char ** ppasswrd)
713 char *new_pass_buf;
714 char *pass;
715 int i,j;
716 int number_of_commas = 0;
717 int len;
719 if(ppasswrd == NULL)
720 return;
721 else
722 (pass = *ppasswrd);
724 len = strlen(pass);
726 for(i=0;i<len;i++) {
727 if(pass[i] == ',')
728 number_of_commas++;
731 if(number_of_commas == 0)
732 return;
733 if(number_of_commas > 64) {
734 /* would otherwise overflow the mount options buffer */
735 printf("\nInvalid password. Password contains too many commas.\n");
736 return;
739 new_pass_buf = (char *)malloc(len+number_of_commas+1);
740 if(new_pass_buf == NULL)
741 return;
743 for(i=0,j=0;i<len;i++,j++) {
744 new_pass_buf[j] = pass[i];
745 if(pass[i] == ',') {
746 j++;
747 new_pass_buf[j] = pass[i];
750 new_pass_buf[len+number_of_commas] = 0;
752 free(*ppasswrd);
753 *ppasswrd = new_pass_buf;
755 return;
758 /* Usernames can not have backslash in them and we use
759 [BB check if usernames can have forward slash in them BB]
760 backslash as domain\user separator character
762 static char * check_for_domain(char **ppuser)
764 char * original_string;
765 char * usernm;
766 char * domainnm;
767 int original_len;
768 int len;
769 int i;
771 if(ppuser == NULL)
772 return NULL;
774 original_string = *ppuser;
776 if (original_string == NULL)
777 return NULL;
779 original_len = strlen(original_string);
781 usernm = strchr(*ppuser,'/');
782 if (usernm == NULL) {
783 usernm = strchr(*ppuser,'\\');
784 if (usernm == NULL)
785 return NULL;
788 if(got_domain) {
789 printf("Domain name specified twice. Username probably malformed\n");
790 return NULL;
793 usernm[0] = 0;
794 domainnm = *ppuser;
795 if (domainnm[0] != 0) {
796 got_domain = 1;
797 } else {
798 printf("null domain\n");
800 len = strlen(domainnm);
801 /* reset domainm to new buffer, and copy
802 domain name into it */
803 domainnm = (char *)malloc(len+1);
804 if(domainnm == NULL)
805 return NULL;
807 strlcpy(domainnm,*ppuser,len+1);
809 /* move_string(*ppuser, usernm+1) */
810 len = strlen(usernm+1);
812 if(len >= original_len) {
813 /* should not happen */
814 return domainnm;
817 for(i=0;i<original_len;i++) {
818 if(i<len)
819 original_string[i] = usernm[i+1];
820 else /* stuff with commas to remove last parm */
821 original_string[i] = ',';
824 /* BB add check for more than one slash?
825 strchr(*ppuser,'/');
826 strchr(*ppuser,'\\')
829 return domainnm;
832 /* replace all occurances of "from" in a string with "to" */
833 static void replace_char(char *string, char from, char to)
835 while (string) {
836 string = strchr(string, from);
837 if (string)
838 *string = to;
842 /* Note that caller frees the returned buffer if necessary */
843 static char * parse_server(char ** punc_name)
845 char * unc_name = *punc_name;
846 int length = strnlen(unc_name, MAX_UNC_LEN);
847 char * share;
848 char * ipaddress_string = NULL;
849 struct hostent * host_entry = NULL;
850 struct in_addr server_ipaddr;
852 if(length > (MAX_UNC_LEN - 1)) {
853 printf("mount error: UNC name too long");
854 return NULL;
856 if (strncasecmp("cifs://",unc_name,7) == 0)
857 return parse_cifs_url(unc_name+7);
858 if (strncasecmp("smb://",unc_name,6) == 0) {
859 return parse_cifs_url(unc_name+6);
862 if(length < 3) {
863 /* BB add code to find DFS root here */
864 printf("\nMounting the DFS root for domain not implemented yet\n");
865 return NULL;
866 } else {
867 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
868 /* check for nfs syntax ie server:share */
869 share = strchr(unc_name,':');
870 if(share) {
871 *punc_name = (char *)malloc(length+3);
872 if(*punc_name == NULL) {
873 /* put the original string back if
874 no memory left */
875 *punc_name = unc_name;
876 return NULL;
878 *share = '/';
879 strncpy((*punc_name)+2,unc_name,length);
880 free(unc_name);
881 unc_name = *punc_name;
882 unc_name[length+2] = 0;
883 goto continue_unc_parsing;
884 } else {
885 printf("mount error: improperly formatted UNC name.");
886 printf(" %s does not begin with \\\\ or //\n",unc_name);
887 return NULL;
889 } else {
890 continue_unc_parsing:
891 unc_name[0] = '\\';
892 unc_name[1] = '\\';
893 unc_name += 2;
895 /* convert any '/' in unc to '\\' */
896 replace_char(unc_name, '/', '\\');
898 if ((share = strchr(unc_name,'\\'))) {
899 *share = 0; /* temporarily terminate the string */
900 share += 1;
901 if(got_ip == 0) {
902 host_entry = gethostbyname(unc_name);
904 *(share - 1) = '\\'; /* put delimiter back */
905 if ((prefixpath = strchr(share, '\\'))) {
906 *prefixpath = 0; /* permanently terminate the string */
907 if (!strlen(++prefixpath))
908 prefixpath = NULL; /* this needs to be done explicitly */
910 if(got_ip) {
911 if(verboseflag)
912 printf("ip address specified explicitly\n");
913 return NULL;
915 if(host_entry == NULL) {
916 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
917 return NULL;
918 } else {
919 /* BB should we pass an alternate version of the share name as Unicode */
920 /* BB what about ipv6? BB */
921 /* BB add retries with alternate servers in list */
923 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
925 ipaddress_string = inet_ntoa(server_ipaddr);
926 if(ipaddress_string == NULL) {
927 printf("mount error: could not get valid ip address for target server\n");
928 return NULL;
930 return ipaddress_string;
932 } else {
933 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
934 printf("Mounting the DFS root for a particular server not implemented yet\n");
935 return NULL;
941 static struct option longopts[] = {
942 { "all", 0, NULL, 'a' },
943 { "help",0, NULL, 'h' },
944 { "move",0, NULL, 'm' },
945 { "bind",0, NULL, 'b' },
946 { "read-only", 0, NULL, 'r' },
947 { "ro", 0, NULL, 'r' },
948 { "verbose", 0, NULL, 'v' },
949 { "version", 0, NULL, 'V' },
950 { "read-write", 0, NULL, 'w' },
951 { "rw", 0, NULL, 'w' },
952 { "options", 1, NULL, 'o' },
953 { "type", 1, NULL, 't' },
954 { "rsize",1, NULL, 'R' },
955 { "wsize",1, NULL, 'W' },
956 { "uid", 1, NULL, '1'},
957 { "gid", 1, NULL, '2'},
958 { "user",1,NULL,'u'},
959 { "username",1,NULL,'u'},
960 { "dom",1,NULL,'d'},
961 { "domain",1,NULL,'d'},
962 { "password",1,NULL,'p'},
963 { "pass",1,NULL,'p'},
964 { "credentials",1,NULL,'c'},
965 { "port",1,NULL,'P'},
966 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
967 { NULL, 0, NULL, 0 }
970 /* convert a string to uppercase. return false if the string
971 * wasn't ASCII or was a NULL ptr */
972 static int
973 uppercase_string(char *string)
975 if (!string)
976 return 0;
978 while (*string) {
979 /* check for unicode */
980 if ((unsigned char) string[0] & 0x80)
981 return 0;
982 *string = toupper((unsigned char) *string);
983 string++;
986 return 1;
989 int main(int argc, char ** argv)
991 int c;
992 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
993 char * orgoptions = NULL;
994 char * share_name = NULL;
995 char * ipaddr = NULL;
996 char * uuid = NULL;
997 char * mountpoint = NULL;
998 char * options = NULL;
999 char * resolved_path = NULL;
1000 char * temp;
1001 char * dev_name;
1002 int rc;
1003 int rsize = 0;
1004 int wsize = 0;
1005 int nomtab = 0;
1006 int uid = 0;
1007 int gid = 0;
1008 int optlen = 0;
1009 int orgoptlen = 0;
1010 size_t options_size = 0;
1011 int retry = 0; /* set when we have to retry mount with uppercase */
1012 struct stat statbuf;
1013 struct utsname sysinfo;
1014 struct mntent mountent;
1015 FILE * pmntfile;
1017 /* setlocale(LC_ALL, "");
1018 bindtextdomain(PACKAGE, LOCALEDIR);
1019 textdomain(PACKAGE); */
1021 if(argc && argv) {
1022 thisprogram = argv[0];
1023 } else {
1024 mount_cifs_usage();
1025 exit(1);
1028 if(thisprogram == NULL)
1029 thisprogram = "mount.cifs";
1031 uname(&sysinfo);
1032 /* BB add workstation name and domain and pass down */
1034 /* #ifdef _GNU_SOURCE
1035 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1036 #endif */
1037 if(argc > 2) {
1038 dev_name = argv[1];
1039 share_name = strndup(argv[1], MAX_UNC_LEN);
1040 if (share_name == NULL) {
1041 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1042 exit(1);
1044 mountpoint = argv[2];
1045 } else {
1046 mount_cifs_usage();
1047 exit(1);
1050 /* add sharename in opts string as unc= parm */
1052 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1053 longopts, NULL)) != -1) {
1054 switch (c) {
1055 /* No code to do the following options yet */
1056 /* case 'l':
1057 list_with_volumelabel = 1;
1058 break;
1059 case 'L':
1060 volumelabel = optarg;
1061 break; */
1062 /* case 'a':
1063 ++mount_all;
1064 break; */
1066 case '?':
1067 case 'h': /* help */
1068 mount_cifs_usage ();
1069 exit(1);
1070 case 'n':
1071 ++nomtab;
1072 break;
1073 case 'b':
1074 #ifdef MS_BIND
1075 flags |= MS_BIND;
1076 #else
1077 fprintf(stderr,
1078 "option 'b' (MS_BIND) not supported\n");
1079 #endif
1080 break;
1081 case 'm':
1082 #ifdef MS_MOVE
1083 flags |= MS_MOVE;
1084 #else
1085 fprintf(stderr,
1086 "option 'm' (MS_MOVE) not supported\n");
1087 #endif
1088 break;
1089 case 'o':
1090 orgoptions = strdup(optarg);
1091 break;
1092 case 'r': /* mount readonly */
1093 flags |= MS_RDONLY;
1094 break;
1095 case 'U':
1096 uuid = optarg;
1097 break;
1098 case 'v':
1099 ++verboseflag;
1100 break;
1101 case 'V':
1102 printf ("mount.cifs version: %s.%s%s\n",
1103 MOUNT_CIFS_VERSION_MAJOR,
1104 MOUNT_CIFS_VERSION_MINOR,
1105 MOUNT_CIFS_VENDOR_SUFFIX);
1106 if(mountpassword) {
1107 memset(mountpassword,0,64);
1109 exit (0);
1110 case 'w':
1111 flags &= ~MS_RDONLY;
1112 break;
1113 case 'R':
1114 rsize = atoi(optarg) ;
1115 break;
1116 case 'W':
1117 wsize = atoi(optarg);
1118 break;
1119 case '1':
1120 if (isdigit(*optarg)) {
1121 char *ep;
1123 uid = strtoul(optarg, &ep, 10);
1124 if (*ep) {
1125 printf("bad uid value \"%s\"\n", optarg);
1126 exit(1);
1128 } else {
1129 struct passwd *pw;
1131 if (!(pw = getpwnam(optarg))) {
1132 printf("bad user name \"%s\"\n", optarg);
1133 exit(1);
1135 uid = pw->pw_uid;
1136 endpwent();
1138 break;
1139 case '2':
1140 if (isdigit(*optarg)) {
1141 char *ep;
1143 gid = strtoul(optarg, &ep, 10);
1144 if (*ep) {
1145 printf("bad gid value \"%s\"\n", optarg);
1146 exit(1);
1148 } else {
1149 struct group *gr;
1151 if (!(gr = getgrnam(optarg))) {
1152 printf("bad user name \"%s\"\n", optarg);
1153 exit(1);
1155 gid = gr->gr_gid;
1156 endpwent();
1158 break;
1159 case 'u':
1160 got_user = 1;
1161 user_name = optarg;
1162 break;
1163 case 'd':
1164 domain_name = optarg; /* BB fix this - currently ignored */
1165 got_domain = 1;
1166 break;
1167 case 'p':
1168 if(mountpassword == NULL)
1169 mountpassword = (char *)calloc(65,1);
1170 if(mountpassword) {
1171 got_password = 1;
1172 strncpy(mountpassword,optarg,64);
1174 break;
1175 case 'S':
1176 get_password_from_file(0 /* stdin */,NULL);
1177 break;
1178 case 't':
1179 break;
1180 default:
1181 printf("unknown mount option %c\n",c);
1182 mount_cifs_usage();
1183 exit(1);
1187 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1188 mount_cifs_usage();
1189 exit(1);
1192 if (getenv("PASSWD")) {
1193 if(mountpassword == NULL)
1194 mountpassword = (char *)calloc(65,1);
1195 if(mountpassword) {
1196 strncpy(mountpassword,getenv("PASSWD"),64);
1197 got_password = 1;
1199 } else if (getenv("PASSWD_FD")) {
1200 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1201 } else if (getenv("PASSWD_FILE")) {
1202 get_password_from_file(0, getenv("PASSWD_FILE"));
1205 if (orgoptions && parse_options(&orgoptions, &flags)) {
1206 rc = -1;
1207 goto mount_exit;
1209 ipaddr = parse_server(&share_name);
1210 if((ipaddr == NULL) && (got_ip == 0)) {
1211 printf("No ip address specified and hostname not found\n");
1212 rc = -1;
1213 goto mount_exit;
1216 /* BB save off path and pop after mount returns? */
1217 resolved_path = (char *)malloc(PATH_MAX+1);
1218 if(resolved_path) {
1219 /* Note that if we can not canonicalize the name, we get
1220 another chance to see if it is valid when we chdir to it */
1221 if (realpath(mountpoint, resolved_path)) {
1222 mountpoint = resolved_path;
1225 if(chdir(mountpoint)) {
1226 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1227 rc = -1;
1228 goto mount_exit;
1231 if(stat (".", &statbuf)) {
1232 printf("mount error: mount point %s does not exist\n",mountpoint);
1233 rc = -1;
1234 goto mount_exit;
1237 if (S_ISDIR(statbuf.st_mode) == 0) {
1238 printf("mount error: mount point %s is not a directory\n",mountpoint);
1239 rc = -1;
1240 goto mount_exit;
1243 if((getuid() != 0) && (geteuid() == 0)) {
1244 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1245 #ifndef CIFS_ALLOW_USR_SUID
1246 /* Do not allow user mounts to control suid flag
1247 for mount unless explicitly built that way */
1248 flags |= MS_NOSUID | MS_NODEV;
1249 #endif
1250 } else {
1251 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1252 return -1;
1256 if(got_user == 0) {
1257 user_name = getusername();
1258 got_user = 1;
1261 if(got_password == 0) {
1262 mountpassword = getpass("Password: "); /* BB obsolete */
1263 got_password = 1;
1265 /* FIXME launch daemon (handles dfs name resolution and credential change)
1266 remember to clear parms and overwrite password field before launching */
1267 mount_retry:
1268 if(orgoptions) {
1269 optlen = strlen(orgoptions);
1270 orgoptlen = optlen;
1271 } else
1272 optlen = 0;
1273 if(share_name)
1274 optlen += strlen(share_name) + 4;
1275 else {
1276 printf("No server share name specified\n");
1277 printf("\nMounting the DFS root for server not implemented yet\n");
1278 exit(1);
1280 if(user_name)
1281 optlen += strlen(user_name) + 6;
1282 if(ipaddr)
1283 optlen += strlen(ipaddr) + 4;
1284 if(mountpassword)
1285 optlen += strlen(mountpassword) + 6;
1286 if(options)
1287 free(options);
1288 options_size = optlen + 10 + 64;
1289 options = (char *)malloc(options_size /* space for commas in password */ + 8 /* space for domain= , domain name itself was counted as part of the length username string above */);
1291 if(options == NULL) {
1292 printf("Could not allocate memory for mount options\n");
1293 return -1;
1296 options[0] = 0;
1297 strlcpy(options,"unc=",options_size);
1298 strlcat(options,share_name,options_size);
1299 /* scan backwards and reverse direction of slash */
1300 temp = strrchr(options, '/');
1301 if(temp > options + 6)
1302 *temp = '\\';
1303 if(ipaddr) {
1304 strlcat(options,",ip=",options_size);
1305 strlcat(options,ipaddr,options_size);
1308 if(user_name) {
1309 /* check for syntax like user=domain\user */
1310 if(got_domain == 0)
1311 domain_name = check_for_domain(&user_name);
1312 strlcat(options,",user=",options_size);
1313 strlcat(options,user_name,options_size);
1315 if(retry == 0) {
1316 if(domain_name) {
1317 /* extra length accounted for in option string above */
1318 strlcat(options,",domain=",options_size);
1319 strlcat(options,domain_name,options_size);
1322 if(mountpassword) {
1323 /* Commas have to be doubled, or else they will
1324 look like the parameter separator */
1325 /* if(sep is not set)*/
1326 if(retry == 0)
1327 check_for_comma(&mountpassword);
1328 strlcat(options,",pass=",options_size);
1329 strlcat(options,mountpassword,options_size);
1332 strlcat(options,",ver=",options_size);
1333 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1335 if(orgoptions) {
1336 strlcat(options,",",options_size);
1337 strlcat(options,orgoptions,options_size);
1339 if(prefixpath) {
1340 strlcat(options,",prefixpath=",options_size);
1341 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1343 if(verboseflag)
1344 printf("\nmount.cifs kernel mount options %s \n",options);
1346 /* convert all '\\' to '/' so that /proc/mounts looks pretty */
1347 replace_char(dev_name, '\\', '/');
1349 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1350 /* remember to kill daemon on error */
1351 switch (errno) {
1352 case 0:
1353 printf("mount failed but no error number set\n");
1354 break;
1355 case ENODEV:
1356 printf("mount error: cifs filesystem not supported by the system\n");
1357 break;
1358 case ENXIO:
1359 if(retry == 0) {
1360 retry = 1;
1361 if (uppercase_string(dev_name) &&
1362 uppercase_string(share_name) &&
1363 uppercase_string(prefixpath)) {
1364 printf("retrying with upper case share name\n");
1365 goto mount_retry;
1368 default:
1369 printf("mount error %d = %s\n",errno,strerror(errno));
1371 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1372 rc = -1;
1373 goto mount_exit;
1374 } else {
1375 pmntfile = setmntent(MOUNTED, "a+");
1376 if(pmntfile) {
1377 mountent.mnt_fsname = dev_name;
1378 mountent.mnt_dir = mountpoint;
1379 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1380 mountent.mnt_opts = (char *)malloc(220);
1381 if(mountent.mnt_opts) {
1382 char * mount_user = getusername();
1383 memset(mountent.mnt_opts,0,200);
1384 if(flags & MS_RDONLY)
1385 strlcat(mountent.mnt_opts,"ro",220);
1386 else
1387 strlcat(mountent.mnt_opts,"rw",220);
1388 if(flags & MS_MANDLOCK)
1389 strlcat(mountent.mnt_opts,",mand",220);
1390 if(flags & MS_NOEXEC)
1391 strlcat(mountent.mnt_opts,",noexec",220);
1392 if(flags & MS_NOSUID)
1393 strlcat(mountent.mnt_opts,",nosuid",220);
1394 if(flags & MS_NODEV)
1395 strlcat(mountent.mnt_opts,",nodev",220);
1396 if(flags & MS_SYNCHRONOUS)
1397 strlcat(mountent.mnt_opts,",synch",220);
1398 if(mount_user) {
1399 if(getuid() != 0) {
1400 strlcat(mountent.mnt_opts,",user=",220);
1401 strlcat(mountent.mnt_opts,mount_user,220);
1403 /* free(mount_user); do not free static mem */
1406 mountent.mnt_freq = 0;
1407 mountent.mnt_passno = 0;
1408 rc = addmntent(pmntfile,&mountent);
1409 endmntent(pmntfile);
1410 if(mountent.mnt_opts)
1411 free(mountent.mnt_opts);
1412 } else {
1413 printf("could not update mount table\n");
1416 rc = 0;
1417 mount_exit:
1418 if(mountpassword) {
1419 int len = strlen(mountpassword);
1420 memset(mountpassword,0,len);
1421 free(mountpassword);
1424 if(options) {
1425 memset(options,0,optlen);
1426 free(options);
1429 if(orgoptions) {
1430 memset(orgoptions,0,orgoptlen);
1431 free(orgoptions);
1433 if(resolved_path) {
1434 free(resolved_path);
1437 free(share_name);
1438 return rc;