s3-nbt: refer to ../libcli/nbt in nbt.idl.
[Samba/gbeck.git] / source3 / client / mount.cifs.c
blobb7a76c61024abfd37651659e0ab34ec924b8d886
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 const char *thisprogram;
83 int verboseflag = 0;
84 static int got_password = 0;
85 static int got_user = 0;
86 static int got_domain = 0;
87 static int got_ip = 0;
88 static int got_unc = 0;
89 static int got_uid = 0;
90 static int got_gid = 0;
91 static char * user_name = NULL;
92 static char * mountpassword = NULL;
93 char * domain_name = NULL;
94 char * prefixpath = NULL;
96 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
97 * don't link to libreplace so need them here. */
99 /* like strncpy but does not 0 fill the buffer and always null
100 * terminates. bufsize is the size of the destination buffer */
102 #ifndef HAVE_STRLCPY
103 static size_t strlcpy(char *d, const char *s, size_t bufsize)
105 size_t len = strlen(s);
106 size_t ret = len;
107 if (bufsize <= 0) return 0;
108 if (len >= bufsize) len = bufsize-1;
109 memcpy(d, s, len);
110 d[len] = 0;
111 return ret;
113 #endif
115 /* like strncat but does not 0 fill the buffer and always null
116 * terminates. bufsize is the length of the buffer, which should
117 * be one more than the maximum resulting string length */
119 #ifndef HAVE_STRLCAT
120 static size_t strlcat(char *d, const char *s, size_t bufsize)
122 size_t len1 = strlen(d);
123 size_t len2 = strlen(s);
124 size_t ret = len1 + len2;
126 if (len1+len2 >= bufsize) {
127 if (bufsize < (len1+1)) {
128 return ret;
130 len2 = bufsize - (len1+1);
132 if (len2 > 0) {
133 memcpy(d+len1, s, len2);
134 d[len1+len2] = 0;
136 return ret;
138 #endif
140 /* BB finish BB
142 cifs_umount
143 open nofollow - avoid symlink exposure?
144 get owner of dir see if matches self or if root
145 call system(umount argv) etc.
147 BB end finish BB */
149 static char * check_for_domain(char **);
152 static void mount_cifs_usage(void)
154 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
155 printf("\nMount the remote target, specified as a UNC name,");
156 printf(" to a local directory.\n\nOptions:\n");
157 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
158 printf("\nLess commonly used options:");
159 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
160 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
161 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
162 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
163 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
164 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
165 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
166 printf("\n\nRarely used options:");
167 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
168 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
169 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
170 printf("\n\tin6_addr");
171 printf("\n\nOptions are described in more detail in the manual page");
172 printf("\n\tman 8 mount.cifs\n");
173 printf("\nTo display the version number of the mount helper:");
174 printf("\n\t%s -V\n",thisprogram);
176 SAFE_FREE(mountpassword);
177 exit(1);
180 /* caller frees username if necessary */
181 static char * getusername(void) {
182 char *username = NULL;
183 struct passwd *password = getpwuid(getuid());
185 if (password) {
186 username = password->pw_name;
188 return username;
191 static char * parse_cifs_url(char * unc_name)
193 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
194 return NULL;
197 static int open_cred_file(char * file_name)
199 char * line_buf;
200 char * temp_val;
201 FILE * fs;
202 int i, length;
203 fs = fopen(file_name,"r");
204 if(fs == NULL)
205 return errno;
206 line_buf = (char *)malloc(4096);
207 if(line_buf == NULL) {
208 fclose(fs);
209 return ENOMEM;
212 while(fgets(line_buf,4096,fs)) {
213 /* parse line from credential file */
215 /* eat leading white space */
216 for(i=0;i<4086;i++) {
217 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
218 break;
219 /* if whitespace - skip past it */
221 if (strncasecmp("username",line_buf+i,8) == 0) {
222 temp_val = strchr(line_buf + i,'=');
223 if(temp_val) {
224 /* go past equals sign */
225 temp_val++;
226 for(length = 0;length<4087;length++) {
227 if ((temp_val[length] == '\n')
228 || (temp_val[length] == '\0')) {
229 temp_val[length] = '\0';
230 break;
233 if(length > 4086) {
234 printf("mount.cifs failed due to malformed username in credentials file");
235 memset(line_buf,0,4096);
236 exit(1);
237 } else {
238 got_user = 1;
239 user_name = (char *)calloc(1 + length,1);
240 /* BB adding free of user_name string before exit,
241 not really necessary but would be cleaner */
242 strlcpy(user_name,temp_val, length+1);
245 } else if (strncasecmp("password",line_buf+i,8) == 0) {
246 temp_val = strchr(line_buf+i,'=');
247 if(temp_val) {
248 /* go past equals sign */
249 temp_val++;
250 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
251 if ((temp_val[length] == '\n')
252 || (temp_val[length] == '\0')) {
253 temp_val[length] = '\0';
254 break;
257 if(length > MOUNT_PASSWD_SIZE) {
258 printf("mount.cifs failed: password in credentials file too long\n");
259 memset(line_buf,0, 4096);
260 exit(1);
261 } else {
262 if(mountpassword == NULL) {
263 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
264 } else
265 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
266 if(mountpassword) {
267 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
268 got_password = 1;
272 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
273 temp_val = strchr(line_buf+i,'=');
274 if(temp_val) {
275 /* go past equals sign */
276 temp_val++;
277 if(verboseflag)
278 printf("\nDomain %s\n",temp_val);
279 for(length = 0;length<DOMAIN_SIZE+1;length++) {
280 if ((temp_val[length] == '\n')
281 || (temp_val[length] == '\0')) {
282 temp_val[length] = '\0';
283 break;
286 if(length > DOMAIN_SIZE) {
287 printf("mount.cifs failed: domain in credentials file too long\n");
288 exit(1);
289 } else {
290 if(domain_name == NULL) {
291 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
292 } else
293 memset(domain_name,0,DOMAIN_SIZE);
294 if(domain_name) {
295 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
296 got_domain = 1;
303 fclose(fs);
304 SAFE_FREE(line_buf);
305 return 0;
308 static int get_password_from_file(int file_descript, char * filename)
310 int rc = 0;
311 int i;
312 char c;
314 if(mountpassword == NULL)
315 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
316 else
317 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
319 if (mountpassword == NULL) {
320 printf("malloc failed\n");
321 exit(1);
324 if(filename != NULL) {
325 file_descript = open(filename, O_RDONLY);
326 if(file_descript < 0) {
327 printf("mount.cifs failed. %s attempting to open password file %s\n",
328 strerror(errno),filename);
329 exit(1);
332 /* else file already open and fd provided */
334 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
335 rc = read(file_descript,&c,1);
336 if(rc < 0) {
337 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
338 if(filename != NULL)
339 close(file_descript);
340 exit(1);
341 } else if(rc == 0) {
342 if(mountpassword[0] == 0) {
343 if(verboseflag)
344 printf("\nWarning: null password used since cifs password file empty");
346 break;
347 } else /* read valid character */ {
348 if((c == 0) || (c == '\n')) {
349 mountpassword[i] = '\0';
350 break;
351 } else
352 mountpassword[i] = c;
355 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
356 printf("\nWarning: password longer than %d characters specified in cifs password file",
357 MOUNT_PASSWD_SIZE);
359 got_password = 1;
360 if(filename != NULL) {
361 close(file_descript);
364 return rc;
367 static int parse_options(char ** optionsp, int * filesys_flags)
369 const char * data;
370 char * percent_char = NULL;
371 char * value = NULL;
372 char * next_keyword = NULL;
373 char * out = NULL;
374 int out_len = 0;
375 int word_len;
376 int rc = 0;
377 char user[32];
378 char group[32];
380 if (!optionsp || !*optionsp)
381 return 1;
382 data = *optionsp;
384 if(verboseflag)
385 printf("parsing options: %s\n", data);
387 /* BB fixme check for separator override BB */
389 if (getuid()) {
390 got_uid = 1;
391 snprintf(user,sizeof(user),"%u",getuid());
392 got_gid = 1;
393 snprintf(group,sizeof(group),"%u",getgid());
396 /* while ((data = strsep(&options, ",")) != NULL) { */
397 while(data != NULL) {
398 /* check if ends with trailing comma */
399 if(*data == 0)
400 break;
402 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
403 /* data = next keyword */
404 /* value = next value ie stuff after equal sign */
406 next_keyword = strchr(data,','); /* BB handle sep= */
408 /* temporarily null terminate end of keyword=value pair */
409 if(next_keyword)
410 *next_keyword++ = 0;
412 /* temporarily null terminate keyword to make keyword and value distinct */
413 if ((value = strchr(data, '=')) != NULL) {
414 *value = '\0';
415 value++;
418 if (strncmp(data, "users",5) == 0) {
419 if(!value || !*value) {
420 goto nocopy;
422 } else if (strncmp(data, "user_xattr",10) == 0) {
423 /* do nothing - need to skip so not parsed as user name */
424 } else if (strncmp(data, "user", 4) == 0) {
426 if (!value || !*value) {
427 if(data[4] == '\0') {
428 if(verboseflag)
429 printf("\nskipping empty user mount parameter\n");
430 /* remove the parm since it would otherwise be confusing
431 to the kernel code which would think it was a real username */
432 goto nocopy;
433 } else {
434 printf("username specified with no parameter\n");
435 return 1; /* needs_arg; */
437 } else {
438 if (strnlen(value, 260) < 260) {
439 got_user=1;
440 percent_char = strchr(value,'%');
441 if(percent_char) {
442 *percent_char = ',';
443 if(mountpassword == NULL)
444 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
445 if(mountpassword) {
446 if(got_password)
447 printf("\nmount.cifs warning - password specified twice\n");
448 got_password = 1;
449 percent_char++;
450 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
451 /* remove password from username */
452 while(*percent_char != 0) {
453 *percent_char = ',';
454 percent_char++;
458 /* this is only case in which the user
459 name buf is not malloc - so we have to
460 check for domain name embedded within
461 the user name here since the later
462 call to check_for_domain will not be
463 invoked */
464 domain_name = check_for_domain(&value);
465 } else {
466 printf("username too long\n");
467 return 1;
470 } else if (strncmp(data, "pass", 4) == 0) {
471 if (!value || !*value) {
472 if(got_password) {
473 printf("\npassword specified twice, ignoring second\n");
474 } else
475 got_password = 1;
476 } else if (strnlen(value, 17) < 17) {
477 if(got_password)
478 printf("\nmount.cifs warning - password specified twice\n");
479 got_password = 1;
480 } else {
481 printf("password too long\n");
482 return 1;
484 } else if (strncmp(data, "sec", 3) == 0) {
485 if (value) {
486 if (!strncmp(value, "none", 4) ||
487 !strncmp(value, "krb5", 4))
488 got_password = 1;
490 } else if (strncmp(data, "ip", 2) == 0) {
491 if (!value || !*value) {
492 printf("target ip address argument missing");
493 } else if (strnlen(value, 35) < 35) {
494 if(verboseflag)
495 printf("ip address %s override specified\n",value);
496 got_ip = 1;
497 } else {
498 printf("ip address too long\n");
499 return 1;
501 } else if ((strncmp(data, "unc", 3) == 0)
502 || (strncmp(data, "target", 6) == 0)
503 || (strncmp(data, "path", 4) == 0)) {
504 if (!value || !*value) {
505 printf("invalid path to network resource\n");
506 return 1; /* needs_arg; */
507 } else if(strnlen(value,5) < 5) {
508 printf("UNC name too short");
511 if (strnlen(value, 300) < 300) {
512 got_unc = 1;
513 if (strncmp(value, "//", 2) == 0) {
514 if(got_unc)
515 printf("unc name specified twice, ignoring second\n");
516 else
517 got_unc = 1;
518 } else if (strncmp(value, "\\\\", 2) != 0) {
519 printf("UNC Path does not begin with // or \\\\ \n");
520 return 1;
521 } else {
522 if(got_unc)
523 printf("unc name specified twice, ignoring second\n");
524 else
525 got_unc = 1;
527 } else {
528 printf("CIFS: UNC name too long\n");
529 return 1;
531 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
532 || (strncmp(data, "workg", 5) == 0)) {
533 /* note this allows for synonyms of "domain"
534 such as "DOM" and "dom" and "workgroup"
535 and "WORKGRP" etc. */
536 if (!value || !*value) {
537 printf("CIFS: invalid domain name\n");
538 return 1; /* needs_arg; */
540 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
541 got_domain = 1;
542 } else {
543 printf("domain name too long\n");
544 return 1;
546 } else if (strncmp(data, "cred", 4) == 0) {
547 if (value && *value) {
548 rc = open_cred_file(value);
549 if(rc) {
550 printf("error %d (%s) opening credential file %s\n",
551 rc, strerror(rc), value);
552 return 1;
554 } else {
555 printf("invalid credential file name specified\n");
556 return 1;
558 } else if (strncmp(data, "uid", 3) == 0) {
559 if (value && *value) {
560 got_uid = 1;
561 if (!isdigit(*value)) {
562 struct passwd *pw;
564 if (!(pw = getpwnam(value))) {
565 printf("bad user name \"%s\"\n", value);
566 exit(1);
568 snprintf(user, sizeof(user), "%u", pw->pw_uid);
569 } else {
570 strlcpy(user,value,sizeof(user));
573 goto nocopy;
574 } else if (strncmp(data, "gid", 3) == 0) {
575 if (value && *value) {
576 got_gid = 1;
577 if (!isdigit(*value)) {
578 struct group *gr;
580 if (!(gr = getgrnam(value))) {
581 printf("bad group name \"%s\"\n", value);
582 exit(1);
584 snprintf(group, sizeof(group), "%u", gr->gr_gid);
585 } else {
586 strlcpy(group,value,sizeof(group));
589 goto nocopy;
590 /* fmask and dmask synonyms for people used to smbfs syntax */
591 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
592 if (!value || !*value) {
593 printf ("Option '%s' requires a numerical argument\n", data);
594 return 1;
597 if (value[0] != '0') {
598 printf ("WARNING: '%s' not expressed in octal.\n", data);
601 if (strcmp (data, "fmask") == 0) {
602 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
603 data = "file_mode"; /* BB fix this */
605 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
606 if (!value || !*value) {
607 printf ("Option '%s' requires a numerical argument\n", data);
608 return 1;
611 if (value[0] != '0') {
612 printf ("WARNING: '%s' not expressed in octal.\n", data);
615 if (strcmp (data, "dmask") == 0) {
616 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
617 data = "dir_mode";
619 /* the following eight mount options should be
620 stripped out from what is passed into the kernel
621 since these eight options are best passed as the
622 mount flags rather than redundantly to the kernel
623 and could generate spurious warnings depending on the
624 level of the corresponding cifs vfs kernel code */
625 } else if (strncmp(data, "nosuid", 6) == 0) {
626 *filesys_flags |= MS_NOSUID;
627 } else if (strncmp(data, "suid", 4) == 0) {
628 *filesys_flags &= ~MS_NOSUID;
629 } else if (strncmp(data, "nodev", 5) == 0) {
630 *filesys_flags |= MS_NODEV;
631 } else if ((strncmp(data, "nobrl", 5) == 0) ||
632 (strncmp(data, "nolock", 6) == 0)) {
633 *filesys_flags &= ~MS_MANDLOCK;
634 } else if (strncmp(data, "dev", 3) == 0) {
635 *filesys_flags &= ~MS_NODEV;
636 } else if (strncmp(data, "noexec", 6) == 0) {
637 *filesys_flags |= MS_NOEXEC;
638 } else if (strncmp(data, "exec", 4) == 0) {
639 *filesys_flags &= ~MS_NOEXEC;
640 } else if (strncmp(data, "guest", 5) == 0) {
641 got_password=1;
642 } else if (strncmp(data, "ro", 2) == 0) {
643 *filesys_flags |= MS_RDONLY;
644 } else if (strncmp(data, "rw", 2) == 0) {
645 *filesys_flags &= ~MS_RDONLY;
646 } else if (strncmp(data, "remount", 7) == 0) {
647 *filesys_flags |= MS_REMOUNT;
648 } /* else if (strnicmp(data, "port", 4) == 0) {
649 if (value && *value) {
650 vol->port =
651 simple_strtoul(value, &value, 0);
653 } else if (strnicmp(data, "rsize", 5) == 0) {
654 if (value && *value) {
655 vol->rsize =
656 simple_strtoul(value, &value, 0);
658 } else if (strnicmp(data, "wsize", 5) == 0) {
659 if (value && *value) {
660 vol->wsize =
661 simple_strtoul(value, &value, 0);
663 } else if (strnicmp(data, "version", 3) == 0) {
664 } else {
665 printf("CIFS: Unknown mount option %s\n",data);
666 } */ /* nothing to do on those four mount options above.
667 Just pass to kernel and ignore them here */
669 /* Copy (possibly modified) option to out */
670 word_len = strlen(data);
671 if (value)
672 word_len += 1 + strlen(value);
674 out = (char *)realloc(out, out_len + word_len + 2);
675 if (out == NULL) {
676 perror("malloc");
677 exit(1);
680 if (out_len) {
681 strlcat(out, ",", out_len + word_len + 2);
682 out_len++;
685 if (value)
686 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
687 else
688 snprintf(out + out_len, word_len + 1, "%s", data);
689 out_len = strlen(out);
691 nocopy:
692 data = next_keyword;
695 /* special-case the uid and gid */
696 if (got_uid) {
697 word_len = strlen(user);
699 out = (char *)realloc(out, out_len + word_len + 6);
700 if (out == NULL) {
701 perror("malloc");
702 exit(1);
705 if (out_len) {
706 strlcat(out, ",", out_len + word_len + 6);
707 out_len++;
709 snprintf(out + out_len, word_len + 5, "uid=%s", user);
710 out_len = strlen(out);
712 if (got_gid) {
713 word_len = strlen(group);
715 out = (char *)realloc(out, out_len + 1 + word_len + 6);
716 if (out == NULL) {
717 perror("malloc");
718 exit(1);
721 if (out_len) {
722 strlcat(out, ",", out_len + word_len + 6);
723 out_len++;
725 snprintf(out + out_len, word_len + 5, "gid=%s", group);
726 out_len = strlen(out);
729 SAFE_FREE(*optionsp);
730 *optionsp = out;
731 return 0;
734 /* replace all (one or more) commas with double commas */
735 static void check_for_comma(char ** ppasswrd)
737 char *new_pass_buf;
738 char *pass;
739 int i,j;
740 int number_of_commas = 0;
741 int len;
743 if(ppasswrd == NULL)
744 return;
745 else
746 (pass = *ppasswrd);
748 len = strlen(pass);
750 for(i=0;i<len;i++) {
751 if(pass[i] == ',')
752 number_of_commas++;
755 if(number_of_commas == 0)
756 return;
757 if(number_of_commas > MOUNT_PASSWD_SIZE) {
758 /* would otherwise overflow the mount options buffer */
759 printf("\nInvalid password. Password contains too many commas.\n");
760 return;
763 new_pass_buf = (char *)malloc(len+number_of_commas+1);
764 if(new_pass_buf == NULL)
765 return;
767 for(i=0,j=0;i<len;i++,j++) {
768 new_pass_buf[j] = pass[i];
769 if(pass[i] == ',') {
770 j++;
771 new_pass_buf[j] = pass[i];
774 new_pass_buf[len+number_of_commas] = 0;
776 SAFE_FREE(*ppasswrd);
777 *ppasswrd = new_pass_buf;
779 return;
782 /* Usernames can not have backslash in them and we use
783 [BB check if usernames can have forward slash in them BB]
784 backslash as domain\user separator character
786 static char * check_for_domain(char **ppuser)
788 char * original_string;
789 char * usernm;
790 char * domainnm;
791 int original_len;
792 int len;
793 int i;
795 if(ppuser == NULL)
796 return NULL;
798 original_string = *ppuser;
800 if (original_string == NULL)
801 return NULL;
803 original_len = strlen(original_string);
805 usernm = strchr(*ppuser,'/');
806 if (usernm == NULL) {
807 usernm = strchr(*ppuser,'\\');
808 if (usernm == NULL)
809 return NULL;
812 if(got_domain) {
813 printf("Domain name specified twice. Username probably malformed\n");
814 return NULL;
817 usernm[0] = 0;
818 domainnm = *ppuser;
819 if (domainnm[0] != 0) {
820 got_domain = 1;
821 } else {
822 printf("null domain\n");
824 len = strlen(domainnm);
825 /* reset domainm to new buffer, and copy
826 domain name into it */
827 domainnm = (char *)malloc(len+1);
828 if(domainnm == NULL)
829 return NULL;
831 strlcpy(domainnm,*ppuser,len+1);
833 /* move_string(*ppuser, usernm+1) */
834 len = strlen(usernm+1);
836 if(len >= original_len) {
837 /* should not happen */
838 return domainnm;
841 for(i=0;i<original_len;i++) {
842 if(i<len)
843 original_string[i] = usernm[i+1];
844 else /* stuff with commas to remove last parm */
845 original_string[i] = ',';
848 /* BB add check for more than one slash?
849 strchr(*ppuser,'/');
850 strchr(*ppuser,'\\')
853 return domainnm;
856 /* replace all occurances of "from" in a string with "to" */
857 static void replace_char(char *string, char from, char to, int maxlen)
859 char *lastchar = string + maxlen;
860 while (string) {
861 string = strchr(string, from);
862 if (string) {
863 *string = to;
864 if (string >= lastchar)
865 return;
870 /* Note that caller frees the returned buffer if necessary */
871 static char * parse_server(char ** punc_name)
873 char * unc_name = *punc_name;
874 int length = strnlen(unc_name, MAX_UNC_LEN);
875 char * share;
876 char * ipaddress_string = NULL;
877 struct hostent * host_entry = NULL;
878 struct in_addr server_ipaddr;
880 if(length > (MAX_UNC_LEN - 1)) {
881 printf("mount error: UNC name too long");
882 return NULL;
884 if (strncasecmp("cifs://",unc_name,7) == 0)
885 return parse_cifs_url(unc_name+7);
886 if (strncasecmp("smb://",unc_name,6) == 0) {
887 return parse_cifs_url(unc_name+6);
890 if(length < 3) {
891 /* BB add code to find DFS root here */
892 printf("\nMounting the DFS root for domain not implemented yet\n");
893 return NULL;
894 } else {
895 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
896 /* check for nfs syntax ie server:share */
897 share = strchr(unc_name,':');
898 if(share) {
899 *punc_name = (char *)malloc(length+3);
900 if(*punc_name == NULL) {
901 /* put the original string back if
902 no memory left */
903 *punc_name = unc_name;
904 return NULL;
906 *share = '/';
907 strlcpy((*punc_name)+2,unc_name,length+1);
908 SAFE_FREE(unc_name);
909 unc_name = *punc_name;
910 unc_name[length+2] = 0;
911 goto continue_unc_parsing;
912 } else {
913 printf("mount error: improperly formatted UNC name.");
914 printf(" %s does not begin with \\\\ or //\n",unc_name);
915 return NULL;
917 } else {
918 continue_unc_parsing:
919 unc_name[0] = '/';
920 unc_name[1] = '/';
921 unc_name += 2;
923 /* allow for either delimiter between host and sharename */
924 if ((share = strpbrk(unc_name, "/\\"))) {
925 *share = 0; /* temporarily terminate the string */
926 share += 1;
927 if(got_ip == 0) {
928 host_entry = gethostbyname(unc_name);
930 *(share - 1) = '/'; /* put delimiter back */
932 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
933 if ((prefixpath = strpbrk(share, "/\\"))) {
934 *prefixpath = 0; /* permanently terminate the string */
935 if (!strlen(++prefixpath))
936 prefixpath = NULL; /* this needs to be done explicitly */
938 if(got_ip) {
939 if(verboseflag)
940 printf("ip address specified explicitly\n");
941 return NULL;
943 if(host_entry == NULL) {
944 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
945 return NULL;
946 } else {
947 /* BB should we pass an alternate version of the share name as Unicode */
948 /* BB what about ipv6? BB */
949 /* BB add retries with alternate servers in list */
951 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
953 ipaddress_string = inet_ntoa(server_ipaddr);
954 if(ipaddress_string == NULL) {
955 printf("mount error: could not get valid ip address for target server\n");
956 return NULL;
958 return ipaddress_string;
960 } else {
961 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
962 printf("Mounting the DFS root for a particular server not implemented yet\n");
963 return NULL;
969 static struct option longopts[] = {
970 { "all", 0, NULL, 'a' },
971 { "help",0, NULL, 'h' },
972 { "move",0, NULL, 'm' },
973 { "bind",0, NULL, 'b' },
974 { "read-only", 0, NULL, 'r' },
975 { "ro", 0, NULL, 'r' },
976 { "verbose", 0, NULL, 'v' },
977 { "version", 0, NULL, 'V' },
978 { "read-write", 0, NULL, 'w' },
979 { "rw", 0, NULL, 'w' },
980 { "options", 1, NULL, 'o' },
981 { "type", 1, NULL, 't' },
982 { "rsize",1, NULL, 'R' },
983 { "wsize",1, NULL, 'W' },
984 { "uid", 1, NULL, '1'},
985 { "gid", 1, NULL, '2'},
986 { "user",1,NULL,'u'},
987 { "username",1,NULL,'u'},
988 { "dom",1,NULL,'d'},
989 { "domain",1,NULL,'d'},
990 { "password",1,NULL,'p'},
991 { "pass",1,NULL,'p'},
992 { "credentials",1,NULL,'c'},
993 { "port",1,NULL,'P'},
994 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
995 { NULL, 0, NULL, 0 }
998 /* convert a string to uppercase. return false if the string
999 * wasn't ASCII or was a NULL ptr */
1000 static int
1001 uppercase_string(char *string)
1003 if (!string)
1004 return 0;
1006 while (*string) {
1007 /* check for unicode */
1008 if ((unsigned char) string[0] & 0x80)
1009 return 0;
1010 *string = toupper((unsigned char) *string);
1011 string++;
1014 return 1;
1017 int main(int argc, char ** argv)
1019 int c;
1020 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1021 char * orgoptions = NULL;
1022 char * share_name = NULL;
1023 char * ipaddr = NULL;
1024 char * uuid = NULL;
1025 char * mountpoint = NULL;
1026 char * options = NULL;
1027 char * resolved_path = NULL;
1028 char * temp;
1029 char * dev_name;
1030 int rc;
1031 int rsize = 0;
1032 int wsize = 0;
1033 int nomtab = 0;
1034 int uid = 0;
1035 int gid = 0;
1036 int optlen = 0;
1037 int orgoptlen = 0;
1038 size_t options_size = 0;
1039 int retry = 0; /* set when we have to retry mount with uppercase */
1040 struct stat statbuf;
1041 struct utsname sysinfo;
1042 struct mntent mountent;
1043 FILE * pmntfile;
1045 /* setlocale(LC_ALL, "");
1046 bindtextdomain(PACKAGE, LOCALEDIR);
1047 textdomain(PACKAGE); */
1049 if(argc && argv) {
1050 thisprogram = argv[0];
1051 } else {
1052 mount_cifs_usage();
1053 exit(1);
1056 if(thisprogram == NULL)
1057 thisprogram = "mount.cifs";
1059 uname(&sysinfo);
1060 /* BB add workstation name and domain and pass down */
1062 /* #ifdef _GNU_SOURCE
1063 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1064 #endif */
1065 if(argc > 2) {
1066 dev_name = argv[1];
1067 share_name = strndup(argv[1], MAX_UNC_LEN);
1068 if (share_name == NULL) {
1069 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1070 exit(1);
1072 mountpoint = argv[2];
1073 } else {
1074 mount_cifs_usage();
1075 exit(1);
1078 /* add sharename in opts string as unc= parm */
1080 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1081 longopts, NULL)) != -1) {
1082 switch (c) {
1083 /* No code to do the following options yet */
1084 /* case 'l':
1085 list_with_volumelabel = 1;
1086 break;
1087 case 'L':
1088 volumelabel = optarg;
1089 break; */
1090 /* case 'a':
1091 ++mount_all;
1092 break; */
1094 case '?':
1095 case 'h': /* help */
1096 mount_cifs_usage ();
1097 exit(1);
1098 case 'n':
1099 ++nomtab;
1100 break;
1101 case 'b':
1102 #ifdef MS_BIND
1103 flags |= MS_BIND;
1104 #else
1105 fprintf(stderr,
1106 "option 'b' (MS_BIND) not supported\n");
1107 #endif
1108 break;
1109 case 'm':
1110 #ifdef MS_MOVE
1111 flags |= MS_MOVE;
1112 #else
1113 fprintf(stderr,
1114 "option 'm' (MS_MOVE) not supported\n");
1115 #endif
1116 break;
1117 case 'o':
1118 orgoptions = strdup(optarg);
1119 break;
1120 case 'r': /* mount readonly */
1121 flags |= MS_RDONLY;
1122 break;
1123 case 'U':
1124 uuid = optarg;
1125 break;
1126 case 'v':
1127 ++verboseflag;
1128 break;
1129 case 'V':
1130 printf ("mount.cifs version: %s.%s%s\n",
1131 MOUNT_CIFS_VERSION_MAJOR,
1132 MOUNT_CIFS_VERSION_MINOR,
1133 MOUNT_CIFS_VENDOR_SUFFIX);
1134 exit (0);
1135 case 'w':
1136 flags &= ~MS_RDONLY;
1137 break;
1138 case 'R':
1139 rsize = atoi(optarg) ;
1140 break;
1141 case 'W':
1142 wsize = atoi(optarg);
1143 break;
1144 case '1':
1145 if (isdigit(*optarg)) {
1146 char *ep;
1148 uid = strtoul(optarg, &ep, 10);
1149 if (*ep) {
1150 printf("bad uid value \"%s\"\n", optarg);
1151 exit(1);
1153 } else {
1154 struct passwd *pw;
1156 if (!(pw = getpwnam(optarg))) {
1157 printf("bad user name \"%s\"\n", optarg);
1158 exit(1);
1160 uid = pw->pw_uid;
1161 endpwent();
1163 break;
1164 case '2':
1165 if (isdigit(*optarg)) {
1166 char *ep;
1168 gid = strtoul(optarg, &ep, 10);
1169 if (*ep) {
1170 printf("bad gid value \"%s\"\n", optarg);
1171 exit(1);
1173 } else {
1174 struct group *gr;
1176 if (!(gr = getgrnam(optarg))) {
1177 printf("bad user name \"%s\"\n", optarg);
1178 exit(1);
1180 gid = gr->gr_gid;
1181 endpwent();
1183 break;
1184 case 'u':
1185 got_user = 1;
1186 user_name = optarg;
1187 break;
1188 case 'd':
1189 domain_name = optarg; /* BB fix this - currently ignored */
1190 got_domain = 1;
1191 break;
1192 case 'p':
1193 if(mountpassword == NULL)
1194 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1195 if(mountpassword) {
1196 got_password = 1;
1197 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1199 break;
1200 case 'S':
1201 get_password_from_file(0 /* stdin */,NULL);
1202 break;
1203 case 't':
1204 break;
1205 default:
1206 printf("unknown mount option %c\n",c);
1207 mount_cifs_usage();
1208 exit(1);
1212 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1213 mount_cifs_usage();
1214 exit(1);
1217 if (getenv("PASSWD")) {
1218 if(mountpassword == NULL)
1219 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1220 if(mountpassword) {
1221 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1222 got_password = 1;
1224 } else if (getenv("PASSWD_FD")) {
1225 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1226 } else if (getenv("PASSWD_FILE")) {
1227 get_password_from_file(0, getenv("PASSWD_FILE"));
1230 if (orgoptions && parse_options(&orgoptions, &flags)) {
1231 rc = -1;
1232 goto mount_exit;
1234 ipaddr = parse_server(&share_name);
1235 if((ipaddr == NULL) && (got_ip == 0)) {
1236 printf("No ip address specified and hostname not found\n");
1237 rc = -1;
1238 goto mount_exit;
1241 /* BB save off path and pop after mount returns? */
1242 resolved_path = (char *)malloc(PATH_MAX+1);
1243 if(resolved_path) {
1244 /* Note that if we can not canonicalize the name, we get
1245 another chance to see if it is valid when we chdir to it */
1246 if (realpath(mountpoint, resolved_path)) {
1247 mountpoint = resolved_path;
1250 if(chdir(mountpoint)) {
1251 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1252 rc = -1;
1253 goto mount_exit;
1256 if(stat (".", &statbuf)) {
1257 printf("mount error: mount point %s does not exist\n",mountpoint);
1258 rc = -1;
1259 goto mount_exit;
1262 if (S_ISDIR(statbuf.st_mode) == 0) {
1263 printf("mount error: mount point %s is not a directory\n",mountpoint);
1264 rc = -1;
1265 goto mount_exit;
1268 if((getuid() != 0) && (geteuid() == 0)) {
1269 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1270 #ifndef CIFS_ALLOW_USR_SUID
1271 /* Do not allow user mounts to control suid flag
1272 for mount unless explicitly built that way */
1273 flags |= MS_NOSUID | MS_NODEV;
1274 #endif
1275 } else {
1276 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1277 return -1;
1281 if(got_user == 0) {
1282 user_name = getusername();
1283 got_user = 1;
1286 if(got_password == 0) {
1287 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1288 no good replacement yet. */
1289 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1290 if (!tmp_pass || !mountpassword) {
1291 printf("Password not entered, exiting\n");
1292 return -1;
1294 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1295 got_password = 1;
1297 /* FIXME launch daemon (handles dfs name resolution and credential change)
1298 remember to clear parms and overwrite password field before launching */
1299 mount_retry:
1300 if(orgoptions) {
1301 optlen = strlen(orgoptions);
1302 orgoptlen = optlen;
1303 } else
1304 optlen = 0;
1305 if(share_name)
1306 optlen += strlen(share_name) + 4;
1307 else {
1308 printf("No server share name specified\n");
1309 printf("\nMounting the DFS root for server not implemented yet\n");
1310 exit(1);
1312 if(user_name)
1313 optlen += strlen(user_name) + 6;
1314 if(ipaddr)
1315 optlen += strlen(ipaddr) + 4;
1316 if(mountpassword)
1317 optlen += strlen(mountpassword) + 6;
1318 SAFE_FREE(options);
1319 options_size = optlen + 10 + DOMAIN_SIZE;
1320 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 */);
1322 if(options == NULL) {
1323 printf("Could not allocate memory for mount options\n");
1324 return -1;
1327 options[0] = 0;
1328 strlcpy(options,"unc=",options_size);
1329 strlcat(options,share_name,options_size);
1330 /* scan backwards and reverse direction of slash */
1331 temp = strrchr(options, '/');
1332 if(temp > options + 6)
1333 *temp = '\\';
1334 if(ipaddr) {
1335 strlcat(options,",ip=",options_size);
1336 strlcat(options,ipaddr,options_size);
1339 if(user_name) {
1340 /* check for syntax like user=domain\user */
1341 if(got_domain == 0)
1342 domain_name = check_for_domain(&user_name);
1343 strlcat(options,",user=",options_size);
1344 strlcat(options,user_name,options_size);
1346 if(retry == 0) {
1347 if(domain_name) {
1348 /* extra length accounted for in option string above */
1349 strlcat(options,",domain=",options_size);
1350 strlcat(options,domain_name,options_size);
1353 if(mountpassword) {
1354 /* Commas have to be doubled, or else they will
1355 look like the parameter separator */
1356 /* if(sep is not set)*/
1357 if(retry == 0)
1358 check_for_comma(&mountpassword);
1359 strlcat(options,",pass=",options_size);
1360 strlcat(options,mountpassword,options_size);
1363 strlcat(options,",ver=",options_size);
1364 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1366 if(orgoptions) {
1367 strlcat(options,",",options_size);
1368 strlcat(options,orgoptions,options_size);
1370 if(prefixpath) {
1371 strlcat(options,",prefixpath=",options_size);
1372 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1374 if(verboseflag)
1375 printf("\nmount.cifs kernel mount options %s \n",options);
1377 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1378 replace_char(dev_name, '\\', '/', strlen(share_name));
1380 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1381 /* remember to kill daemon on error */
1382 switch (errno) {
1383 case 0:
1384 printf("mount failed but no error number set\n");
1385 break;
1386 case ENODEV:
1387 printf("mount error: cifs filesystem not supported by the system\n");
1388 break;
1389 case ENXIO:
1390 if(retry == 0) {
1391 retry = 1;
1392 if (uppercase_string(dev_name) &&
1393 uppercase_string(share_name) &&
1394 uppercase_string(prefixpath)) {
1395 printf("retrying with upper case share name\n");
1396 goto mount_retry;
1399 default:
1400 printf("mount error %d = %s\n",errno,strerror(errno));
1402 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1403 rc = -1;
1404 goto mount_exit;
1405 } else {
1406 pmntfile = setmntent(MOUNTED, "a+");
1407 if(pmntfile) {
1408 mountent.mnt_fsname = dev_name;
1409 mountent.mnt_dir = mountpoint;
1410 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1411 mountent.mnt_opts = (char *)malloc(220);
1412 if(mountent.mnt_opts) {
1413 char * mount_user = getusername();
1414 memset(mountent.mnt_opts,0,200);
1415 if(flags & MS_RDONLY)
1416 strlcat(mountent.mnt_opts,"ro",220);
1417 else
1418 strlcat(mountent.mnt_opts,"rw",220);
1419 if(flags & MS_MANDLOCK)
1420 strlcat(mountent.mnt_opts,",mand",220);
1421 if(flags & MS_NOEXEC)
1422 strlcat(mountent.mnt_opts,",noexec",220);
1423 if(flags & MS_NOSUID)
1424 strlcat(mountent.mnt_opts,",nosuid",220);
1425 if(flags & MS_NODEV)
1426 strlcat(mountent.mnt_opts,",nodev",220);
1427 if(flags & MS_SYNCHRONOUS)
1428 strlcat(mountent.mnt_opts,",synch",220);
1429 if(mount_user) {
1430 if(getuid() != 0) {
1431 strlcat(mountent.mnt_opts,",user=",220);
1432 strlcat(mountent.mnt_opts,mount_user,220);
1434 /* free(mount_user); do not free static mem */
1437 mountent.mnt_freq = 0;
1438 mountent.mnt_passno = 0;
1439 rc = addmntent(pmntfile,&mountent);
1440 endmntent(pmntfile);
1441 SAFE_FREE(mountent.mnt_opts);
1442 } else {
1443 printf("could not update mount table\n");
1446 rc = 0;
1447 mount_exit:
1448 if(mountpassword) {
1449 int len = strlen(mountpassword);
1450 memset(mountpassword,0,len);
1451 SAFE_FREE(mountpassword);
1454 SAFE_FREE(options);
1455 SAFE_FREE(orgoptions);
1456 SAFE_FREE(resolved_path);
1457 SAFE_FREE(share_name);
1458 return rc;