packaging(RHEL): fix build of [u]mount.cifs
[Samba.git] / source / client / mount.cifs.c
blob79a84e6468c1015c0eaefc9561052e85ccdb1e4c
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 /* currently maximum length of IPv6 address string */
84 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
86 const char *thisprogram;
87 int verboseflag = 0;
88 static int got_password = 0;
89 static int got_user = 0;
90 static int got_domain = 0;
91 static int got_ip = 0;
92 static int got_unc = 0;
93 static int got_uid = 0;
94 static int got_gid = 0;
95 static char * user_name = NULL;
96 static char * mountpassword = NULL;
97 char * domain_name = NULL;
98 char * prefixpath = NULL;
100 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
101 * don't link to libreplace so need them here. */
103 /* like strncpy but does not 0 fill the buffer and always null
104 * terminates. bufsize is the size of the destination buffer */
106 #ifndef HAVE_STRLCPY
107 static size_t strlcpy(char *d, const char *s, size_t bufsize)
109 size_t len = strlen(s);
110 size_t ret = len;
111 if (bufsize <= 0) return 0;
112 if (len >= bufsize) len = bufsize-1;
113 memcpy(d, s, len);
114 d[len] = 0;
115 return ret;
117 #endif
119 /* like strncat but does not 0 fill the buffer and always null
120 * terminates. bufsize is the length of the buffer, which should
121 * be one more than the maximum resulting string length */
123 #ifndef HAVE_STRLCAT
124 static size_t strlcat(char *d, const char *s, size_t bufsize)
126 size_t len1 = strlen(d);
127 size_t len2 = strlen(s);
128 size_t ret = len1 + len2;
130 if (len1+len2 >= bufsize) {
131 if (bufsize < (len1+1)) {
132 return ret;
134 len2 = bufsize - (len1+1);
136 if (len2 > 0) {
137 memcpy(d+len1, s, len2);
138 d[len1+len2] = 0;
140 return ret;
142 #endif
144 /* BB finish BB
146 cifs_umount
147 open nofollow - avoid symlink exposure?
148 get owner of dir see if matches self or if root
149 call system(umount argv) etc.
151 BB end finish BB */
153 static char * check_for_domain(char **);
156 static void mount_cifs_usage(void)
158 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
159 printf("\nMount the remote target, specified as a UNC name,");
160 printf(" to a local directory.\n\nOptions:\n");
161 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
162 printf("\nLess commonly used options:");
163 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
164 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
165 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
166 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
167 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
168 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
169 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
170 printf("\n\nRarely used options:");
171 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
172 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
173 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
174 printf("\n\tin6_addr");
175 printf("\n\nOptions are described in more detail in the manual page");
176 printf("\n\tman 8 mount.cifs\n");
177 printf("\nTo display the version number of the mount helper:");
178 printf("\n\t%s -V\n",thisprogram);
180 SAFE_FREE(mountpassword);
181 exit(EX_USAGE);
184 /* caller frees username if necessary */
185 static char * getusername(void) {
186 char *username = NULL;
187 struct passwd *password = getpwuid(getuid());
189 if (password) {
190 username = password->pw_name;
192 return username;
195 static int open_cred_file(char * file_name)
197 char * line_buf;
198 char * temp_val;
199 FILE * fs;
200 int i, length;
201 fs = fopen(file_name,"r");
202 if(fs == NULL)
203 return errno;
204 line_buf = (char *)malloc(4096);
205 if(line_buf == NULL) {
206 fclose(fs);
207 return ENOMEM;
210 while(fgets(line_buf,4096,fs)) {
211 /* parse line from credential file */
213 /* eat leading white space */
214 for(i=0;i<4086;i++) {
215 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
216 break;
217 /* if whitespace - skip past it */
219 if (strncasecmp("username",line_buf+i,8) == 0) {
220 temp_val = strchr(line_buf + i,'=');
221 if(temp_val) {
222 /* go past equals sign */
223 temp_val++;
224 for(length = 0;length<4087;length++) {
225 if ((temp_val[length] == '\n')
226 || (temp_val[length] == '\0')) {
227 temp_val[length] = '\0';
228 break;
231 if(length > 4086) {
232 printf("mount.cifs failed due to malformed username in credentials file");
233 memset(line_buf,0,4096);
234 exit(EX_USAGE);
235 } else {
236 got_user = 1;
237 user_name = (char *)calloc(1 + length,1);
238 /* BB adding free of user_name string before exit,
239 not really necessary but would be cleaner */
240 strlcpy(user_name,temp_val, length+1);
243 } else if (strncasecmp("password",line_buf+i,8) == 0) {
244 temp_val = strchr(line_buf+i,'=');
245 if(temp_val) {
246 /* go past equals sign */
247 temp_val++;
248 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
249 if ((temp_val[length] == '\n')
250 || (temp_val[length] == '\0')) {
251 temp_val[length] = '\0';
252 break;
255 if(length > MOUNT_PASSWD_SIZE) {
256 printf("mount.cifs failed: password in credentials file too long\n");
257 memset(line_buf,0, 4096);
258 exit(EX_USAGE);
259 } else {
260 if(mountpassword == NULL) {
261 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
262 } else
263 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
264 if(mountpassword) {
265 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
266 got_password = 1;
270 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
271 temp_val = strchr(line_buf+i,'=');
272 if(temp_val) {
273 /* go past equals sign */
274 temp_val++;
275 if(verboseflag)
276 printf("\nDomain %s\n",temp_val);
277 for(length = 0;length<DOMAIN_SIZE+1;length++) {
278 if ((temp_val[length] == '\n')
279 || (temp_val[length] == '\0')) {
280 temp_val[length] = '\0';
281 break;
284 if(length > DOMAIN_SIZE) {
285 printf("mount.cifs failed: domain in credentials file too long\n");
286 exit(EX_USAGE);
287 } else {
288 if(domain_name == NULL) {
289 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
290 } else
291 memset(domain_name,0,DOMAIN_SIZE);
292 if(domain_name) {
293 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
294 got_domain = 1;
301 fclose(fs);
302 SAFE_FREE(line_buf);
303 return 0;
306 static int get_password_from_file(int file_descript, char * filename)
308 int rc = 0;
309 int i;
310 char c;
312 if(mountpassword == NULL)
313 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
314 else
315 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
317 if (mountpassword == NULL) {
318 printf("malloc failed\n");
319 exit(EX_SYSERR);
322 if(filename != NULL) {
323 file_descript = open(filename, O_RDONLY);
324 if(file_descript < 0) {
325 printf("mount.cifs failed. %s attempting to open password file %s\n",
326 strerror(errno),filename);
327 exit(EX_SYSERR);
330 /* else file already open and fd provided */
332 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
333 rc = read(file_descript,&c,1);
334 if(rc < 0) {
335 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
336 if(filename != NULL)
337 close(file_descript);
338 exit(EX_SYSERR);
339 } else if(rc == 0) {
340 if(mountpassword[0] == 0) {
341 if(verboseflag)
342 printf("\nWarning: null password used since cifs password file empty");
344 break;
345 } else /* read valid character */ {
346 if((c == 0) || (c == '\n')) {
347 mountpassword[i] = '\0';
348 break;
349 } else
350 mountpassword[i] = c;
353 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
354 printf("\nWarning: password longer than %d characters specified in cifs password file",
355 MOUNT_PASSWD_SIZE);
357 got_password = 1;
358 if(filename != NULL) {
359 close(file_descript);
362 return rc;
365 static int parse_options(char ** optionsp, int * filesys_flags)
367 const char * data;
368 char * percent_char = NULL;
369 char * value = NULL;
370 char * next_keyword = NULL;
371 char * out = NULL;
372 int out_len = 0;
373 int word_len;
374 int rc = 0;
375 char user[32];
376 char group[32];
378 if (!optionsp || !*optionsp)
379 return 1;
380 data = *optionsp;
382 if(verboseflag)
383 printf("parsing options: %s\n", data);
385 /* BB fixme check for separator override BB */
387 if (getuid()) {
388 got_uid = 1;
389 snprintf(user,sizeof(user),"%u",getuid());
390 got_gid = 1;
391 snprintf(group,sizeof(group),"%u",getgid());
394 /* while ((data = strsep(&options, ",")) != NULL) { */
395 while(data != NULL) {
396 /* check if ends with trailing comma */
397 if(*data == 0)
398 break;
400 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
401 /* data = next keyword */
402 /* value = next value ie stuff after equal sign */
404 next_keyword = strchr(data,','); /* BB handle sep= */
406 /* temporarily null terminate end of keyword=value pair */
407 if(next_keyword)
408 *next_keyword++ = 0;
410 /* temporarily null terminate keyword to make keyword and value distinct */
411 if ((value = strchr(data, '=')) != NULL) {
412 *value = '\0';
413 value++;
416 if (strncmp(data, "users",5) == 0) {
417 if(!value || !*value) {
418 goto nocopy;
420 } else if (strncmp(data, "user_xattr",10) == 0) {
421 /* do nothing - need to skip so not parsed as user name */
422 } else if (strncmp(data, "user", 4) == 0) {
424 if (!value || !*value) {
425 if(data[4] == '\0') {
426 if(verboseflag)
427 printf("\nskipping empty user mount parameter\n");
428 /* remove the parm since it would otherwise be confusing
429 to the kernel code which would think it was a real username */
430 goto nocopy;
431 } else {
432 printf("username specified with no parameter\n");
433 SAFE_FREE(out);
434 return 1; /* needs_arg; */
436 } else {
437 if (strnlen(value, 260) < 260) {
438 got_user=1;
439 percent_char = strchr(value,'%');
440 if(percent_char) {
441 *percent_char = ',';
442 if(mountpassword == NULL)
443 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
444 if(mountpassword) {
445 if(got_password)
446 printf("\nmount.cifs warning - password specified twice\n");
447 got_password = 1;
448 percent_char++;
449 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
450 /* remove password from username */
451 while(*percent_char != 0) {
452 *percent_char = ',';
453 percent_char++;
457 /* this is only case in which the user
458 name buf is not malloc - so we have to
459 check for domain name embedded within
460 the user name here since the later
461 call to check_for_domain will not be
462 invoked */
463 domain_name = check_for_domain(&value);
464 } else {
465 printf("username too long\n");
466 SAFE_FREE(out);
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 SAFE_FREE(out);
483 return 1;
485 } else if (strncmp(data, "sec", 3) == 0) {
486 if (value) {
487 if (!strcmp(value, "none"))
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, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
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 SAFE_FREE(out);
500 return 1;
502 } else if ((strncmp(data, "unc", 3) == 0)
503 || (strncmp(data, "target", 6) == 0)
504 || (strncmp(data, "path", 4) == 0)) {
505 if (!value || !*value) {
506 printf("invalid path to network resource\n");
507 SAFE_FREE(out);
508 return 1; /* needs_arg; */
509 } else if(strnlen(value,5) < 5) {
510 printf("UNC name too short");
513 if (strnlen(value, 300) < 300) {
514 got_unc = 1;
515 if (strncmp(value, "//", 2) == 0) {
516 if(got_unc)
517 printf("unc name specified twice, ignoring second\n");
518 else
519 got_unc = 1;
520 } else if (strncmp(value, "\\\\", 2) != 0) {
521 printf("UNC Path does not begin with // or \\\\ \n");
522 SAFE_FREE(out);
523 return 1;
524 } else {
525 if(got_unc)
526 printf("unc name specified twice, ignoring second\n");
527 else
528 got_unc = 1;
530 } else {
531 printf("CIFS: UNC name too long\n");
532 SAFE_FREE(out);
533 return 1;
535 } else if ((strncmp(data, "domain", 3) == 0)
536 || (strncmp(data, "workgroup", 5) == 0)) {
537 if (!value || !*value) {
538 printf("CIFS: invalid domain name\n");
539 SAFE_FREE(out);
540 return 1; /* needs_arg; */
542 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
543 got_domain = 1;
544 } else {
545 printf("domain name too long\n");
546 SAFE_FREE(out);
547 return 1;
549 } else if (strncmp(data, "cred", 4) == 0) {
550 if (value && *value) {
551 rc = open_cred_file(value);
552 if(rc) {
553 printf("error %d (%s) opening credential file %s\n",
554 rc, strerror(rc), value);
555 SAFE_FREE(out);
556 return 1;
558 } else {
559 printf("invalid credential file name specified\n");
560 SAFE_FREE(out);
561 return 1;
563 } else if (strncmp(data, "uid", 3) == 0) {
564 if (value && *value) {
565 got_uid = 1;
566 if (!isdigit(*value)) {
567 struct passwd *pw;
569 if (!(pw = getpwnam(value))) {
570 printf("bad user name \"%s\"\n", value);
571 exit(EX_USAGE);
573 snprintf(user, sizeof(user), "%u", pw->pw_uid);
574 } else {
575 strlcpy(user,value,sizeof(user));
578 goto nocopy;
579 } else if (strncmp(data, "gid", 3) == 0) {
580 if (value && *value) {
581 got_gid = 1;
582 if (!isdigit(*value)) {
583 struct group *gr;
585 if (!(gr = getgrnam(value))) {
586 printf("bad group name \"%s\"\n", value);
587 exit(EX_USAGE);
589 snprintf(group, sizeof(group), "%u", gr->gr_gid);
590 } else {
591 strlcpy(group,value,sizeof(group));
594 goto nocopy;
595 /* fmask and dmask synonyms for people used to smbfs syntax */
596 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
597 if (!value || !*value) {
598 printf ("Option '%s' requires a numerical argument\n", data);
599 SAFE_FREE(out);
600 return 1;
603 if (value[0] != '0') {
604 printf ("WARNING: '%s' not expressed in octal.\n", data);
607 if (strcmp (data, "fmask") == 0) {
608 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
609 data = "file_mode"; /* BB fix this */
611 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
612 if (!value || !*value) {
613 printf ("Option '%s' requires a numerical argument\n", data);
614 SAFE_FREE(out);
615 return 1;
618 if (value[0] != '0') {
619 printf ("WARNING: '%s' not expressed in octal.\n", data);
622 if (strcmp (data, "dmask") == 0) {
623 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
624 data = "dir_mode";
626 /* the following eight mount options should be
627 stripped out from what is passed into the kernel
628 since these eight options are best passed as the
629 mount flags rather than redundantly to the kernel
630 and could generate spurious warnings depending on the
631 level of the corresponding cifs vfs kernel code */
632 } else if (strncmp(data, "nosuid", 6) == 0) {
633 *filesys_flags |= MS_NOSUID;
634 } else if (strncmp(data, "suid", 4) == 0) {
635 *filesys_flags &= ~MS_NOSUID;
636 } else if (strncmp(data, "nodev", 5) == 0) {
637 *filesys_flags |= MS_NODEV;
638 } else if ((strncmp(data, "nobrl", 5) == 0) ||
639 (strncmp(data, "nolock", 6) == 0)) {
640 *filesys_flags &= ~MS_MANDLOCK;
641 } else if (strncmp(data, "dev", 3) == 0) {
642 *filesys_flags &= ~MS_NODEV;
643 } else if (strncmp(data, "noexec", 6) == 0) {
644 *filesys_flags |= MS_NOEXEC;
645 } else if (strncmp(data, "exec", 4) == 0) {
646 *filesys_flags &= ~MS_NOEXEC;
647 } else if (strncmp(data, "guest", 5) == 0) {
648 got_password=1;
649 } else if (strncmp(data, "ro", 2) == 0) {
650 *filesys_flags |= MS_RDONLY;
651 } else if (strncmp(data, "rw", 2) == 0) {
652 *filesys_flags &= ~MS_RDONLY;
653 } else if (strncmp(data, "remount", 7) == 0) {
654 *filesys_flags |= MS_REMOUNT;
655 } /* else if (strnicmp(data, "port", 4) == 0) {
656 if (value && *value) {
657 vol->port =
658 simple_strtoul(value, &value, 0);
660 } else if (strnicmp(data, "rsize", 5) == 0) {
661 if (value && *value) {
662 vol->rsize =
663 simple_strtoul(value, &value, 0);
665 } else if (strnicmp(data, "wsize", 5) == 0) {
666 if (value && *value) {
667 vol->wsize =
668 simple_strtoul(value, &value, 0);
670 } else if (strnicmp(data, "version", 3) == 0) {
671 } else {
672 printf("CIFS: Unknown mount option %s\n",data);
673 } */ /* nothing to do on those four mount options above.
674 Just pass to kernel and ignore them here */
676 /* Copy (possibly modified) option to out */
677 word_len = strlen(data);
678 if (value)
679 word_len += 1 + strlen(value);
681 out = (char *)realloc(out, out_len + word_len + 2);
682 if (out == NULL) {
683 perror("malloc");
684 exit(EX_SYSERR);
687 if (out_len) {
688 strlcat(out, ",", out_len + word_len + 2);
689 out_len++;
692 if (value)
693 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
694 else
695 snprintf(out + out_len, word_len + 1, "%s", data);
696 out_len = strlen(out);
698 nocopy:
699 data = next_keyword;
702 /* special-case the uid and gid */
703 if (got_uid) {
704 word_len = strlen(user);
706 out = (char *)realloc(out, out_len + word_len + 6);
707 if (out == NULL) {
708 perror("malloc");
709 exit(EX_SYSERR);
712 if (out_len) {
713 strlcat(out, ",", out_len + word_len + 6);
714 out_len++;
716 snprintf(out + out_len, word_len + 5, "uid=%s", user);
717 out_len = strlen(out);
719 if (got_gid) {
720 word_len = strlen(group);
722 out = (char *)realloc(out, out_len + 1 + word_len + 6);
723 if (out == NULL) {
724 perror("malloc");
725 exit(EX_SYSERR);
728 if (out_len) {
729 strlcat(out, ",", out_len + word_len + 6);
730 out_len++;
732 snprintf(out + out_len, word_len + 5, "gid=%s", group);
733 out_len = strlen(out);
736 SAFE_FREE(*optionsp);
737 *optionsp = out;
738 return 0;
741 /* replace all (one or more) commas with double commas */
742 static void check_for_comma(char ** ppasswrd)
744 char *new_pass_buf;
745 char *pass;
746 int i,j;
747 int number_of_commas = 0;
748 int len;
750 if(ppasswrd == NULL)
751 return;
752 else
753 (pass = *ppasswrd);
755 len = strlen(pass);
757 for(i=0;i<len;i++) {
758 if(pass[i] == ',')
759 number_of_commas++;
762 if(number_of_commas == 0)
763 return;
764 if(number_of_commas > MOUNT_PASSWD_SIZE) {
765 /* would otherwise overflow the mount options buffer */
766 printf("\nInvalid password. Password contains too many commas.\n");
767 return;
770 new_pass_buf = (char *)malloc(len+number_of_commas+1);
771 if(new_pass_buf == NULL)
772 return;
774 for(i=0,j=0;i<len;i++,j++) {
775 new_pass_buf[j] = pass[i];
776 if(pass[i] == ',') {
777 j++;
778 new_pass_buf[j] = pass[i];
781 new_pass_buf[len+number_of_commas] = 0;
783 SAFE_FREE(*ppasswrd);
784 *ppasswrd = new_pass_buf;
786 return;
789 /* Usernames can not have backslash in them and we use
790 [BB check if usernames can have forward slash in them BB]
791 backslash as domain\user separator character
793 static char * check_for_domain(char **ppuser)
795 char * original_string;
796 char * usernm;
797 char * domainnm;
798 int original_len;
799 int len;
800 int i;
802 if(ppuser == NULL)
803 return NULL;
805 original_string = *ppuser;
807 if (original_string == NULL)
808 return NULL;
810 original_len = strlen(original_string);
812 usernm = strchr(*ppuser,'/');
813 if (usernm == NULL) {
814 usernm = strchr(*ppuser,'\\');
815 if (usernm == NULL)
816 return NULL;
819 if(got_domain) {
820 printf("Domain name specified twice. Username probably malformed\n");
821 return NULL;
824 usernm[0] = 0;
825 domainnm = *ppuser;
826 if (domainnm[0] != 0) {
827 got_domain = 1;
828 } else {
829 printf("null domain\n");
831 len = strlen(domainnm);
832 /* reset domainm to new buffer, and copy
833 domain name into it */
834 domainnm = (char *)malloc(len+1);
835 if(domainnm == NULL)
836 return NULL;
838 strlcpy(domainnm,*ppuser,len+1);
840 /* move_string(*ppuser, usernm+1) */
841 len = strlen(usernm+1);
843 if(len >= original_len) {
844 /* should not happen */
845 return domainnm;
848 for(i=0;i<original_len;i++) {
849 if(i<len)
850 original_string[i] = usernm[i+1];
851 else /* stuff with commas to remove last parm */
852 original_string[i] = ',';
855 /* BB add check for more than one slash?
856 strchr(*ppuser,'/');
857 strchr(*ppuser,'\\')
860 return domainnm;
863 /* replace all occurances of "from" in a string with "to" */
864 static void replace_char(char *string, char from, char to, int maxlen)
866 char *lastchar = string + maxlen;
867 while (string) {
868 string = strchr(string, from);
869 if (string) {
870 *string = to;
871 if (string >= lastchar)
872 return;
877 /* Note that caller frees the returned buffer if necessary */
878 static struct addrinfo *
879 parse_server(char ** punc_name)
881 char * unc_name = *punc_name;
882 int length = strnlen(unc_name, MAX_UNC_LEN);
883 char * share;
884 struct addrinfo *addrlist;
885 int rc;
887 if(length > (MAX_UNC_LEN - 1)) {
888 printf("mount error: UNC name too long");
889 return NULL;
891 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
892 (strncasecmp("smb://", unc_name, 6) == 0)) {
893 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
894 return NULL;
897 if(length < 3) {
898 /* BB add code to find DFS root here */
899 printf("\nMounting the DFS root for domain not implemented yet\n");
900 return NULL;
901 } else {
902 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
903 /* check for nfs syntax ie server:share */
904 share = strchr(unc_name,':');
905 if(share) {
906 *punc_name = (char *)malloc(length+3);
907 if(*punc_name == NULL) {
908 /* put the original string back if
909 no memory left */
910 *punc_name = unc_name;
911 return NULL;
913 *share = '/';
914 strlcpy((*punc_name)+2,unc_name,length+1);
915 SAFE_FREE(unc_name);
916 unc_name = *punc_name;
917 unc_name[length+2] = 0;
918 goto continue_unc_parsing;
919 } else {
920 printf("mount error: improperly formatted UNC name.");
921 printf(" %s does not begin with \\\\ or //\n",unc_name);
922 return NULL;
924 } else {
925 continue_unc_parsing:
926 unc_name[0] = '/';
927 unc_name[1] = '/';
928 unc_name += 2;
930 /* allow for either delimiter between host and sharename */
931 if ((share = strpbrk(unc_name, "/\\"))) {
932 *share = 0; /* temporarily terminate the string */
933 share += 1;
934 if(got_ip == 0) {
935 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
936 if (rc != 0) {
937 printf("mount error: could not resolve address for %s: %s\n",
938 unc_name, gai_strerror(rc));
939 addrlist = NULL;
942 *(share - 1) = '/'; /* put delimiter back */
944 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
945 if ((prefixpath = strpbrk(share, "/\\"))) {
946 *prefixpath = 0; /* permanently terminate the string */
947 if (!strlen(++prefixpath))
948 prefixpath = NULL; /* this needs to be done explicitly */
950 if(got_ip) {
951 if(verboseflag)
952 printf("ip address specified explicitly\n");
953 return NULL;
955 /* BB should we pass an alternate version of the share name as Unicode */
957 return addrlist;
958 } else {
959 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
960 printf("Mounting the DFS root for a particular server not implemented yet\n");
961 return NULL;
967 static struct option longopts[] = {
968 { "all", 0, NULL, 'a' },
969 { "help",0, NULL, 'h' },
970 { "move",0, NULL, 'm' },
971 { "bind",0, NULL, 'b' },
972 { "read-only", 0, NULL, 'r' },
973 { "ro", 0, NULL, 'r' },
974 { "verbose", 0, NULL, 'v' },
975 { "version", 0, NULL, 'V' },
976 { "read-write", 0, NULL, 'w' },
977 { "rw", 0, NULL, 'w' },
978 { "options", 1, NULL, 'o' },
979 { "type", 1, NULL, 't' },
980 { "rsize",1, NULL, 'R' },
981 { "wsize",1, NULL, 'W' },
982 { "uid", 1, NULL, '1'},
983 { "gid", 1, NULL, '2'},
984 { "user",1,NULL,'u'},
985 { "username",1,NULL,'u'},
986 { "dom",1,NULL,'d'},
987 { "domain",1,NULL,'d'},
988 { "password",1,NULL,'p'},
989 { "pass",1,NULL,'p'},
990 { "credentials",1,NULL,'c'},
991 { "port",1,NULL,'P'},
992 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
993 { NULL, 0, NULL, 0 }
996 /* convert a string to uppercase. return false if the string
997 * wasn't ASCII. Return success on a NULL ptr */
998 static int
999 uppercase_string(char *string)
1001 if (!string)
1002 return 1;
1004 while (*string) {
1005 /* check for unicode */
1006 if ((unsigned char) string[0] & 0x80)
1007 return 0;
1008 *string = toupper((unsigned char) *string);
1009 string++;
1012 return 1;
1015 int main(int argc, char ** argv)
1017 int c;
1018 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1019 char * orgoptions = NULL;
1020 char * share_name = NULL;
1021 const char * ipaddr = NULL;
1022 char * uuid = NULL;
1023 char * mountpoint = NULL;
1024 char * options = NULL;
1025 char * optionstail;
1026 char * resolved_path = NULL;
1027 char * temp;
1028 char * dev_name;
1029 int rc;
1030 int rsize = 0;
1031 int wsize = 0;
1032 int nomtab = 0;
1033 int uid = 0;
1034 int gid = 0;
1035 int optlen = 0;
1036 int orgoptlen = 0;
1037 size_t options_size = 0;
1038 size_t current_len;
1039 int retry = 0; /* set when we have to retry mount with uppercase */
1040 struct addrinfo *addrhead = NULL, *addr;
1041 struct stat statbuf;
1042 struct utsname sysinfo;
1043 struct mntent mountent;
1044 struct sockaddr_in *addr4;
1045 struct sockaddr_in6 *addr6;
1046 FILE * pmntfile;
1048 /* setlocale(LC_ALL, "");
1049 bindtextdomain(PACKAGE, LOCALEDIR);
1050 textdomain(PACKAGE); */
1052 if(argc && argv) {
1053 thisprogram = argv[0];
1054 } else {
1055 mount_cifs_usage();
1056 exit(EX_USAGE);
1059 if(thisprogram == NULL)
1060 thisprogram = "mount.cifs";
1062 uname(&sysinfo);
1063 /* BB add workstation name and domain and pass down */
1065 /* #ifdef _GNU_SOURCE
1066 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1067 #endif */
1068 if(argc > 2) {
1069 dev_name = argv[1];
1070 share_name = strndup(argv[1], MAX_UNC_LEN);
1071 if (share_name == NULL) {
1072 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1073 exit(EX_SYSERR);
1075 mountpoint = argv[2];
1076 } else {
1077 mount_cifs_usage();
1078 exit(EX_USAGE);
1081 /* add sharename in opts string as unc= parm */
1083 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1084 longopts, NULL)) != -1) {
1085 switch (c) {
1086 /* No code to do the following options yet */
1087 /* case 'l':
1088 list_with_volumelabel = 1;
1089 break;
1090 case 'L':
1091 volumelabel = optarg;
1092 break; */
1093 /* case 'a':
1094 ++mount_all;
1095 break; */
1097 case '?':
1098 case 'h': /* help */
1099 mount_cifs_usage ();
1100 exit(EX_USAGE);
1101 case 'n':
1102 ++nomtab;
1103 break;
1104 case 'b':
1105 #ifdef MS_BIND
1106 flags |= MS_BIND;
1107 #else
1108 fprintf(stderr,
1109 "option 'b' (MS_BIND) not supported\n");
1110 #endif
1111 break;
1112 case 'm':
1113 #ifdef MS_MOVE
1114 flags |= MS_MOVE;
1115 #else
1116 fprintf(stderr,
1117 "option 'm' (MS_MOVE) not supported\n");
1118 #endif
1119 break;
1120 case 'o':
1121 orgoptions = strdup(optarg);
1122 break;
1123 case 'r': /* mount readonly */
1124 flags |= MS_RDONLY;
1125 break;
1126 case 'U':
1127 uuid = optarg;
1128 break;
1129 case 'v':
1130 ++verboseflag;
1131 break;
1132 case 'V':
1133 printf ("mount.cifs version: %s.%s%s\n",
1134 MOUNT_CIFS_VERSION_MAJOR,
1135 MOUNT_CIFS_VERSION_MINOR,
1136 MOUNT_CIFS_VENDOR_SUFFIX);
1137 exit (0);
1138 case 'w':
1139 flags &= ~MS_RDONLY;
1140 break;
1141 case 'R':
1142 rsize = atoi(optarg) ;
1143 break;
1144 case 'W':
1145 wsize = atoi(optarg);
1146 break;
1147 case '1':
1148 if (isdigit(*optarg)) {
1149 char *ep;
1151 uid = strtoul(optarg, &ep, 10);
1152 if (*ep) {
1153 printf("bad uid value \"%s\"\n", optarg);
1154 exit(EX_USAGE);
1156 } else {
1157 struct passwd *pw;
1159 if (!(pw = getpwnam(optarg))) {
1160 printf("bad user name \"%s\"\n", optarg);
1161 exit(EX_USAGE);
1163 uid = pw->pw_uid;
1164 endpwent();
1166 break;
1167 case '2':
1168 if (isdigit(*optarg)) {
1169 char *ep;
1171 gid = strtoul(optarg, &ep, 10);
1172 if (*ep) {
1173 printf("bad gid value \"%s\"\n", optarg);
1174 exit(EX_USAGE);
1176 } else {
1177 struct group *gr;
1179 if (!(gr = getgrnam(optarg))) {
1180 printf("bad user name \"%s\"\n", optarg);
1181 exit(EX_USAGE);
1183 gid = gr->gr_gid;
1184 endpwent();
1186 break;
1187 case 'u':
1188 got_user = 1;
1189 user_name = optarg;
1190 break;
1191 case 'd':
1192 domain_name = optarg; /* BB fix this - currently ignored */
1193 got_domain = 1;
1194 break;
1195 case 'p':
1196 if(mountpassword == NULL)
1197 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1198 if(mountpassword) {
1199 got_password = 1;
1200 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1202 break;
1203 case 'S':
1204 get_password_from_file(0 /* stdin */,NULL);
1205 break;
1206 case 't':
1207 break;
1208 default:
1209 printf("unknown mount option %c\n",c);
1210 mount_cifs_usage();
1211 exit(EX_USAGE);
1215 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1216 mount_cifs_usage();
1217 exit(EX_USAGE);
1220 if (getenv("PASSWD")) {
1221 if(mountpassword == NULL)
1222 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1223 if(mountpassword) {
1224 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1225 got_password = 1;
1227 } else if (getenv("PASSWD_FD")) {
1228 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1229 } else if (getenv("PASSWD_FILE")) {
1230 get_password_from_file(0, getenv("PASSWD_FILE"));
1233 if (orgoptions && parse_options(&orgoptions, &flags)) {
1234 rc = EX_USAGE;
1235 goto mount_exit;
1237 addrhead = addr = parse_server(&share_name);
1238 if((addrhead == NULL) && (got_ip == 0)) {
1239 printf("No ip address specified and hostname not found\n");
1240 rc = EX_USAGE;
1241 goto mount_exit;
1244 /* BB save off path and pop after mount returns? */
1245 resolved_path = (char *)malloc(PATH_MAX+1);
1246 if(resolved_path) {
1247 /* Note that if we can not canonicalize the name, we get
1248 another chance to see if it is valid when we chdir to it */
1249 if (realpath(mountpoint, resolved_path)) {
1250 mountpoint = resolved_path;
1253 if(chdir(mountpoint)) {
1254 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1255 rc = EX_USAGE;
1256 goto mount_exit;
1259 if(stat (".", &statbuf)) {
1260 printf("mount error: mount point %s does not exist\n",mountpoint);
1261 rc = EX_USAGE;
1262 goto mount_exit;
1265 if (S_ISDIR(statbuf.st_mode) == 0) {
1266 printf("mount error: mount point %s is not a directory\n",mountpoint);
1267 rc = EX_USAGE;
1268 goto mount_exit;
1271 if((getuid() != 0) && (geteuid() == 0)) {
1272 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1273 #ifndef CIFS_ALLOW_USR_SUID
1274 /* Do not allow user mounts to control suid flag
1275 for mount unless explicitly built that way */
1276 flags |= MS_NOSUID | MS_NODEV;
1277 #endif
1278 } else {
1279 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1280 exit(EX_USAGE);
1284 if(got_user == 0) {
1285 user_name = getusername();
1286 got_user = 1;
1289 if(got_password == 0) {
1290 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1291 no good replacement yet. */
1292 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1293 if (!tmp_pass || !mountpassword) {
1294 printf("Password not entered, exiting\n");
1295 exit(EX_USAGE);
1297 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1298 got_password = 1;
1300 /* FIXME launch daemon (handles dfs name resolution and credential change)
1301 remember to clear parms and overwrite password field before launching */
1302 if(orgoptions) {
1303 optlen = strlen(orgoptions);
1304 orgoptlen = optlen;
1305 } else
1306 optlen = 0;
1307 if(share_name)
1308 optlen += strlen(share_name) + 4;
1309 else {
1310 printf("No server share name specified\n");
1311 printf("\nMounting the DFS root for server not implemented yet\n");
1312 exit(EX_USAGE);
1314 if(user_name)
1315 optlen += strlen(user_name) + 6;
1316 optlen += MAX_ADDRESS_LEN + 4;
1317 if(mountpassword)
1318 optlen += strlen(mountpassword) + 6;
1319 mount_retry:
1320 SAFE_FREE(options);
1321 options_size = optlen + 10 + DOMAIN_SIZE;
1322 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 */);
1324 if(options == NULL) {
1325 printf("Could not allocate memory for mount options\n");
1326 exit(EX_SYSERR);
1329 strlcpy(options, "unc=", options_size);
1330 strlcat(options,share_name,options_size);
1331 /* scan backwards and reverse direction of slash */
1332 temp = strrchr(options, '/');
1333 if(temp > options + 6)
1334 *temp = '\\';
1335 if(user_name) {
1336 /* check for syntax like user=domain\user */
1337 if(got_domain == 0)
1338 domain_name = check_for_domain(&user_name);
1339 strlcat(options,",user=",options_size);
1340 strlcat(options,user_name,options_size);
1342 if(retry == 0) {
1343 if(domain_name) {
1344 /* extra length accounted for in option string above */
1345 strlcat(options,",domain=",options_size);
1346 strlcat(options,domain_name,options_size);
1349 if(mountpassword) {
1350 /* Commas have to be doubled, or else they will
1351 look like the parameter separator */
1352 /* if(sep is not set)*/
1353 if(retry == 0)
1354 check_for_comma(&mountpassword);
1355 strlcat(options,",pass=",options_size);
1356 strlcat(options,mountpassword,options_size);
1359 strlcat(options,",ver=",options_size);
1360 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1362 if(orgoptions) {
1363 strlcat(options,",",options_size);
1364 strlcat(options,orgoptions,options_size);
1366 if(prefixpath) {
1367 strlcat(options,",prefixpath=",options_size);
1368 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1370 if(verboseflag)
1371 printf("\nmount.cifs kernel mount options %s \n",options);
1373 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1374 replace_char(dev_name, '\\', '/', strlen(share_name));
1376 if (!got_ip && addr) {
1377 strlcat(options, ",ip=", options_size);
1378 current_len = strnlen(options, options_size);
1379 optionstail = options + current_len;
1380 switch (addr->ai_addr->sa_family) {
1381 case AF_INET6:
1382 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1383 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1384 options_size - current_len);
1385 break;
1386 case AF_INET:
1387 addr4 = (struct sockaddr_in *) addr->ai_addr;
1388 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1389 options_size - current_len);
1390 break;
1393 /* if the address looks bogus, try the next one */
1394 if (!ipaddr) {
1395 addr = addr->ai_next;
1396 if (addr)
1397 goto mount_retry;
1398 rc = EX_SYSERR;
1399 goto mount_exit;
1403 if (mount(dev_name, mountpoint, "cifs", flags, options)) {
1404 switch (errno) {
1405 case ECONNREFUSED:
1406 case EHOSTUNREACH:
1407 if (addr) {
1408 addr = addr->ai_next;
1409 if (addr)
1410 goto mount_retry;
1412 break;
1413 case ENODEV:
1414 printf("mount error: cifs filesystem not supported by the system\n");
1415 break;
1416 case ENXIO:
1417 if(retry == 0) {
1418 retry = 1;
1419 if (uppercase_string(dev_name) &&
1420 uppercase_string(share_name) &&
1421 uppercase_string(prefixpath)) {
1422 printf("retrying with upper case share name\n");
1423 goto mount_retry;
1427 printf("mount error(%d): %s\n", errno, strerror(errno));
1428 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1429 rc = EX_FAIL;
1430 goto mount_exit;
1433 atexit(unlock_mtab);
1434 rc = lock_mtab();
1435 if (rc) {
1436 printf("cannot lock mtab");
1437 goto mount_exit;
1439 pmntfile = setmntent(MOUNTED, "a+");
1440 if (!pmntfile) {
1441 printf("could not update mount table\n");
1442 unlock_mtab();
1443 rc = EX_FILEIO;
1444 goto mount_exit;
1446 mountent.mnt_fsname = dev_name;
1447 mountent.mnt_dir = mountpoint;
1448 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1449 mountent.mnt_opts = (char *)malloc(220);
1450 if(mountent.mnt_opts) {
1451 char * mount_user = getusername();
1452 memset(mountent.mnt_opts,0,200);
1453 if(flags & MS_RDONLY)
1454 strlcat(mountent.mnt_opts,"ro",220);
1455 else
1456 strlcat(mountent.mnt_opts,"rw",220);
1457 if(flags & MS_MANDLOCK)
1458 strlcat(mountent.mnt_opts,",mand",220);
1459 if(flags & MS_NOEXEC)
1460 strlcat(mountent.mnt_opts,",noexec",220);
1461 if(flags & MS_NOSUID)
1462 strlcat(mountent.mnt_opts,",nosuid",220);
1463 if(flags & MS_NODEV)
1464 strlcat(mountent.mnt_opts,",nodev",220);
1465 if(flags & MS_SYNCHRONOUS)
1466 strlcat(mountent.mnt_opts,",sync",220);
1467 if(mount_user) {
1468 if(getuid() != 0) {
1469 strlcat(mountent.mnt_opts,
1470 ",user=", 220);
1471 strlcat(mountent.mnt_opts,
1472 mount_user, 220);
1476 mountent.mnt_freq = 0;
1477 mountent.mnt_passno = 0;
1478 rc = addmntent(pmntfile,&mountent);
1479 endmntent(pmntfile);
1480 unlock_mtab();
1481 SAFE_FREE(mountent.mnt_opts);
1482 if (rc)
1483 rc = EX_FILEIO;
1484 mount_exit:
1485 if(mountpassword) {
1486 int len = strlen(mountpassword);
1487 memset(mountpassword,0,len);
1488 SAFE_FREE(mountpassword);
1491 if (addrhead)
1492 freeaddrinfo(addrhead);
1493 SAFE_FREE(options);
1494 SAFE_FREE(orgoptions);
1495 SAFE_FREE(resolved_path);
1496 SAFE_FREE(share_name);
1497 exit(rc);