s3: copy nbt/netlogon helper from s4.
[Samba.git] / source / client / mount.cifs.c
blobda2f98bff86e8f1f9ac19962a19cd688165b447c
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>
42 #include "mount.h"
44 #define MOUNT_CIFS_VERSION_MAJOR "1"
45 #define MOUNT_CIFS_VERSION_MINOR "12"
47 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
48 #ifdef _SAMBA_BUILD_
49 #include "include/version.h"
50 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
52 #else
53 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
54 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
55 #else
56 #define MOUNT_CIFS_VENDOR_SUFFIX ""
57 #endif /* _SAMBA_BUILD_ */
58 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
60 #ifdef _SAMBA_BUILD_
61 #include "include/config.h"
62 #endif
64 #ifndef MS_MOVE
65 #define MS_MOVE 8192
66 #endif
68 #ifndef MS_BIND
69 #define MS_BIND 4096
70 #endif
72 #define MAX_UNC_LEN 1024
74 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
76 #ifndef SAFE_FREE
77 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
78 #endif
80 #define MOUNT_PASSWD_SIZE 64
81 #define DOMAIN_SIZE 64
83 const char *thisprogram;
84 int verboseflag = 0;
85 static int got_password = 0;
86 static int got_user = 0;
87 static int got_domain = 0;
88 static int got_ip = 0;
89 static int got_unc = 0;
90 static int got_uid = 0;
91 static int got_gid = 0;
92 static char * user_name = NULL;
93 static char * mountpassword = NULL;
94 char * domain_name = NULL;
95 char * prefixpath = NULL;
97 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
98 * don't link to libreplace so need them here. */
100 /* like strncpy but does not 0 fill the buffer and always null
101 * terminates. bufsize is the size of the destination buffer */
103 #ifndef HAVE_STRLCPY
104 static size_t strlcpy(char *d, const char *s, size_t bufsize)
106 size_t len = strlen(s);
107 size_t ret = len;
108 if (bufsize <= 0) return 0;
109 if (len >= bufsize) len = bufsize-1;
110 memcpy(d, s, len);
111 d[len] = 0;
112 return ret;
114 #endif
116 /* like strncat but does not 0 fill the buffer and always null
117 * terminates. bufsize is the length of the buffer, which should
118 * be one more than the maximum resulting string length */
120 #ifndef HAVE_STRLCAT
121 static size_t strlcat(char *d, const char *s, size_t bufsize)
123 size_t len1 = strlen(d);
124 size_t len2 = strlen(s);
125 size_t ret = len1 + len2;
127 if (len1+len2 >= bufsize) {
128 if (bufsize < (len1+1)) {
129 return ret;
131 len2 = bufsize - (len1+1);
133 if (len2 > 0) {
134 memcpy(d+len1, s, len2);
135 d[len1+len2] = 0;
137 return ret;
139 #endif
141 /* BB finish BB
143 cifs_umount
144 open nofollow - avoid symlink exposure?
145 get owner of dir see if matches self or if root
146 call system(umount argv) etc.
148 BB end finish BB */
150 static char * check_for_domain(char **);
153 static void mount_cifs_usage(void)
155 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
156 printf("\nMount the remote target, specified as a UNC name,");
157 printf(" to a local directory.\n\nOptions:\n");
158 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
159 printf("\nLess commonly used options:");
160 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
161 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
162 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
163 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
164 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
165 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
166 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
167 printf("\n\nRarely used options:");
168 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
169 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
170 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
171 printf("\n\tin6_addr");
172 printf("\n\nOptions are described in more detail in the manual page");
173 printf("\n\tman 8 mount.cifs\n");
174 printf("\nTo display the version number of the mount helper:");
175 printf("\n\t%s -V\n",thisprogram);
177 SAFE_FREE(mountpassword);
178 exit(EX_USAGE);
181 /* caller frees username if necessary */
182 static char * getusername(void) {
183 char *username = NULL;
184 struct passwd *password = getpwuid(getuid());
186 if (password) {
187 username = password->pw_name;
189 return username;
192 static char * parse_cifs_url(char * unc_name)
194 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
195 return NULL;
198 static int open_cred_file(char * file_name)
200 char * line_buf;
201 char * temp_val;
202 FILE * fs;
203 int i, length;
204 fs = fopen(file_name,"r");
205 if(fs == NULL)
206 return errno;
207 line_buf = (char *)malloc(4096);
208 if(line_buf == NULL) {
209 fclose(fs);
210 return ENOMEM;
213 while(fgets(line_buf,4096,fs)) {
214 /* parse line from credential file */
216 /* eat leading white space */
217 for(i=0;i<4086;i++) {
218 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
219 break;
220 /* if whitespace - skip past it */
222 if (strncasecmp("username",line_buf+i,8) == 0) {
223 temp_val = strchr(line_buf + i,'=');
224 if(temp_val) {
225 /* go past equals sign */
226 temp_val++;
227 for(length = 0;length<4087;length++) {
228 if ((temp_val[length] == '\n')
229 || (temp_val[length] == '\0')) {
230 temp_val[length] = '\0';
231 break;
234 if(length > 4086) {
235 printf("mount.cifs failed due to malformed username in credentials file");
236 memset(line_buf,0,4096);
237 exit(EX_USAGE);
238 } else {
239 got_user = 1;
240 user_name = (char *)calloc(1 + length,1);
241 /* BB adding free of user_name string before exit,
242 not really necessary but would be cleaner */
243 strlcpy(user_name,temp_val, length+1);
246 } else if (strncasecmp("password",line_buf+i,8) == 0) {
247 temp_val = strchr(line_buf+i,'=');
248 if(temp_val) {
249 /* go past equals sign */
250 temp_val++;
251 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
252 if ((temp_val[length] == '\n')
253 || (temp_val[length] == '\0')) {
254 temp_val[length] = '\0';
255 break;
258 if(length > MOUNT_PASSWD_SIZE) {
259 printf("mount.cifs failed: password in credentials file too long\n");
260 memset(line_buf,0, 4096);
261 exit(EX_USAGE);
262 } else {
263 if(mountpassword == NULL) {
264 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
265 } else
266 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
267 if(mountpassword) {
268 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
269 got_password = 1;
273 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
274 temp_val = strchr(line_buf+i,'=');
275 if(temp_val) {
276 /* go past equals sign */
277 temp_val++;
278 if(verboseflag)
279 printf("\nDomain %s\n",temp_val);
280 for(length = 0;length<DOMAIN_SIZE+1;length++) {
281 if ((temp_val[length] == '\n')
282 || (temp_val[length] == '\0')) {
283 temp_val[length] = '\0';
284 break;
287 if(length > DOMAIN_SIZE) {
288 printf("mount.cifs failed: domain in credentials file too long\n");
289 exit(EX_USAGE);
290 } else {
291 if(domain_name == NULL) {
292 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
293 } else
294 memset(domain_name,0,DOMAIN_SIZE);
295 if(domain_name) {
296 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
297 got_domain = 1;
304 fclose(fs);
305 SAFE_FREE(line_buf);
306 return 0;
309 static int get_password_from_file(int file_descript, char * filename)
311 int rc = 0;
312 int i;
313 char c;
315 if(mountpassword == NULL)
316 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
317 else
318 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
320 if (mountpassword == NULL) {
321 printf("malloc failed\n");
322 exit(EX_SYSERR);
325 if(filename != NULL) {
326 file_descript = open(filename, O_RDONLY);
327 if(file_descript < 0) {
328 printf("mount.cifs failed. %s attempting to open password file %s\n",
329 strerror(errno),filename);
330 exit(EX_SYSERR);
333 /* else file already open and fd provided */
335 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
336 rc = read(file_descript,&c,1);
337 if(rc < 0) {
338 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
339 if(filename != NULL)
340 close(file_descript);
341 exit(EX_SYSERR);
342 } else if(rc == 0) {
343 if(mountpassword[0] == 0) {
344 if(verboseflag)
345 printf("\nWarning: null password used since cifs password file empty");
347 break;
348 } else /* read valid character */ {
349 if((c == 0) || (c == '\n')) {
350 mountpassword[i] = '\0';
351 break;
352 } else
353 mountpassword[i] = c;
356 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
357 printf("\nWarning: password longer than %d characters specified in cifs password file",
358 MOUNT_PASSWD_SIZE);
360 got_password = 1;
361 if(filename != NULL) {
362 close(file_descript);
365 return rc;
368 static int parse_options(char ** optionsp, int * filesys_flags)
370 const char * data;
371 char * percent_char = NULL;
372 char * value = NULL;
373 char * next_keyword = NULL;
374 char * out = NULL;
375 int out_len = 0;
376 int word_len;
377 int rc = 0;
378 char user[32];
379 char group[32];
381 if (!optionsp || !*optionsp)
382 return 1;
383 data = *optionsp;
385 if(verboseflag)
386 printf("parsing options: %s\n", data);
388 /* BB fixme check for separator override BB */
390 if (getuid()) {
391 got_uid = 1;
392 snprintf(user,sizeof(user),"%u",getuid());
393 got_gid = 1;
394 snprintf(group,sizeof(group),"%u",getgid());
397 /* while ((data = strsep(&options, ",")) != NULL) { */
398 while(data != NULL) {
399 /* check if ends with trailing comma */
400 if(*data == 0)
401 break;
403 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
404 /* data = next keyword */
405 /* value = next value ie stuff after equal sign */
407 next_keyword = strchr(data,','); /* BB handle sep= */
409 /* temporarily null terminate end of keyword=value pair */
410 if(next_keyword)
411 *next_keyword++ = 0;
413 /* temporarily null terminate keyword to make keyword and value distinct */
414 if ((value = strchr(data, '=')) != NULL) {
415 *value = '\0';
416 value++;
419 if (strncmp(data, "users",5) == 0) {
420 if(!value || !*value) {
421 goto nocopy;
423 } else if (strncmp(data, "user_xattr",10) == 0) {
424 /* do nothing - need to skip so not parsed as user name */
425 } else if (strncmp(data, "user", 4) == 0) {
427 if (!value || !*value) {
428 if(data[4] == '\0') {
429 if(verboseflag)
430 printf("\nskipping empty user mount parameter\n");
431 /* remove the parm since it would otherwise be confusing
432 to the kernel code which would think it was a real username */
433 goto nocopy;
434 } else {
435 printf("username specified with no parameter\n");
436 SAFE_FREE(out);
437 return 1; /* needs_arg; */
439 } else {
440 if (strnlen(value, 260) < 260) {
441 got_user=1;
442 percent_char = strchr(value,'%');
443 if(percent_char) {
444 *percent_char = ',';
445 if(mountpassword == NULL)
446 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
447 if(mountpassword) {
448 if(got_password)
449 printf("\nmount.cifs warning - password specified twice\n");
450 got_password = 1;
451 percent_char++;
452 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
453 /* remove password from username */
454 while(*percent_char != 0) {
455 *percent_char = ',';
456 percent_char++;
460 /* this is only case in which the user
461 name buf is not malloc - so we have to
462 check for domain name embedded within
463 the user name here since the later
464 call to check_for_domain will not be
465 invoked */
466 domain_name = check_for_domain(&value);
467 } else {
468 printf("username too long\n");
469 SAFE_FREE(out);
470 return 1;
473 } else if (strncmp(data, "pass", 4) == 0) {
474 if (!value || !*value) {
475 if(got_password) {
476 printf("\npassword specified twice, ignoring second\n");
477 } else
478 got_password = 1;
479 } else if (strnlen(value, 17) < 17) {
480 if(got_password)
481 printf("\nmount.cifs warning - password specified twice\n");
482 got_password = 1;
483 } else {
484 printf("password too long\n");
485 SAFE_FREE(out);
486 return 1;
488 } else if (strncmp(data, "sec", 3) == 0) {
489 if (value) {
490 if (!strncmp(value, "none", 4) ||
491 !strncmp(value, "krb5", 4))
492 got_password = 1;
494 } else if (strncmp(data, "ip", 2) == 0) {
495 if (!value || !*value) {
496 printf("target ip address argument missing");
497 } else if (strnlen(value, 35) < 35) {
498 if(verboseflag)
499 printf("ip address %s override specified\n",value);
500 got_ip = 1;
501 } else {
502 printf("ip address too long\n");
503 SAFE_FREE(out);
504 return 1;
506 } else if ((strncmp(data, "unc", 3) == 0)
507 || (strncmp(data, "target", 6) == 0)
508 || (strncmp(data, "path", 4) == 0)) {
509 if (!value || !*value) {
510 printf("invalid path to network resource\n");
511 SAFE_FREE(out);
512 return 1; /* needs_arg; */
513 } else if(strnlen(value,5) < 5) {
514 printf("UNC name too short");
517 if (strnlen(value, 300) < 300) {
518 got_unc = 1;
519 if (strncmp(value, "//", 2) == 0) {
520 if(got_unc)
521 printf("unc name specified twice, ignoring second\n");
522 else
523 got_unc = 1;
524 } else if (strncmp(value, "\\\\", 2) != 0) {
525 printf("UNC Path does not begin with // or \\\\ \n");
526 SAFE_FREE(out);
527 return 1;
528 } else {
529 if(got_unc)
530 printf("unc name specified twice, ignoring second\n");
531 else
532 got_unc = 1;
534 } else {
535 printf("CIFS: UNC name too long\n");
536 SAFE_FREE(out);
537 return 1;
539 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
540 || (strncmp(data, "workg", 5) == 0)) {
541 /* note this allows for synonyms of "domain"
542 such as "DOM" and "dom" and "workgroup"
543 and "WORKGRP" etc. */
544 if (!value || !*value) {
545 printf("CIFS: invalid domain name\n");
546 SAFE_FREE(out);
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 SAFE_FREE(out);
554 return 1;
556 } else if (strncmp(data, "cred", 4) == 0) {
557 if (value && *value) {
558 rc = open_cred_file(value);
559 if(rc) {
560 printf("error %d (%s) opening credential file %s\n",
561 rc, strerror(rc), value);
562 SAFE_FREE(out);
563 return 1;
565 } else {
566 printf("invalid credential file name specified\n");
567 SAFE_FREE(out);
568 return 1;
570 } else if (strncmp(data, "uid", 3) == 0) {
571 if (value && *value) {
572 got_uid = 1;
573 if (!isdigit(*value)) {
574 struct passwd *pw;
576 if (!(pw = getpwnam(value))) {
577 printf("bad user name \"%s\"\n", value);
578 exit(EX_USAGE);
580 snprintf(user, sizeof(user), "%u", pw->pw_uid);
581 } else {
582 strlcpy(user,value,sizeof(user));
585 goto nocopy;
586 } else if (strncmp(data, "gid", 3) == 0) {
587 if (value && *value) {
588 got_gid = 1;
589 if (!isdigit(*value)) {
590 struct group *gr;
592 if (!(gr = getgrnam(value))) {
593 printf("bad group name \"%s\"\n", value);
594 exit(EX_USAGE);
596 snprintf(group, sizeof(group), "%u", gr->gr_gid);
597 } else {
598 strlcpy(group,value,sizeof(group));
601 goto nocopy;
602 /* fmask and dmask synonyms for people used to smbfs syntax */
603 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
604 if (!value || !*value) {
605 printf ("Option '%s' requires a numerical argument\n", data);
606 SAFE_FREE(out);
607 return 1;
610 if (value[0] != '0') {
611 printf ("WARNING: '%s' not expressed in octal.\n", data);
614 if (strcmp (data, "fmask") == 0) {
615 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
616 data = "file_mode"; /* BB fix this */
618 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
619 if (!value || !*value) {
620 printf ("Option '%s' requires a numerical argument\n", data);
621 SAFE_FREE(out);
622 return 1;
625 if (value[0] != '0') {
626 printf ("WARNING: '%s' not expressed in octal.\n", data);
629 if (strcmp (data, "dmask") == 0) {
630 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
631 data = "dir_mode";
633 /* the following eight mount options should be
634 stripped out from what is passed into the kernel
635 since these eight options are best passed as the
636 mount flags rather than redundantly to the kernel
637 and could generate spurious warnings depending on the
638 level of the corresponding cifs vfs kernel code */
639 } else if (strncmp(data, "nosuid", 6) == 0) {
640 *filesys_flags |= MS_NOSUID;
641 } else if (strncmp(data, "suid", 4) == 0) {
642 *filesys_flags &= ~MS_NOSUID;
643 } else if (strncmp(data, "nodev", 5) == 0) {
644 *filesys_flags |= MS_NODEV;
645 } else if ((strncmp(data, "nobrl", 5) == 0) ||
646 (strncmp(data, "nolock", 6) == 0)) {
647 *filesys_flags &= ~MS_MANDLOCK;
648 } else if (strncmp(data, "dev", 3) == 0) {
649 *filesys_flags &= ~MS_NODEV;
650 } else if (strncmp(data, "noexec", 6) == 0) {
651 *filesys_flags |= MS_NOEXEC;
652 } else if (strncmp(data, "exec", 4) == 0) {
653 *filesys_flags &= ~MS_NOEXEC;
654 } else if (strncmp(data, "guest", 5) == 0) {
655 got_password=1;
656 } else if (strncmp(data, "ro", 2) == 0) {
657 *filesys_flags |= MS_RDONLY;
658 } else if (strncmp(data, "rw", 2) == 0) {
659 *filesys_flags &= ~MS_RDONLY;
660 } else if (strncmp(data, "remount", 7) == 0) {
661 *filesys_flags |= MS_REMOUNT;
662 } /* else if (strnicmp(data, "port", 4) == 0) {
663 if (value && *value) {
664 vol->port =
665 simple_strtoul(value, &value, 0);
667 } else if (strnicmp(data, "rsize", 5) == 0) {
668 if (value && *value) {
669 vol->rsize =
670 simple_strtoul(value, &value, 0);
672 } else if (strnicmp(data, "wsize", 5) == 0) {
673 if (value && *value) {
674 vol->wsize =
675 simple_strtoul(value, &value, 0);
677 } else if (strnicmp(data, "version", 3) == 0) {
678 } else {
679 printf("CIFS: Unknown mount option %s\n",data);
680 } */ /* nothing to do on those four mount options above.
681 Just pass to kernel and ignore them here */
683 /* Copy (possibly modified) option to out */
684 word_len = strlen(data);
685 if (value)
686 word_len += 1 + strlen(value);
688 out = (char *)realloc(out, out_len + word_len + 2);
689 if (out == NULL) {
690 perror("malloc");
691 exit(EX_SYSERR);
694 if (out_len) {
695 strlcat(out, ",", out_len + word_len + 2);
696 out_len++;
699 if (value)
700 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
701 else
702 snprintf(out + out_len, word_len + 1, "%s", data);
703 out_len = strlen(out);
705 nocopy:
706 data = next_keyword;
709 /* special-case the uid and gid */
710 if (got_uid) {
711 word_len = strlen(user);
713 out = (char *)realloc(out, out_len + word_len + 6);
714 if (out == NULL) {
715 perror("malloc");
716 exit(EX_SYSERR);
719 if (out_len) {
720 strlcat(out, ",", out_len + word_len + 6);
721 out_len++;
723 snprintf(out + out_len, word_len + 5, "uid=%s", user);
724 out_len = strlen(out);
726 if (got_gid) {
727 word_len = strlen(group);
729 out = (char *)realloc(out, out_len + 1 + word_len + 6);
730 if (out == NULL) {
731 perror("malloc");
732 exit(EX_SYSERR);
735 if (out_len) {
736 strlcat(out, ",", out_len + word_len + 6);
737 out_len++;
739 snprintf(out + out_len, word_len + 5, "gid=%s", group);
740 out_len = strlen(out);
743 SAFE_FREE(*optionsp);
744 *optionsp = out;
745 return 0;
748 /* replace all (one or more) commas with double commas */
749 static void check_for_comma(char ** ppasswrd)
751 char *new_pass_buf;
752 char *pass;
753 int i,j;
754 int number_of_commas = 0;
755 int len;
757 if(ppasswrd == NULL)
758 return;
759 else
760 (pass = *ppasswrd);
762 len = strlen(pass);
764 for(i=0;i<len;i++) {
765 if(pass[i] == ',')
766 number_of_commas++;
769 if(number_of_commas == 0)
770 return;
771 if(number_of_commas > MOUNT_PASSWD_SIZE) {
772 /* would otherwise overflow the mount options buffer */
773 printf("\nInvalid password. Password contains too many commas.\n");
774 return;
777 new_pass_buf = (char *)malloc(len+number_of_commas+1);
778 if(new_pass_buf == NULL)
779 return;
781 for(i=0,j=0;i<len;i++,j++) {
782 new_pass_buf[j] = pass[i];
783 if(pass[i] == ',') {
784 j++;
785 new_pass_buf[j] = pass[i];
788 new_pass_buf[len+number_of_commas] = 0;
790 SAFE_FREE(*ppasswrd);
791 *ppasswrd = new_pass_buf;
793 return;
796 /* Usernames can not have backslash in them and we use
797 [BB check if usernames can have forward slash in them BB]
798 backslash as domain\user separator character
800 static char * check_for_domain(char **ppuser)
802 char * original_string;
803 char * usernm;
804 char * domainnm;
805 int original_len;
806 int len;
807 int i;
809 if(ppuser == NULL)
810 return NULL;
812 original_string = *ppuser;
814 if (original_string == NULL)
815 return NULL;
817 original_len = strlen(original_string);
819 usernm = strchr(*ppuser,'/');
820 if (usernm == NULL) {
821 usernm = strchr(*ppuser,'\\');
822 if (usernm == NULL)
823 return NULL;
826 if(got_domain) {
827 printf("Domain name specified twice. Username probably malformed\n");
828 return NULL;
831 usernm[0] = 0;
832 domainnm = *ppuser;
833 if (domainnm[0] != 0) {
834 got_domain = 1;
835 } else {
836 printf("null domain\n");
838 len = strlen(domainnm);
839 /* reset domainm to new buffer, and copy
840 domain name into it */
841 domainnm = (char *)malloc(len+1);
842 if(domainnm == NULL)
843 return NULL;
845 strlcpy(domainnm,*ppuser,len+1);
847 /* move_string(*ppuser, usernm+1) */
848 len = strlen(usernm+1);
850 if(len >= original_len) {
851 /* should not happen */
852 return domainnm;
855 for(i=0;i<original_len;i++) {
856 if(i<len)
857 original_string[i] = usernm[i+1];
858 else /* stuff with commas to remove last parm */
859 original_string[i] = ',';
862 /* BB add check for more than one slash?
863 strchr(*ppuser,'/');
864 strchr(*ppuser,'\\')
867 return domainnm;
870 /* replace all occurances of "from" in a string with "to" */
871 static void replace_char(char *string, char from, char to, int maxlen)
873 char *lastchar = string + maxlen;
874 while (string) {
875 string = strchr(string, from);
876 if (string) {
877 *string = to;
878 if (string >= lastchar)
879 return;
884 /* Note that caller frees the returned buffer if necessary */
885 static char * parse_server(char ** punc_name)
887 char * unc_name = *punc_name;
888 int length = strnlen(unc_name, MAX_UNC_LEN);
889 char * share;
890 char * ipaddress_string = NULL;
891 struct hostent * host_entry = NULL;
892 struct in_addr server_ipaddr;
894 if(length > (MAX_UNC_LEN - 1)) {
895 printf("mount error: UNC name too long");
896 return NULL;
898 if (strncasecmp("cifs://",unc_name,7) == 0)
899 return parse_cifs_url(unc_name+7);
900 if (strncasecmp("smb://",unc_name,6) == 0) {
901 return parse_cifs_url(unc_name+6);
904 if(length < 3) {
905 /* BB add code to find DFS root here */
906 printf("\nMounting the DFS root for domain not implemented yet\n");
907 return NULL;
908 } else {
909 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
910 /* check for nfs syntax ie server:share */
911 share = strchr(unc_name,':');
912 if(share) {
913 *punc_name = (char *)malloc(length+3);
914 if(*punc_name == NULL) {
915 /* put the original string back if
916 no memory left */
917 *punc_name = unc_name;
918 return NULL;
920 *share = '/';
921 strlcpy((*punc_name)+2,unc_name,length+1);
922 SAFE_FREE(unc_name);
923 unc_name = *punc_name;
924 unc_name[length+2] = 0;
925 goto continue_unc_parsing;
926 } else {
927 printf("mount error: improperly formatted UNC name.");
928 printf(" %s does not begin with \\\\ or //\n",unc_name);
929 return NULL;
931 } else {
932 continue_unc_parsing:
933 unc_name[0] = '/';
934 unc_name[1] = '/';
935 unc_name += 2;
937 /* allow for either delimiter between host and sharename */
938 if ((share = strpbrk(unc_name, "/\\"))) {
939 *share = 0; /* temporarily terminate the string */
940 share += 1;
941 if(got_ip == 0) {
942 host_entry = gethostbyname(unc_name);
944 *(share - 1) = '/'; /* put delimiter back */
946 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
947 if ((prefixpath = strpbrk(share, "/\\"))) {
948 *prefixpath = 0; /* permanently terminate the string */
949 if (!strlen(++prefixpath))
950 prefixpath = NULL; /* this needs to be done explicitly */
952 if(got_ip) {
953 if(verboseflag)
954 printf("ip address specified explicitly\n");
955 return NULL;
957 if(host_entry == NULL) {
958 printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
959 return NULL;
960 } else {
961 /* BB should we pass an alternate version of the share name as Unicode */
962 /* BB what about ipv6? BB */
963 /* BB add retries with alternate servers in list */
965 memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
967 ipaddress_string = inet_ntoa(server_ipaddr);
968 if(ipaddress_string == NULL) {
969 printf("mount error: could not get valid ip address for target server\n");
970 return NULL;
972 return ipaddress_string;
974 } else {
975 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
976 printf("Mounting the DFS root for a particular server not implemented yet\n");
977 return NULL;
983 static struct option longopts[] = {
984 { "all", 0, NULL, 'a' },
985 { "help",0, NULL, 'h' },
986 { "move",0, NULL, 'm' },
987 { "bind",0, NULL, 'b' },
988 { "read-only", 0, NULL, 'r' },
989 { "ro", 0, NULL, 'r' },
990 { "verbose", 0, NULL, 'v' },
991 { "version", 0, NULL, 'V' },
992 { "read-write", 0, NULL, 'w' },
993 { "rw", 0, NULL, 'w' },
994 { "options", 1, NULL, 'o' },
995 { "type", 1, NULL, 't' },
996 { "rsize",1, NULL, 'R' },
997 { "wsize",1, NULL, 'W' },
998 { "uid", 1, NULL, '1'},
999 { "gid", 1, NULL, '2'},
1000 { "user",1,NULL,'u'},
1001 { "username",1,NULL,'u'},
1002 { "dom",1,NULL,'d'},
1003 { "domain",1,NULL,'d'},
1004 { "password",1,NULL,'p'},
1005 { "pass",1,NULL,'p'},
1006 { "credentials",1,NULL,'c'},
1007 { "port",1,NULL,'P'},
1008 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1009 { NULL, 0, NULL, 0 }
1012 /* convert a string to uppercase. return false if the string
1013 * wasn't ASCII. Return success on a NULL ptr */
1014 static int
1015 uppercase_string(char *string)
1017 if (!string)
1018 return 1;
1020 while (*string) {
1021 /* check for unicode */
1022 if ((unsigned char) string[0] & 0x80)
1023 return 0;
1024 *string = toupper((unsigned char) *string);
1025 string++;
1028 return 1;
1031 int main(int argc, char ** argv)
1033 int c;
1034 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1035 char * orgoptions = NULL;
1036 char * share_name = NULL;
1037 char * ipaddr = NULL;
1038 char * uuid = NULL;
1039 char * mountpoint = NULL;
1040 char * options = NULL;
1041 char * resolved_path = NULL;
1042 char * temp;
1043 char * dev_name;
1044 int rc;
1045 int rsize = 0;
1046 int wsize = 0;
1047 int nomtab = 0;
1048 int uid = 0;
1049 int gid = 0;
1050 int optlen = 0;
1051 int orgoptlen = 0;
1052 size_t options_size = 0;
1053 int retry = 0; /* set when we have to retry mount with uppercase */
1054 struct stat statbuf;
1055 struct utsname sysinfo;
1056 struct mntent mountent;
1057 FILE * pmntfile;
1059 /* setlocale(LC_ALL, "");
1060 bindtextdomain(PACKAGE, LOCALEDIR);
1061 textdomain(PACKAGE); */
1063 if(argc && argv) {
1064 thisprogram = argv[0];
1065 } else {
1066 mount_cifs_usage();
1067 exit(EX_USAGE);
1070 if(thisprogram == NULL)
1071 thisprogram = "mount.cifs";
1073 uname(&sysinfo);
1074 /* BB add workstation name and domain and pass down */
1076 /* #ifdef _GNU_SOURCE
1077 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1078 #endif */
1079 if(argc > 2) {
1080 dev_name = argv[1];
1081 share_name = strndup(argv[1], MAX_UNC_LEN);
1082 if (share_name == NULL) {
1083 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1084 exit(EX_SYSERR);
1086 mountpoint = argv[2];
1087 } else {
1088 mount_cifs_usage();
1089 exit(EX_USAGE);
1092 /* add sharename in opts string as unc= parm */
1094 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1095 longopts, NULL)) != -1) {
1096 switch (c) {
1097 /* No code to do the following options yet */
1098 /* case 'l':
1099 list_with_volumelabel = 1;
1100 break;
1101 case 'L':
1102 volumelabel = optarg;
1103 break; */
1104 /* case 'a':
1105 ++mount_all;
1106 break; */
1108 case '?':
1109 case 'h': /* help */
1110 mount_cifs_usage ();
1111 exit(EX_USAGE);
1112 case 'n':
1113 ++nomtab;
1114 break;
1115 case 'b':
1116 #ifdef MS_BIND
1117 flags |= MS_BIND;
1118 #else
1119 fprintf(stderr,
1120 "option 'b' (MS_BIND) not supported\n");
1121 #endif
1122 break;
1123 case 'm':
1124 #ifdef MS_MOVE
1125 flags |= MS_MOVE;
1126 #else
1127 fprintf(stderr,
1128 "option 'm' (MS_MOVE) not supported\n");
1129 #endif
1130 break;
1131 case 'o':
1132 orgoptions = strdup(optarg);
1133 break;
1134 case 'r': /* mount readonly */
1135 flags |= MS_RDONLY;
1136 break;
1137 case 'U':
1138 uuid = optarg;
1139 break;
1140 case 'v':
1141 ++verboseflag;
1142 break;
1143 case 'V':
1144 printf ("mount.cifs version: %s.%s%s\n",
1145 MOUNT_CIFS_VERSION_MAJOR,
1146 MOUNT_CIFS_VERSION_MINOR,
1147 MOUNT_CIFS_VENDOR_SUFFIX);
1148 exit (0);
1149 case 'w':
1150 flags &= ~MS_RDONLY;
1151 break;
1152 case 'R':
1153 rsize = atoi(optarg) ;
1154 break;
1155 case 'W':
1156 wsize = atoi(optarg);
1157 break;
1158 case '1':
1159 if (isdigit(*optarg)) {
1160 char *ep;
1162 uid = strtoul(optarg, &ep, 10);
1163 if (*ep) {
1164 printf("bad uid value \"%s\"\n", optarg);
1165 exit(EX_USAGE);
1167 } else {
1168 struct passwd *pw;
1170 if (!(pw = getpwnam(optarg))) {
1171 printf("bad user name \"%s\"\n", optarg);
1172 exit(EX_USAGE);
1174 uid = pw->pw_uid;
1175 endpwent();
1177 break;
1178 case '2':
1179 if (isdigit(*optarg)) {
1180 char *ep;
1182 gid = strtoul(optarg, &ep, 10);
1183 if (*ep) {
1184 printf("bad gid value \"%s\"\n", optarg);
1185 exit(EX_USAGE);
1187 } else {
1188 struct group *gr;
1190 if (!(gr = getgrnam(optarg))) {
1191 printf("bad user name \"%s\"\n", optarg);
1192 exit(EX_USAGE);
1194 gid = gr->gr_gid;
1195 endpwent();
1197 break;
1198 case 'u':
1199 got_user = 1;
1200 user_name = optarg;
1201 break;
1202 case 'd':
1203 domain_name = optarg; /* BB fix this - currently ignored */
1204 got_domain = 1;
1205 break;
1206 case 'p':
1207 if(mountpassword == NULL)
1208 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1209 if(mountpassword) {
1210 got_password = 1;
1211 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1213 break;
1214 case 'S':
1215 get_password_from_file(0 /* stdin */,NULL);
1216 break;
1217 case 't':
1218 break;
1219 default:
1220 printf("unknown mount option %c\n",c);
1221 mount_cifs_usage();
1222 exit(EX_USAGE);
1226 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1227 mount_cifs_usage();
1228 exit(EX_USAGE);
1231 if (getenv("PASSWD")) {
1232 if(mountpassword == NULL)
1233 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1234 if(mountpassword) {
1235 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1236 got_password = 1;
1238 } else if (getenv("PASSWD_FD")) {
1239 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1240 } else if (getenv("PASSWD_FILE")) {
1241 get_password_from_file(0, getenv("PASSWD_FILE"));
1244 if (orgoptions && parse_options(&orgoptions, &flags)) {
1245 rc = EX_USAGE;
1246 goto mount_exit;
1248 ipaddr = parse_server(&share_name);
1249 if((ipaddr == NULL) && (got_ip == 0)) {
1250 printf("No ip address specified and hostname not found\n");
1251 rc = EX_USAGE;
1252 goto mount_exit;
1255 /* BB save off path and pop after mount returns? */
1256 resolved_path = (char *)malloc(PATH_MAX+1);
1257 if(resolved_path) {
1258 /* Note that if we can not canonicalize the name, we get
1259 another chance to see if it is valid when we chdir to it */
1260 if (realpath(mountpoint, resolved_path)) {
1261 mountpoint = resolved_path;
1264 if(chdir(mountpoint)) {
1265 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1266 rc = EX_USAGE;
1267 goto mount_exit;
1270 if(stat (".", &statbuf)) {
1271 printf("mount error: mount point %s does not exist\n",mountpoint);
1272 rc = EX_USAGE;
1273 goto mount_exit;
1276 if (S_ISDIR(statbuf.st_mode) == 0) {
1277 printf("mount error: mount point %s is not a directory\n",mountpoint);
1278 rc = EX_USAGE;
1279 goto mount_exit;
1282 if((getuid() != 0) && (geteuid() == 0)) {
1283 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1284 #ifndef CIFS_ALLOW_USR_SUID
1285 /* Do not allow user mounts to control suid flag
1286 for mount unless explicitly built that way */
1287 flags |= MS_NOSUID | MS_NODEV;
1288 #endif
1289 } else {
1290 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1291 exit(EX_USAGE);
1295 if(got_user == 0) {
1296 user_name = getusername();
1297 got_user = 1;
1300 if(got_password == 0) {
1301 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1302 no good replacement yet. */
1303 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1304 if (!tmp_pass || !mountpassword) {
1305 printf("Password not entered, exiting\n");
1306 exit(EX_USAGE);
1308 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1309 got_password = 1;
1311 /* FIXME launch daemon (handles dfs name resolution and credential change)
1312 remember to clear parms and overwrite password field before launching */
1313 mount_retry:
1314 if(orgoptions) {
1315 optlen = strlen(orgoptions);
1316 orgoptlen = optlen;
1317 } else
1318 optlen = 0;
1319 if(share_name)
1320 optlen += strlen(share_name) + 4;
1321 else {
1322 printf("No server share name specified\n");
1323 printf("\nMounting the DFS root for server not implemented yet\n");
1324 exit(EX_USAGE);
1326 if(user_name)
1327 optlen += strlen(user_name) + 6;
1328 if(ipaddr)
1329 optlen += strlen(ipaddr) + 4;
1330 if(mountpassword)
1331 optlen += strlen(mountpassword) + 6;
1332 SAFE_FREE(options);
1333 options_size = optlen + 10 + DOMAIN_SIZE;
1334 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 */);
1336 if(options == NULL) {
1337 printf("Could not allocate memory for mount options\n");
1338 exit(EX_SYSERR);
1341 options[0] = 0;
1342 strlcpy(options,"unc=",options_size);
1343 strlcat(options,share_name,options_size);
1344 /* scan backwards and reverse direction of slash */
1345 temp = strrchr(options, '/');
1346 if(temp > options + 6)
1347 *temp = '\\';
1348 if(ipaddr) {
1349 strlcat(options,",ip=",options_size);
1350 strlcat(options,ipaddr,options_size);
1353 if(user_name) {
1354 /* check for syntax like user=domain\user */
1355 if(got_domain == 0)
1356 domain_name = check_for_domain(&user_name);
1357 strlcat(options,",user=",options_size);
1358 strlcat(options,user_name,options_size);
1360 if(retry == 0) {
1361 if(domain_name) {
1362 /* extra length accounted for in option string above */
1363 strlcat(options,",domain=",options_size);
1364 strlcat(options,domain_name,options_size);
1367 if(mountpassword) {
1368 /* Commas have to be doubled, or else they will
1369 look like the parameter separator */
1370 /* if(sep is not set)*/
1371 if(retry == 0)
1372 check_for_comma(&mountpassword);
1373 strlcat(options,",pass=",options_size);
1374 strlcat(options,mountpassword,options_size);
1377 strlcat(options,",ver=",options_size);
1378 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1380 if(orgoptions) {
1381 strlcat(options,",",options_size);
1382 strlcat(options,orgoptions,options_size);
1384 if(prefixpath) {
1385 strlcat(options,",prefixpath=",options_size);
1386 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1388 if(verboseflag)
1389 printf("\nmount.cifs kernel mount options %s \n",options);
1391 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1392 replace_char(dev_name, '\\', '/', strlen(share_name));
1394 if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1395 /* remember to kill daemon on error */
1396 switch (errno) {
1397 case 0:
1398 printf("mount failed but no error number set\n");
1399 break;
1400 case ENODEV:
1401 printf("mount error: cifs filesystem not supported by the system\n");
1402 break;
1403 case ENXIO:
1404 if(retry == 0) {
1405 retry = 1;
1406 if (uppercase_string(dev_name) &&
1407 uppercase_string(share_name) &&
1408 uppercase_string(prefixpath)) {
1409 printf("retrying with upper case share name\n");
1410 goto mount_retry;
1413 default:
1414 printf("mount error %d = %s\n",errno,strerror(errno));
1416 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1417 rc = EX_FAIL;
1418 } else {
1419 atexit(unlock_mtab);
1420 rc = lock_mtab();
1421 if (rc) {
1422 printf("cannot lock mtab");
1423 goto mount_exit;
1425 pmntfile = setmntent(MOUNTED, "a+");
1426 if (!pmntfile) {
1427 printf("could not update mount table\n");
1428 unlock_mtab();
1429 rc = EX_FILEIO;
1430 goto mount_exit;
1432 mountent.mnt_fsname = dev_name;
1433 mountent.mnt_dir = mountpoint;
1434 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1435 mountent.mnt_opts = (char *)malloc(220);
1436 if(mountent.mnt_opts) {
1437 char * mount_user = getusername();
1438 memset(mountent.mnt_opts,0,200);
1439 if(flags & MS_RDONLY)
1440 strlcat(mountent.mnt_opts,"ro",220);
1441 else
1442 strlcat(mountent.mnt_opts,"rw",220);
1443 if(flags & MS_MANDLOCK)
1444 strlcat(mountent.mnt_opts,",mand",220);
1445 if(flags & MS_NOEXEC)
1446 strlcat(mountent.mnt_opts,",noexec",220);
1447 if(flags & MS_NOSUID)
1448 strlcat(mountent.mnt_opts,",nosuid",220);
1449 if(flags & MS_NODEV)
1450 strlcat(mountent.mnt_opts,",nodev",220);
1451 if(flags & MS_SYNCHRONOUS)
1452 strlcat(mountent.mnt_opts,",sync",220);
1453 if(mount_user) {
1454 if(getuid() != 0) {
1455 strlcat(mountent.mnt_opts,
1456 ",user=", 220);
1457 strlcat(mountent.mnt_opts,
1458 mount_user, 220);
1462 mountent.mnt_freq = 0;
1463 mountent.mnt_passno = 0;
1464 rc = addmntent(pmntfile,&mountent);
1465 endmntent(pmntfile);
1466 unlock_mtab();
1467 SAFE_FREE(mountent.mnt_opts);
1468 if (rc)
1469 rc = EX_FILEIO;
1471 mount_exit:
1472 if(mountpassword) {
1473 int len = strlen(mountpassword);
1474 memset(mountpassword,0,len);
1475 SAFE_FREE(mountpassword);
1478 SAFE_FREE(options);
1479 SAFE_FREE(orgoptions);
1480 SAFE_FREE(resolved_path);
1481 SAFE_FREE(share_name);
1482 exit(rc);