WHATSNEW: Update changes since 3.3.0pre2.
[Samba/bb.git] / source / client / mount.cifs.c
blobfd8014cf9ff7c5248cb955a7f02b9d9c32bce7c3
1 /*
2 Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3 Copyright (C) 2003,2008 Steve French (sfrench@us.ibm.com)
4 Copyright (C) 2008 Jeremy Allison (jra@samba.org)
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/mount.h>
31 #include <sys/stat.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <getopt.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <mntent.h>
40 #include <fcntl.h>
41 #include <limits.h>
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
47 #ifdef _SAMBA_BUILD_
48 #include "include/version.h"
49 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
51 #else
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
54 #else
55 #define MOUNT_CIFS_VENDOR_SUFFIX ""
56 #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
59 #ifdef _SAMBA_BUILD_
60 #include "include/config.h"
61 #endif
63 #ifndef MS_MOVE
64 #define MS_MOVE 8192
65 #endif
67 #ifndef MS_BIND
68 #define MS_BIND 4096
69 #endif
71 #define MAX_UNC_LEN 1024
73 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
75 #ifndef SAFE_FREE
76 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
77 #endif
79 #define MOUNT_PASSWD_SIZE 64
80 #define DOMAIN_SIZE 64
82 /* exit status - bits below are ORed */
83 #define EX_USAGE 1 /* incorrect invocation or permission */
84 #define EX_SYSERR 2 /* out of memory, cannot fork, ... */
85 #define EX_SOFTWARE 4 /* internal mount bug or wrong version */
86 #define EX_USER 8 /* user interrupt */
87 #define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */
88 #define EX_FAIL 32 /* mount failure */
89 #define EX_SOMEOK 64 /* some mount succeeded */
91 const char *thisprogram;
92 int verboseflag = 0;
93 static int got_password = 0;
94 static int got_user = 0;
95 static int got_domain = 0;
96 static int got_ip = 0;
97 static int got_unc = 0;
98 static int got_uid = 0;
99 static int got_gid = 0;
100 static char * user_name = NULL;
101 static char * mountpassword = NULL;
102 char * domain_name = NULL;
103 char * prefixpath = NULL;
105 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
106 * don't link to libreplace so need them here. */
108 /* like strncpy but does not 0 fill the buffer and always null
109 * terminates. bufsize is the size of the destination buffer */
111 #ifndef HAVE_STRLCPY
112 static size_t strlcpy(char *d, const char *s, size_t bufsize)
114 size_t len = strlen(s);
115 size_t ret = len;
116 if (bufsize <= 0) return 0;
117 if (len >= bufsize) len = bufsize-1;
118 memcpy(d, s, len);
119 d[len] = 0;
120 return ret;
122 #endif
124 /* like strncat but does not 0 fill the buffer and always null
125 * terminates. bufsize is the length of the buffer, which should
126 * be one more than the maximum resulting string length */
128 #ifndef HAVE_STRLCAT
129 static size_t strlcat(char *d, const char *s, size_t bufsize)
131 size_t len1 = strlen(d);
132 size_t len2 = strlen(s);
133 size_t ret = len1 + len2;
135 if (len1+len2 >= bufsize) {
136 if (bufsize < (len1+1)) {
137 return ret;
139 len2 = bufsize - (len1+1);
141 if (len2 > 0) {
142 memcpy(d+len1, s, len2);
143 d[len1+len2] = 0;
145 return ret;
147 #endif
149 /* BB finish BB
151 cifs_umount
152 open nofollow - avoid symlink exposure?
153 get owner of dir see if matches self or if root
154 call system(umount argv) etc.
156 BB end finish BB */
158 static char * check_for_domain(char **);
161 static void mount_cifs_usage(void)
163 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
164 printf("\nMount the remote target, specified as a UNC name,");
165 printf(" to a local directory.\n\nOptions:\n");
166 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
167 printf("\nLess commonly used options:");
168 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
169 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
170 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
171 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
172 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
173 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
174 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
175 printf("\n\nRarely used options:");
176 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
177 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
178 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
179 printf("\n\tin6_addr");
180 printf("\n\nOptions are described in more detail in the manual page");
181 printf("\n\tman 8 mount.cifs\n");
182 printf("\nTo display the version number of the mount helper:");
183 printf("\n\t%s -V\n",thisprogram);
185 SAFE_FREE(mountpassword);
186 exit(EX_USAGE);
189 /* caller frees username if necessary */
190 static char * getusername(void) {
191 char *username = NULL;
192 struct passwd *password = getpwuid(getuid());
194 if (password) {
195 username = password->pw_name;
197 return username;
200 static char * parse_cifs_url(char * unc_name)
202 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
203 return NULL;
206 static int open_cred_file(char * file_name)
208 char * line_buf;
209 char * temp_val;
210 FILE * fs;
211 int i, length;
212 fs = fopen(file_name,"r");
213 if(fs == NULL)
214 return errno;
215 line_buf = (char *)malloc(4096);
216 if(line_buf == NULL) {
217 fclose(fs);
218 return ENOMEM;
221 while(fgets(line_buf,4096,fs)) {
222 /* parse line from credential file */
224 /* eat leading white space */
225 for(i=0;i<4086;i++) {
226 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
227 break;
228 /* if whitespace - skip past it */
230 if (strncasecmp("username",line_buf+i,8) == 0) {
231 temp_val = strchr(line_buf + i,'=');
232 if(temp_val) {
233 /* go past equals sign */
234 temp_val++;
235 for(length = 0;length<4087;length++) {
236 if ((temp_val[length] == '\n')
237 || (temp_val[length] == '\0')) {
238 temp_val[length] = '\0';
239 break;
242 if(length > 4086) {
243 printf("mount.cifs failed due to malformed username in credentials file");
244 memset(line_buf,0,4096);
245 exit(EX_USAGE);
246 } else {
247 got_user = 1;
248 user_name = (char *)calloc(1 + length,1);
249 /* BB adding free of user_name string before exit,
250 not really necessary but would be cleaner */
251 strlcpy(user_name,temp_val, length+1);
254 } else if (strncasecmp("password",line_buf+i,8) == 0) {
255 temp_val = strchr(line_buf+i,'=');
256 if(temp_val) {
257 /* go past equals sign */
258 temp_val++;
259 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
260 if ((temp_val[length] == '\n')
261 || (temp_val[length] == '\0')) {
262 temp_val[length] = '\0';
263 break;
266 if(length > MOUNT_PASSWD_SIZE) {
267 printf("mount.cifs failed: password in credentials file too long\n");
268 memset(line_buf,0, 4096);
269 exit(EX_USAGE);
270 } else {
271 if(mountpassword == NULL) {
272 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
273 } else
274 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
275 if(mountpassword) {
276 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
277 got_password = 1;
281 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
282 temp_val = strchr(line_buf+i,'=');
283 if(temp_val) {
284 /* go past equals sign */
285 temp_val++;
286 if(verboseflag)
287 printf("\nDomain %s\n",temp_val);
288 for(length = 0;length<DOMAIN_SIZE+1;length++) {
289 if ((temp_val[length] == '\n')
290 || (temp_val[length] == '\0')) {
291 temp_val[length] = '\0';
292 break;
295 if(length > DOMAIN_SIZE) {
296 printf("mount.cifs failed: domain in credentials file too long\n");
297 exit(EX_USAGE);
298 } else {
299 if(domain_name == NULL) {
300 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
301 } else
302 memset(domain_name,0,DOMAIN_SIZE);
303 if(domain_name) {
304 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
305 got_domain = 1;
312 fclose(fs);
313 SAFE_FREE(line_buf);
314 return 0;
317 static int get_password_from_file(int file_descript, char * filename)
319 int rc = 0;
320 int i;
321 char c;
323 if(mountpassword == NULL)
324 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
325 else
326 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
328 if (mountpassword == NULL) {
329 printf("malloc failed\n");
330 exit(EX_SYSERR);
333 if(filename != NULL) {
334 file_descript = open(filename, O_RDONLY);
335 if(file_descript < 0) {
336 printf("mount.cifs failed. %s attempting to open password file %s\n",
337 strerror(errno),filename);
338 exit(EX_SYSERR);
341 /* else file already open and fd provided */
343 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
344 rc = read(file_descript,&c,1);
345 if(rc < 0) {
346 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
347 if(filename != NULL)
348 close(file_descript);
349 exit(EX_SYSERR);
350 } else if(rc == 0) {
351 if(mountpassword[0] == 0) {
352 if(verboseflag)
353 printf("\nWarning: null password used since cifs password file empty");
355 break;
356 } else /* read valid character */ {
357 if((c == 0) || (c == '\n')) {
358 mountpassword[i] = '\0';
359 break;
360 } else
361 mountpassword[i] = c;
364 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
365 printf("\nWarning: password longer than %d characters specified in cifs password file",
366 MOUNT_PASSWD_SIZE);
368 got_password = 1;
369 if(filename != NULL) {
370 close(file_descript);
373 return rc;
376 static int parse_options(char ** optionsp, int * filesys_flags)
378 const char * data;
379 char * percent_char = NULL;
380 char * value = NULL;
381 char * next_keyword = NULL;
382 char * out = NULL;
383 int out_len = 0;
384 int word_len;
385 int rc = 0;
386 char user[32];
387 char group[32];
389 if (!optionsp || !*optionsp)
390 return 1;
391 data = *optionsp;
393 if(verboseflag)
394 printf("parsing options: %s\n", data);
396 /* BB fixme check for separator override BB */
398 if (getuid()) {
399 got_uid = 1;
400 snprintf(user,sizeof(user),"%u",getuid());
401 got_gid = 1;
402 snprintf(group,sizeof(group),"%u",getgid());
405 /* while ((data = strsep(&options, ",")) != NULL) { */
406 while(data != NULL) {
407 /* check if ends with trailing comma */
408 if(*data == 0)
409 break;
411 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
412 /* data = next keyword */
413 /* value = next value ie stuff after equal sign */
415 next_keyword = strchr(data,','); /* BB handle sep= */
417 /* temporarily null terminate end of keyword=value pair */
418 if(next_keyword)
419 *next_keyword++ = 0;
421 /* temporarily null terminate keyword to make keyword and value distinct */
422 if ((value = strchr(data, '=')) != NULL) {
423 *value = '\0';
424 value++;
427 if (strncmp(data, "users",5) == 0) {
428 if(!value || !*value) {
429 goto nocopy;
431 } else if (strncmp(data, "user_xattr",10) == 0) {
432 /* do nothing - need to skip so not parsed as user name */
433 } else if (strncmp(data, "user", 4) == 0) {
435 if (!value || !*value) {
436 if(data[4] == '\0') {
437 if(verboseflag)
438 printf("\nskipping empty user mount parameter\n");
439 /* remove the parm since it would otherwise be confusing
440 to the kernel code which would think it was a real username */
441 goto nocopy;
442 } else {
443 printf("username specified with no parameter\n");
444 return 1; /* needs_arg; */
446 } else {
447 if (strnlen(value, 260) < 260) {
448 got_user=1;
449 percent_char = strchr(value,'%');
450 if(percent_char) {
451 *percent_char = ',';
452 if(mountpassword == NULL)
453 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
454 if(mountpassword) {
455 if(got_password)
456 printf("\nmount.cifs warning - password specified twice\n");
457 got_password = 1;
458 percent_char++;
459 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
460 /* remove password from username */
461 while(*percent_char != 0) {
462 *percent_char = ',';
463 percent_char++;
467 /* this is only case in which the user
468 name buf is not malloc - so we have to
469 check for domain name embedded within
470 the user name here since the later
471 call to check_for_domain will not be
472 invoked */
473 domain_name = check_for_domain(&value);
474 } else {
475 printf("username too long\n");
476 return 1;
479 } else if (strncmp(data, "pass", 4) == 0) {
480 if (!value || !*value) {
481 if(got_password) {
482 printf("\npassword specified twice, ignoring second\n");
483 } else
484 got_password = 1;
485 } else if (strnlen(value, 17) < 17) {
486 if(got_password)
487 printf("\nmount.cifs warning - password specified twice\n");
488 got_password = 1;
489 } else {
490 printf("password too long\n");
491 return 1;
493 } else if (strncmp(data, "sec", 3) == 0) {
494 if (value) {
495 if (!strncmp(value, "none", 4) ||
496 !strncmp(value, "krb5", 4))
497 got_password = 1;
499 } else if (strncmp(data, "ip", 2) == 0) {
500 if (!value || !*value) {
501 printf("target ip address argument missing");
502 } else if (strnlen(value, 35) < 35) {
503 if(verboseflag)
504 printf("ip address %s override specified\n",value);
505 got_ip = 1;
506 } else {
507 printf("ip address too long\n");
508 return 1;
510 } else if ((strncmp(data, "unc", 3) == 0)
511 || (strncmp(data, "target", 6) == 0)
512 || (strncmp(data, "path", 4) == 0)) {
513 if (!value || !*value) {
514 printf("invalid path to network resource\n");
515 return 1; /* needs_arg; */
516 } else if(strnlen(value,5) < 5) {
517 printf("UNC name too short");
520 if (strnlen(value, 300) < 300) {
521 got_unc = 1;
522 if (strncmp(value, "//", 2) == 0) {
523 if(got_unc)
524 printf("unc name specified twice, ignoring second\n");
525 else
526 got_unc = 1;
527 } else if (strncmp(value, "\\\\", 2) != 0) {
528 printf("UNC Path does not begin with // or \\\\ \n");
529 return 1;
530 } else {
531 if(got_unc)
532 printf("unc name specified twice, ignoring second\n");
533 else
534 got_unc = 1;
536 } else {
537 printf("CIFS: UNC name too long\n");
538 return 1;
540 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
541 || (strncmp(data, "workg", 5) == 0)) {
542 /* note this allows for synonyms of "domain"
543 such as "DOM" and "dom" and "workgroup"
544 and "WORKGRP" etc. */
545 if (!value || !*value) {
546 printf("CIFS: invalid domain name\n");
547 return 1; /* needs_arg; */
549 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
550 got_domain = 1;
551 } else {
552 printf("domain name too long\n");
553 return 1;
555 } else if (strncmp(data, "cred", 4) == 0) {
556 if (value && *value) {
557 rc = open_cred_file(value);
558 if(rc) {
559 printf("error %d (%s) opening credential file %s\n",
560 rc, strerror(rc), value);
561 return 1;
563 } else {
564 printf("invalid credential file name specified\n");
565 return 1;
567 } else if (strncmp(data, "uid", 3) == 0) {
568 if (value && *value) {
569 got_uid = 1;
570 if (!isdigit(*value)) {
571 struct passwd *pw;
573 if (!(pw = getpwnam(value))) {
574 printf("bad user name \"%s\"\n", value);
575 exit(EX_USAGE);
577 snprintf(user, sizeof(user), "%u", pw->pw_uid);
578 } else {
579 strlcpy(user,value,sizeof(user));
582 goto nocopy;
583 } else if (strncmp(data, "gid", 3) == 0) {
584 if (value && *value) {
585 got_gid = 1;
586 if (!isdigit(*value)) {
587 struct group *gr;
589 if (!(gr = getgrnam(value))) {
590 printf("bad group name \"%s\"\n", value);
591 exit(EX_USAGE);
593 snprintf(group, sizeof(group), "%u", gr->gr_gid);
594 } else {
595 strlcpy(group,value,sizeof(group));
598 goto nocopy;
599 /* fmask and dmask synonyms for people used to smbfs syntax */
600 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
601 if (!value || !*value) {
602 printf ("Option '%s' requires a numerical argument\n", data);
603 return 1;
606 if (value[0] != '0') {
607 printf ("WARNING: '%s' not expressed in octal.\n", data);
610 if (strcmp (data, "fmask") == 0) {
611 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
612 data = "file_mode"; /* BB fix this */
614 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
615 if (!value || !*value) {
616 printf ("Option '%s' requires a numerical argument\n", data);
617 return 1;
620 if (value[0] != '0') {
621 printf ("WARNING: '%s' not expressed in octal.\n", data);
624 if (strcmp (data, "dmask") == 0) {
625 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
626 data = "dir_mode";
628 /* the following eight mount options should be
629 stripped out from what is passed into the kernel
630 since these eight options are best passed as the
631 mount flags rather than redundantly to the kernel
632 and could generate spurious warnings depending on the
633 level of the corresponding cifs vfs kernel code */
634 } else if (strncmp(data, "nosuid", 6) == 0) {
635 *filesys_flags |= MS_NOSUID;
636 } else if (strncmp(data, "suid", 4) == 0) {
637 *filesys_flags &= ~MS_NOSUID;
638 } else if (strncmp(data, "nodev", 5) == 0) {
639 *filesys_flags |= MS_NODEV;
640 } else if ((strncmp(data, "nobrl", 5) == 0) ||
641 (strncmp(data, "nolock", 6) == 0)) {
642 *filesys_flags &= ~MS_MANDLOCK;
643 } else if (strncmp(data, "dev", 3) == 0) {
644 *filesys_flags &= ~MS_NODEV;
645 } else if (strncmp(data, "noexec", 6) == 0) {
646 *filesys_flags |= MS_NOEXEC;
647 } else if (strncmp(data, "exec", 4) == 0) {
648 *filesys_flags &= ~MS_NOEXEC;
649 } else if (strncmp(data, "guest", 5) == 0) {
650 got_password=1;
651 } else if (strncmp(data, "ro", 2) == 0) {
652 *filesys_flags |= MS_RDONLY;
653 } else if (strncmp(data, "rw", 2) == 0) {
654 *filesys_flags &= ~MS_RDONLY;
655 } else if (strncmp(data, "remount", 7) == 0) {
656 *filesys_flags |= MS_REMOUNT;
657 } /* else if (strnicmp(data, "port", 4) == 0) {
658 if (value && *value) {
659 vol->port =
660 simple_strtoul(value, &value, 0);
662 } else if (strnicmp(data, "rsize", 5) == 0) {
663 if (value && *value) {
664 vol->rsize =
665 simple_strtoul(value, &value, 0);
667 } else if (strnicmp(data, "wsize", 5) == 0) {
668 if (value && *value) {
669 vol->wsize =
670 simple_strtoul(value, &value, 0);
672 } else if (strnicmp(data, "version", 3) == 0) {
673 } else {
674 printf("CIFS: Unknown mount option %s\n",data);
675 } */ /* nothing to do on those four mount options above.
676 Just pass to kernel and ignore them here */
678 /* Copy (possibly modified) option to out */
679 word_len = strlen(data);
680 if (value)
681 word_len += 1 + strlen(value);
683 out = (char *)realloc(out, out_len + word_len + 2);
684 if (out == NULL) {
685 perror("malloc");
686 exit(EX_SYSERR);
689 if (out_len) {
690 strlcat(out, ",", out_len + word_len + 2);
691 out_len++;
694 if (value)
695 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
696 else
697 snprintf(out + out_len, word_len + 1, "%s", data);
698 out_len = strlen(out);
700 nocopy:
701 data = next_keyword;
704 /* special-case the uid and gid */
705 if (got_uid) {
706 word_len = strlen(user);
708 out = (char *)realloc(out, out_len + word_len + 6);
709 if (out == NULL) {
710 perror("malloc");
711 exit(EX_SYSERR);
714 if (out_len) {
715 strlcat(out, ",", out_len + word_len + 6);
716 out_len++;
718 snprintf(out + out_len, word_len + 5, "uid=%s", user);
719 out_len = strlen(out);
721 if (got_gid) {
722 word_len = strlen(group);
724 out = (char *)realloc(out, out_len + 1 + word_len + 6);
725 if (out == NULL) {
726 perror("malloc");
727 exit(EX_SYSERR);
730 if (out_len) {
731 strlcat(out, ",", out_len + word_len + 6);
732 out_len++;
734 snprintf(out + out_len, word_len + 5, "gid=%s", group);
735 out_len = strlen(out);
738 SAFE_FREE(*optionsp);
739 *optionsp = out;
740 return 0;
743 /* replace all (one or more) commas with double commas */
744 static void check_for_comma(char ** ppasswrd)
746 char *new_pass_buf;
747 char *pass;
748 int i,j;
749 int number_of_commas = 0;
750 int len;
752 if(ppasswrd == NULL)
753 return;
754 else
755 (pass = *ppasswrd);
757 len = strlen(pass);
759 for(i=0;i<len;i++) {
760 if(pass[i] == ',')
761 number_of_commas++;
764 if(number_of_commas == 0)
765 return;
766 if(number_of_commas > MOUNT_PASSWD_SIZE) {
767 /* would otherwise overflow the mount options buffer */
768 printf("\nInvalid password. Password contains too many commas.\n");
769 return;
772 new_pass_buf = (char *)malloc(len+number_of_commas+1);
773 if(new_pass_buf == NULL)
774 return;
776 for(i=0,j=0;i<len;i++,j++) {
777 new_pass_buf[j] = pass[i];
778 if(pass[i] == ',') {
779 j++;
780 new_pass_buf[j] = pass[i];
783 new_pass_buf[len+number_of_commas] = 0;
785 SAFE_FREE(*ppasswrd);
786 *ppasswrd = new_pass_buf;
788 return;
791 /* Usernames can not have backslash in them and we use
792 [BB check if usernames can have forward slash in them BB]
793 backslash as domain\user separator character
795 static char * check_for_domain(char **ppuser)
797 char * original_string;
798 char * usernm;
799 char * domainnm;
800 int original_len;
801 int len;
802 int i;
804 if(ppuser == NULL)
805 return NULL;
807 original_string = *ppuser;
809 if (original_string == NULL)
810 return NULL;
812 original_len = strlen(original_string);
814 usernm = strchr(*ppuser,'/');
815 if (usernm == NULL) {
816 usernm = strchr(*ppuser,'\\');
817 if (usernm == NULL)
818 return NULL;
821 if(got_domain) {
822 printf("Domain name specified twice. Username probably malformed\n");
823 return NULL;
826 usernm[0] = 0;
827 domainnm = *ppuser;
828 if (domainnm[0] != 0) {
829 got_domain = 1;
830 } else {
831 printf("null domain\n");
833 len = strlen(domainnm);
834 /* reset domainm to new buffer, and copy
835 domain name into it */
836 domainnm = (char *)malloc(len+1);
837 if(domainnm == NULL)
838 return NULL;
840 strlcpy(domainnm,*ppuser,len+1);
842 /* move_string(*ppuser, usernm+1) */
843 len = strlen(usernm+1);
845 if(len >= original_len) {
846 /* should not happen */
847 return domainnm;
850 for(i=0;i<original_len;i++) {
851 if(i<len)
852 original_string[i] = usernm[i+1];
853 else /* stuff with commas to remove last parm */
854 original_string[i] = ',';
857 /* BB add check for more than one slash?
858 strchr(*ppuser,'/');
859 strchr(*ppuser,'\\')
862 return domainnm;
865 /* replace all occurances of "from" in a string with "to" */
866 static void replace_char(char *string, char from, char to, int maxlen)
868 char *lastchar = string + maxlen;
869 while (string) {
870 string = strchr(string, from);
871 if (string) {
872 *string = to;
873 if (string >= lastchar)
874 return;
879 /* Note that caller frees the returned buffer if necessary */
880 static char * parse_server(char ** punc_name)
882 char * unc_name = *punc_name;
883 int length = strnlen(unc_name, MAX_UNC_LEN);
884 char * share;
885 char * ipaddress_string = NULL;
886 struct hostent * host_entry = NULL;
887 struct in_addr server_ipaddr;
889 if(length > (MAX_UNC_LEN - 1)) {
890 printf("mount error: UNC name too long");
891 return NULL;
893 if (strncasecmp("cifs://",unc_name,7) == 0)
894 return parse_cifs_url(unc_name+7);
895 if (strncasecmp("smb://",unc_name,6) == 0) {
896 return parse_cifs_url(unc_name+6);
899 if(length < 3) {
900 /* BB add code to find DFS root here */
901 printf("\nMounting the DFS root for domain not implemented yet\n");
902 return NULL;
903 } else {
904 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
905 /* check for nfs syntax ie server:share */
906 share = strchr(unc_name,':');
907 if(share) {
908 *punc_name = (char *)malloc(length+3);
909 if(*punc_name == NULL) {
910 /* put the original string back if
911 no memory left */
912 *punc_name = unc_name;
913 return NULL;
915 *share = '/';
916 strlcpy((*punc_name)+2,unc_name,length+1);
917 SAFE_FREE(unc_name);
918 unc_name = *punc_name;
919 unc_name[length+2] = 0;
920 goto continue_unc_parsing;
921 } else {
922 printf("mount error: improperly formatted UNC name.");
923 printf(" %s does not begin with \\\\ or //\n",unc_name);
924 return NULL;
926 } else {
927 continue_unc_parsing:
928 unc_name[0] = '/';
929 unc_name[1] = '/';
930 unc_name += 2;
932 /* allow for either delimiter between host and sharename */
933 if ((share = strpbrk(unc_name, "/\\"))) {
934 *share = 0; /* temporarily terminate the string */
935 share += 1;
936 if(got_ip == 0) {
937 host_entry = gethostbyname(unc_name);
939 *(share - 1) = '/'; /* put delimiter back */
941 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
942 if ((prefixpath = strpbrk(share, "/\\"))) {
943 *prefixpath = 0; /* permanently terminate the string */
944 if (!strlen(++prefixpath))
945 prefixpath = NULL; /* this needs to be done explicitly */
947 if(got_ip) {
948 if(verboseflag)
949 printf("ip address specified explicitly\n");
950 return NULL;
952 if(host_entry == NULL) {
953 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
954 return NULL;
955 } else {
956 /* BB should we pass an alternate version of the share name as Unicode */
957 /* BB what about ipv6? BB */
958 /* BB add retries with alternate servers in list */
960 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
962 ipaddress_string = inet_ntoa(server_ipaddr);
963 if(ipaddress_string == NULL) {
964 printf("mount error: could not get valid ip address for target server\n");
965 return NULL;
967 return ipaddress_string;
969 } else {
970 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
971 printf("Mounting the DFS root for a particular server not implemented yet\n");
972 return NULL;
978 static struct option longopts[] = {
979 { "all", 0, NULL, 'a' },
980 { "help",0, NULL, 'h' },
981 { "move",0, NULL, 'm' },
982 { "bind",0, NULL, 'b' },
983 { "read-only", 0, NULL, 'r' },
984 { "ro", 0, NULL, 'r' },
985 { "verbose", 0, NULL, 'v' },
986 { "version", 0, NULL, 'V' },
987 { "read-write", 0, NULL, 'w' },
988 { "rw", 0, NULL, 'w' },
989 { "options", 1, NULL, 'o' },
990 { "type", 1, NULL, 't' },
991 { "rsize",1, NULL, 'R' },
992 { "wsize",1, NULL, 'W' },
993 { "uid", 1, NULL, '1'},
994 { "gid", 1, NULL, '2'},
995 { "user",1,NULL,'u'},
996 { "username",1,NULL,'u'},
997 { "dom",1,NULL,'d'},
998 { "domain",1,NULL,'d'},
999 { "password",1,NULL,'p'},
1000 { "pass",1,NULL,'p'},
1001 { "credentials",1,NULL,'c'},
1002 { "port",1,NULL,'P'},
1003 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1004 { NULL, 0, NULL, 0 }
1007 /* convert a string to uppercase. return false if the string
1008 * wasn't ASCII. Return success on a NULL ptr */
1009 static int
1010 uppercase_string(char *string)
1012 if (!string)
1013 return 1;
1015 while (*string) {
1016 /* check for unicode */
1017 if ((unsigned char) string[0] & 0x80)
1018 return 0;
1019 *string = toupper((unsigned char) *string);
1020 string++;
1023 return 1;
1026 int main(int argc, char ** argv)
1028 int c;
1029 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1030 char * orgoptions = NULL;
1031 char * share_name = NULL;
1032 char * ipaddr = NULL;
1033 char * uuid = NULL;
1034 char * mountpoint = NULL;
1035 char * options = NULL;
1036 char * resolved_path = NULL;
1037 char * temp;
1038 char * dev_name;
1039 int rc;
1040 int rsize = 0;
1041 int wsize = 0;
1042 int nomtab = 0;
1043 int uid = 0;
1044 int gid = 0;
1045 int optlen = 0;
1046 int orgoptlen = 0;
1047 size_t options_size = 0;
1048 int retry = 0; /* set when we have to retry mount with uppercase */
1049 struct stat statbuf;
1050 struct utsname sysinfo;
1051 struct mntent mountent;
1052 FILE * pmntfile;
1054 /* setlocale(LC_ALL, "");
1055 bindtextdomain(PACKAGE, LOCALEDIR);
1056 textdomain(PACKAGE); */
1058 if(argc && argv) {
1059 thisprogram = argv[0];
1060 } else {
1061 mount_cifs_usage();
1062 exit(EX_USAGE);
1065 if(thisprogram == NULL)
1066 thisprogram = "mount.cifs";
1068 uname(&sysinfo);
1069 /* BB add workstation name and domain and pass down */
1071 /* #ifdef _GNU_SOURCE
1072 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1073 #endif */
1074 if(argc > 2) {
1075 dev_name = argv[1];
1076 share_name = strndup(argv[1], MAX_UNC_LEN);
1077 if (share_name == NULL) {
1078 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1079 exit(EX_SYSERR);
1081 mountpoint = argv[2];
1082 } else {
1083 mount_cifs_usage();
1084 exit(EX_USAGE);
1087 /* add sharename in opts string as unc= parm */
1089 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1090 longopts, NULL)) != -1) {
1091 switch (c) {
1092 /* No code to do the following options yet */
1093 /* case 'l':
1094 list_with_volumelabel = 1;
1095 break;
1096 case 'L':
1097 volumelabel = optarg;
1098 break; */
1099 /* case 'a':
1100 ++mount_all;
1101 break; */
1103 case '?':
1104 case 'h': /* help */
1105 mount_cifs_usage ();
1106 exit(EX_USAGE);
1107 case 'n':
1108 ++nomtab;
1109 break;
1110 case 'b':
1111 #ifdef MS_BIND
1112 flags |= MS_BIND;
1113 #else
1114 fprintf(stderr,
1115 "option 'b' (MS_BIND) not supported\n");
1116 #endif
1117 break;
1118 case 'm':
1119 #ifdef MS_MOVE
1120 flags |= MS_MOVE;
1121 #else
1122 fprintf(stderr,
1123 "option 'm' (MS_MOVE) not supported\n");
1124 #endif
1125 break;
1126 case 'o':
1127 orgoptions = strdup(optarg);
1128 break;
1129 case 'r': /* mount readonly */
1130 flags |= MS_RDONLY;
1131 break;
1132 case 'U':
1133 uuid = optarg;
1134 break;
1135 case 'v':
1136 ++verboseflag;
1137 break;
1138 case 'V':
1139 printf ("mount.cifs version: %s.%s%s\n",
1140 MOUNT_CIFS_VERSION_MAJOR,
1141 MOUNT_CIFS_VERSION_MINOR,
1142 MOUNT_CIFS_VENDOR_SUFFIX);
1143 exit (0);
1144 case 'w':
1145 flags &= ~MS_RDONLY;
1146 break;
1147 case 'R':
1148 rsize = atoi(optarg) ;
1149 break;
1150 case 'W':
1151 wsize = atoi(optarg);
1152 break;
1153 case '1':
1154 if (isdigit(*optarg)) {
1155 char *ep;
1157 uid = strtoul(optarg, &ep, 10);
1158 if (*ep) {
1159 printf("bad uid value \"%s\"\n", optarg);
1160 exit(EX_USAGE);
1162 } else {
1163 struct passwd *pw;
1165 if (!(pw = getpwnam(optarg))) {
1166 printf("bad user name \"%s\"\n", optarg);
1167 exit(EX_USAGE);
1169 uid = pw->pw_uid;
1170 endpwent();
1172 break;
1173 case '2':
1174 if (isdigit(*optarg)) {
1175 char *ep;
1177 gid = strtoul(optarg, &ep, 10);
1178 if (*ep) {
1179 printf("bad gid value \"%s\"\n", optarg);
1180 exit(EX_USAGE);
1182 } else {
1183 struct group *gr;
1185 if (!(gr = getgrnam(optarg))) {
1186 printf("bad user name \"%s\"\n", optarg);
1187 exit(EX_USAGE);
1189 gid = gr->gr_gid;
1190 endpwent();
1192 break;
1193 case 'u':
1194 got_user = 1;
1195 user_name = optarg;
1196 break;
1197 case 'd':
1198 domain_name = optarg; /* BB fix this - currently ignored */
1199 got_domain = 1;
1200 break;
1201 case 'p':
1202 if(mountpassword == NULL)
1203 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1204 if(mountpassword) {
1205 got_password = 1;
1206 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1208 break;
1209 case 'S':
1210 get_password_from_file(0 /* stdin */,NULL);
1211 break;
1212 case 't':
1213 break;
1214 default:
1215 printf("unknown mount option %c\n",c);
1216 mount_cifs_usage();
1217 exit(EX_USAGE);
1221 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1222 mount_cifs_usage();
1223 exit(EX_USAGE);
1226 if (getenv("PASSWD")) {
1227 if(mountpassword == NULL)
1228 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1229 if(mountpassword) {
1230 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1231 got_password = 1;
1233 } else if (getenv("PASSWD_FD")) {
1234 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1235 } else if (getenv("PASSWD_FILE")) {
1236 get_password_from_file(0, getenv("PASSWD_FILE"));
1239 if (orgoptions && parse_options(&orgoptions, &flags)) {
1240 rc = EX_USAGE;
1241 goto mount_exit;
1243 ipaddr = parse_server(&share_name);
1244 if((ipaddr == NULL) && (got_ip == 0)) {
1245 printf("No ip address specified and hostname not found\n");
1246 rc = EX_USAGE;
1247 goto mount_exit;
1250 /* BB save off path and pop after mount returns? */
1251 resolved_path = (char *)malloc(PATH_MAX+1);
1252 if(resolved_path) {
1253 /* Note that if we can not canonicalize the name, we get
1254 another chance to see if it is valid when we chdir to it */
1255 if (realpath(mountpoint, resolved_path)) {
1256 mountpoint = resolved_path;
1259 if(chdir(mountpoint)) {
1260 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1261 rc = EX_USAGE;
1262 goto mount_exit;
1265 if(stat (".", &statbuf)) {
1266 printf("mount error: mount point %s does not exist\n",mountpoint);
1267 rc = EX_USAGE;
1268 goto mount_exit;
1271 if (S_ISDIR(statbuf.st_mode) == 0) {
1272 printf("mount error: mount point %s is not a directory\n",mountpoint);
1273 rc = EX_USAGE;
1274 goto mount_exit;
1277 if((getuid() != 0) && (geteuid() == 0)) {
1278 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1279 #ifndef CIFS_ALLOW_USR_SUID
1280 /* Do not allow user mounts to control suid flag
1281 for mount unless explicitly built that way */
1282 flags |= MS_NOSUID | MS_NODEV;
1283 #endif
1284 } else {
1285 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1286 exit(EX_USAGE);
1290 if(got_user == 0) {
1291 user_name = getusername();
1292 got_user = 1;
1295 if(got_password == 0) {
1296 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1297 no good replacement yet. */
1298 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1299 if (!tmp_pass || !mountpassword) {
1300 printf("Password not entered, exiting\n");
1301 exit(EX_USAGE);
1303 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1304 got_password = 1;
1306 /* FIXME launch daemon (handles dfs name resolution and credential change)
1307 remember to clear parms and overwrite password field before launching */
1308 mount_retry:
1309 if(orgoptions) {
1310 optlen = strlen(orgoptions);
1311 orgoptlen = optlen;
1312 } else
1313 optlen = 0;
1314 if(share_name)
1315 optlen += strlen(share_name) + 4;
1316 else {
1317 printf("No server share name specified\n");
1318 printf("\nMounting the DFS root for server not implemented yet\n");
1319 exit(EX_USAGE);
1321 if(user_name)
1322 optlen += strlen(user_name) + 6;
1323 if(ipaddr)
1324 optlen += strlen(ipaddr) + 4;
1325 if(mountpassword)
1326 optlen += strlen(mountpassword) + 6;
1327 SAFE_FREE(options);
1328 options_size = optlen + 10 + DOMAIN_SIZE;
1329 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 */);
1331 if(options == NULL) {
1332 printf("Could not allocate memory for mount options\n");
1333 exit(EX_SYSERR);
1336 options[0] = 0;
1337 strlcpy(options,"unc=",options_size);
1338 strlcat(options,share_name,options_size);
1339 /* scan backwards and reverse direction of slash */
1340 temp = strrchr(options, '/');
1341 if(temp > options + 6)
1342 *temp = '\\';
1343 if(ipaddr) {
1344 strlcat(options,",ip=",options_size);
1345 strlcat(options,ipaddr,options_size);
1348 if(user_name) {
1349 /* check for syntax like user=domain\user */
1350 if(got_domain == 0)
1351 domain_name = check_for_domain(&user_name);
1352 strlcat(options,",user=",options_size);
1353 strlcat(options,user_name,options_size);
1355 if(retry == 0) {
1356 if(domain_name) {
1357 /* extra length accounted for in option string above */
1358 strlcat(options,",domain=",options_size);
1359 strlcat(options,domain_name,options_size);
1362 if(mountpassword) {
1363 /* Commas have to be doubled, or else they will
1364 look like the parameter separator */
1365 /* if(sep is not set)*/
1366 if(retry == 0)
1367 check_for_comma(&mountpassword);
1368 strlcat(options,",pass=",options_size);
1369 strlcat(options,mountpassword,options_size);
1372 strlcat(options,",ver=",options_size);
1373 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1375 if(orgoptions) {
1376 strlcat(options,",",options_size);
1377 strlcat(options,orgoptions,options_size);
1379 if(prefixpath) {
1380 strlcat(options,",prefixpath=",options_size);
1381 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1383 if(verboseflag)
1384 printf("\nmount.cifs kernel mount options %s \n",options);
1386 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1387 replace_char(dev_name, '\\', '/', strlen(share_name));
1389 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1390 /* remember to kill daemon on error */
1391 switch (errno) {
1392 case 0:
1393 printf("mount failed but no error number set\n");
1394 break;
1395 case ENODEV:
1396 printf("mount error: cifs filesystem not supported by the system\n");
1397 break;
1398 case ENXIO:
1399 if(retry == 0) {
1400 retry = 1;
1401 if (uppercase_string(dev_name) &&
1402 uppercase_string(share_name) &&
1403 uppercase_string(prefixpath)) {
1404 printf("retrying with upper case share name\n");
1405 goto mount_retry;
1408 default:
1409 printf("mount error %d = %s\n",errno,strerror(errno));
1411 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1412 rc = EX_FAIL;
1413 } else {
1414 pmntfile = setmntent(MOUNTED, "a+");
1415 if(pmntfile) {
1416 mountent.mnt_fsname = dev_name;
1417 mountent.mnt_dir = mountpoint;
1418 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1419 mountent.mnt_opts = (char *)malloc(220);
1420 if(mountent.mnt_opts) {
1421 char * mount_user = getusername();
1422 memset(mountent.mnt_opts,0,200);
1423 if(flags & MS_RDONLY)
1424 strlcat(mountent.mnt_opts,"ro",220);
1425 else
1426 strlcat(mountent.mnt_opts,"rw",220);
1427 if(flags & MS_MANDLOCK)
1428 strlcat(mountent.mnt_opts,",mand",220);
1429 if(flags & MS_NOEXEC)
1430 strlcat(mountent.mnt_opts,",noexec",220);
1431 if(flags & MS_NOSUID)
1432 strlcat(mountent.mnt_opts,",nosuid",220);
1433 if(flags & MS_NODEV)
1434 strlcat(mountent.mnt_opts,",nodev",220);
1435 if(flags & MS_SYNCHRONOUS)
1436 strlcat(mountent.mnt_opts,",synch",220);
1437 if(mount_user) {
1438 if(getuid() != 0) {
1439 strlcat(mountent.mnt_opts,",user=",220);
1440 strlcat(mountent.mnt_opts,mount_user,220);
1442 /* free(mount_user); do not free static mem */
1445 mountent.mnt_freq = 0;
1446 mountent.mnt_passno = 0;
1447 rc = addmntent(pmntfile,&mountent);
1448 endmntent(pmntfile);
1449 SAFE_FREE(mountent.mnt_opts);
1450 if (rc)
1451 rc = EX_FILEIO;
1452 } else {
1453 printf("could not update mount table\n");
1454 rc = EX_FILEIO;
1457 mount_exit:
1458 if(mountpassword) {
1459 int len = strlen(mountpassword);
1460 memset(mountpassword,0,len);
1461 SAFE_FREE(mountpassword);
1464 SAFE_FREE(options);
1465 SAFE_FREE(orgoptions);
1466 SAFE_FREE(resolved_path);
1467 SAFE_FREE(share_name);
1468 exit(rc);