mount.cifs: take extra care that mountpoint isn't changed during mount
[Samba/fernandojvsilva.git] / client / mount.cifs.c
blob22c545b25ad76c4504d9562458e57d9990c1bceb
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 <fstab.h>
43 #include "mount.h"
45 #define MOUNT_CIFS_VERSION_MAJOR "1"
46 #define MOUNT_CIFS_VERSION_MINOR "13"
48 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
49 #ifdef _SAMBA_BUILD_
50 #include "version.h"
51 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
52 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
53 #else
54 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
55 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
56 #else
57 #define MOUNT_CIFS_VENDOR_SUFFIX ""
58 #endif /* _SAMBA_BUILD_ */
59 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
61 #ifdef _SAMBA_BUILD_
62 #include "include/config.h"
63 #endif
65 #ifndef MS_MOVE
66 #define MS_MOVE 8192
67 #endif
69 #ifndef MS_BIND
70 #define MS_BIND 4096
71 #endif
73 /* private flags - clear these before passing to kernel */
74 #define MS_USERS 0x40000000
75 #define MS_USER 0x80000000
77 #define MAX_UNC_LEN 1024
79 #ifndef SAFE_FREE
80 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
81 #endif
83 #define MOUNT_PASSWD_SIZE 128
84 #define DOMAIN_SIZE 64
86 /* currently maximum length of IPv6 address string */
87 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
90 * By default, mount.cifs follows the conventions set forth by /bin/mount
91 * for user mounts. That is, it requires that the mount be listed in
92 * /etc/fstab with the "user" option when run as an unprivileged user and
93 * mount.cifs is setuid root.
95 * Older versions of mount.cifs however were "looser" in this regard. When
96 * made setuid root, a user could run mount.cifs directly and mount any share
97 * on a directory owned by that user.
99 * The legacy behavior is now disabled by default. To reenable it, set the
100 * following #define to true.
102 #define CIFS_LEGACY_SETUID_CHECK 0
105 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
106 * flags by default. These defaults can be changed here.
108 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
110 const char *thisprogram;
111 int verboseflag = 0;
112 int fakemnt = 0;
113 static int got_password = 0;
114 static int got_user = 0;
115 static int got_domain = 0;
116 static int got_ip = 0;
117 static int got_unc = 0;
118 static int got_uid = 0;
119 static int got_gid = 0;
120 static char * user_name = NULL;
121 static char * mountpassword = NULL;
122 char * domain_name = NULL;
123 char * prefixpath = NULL;
124 const char *cifs_fstype = "cifs";
126 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
127 * don't link to libreplace so need them here. */
129 /* like strncpy but does not 0 fill the buffer and always null
130 * terminates. bufsize is the size of the destination buffer */
132 #ifndef HAVE_STRLCPY
133 static size_t strlcpy(char *d, const char *s, size_t bufsize)
135 size_t len = strlen(s);
136 size_t ret = len;
137 if (bufsize <= 0) return 0;
138 if (len >= bufsize) len = bufsize-1;
139 memcpy(d, s, len);
140 d[len] = 0;
141 return ret;
143 #endif
145 /* like strncat but does not 0 fill the buffer and always null
146 * terminates. bufsize is the length of the buffer, which should
147 * be one more than the maximum resulting string length */
149 #ifndef HAVE_STRLCAT
150 static size_t strlcat(char *d, const char *s, size_t bufsize)
152 size_t len1 = strlen(d);
153 size_t len2 = strlen(s);
154 size_t ret = len1 + len2;
156 if (len1+len2 >= bufsize) {
157 if (bufsize < (len1+1)) {
158 return ret;
160 len2 = bufsize - (len1+1);
162 if (len2 > 0) {
163 memcpy(d+len1, s, len2);
164 d[len1+len2] = 0;
166 return ret;
168 #endif
171 * If an unprivileged user is doing the mounting then we need to ensure
172 * that the entry is in /etc/fstab.
174 static int
175 check_mountpoint(const char *progname, char *mountpoint)
177 int err;
178 struct stat statbuf;
180 /* does mountpoint exist and is it a directory? */
181 err = stat(".", &statbuf);
182 if (err) {
183 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
184 mountpoint, strerror(errno));
185 return EX_USAGE;
188 if (!S_ISDIR(statbuf.st_mode)) {
189 fprintf(stderr, "%s: %s is not a directory!", progname,
190 mountpoint);
191 return EX_USAGE;
194 #if CIFS_LEGACY_SETUID_CHECK
195 /* do extra checks on mountpoint for legacy setuid behavior */
196 if (!getuid() || geteuid())
197 return 0;
199 if (statbuf.st_uid != getuid()) {
200 fprintf(stderr, "%s: %s is not owned by user\n", progname,
201 mountpoint);
202 return EX_USAGE;
205 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
206 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
207 mountpoint);
208 return EX_USAGE;
210 #endif /* CIFS_LEGACY_SETUID_CHECK */
212 return 0;
215 #if CIFS_LEGACY_SETUID_CHECK
216 static int
217 check_fstab(const char *progname, char *mountpoint, char *devname,
218 char **options)
220 return 0;
222 #else /* CIFS_LEGACY_SETUID_CHECK */
223 static int
224 check_fstab(const char *progname, char *mountpoint, char *devname,
225 char **options)
227 FILE *fstab;
228 struct mntent *mnt;
230 /* make sure this mount is listed in /etc/fstab */
231 fstab = setmntent(_PATH_FSTAB, "r");
232 if (!fstab) {
233 fprintf(stderr, "Couldn't open %s for reading!\n",
234 _PATH_FSTAB);
235 return EX_FILEIO;
238 while((mnt = getmntent(fstab))) {
239 if (!strcmp(mountpoint, mnt->mnt_dir))
240 break;
242 endmntent(fstab);
244 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
245 fprintf(stderr, "%s: permission denied: no match for "
246 "%s found in %s\n", progname, mountpoint,
247 _PATH_FSTAB);
248 return EX_USAGE;
252 * 'mount' munges the options from fstab before passing them
253 * to us. It is non-trivial to test that we have the correct
254 * set of options. We don't want to trust what the user
255 * gave us, so just take whatever is in /etc/fstab.
257 free(*options);
258 *options = strdup(mnt->mnt_opts);
259 return 0;
261 #endif /* CIFS_LEGACY_SETUID_CHECK */
263 /* BB finish BB
265 cifs_umount
266 open nofollow - avoid symlink exposure?
267 get owner of dir see if matches self or if root
268 call system(umount argv) etc.
270 BB end finish BB */
272 static char * check_for_domain(char **);
275 static void mount_cifs_usage(FILE *stream)
277 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
278 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
279 fprintf(stream, " to a local directory.\n\nOptions:\n");
280 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
281 fprintf(stream, "\nLess commonly used options:");
282 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
283 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
284 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
285 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
286 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
287 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
288 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
289 fprintf(stream, "\n\nRarely used options:");
290 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
291 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
292 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
293 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
294 fprintf(stream, "\n\tman 8 mount.cifs\n");
295 fprintf(stream, "\nTo display the version number of the mount helper:");
296 fprintf(stream, "\n\t%s -V\n",thisprogram);
298 SAFE_FREE(mountpassword);
300 if (stream == stderr)
301 exit(EX_USAGE);
302 exit(0);
305 /* caller frees username if necessary */
306 static char * getusername(void) {
307 char *username = NULL;
308 struct passwd *password = getpwuid(getuid());
310 if (password) {
311 username = password->pw_name;
313 return username;
316 static int open_cred_file(char * file_name)
318 char * line_buf;
319 char * temp_val;
320 FILE * fs;
321 int i, length;
323 i = access(file_name, R_OK);
324 if (i)
325 return i;
327 fs = fopen(file_name,"r");
328 if(fs == NULL)
329 return errno;
330 line_buf = (char *)malloc(4096);
331 if(line_buf == NULL) {
332 fclose(fs);
333 return ENOMEM;
336 while(fgets(line_buf,4096,fs)) {
337 /* parse line from credential file */
339 /* eat leading white space */
340 for(i=0;i<4086;i++) {
341 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
342 break;
343 /* if whitespace - skip past it */
345 if (strncasecmp("username",line_buf+i,8) == 0) {
346 temp_val = strchr(line_buf + i,'=');
347 if(temp_val) {
348 /* go past equals sign */
349 temp_val++;
350 for(length = 0;length<4087;length++) {
351 if ((temp_val[length] == '\n')
352 || (temp_val[length] == '\0')) {
353 temp_val[length] = '\0';
354 break;
357 if(length > 4086) {
358 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
359 memset(line_buf,0,4096);
360 exit(EX_USAGE);
361 } else {
362 got_user = 1;
363 user_name = (char *)calloc(1 + length,1);
364 /* BB adding free of user_name string before exit,
365 not really necessary but would be cleaner */
366 strlcpy(user_name,temp_val, length+1);
369 } else if (strncasecmp("password",line_buf+i,8) == 0) {
370 temp_val = strchr(line_buf+i,'=');
371 if(temp_val) {
372 /* go past equals sign */
373 temp_val++;
374 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
375 if ((temp_val[length] == '\n')
376 || (temp_val[length] == '\0')) {
377 temp_val[length] = '\0';
378 break;
381 if(length > MOUNT_PASSWD_SIZE) {
382 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
383 memset(line_buf,0, 4096);
384 exit(EX_USAGE);
385 } else {
386 if(mountpassword == NULL) {
387 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
388 } else
389 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
390 if(mountpassword) {
391 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
392 got_password = 1;
396 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
397 temp_val = strchr(line_buf+i,'=');
398 if(temp_val) {
399 /* go past equals sign */
400 temp_val++;
401 if(verboseflag)
402 fprintf(stderr, "\nDomain %s\n",temp_val);
403 for(length = 0;length<DOMAIN_SIZE+1;length++) {
404 if ((temp_val[length] == '\n')
405 || (temp_val[length] == '\0')) {
406 temp_val[length] = '\0';
407 break;
410 if(length > DOMAIN_SIZE) {
411 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
412 exit(EX_USAGE);
413 } else {
414 if(domain_name == NULL) {
415 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
416 } else
417 memset(domain_name,0,DOMAIN_SIZE);
418 if(domain_name) {
419 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
420 got_domain = 1;
427 fclose(fs);
428 SAFE_FREE(line_buf);
429 return 0;
432 static int get_password_from_file(int file_descript, char * filename)
434 int rc = 0;
435 int i;
436 char c;
438 if(mountpassword == NULL)
439 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
440 else
441 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
443 if (mountpassword == NULL) {
444 fprintf(stderr, "malloc failed\n");
445 exit(EX_SYSERR);
448 if(filename != NULL) {
449 rc = access(filename, R_OK);
450 if (rc) {
451 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
452 filename, strerror(errno));
453 exit(EX_SYSERR);
455 file_descript = open(filename, O_RDONLY);
456 if(file_descript < 0) {
457 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
458 strerror(errno),filename);
459 exit(EX_SYSERR);
462 /* else file already open and fd provided */
464 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
465 rc = read(file_descript,&c,1);
466 if(rc < 0) {
467 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
468 if(filename != NULL)
469 close(file_descript);
470 exit(EX_SYSERR);
471 } else if(rc == 0) {
472 if(mountpassword[0] == 0) {
473 if(verboseflag)
474 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
476 break;
477 } else /* read valid character */ {
478 if((c == 0) || (c == '\n')) {
479 mountpassword[i] = '\0';
480 break;
481 } else
482 mountpassword[i] = c;
485 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
486 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
487 MOUNT_PASSWD_SIZE);
489 got_password = 1;
490 if(filename != NULL) {
491 close(file_descript);
494 return rc;
497 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
499 const char * data;
500 char * percent_char = NULL;
501 char * value = NULL;
502 char * next_keyword = NULL;
503 char * out = NULL;
504 int out_len = 0;
505 int word_len;
506 int rc = 0;
507 char user[32];
508 char group[32];
510 if (!optionsp || !*optionsp)
511 return 1;
512 data = *optionsp;
514 /* BB fixme check for separator override BB */
516 if (getuid()) {
517 got_uid = 1;
518 snprintf(user,sizeof(user),"%u",getuid());
519 got_gid = 1;
520 snprintf(group,sizeof(group),"%u",getgid());
523 /* while ((data = strsep(&options, ",")) != NULL) { */
524 while(data != NULL) {
525 /* check if ends with trailing comma */
526 if(*data == 0)
527 break;
529 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
530 /* data = next keyword */
531 /* value = next value ie stuff after equal sign */
533 next_keyword = strchr(data,','); /* BB handle sep= */
535 /* temporarily null terminate end of keyword=value pair */
536 if(next_keyword)
537 *next_keyword++ = 0;
539 /* temporarily null terminate keyword to make keyword and value distinct */
540 if ((value = strchr(data, '=')) != NULL) {
541 *value = '\0';
542 value++;
545 if (strncmp(data, "users",5) == 0) {
546 if(!value || !*value) {
547 *filesys_flags |= MS_USERS;
548 goto nocopy;
550 } else if (strncmp(data, "user_xattr",10) == 0) {
551 /* do nothing - need to skip so not parsed as user name */
552 } else if (strncmp(data, "user", 4) == 0) {
554 if (!value || !*value) {
555 if(data[4] == '\0') {
556 *filesys_flags |= MS_USER;
557 goto nocopy;
558 } else {
559 fprintf(stderr, "username specified with no parameter\n");
560 SAFE_FREE(out);
561 return 1; /* needs_arg; */
563 } else {
564 if (strnlen(value, 260) < 260) {
565 got_user=1;
566 percent_char = strchr(value,'%');
567 if(percent_char) {
568 *percent_char = ',';
569 if(mountpassword == NULL)
570 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
571 if(mountpassword) {
572 if(got_password)
573 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
574 got_password = 1;
575 percent_char++;
576 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
577 /* remove password from username */
578 while(*percent_char != 0) {
579 *percent_char = ',';
580 percent_char++;
584 /* this is only case in which the user
585 name buf is not malloc - so we have to
586 check for domain name embedded within
587 the user name here since the later
588 call to check_for_domain will not be
589 invoked */
590 domain_name = check_for_domain(&value);
591 } else {
592 fprintf(stderr, "username too long\n");
593 SAFE_FREE(out);
594 return 1;
597 } else if (strncmp(data, "pass", 4) == 0) {
598 if (!value || !*value) {
599 if(got_password) {
600 fprintf(stderr, "\npassword specified twice, ignoring second\n");
601 } else
602 got_password = 1;
603 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
604 if (got_password) {
605 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
606 } else {
607 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
608 if (!mountpassword) {
609 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
610 SAFE_FREE(out);
611 return 1;
613 got_password = 1;
615 } else {
616 fprintf(stderr, "password too long\n");
617 SAFE_FREE(out);
618 return 1;
620 goto nocopy;
621 } else if (strncmp(data, "sec", 3) == 0) {
622 if (value) {
623 if (!strncmp(value, "none", 4) ||
624 !strncmp(value, "krb5", 4))
625 got_password = 1;
627 } else if (strncmp(data, "ip", 2) == 0) {
628 if (!value || !*value) {
629 fprintf(stderr, "target ip address argument missing");
630 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
631 if(verboseflag)
632 fprintf(stderr, "ip address %s override specified\n",value);
633 got_ip = 1;
634 } else {
635 fprintf(stderr, "ip address too long\n");
636 SAFE_FREE(out);
637 return 1;
639 } else if ((strncmp(data, "unc", 3) == 0)
640 || (strncmp(data, "target", 6) == 0)
641 || (strncmp(data, "path", 4) == 0)) {
642 if (!value || !*value) {
643 fprintf(stderr, "invalid path to network resource\n");
644 SAFE_FREE(out);
645 return 1; /* needs_arg; */
646 } else if(strnlen(value,5) < 5) {
647 fprintf(stderr, "UNC name too short");
650 if (strnlen(value, 300) < 300) {
651 got_unc = 1;
652 if (strncmp(value, "//", 2) == 0) {
653 if(got_unc)
654 fprintf(stderr, "unc name specified twice, ignoring second\n");
655 else
656 got_unc = 1;
657 } else if (strncmp(value, "\\\\", 2) != 0) {
658 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
659 SAFE_FREE(out);
660 return 1;
661 } else {
662 if(got_unc)
663 fprintf(stderr, "unc name specified twice, ignoring second\n");
664 else
665 got_unc = 1;
667 } else {
668 fprintf(stderr, "CIFS: UNC name too long\n");
669 SAFE_FREE(out);
670 return 1;
672 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
673 || (strncmp(data, "workg", 5) == 0)) {
674 /* note this allows for synonyms of "domain"
675 such as "DOM" and "dom" and "workgroup"
676 and "WORKGRP" etc. */
677 if (!value || !*value) {
678 fprintf(stderr, "CIFS: invalid domain name\n");
679 SAFE_FREE(out);
680 return 1; /* needs_arg; */
682 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
683 got_domain = 1;
684 } else {
685 fprintf(stderr, "domain name too long\n");
686 SAFE_FREE(out);
687 return 1;
689 } else if (strncmp(data, "cred", 4) == 0) {
690 if (value && *value) {
691 rc = open_cred_file(value);
692 if(rc) {
693 fprintf(stderr, "error %d (%s) opening credential file %s\n",
694 rc, strerror(rc), value);
695 SAFE_FREE(out);
696 return 1;
698 } else {
699 fprintf(stderr, "invalid credential file name specified\n");
700 SAFE_FREE(out);
701 return 1;
703 } else if (strncmp(data, "uid", 3) == 0) {
704 if (value && *value) {
705 got_uid = 1;
706 if (!isdigit(*value)) {
707 struct passwd *pw;
709 if (!(pw = getpwnam(value))) {
710 fprintf(stderr, "bad user name \"%s\"\n", value);
711 exit(EX_USAGE);
713 snprintf(user, sizeof(user), "%u", pw->pw_uid);
714 } else {
715 strlcpy(user,value,sizeof(user));
718 goto nocopy;
719 } else if (strncmp(data, "gid", 3) == 0) {
720 if (value && *value) {
721 got_gid = 1;
722 if (!isdigit(*value)) {
723 struct group *gr;
725 if (!(gr = getgrnam(value))) {
726 fprintf(stderr, "bad group name \"%s\"\n", value);
727 exit(EX_USAGE);
729 snprintf(group, sizeof(group), "%u", gr->gr_gid);
730 } else {
731 strlcpy(group,value,sizeof(group));
734 goto nocopy;
735 /* fmask and dmask synonyms for people used to smbfs syntax */
736 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
737 if (!value || !*value) {
738 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
739 SAFE_FREE(out);
740 return 1;
743 if (value[0] != '0') {
744 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
747 if (strcmp (data, "fmask") == 0) {
748 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
749 data = "file_mode"; /* BB fix this */
751 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
752 if (!value || !*value) {
753 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
754 SAFE_FREE(out);
755 return 1;
758 if (value[0] != '0') {
759 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
762 if (strcmp (data, "dmask") == 0) {
763 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
764 data = "dir_mode";
766 /* the following eight mount options should be
767 stripped out from what is passed into the kernel
768 since these eight options are best passed as the
769 mount flags rather than redundantly to the kernel
770 and could generate spurious warnings depending on the
771 level of the corresponding cifs vfs kernel code */
772 } else if (strncmp(data, "nosuid", 6) == 0) {
773 *filesys_flags |= MS_NOSUID;
774 } else if (strncmp(data, "suid", 4) == 0) {
775 *filesys_flags &= ~MS_NOSUID;
776 } else if (strncmp(data, "nodev", 5) == 0) {
777 *filesys_flags |= MS_NODEV;
778 } else if ((strncmp(data, "nobrl", 5) == 0) ||
779 (strncmp(data, "nolock", 6) == 0)) {
780 *filesys_flags &= ~MS_MANDLOCK;
781 } else if (strncmp(data, "dev", 3) == 0) {
782 *filesys_flags &= ~MS_NODEV;
783 } else if (strncmp(data, "noexec", 6) == 0) {
784 *filesys_flags |= MS_NOEXEC;
785 } else if (strncmp(data, "exec", 4) == 0) {
786 *filesys_flags &= ~MS_NOEXEC;
787 } else if (strncmp(data, "guest", 5) == 0) {
788 user_name = (char *)calloc(1, 1);
789 got_user = 1;
790 got_password = 1;
791 } else if (strncmp(data, "ro", 2) == 0) {
792 *filesys_flags |= MS_RDONLY;
793 goto nocopy;
794 } else if (strncmp(data, "rw", 2) == 0) {
795 *filesys_flags &= ~MS_RDONLY;
796 goto nocopy;
797 } else if (strncmp(data, "remount", 7) == 0) {
798 *filesys_flags |= MS_REMOUNT;
799 } /* else if (strnicmp(data, "port", 4) == 0) {
800 if (value && *value) {
801 vol->port =
802 simple_strtoul(value, &value, 0);
804 } else if (strnicmp(data, "rsize", 5) == 0) {
805 if (value && *value) {
806 vol->rsize =
807 simple_strtoul(value, &value, 0);
809 } else if (strnicmp(data, "wsize", 5) == 0) {
810 if (value && *value) {
811 vol->wsize =
812 simple_strtoul(value, &value, 0);
814 } else if (strnicmp(data, "version", 3) == 0) {
815 } else {
816 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
817 } */ /* nothing to do on those four mount options above.
818 Just pass to kernel and ignore them here */
820 /* Copy (possibly modified) option to out */
821 word_len = strlen(data);
822 if (value)
823 word_len += 1 + strlen(value);
825 out = (char *)realloc(out, out_len + word_len + 2);
826 if (out == NULL) {
827 perror("malloc");
828 exit(EX_SYSERR);
831 if (out_len) {
832 strlcat(out, ",", out_len + word_len + 2);
833 out_len++;
836 if (value)
837 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
838 else
839 snprintf(out + out_len, word_len + 1, "%s", data);
840 out_len = strlen(out);
842 nocopy:
843 data = next_keyword;
846 /* special-case the uid and gid */
847 if (got_uid) {
848 word_len = strlen(user);
850 out = (char *)realloc(out, out_len + word_len + 6);
851 if (out == NULL) {
852 perror("malloc");
853 exit(EX_SYSERR);
856 if (out_len) {
857 strlcat(out, ",", out_len + word_len + 6);
858 out_len++;
860 snprintf(out + out_len, word_len + 5, "uid=%s", user);
861 out_len = strlen(out);
863 if (got_gid) {
864 word_len = strlen(group);
866 out = (char *)realloc(out, out_len + 1 + word_len + 6);
867 if (out == NULL) {
868 perror("malloc");
869 exit(EX_SYSERR);
872 if (out_len) {
873 strlcat(out, ",", out_len + word_len + 6);
874 out_len++;
876 snprintf(out + out_len, word_len + 5, "gid=%s", group);
877 out_len = strlen(out);
880 SAFE_FREE(*optionsp);
881 *optionsp = out;
882 return 0;
885 /* replace all (one or more) commas with double commas */
886 static void check_for_comma(char ** ppasswrd)
888 char *new_pass_buf;
889 char *pass;
890 int i,j;
891 int number_of_commas = 0;
892 int len;
894 if(ppasswrd == NULL)
895 return;
896 else
897 (pass = *ppasswrd);
899 len = strlen(pass);
901 for(i=0;i<len;i++) {
902 if(pass[i] == ',')
903 number_of_commas++;
906 if(number_of_commas == 0)
907 return;
908 if(number_of_commas > MOUNT_PASSWD_SIZE) {
909 /* would otherwise overflow the mount options buffer */
910 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
911 return;
914 new_pass_buf = (char *)malloc(len+number_of_commas+1);
915 if(new_pass_buf == NULL)
916 return;
918 for(i=0,j=0;i<len;i++,j++) {
919 new_pass_buf[j] = pass[i];
920 if(pass[i] == ',') {
921 j++;
922 new_pass_buf[j] = pass[i];
925 new_pass_buf[len+number_of_commas] = 0;
927 SAFE_FREE(*ppasswrd);
928 *ppasswrd = new_pass_buf;
930 return;
933 /* Usernames can not have backslash in them and we use
934 [BB check if usernames can have forward slash in them BB]
935 backslash as domain\user separator character
937 static char * check_for_domain(char **ppuser)
939 char * original_string;
940 char * usernm;
941 char * domainnm;
942 int original_len;
943 int len;
944 int i;
946 if(ppuser == NULL)
947 return NULL;
949 original_string = *ppuser;
951 if (original_string == NULL)
952 return NULL;
954 original_len = strlen(original_string);
956 usernm = strchr(*ppuser,'/');
957 if (usernm == NULL) {
958 usernm = strchr(*ppuser,'\\');
959 if (usernm == NULL)
960 return NULL;
963 if(got_domain) {
964 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
965 return NULL;
968 usernm[0] = 0;
969 domainnm = *ppuser;
970 if (domainnm[0] != 0) {
971 got_domain = 1;
972 } else {
973 fprintf(stderr, "null domain\n");
975 len = strlen(domainnm);
976 /* reset domainm to new buffer, and copy
977 domain name into it */
978 domainnm = (char *)malloc(len+1);
979 if(domainnm == NULL)
980 return NULL;
982 strlcpy(domainnm,*ppuser,len+1);
984 /* move_string(*ppuser, usernm+1) */
985 len = strlen(usernm+1);
987 if(len >= original_len) {
988 /* should not happen */
989 return domainnm;
992 for(i=0;i<original_len;i++) {
993 if(i<len)
994 original_string[i] = usernm[i+1];
995 else /* stuff with commas to remove last parm */
996 original_string[i] = ',';
999 /* BB add check for more than one slash?
1000 strchr(*ppuser,'/');
1001 strchr(*ppuser,'\\')
1004 return domainnm;
1007 /* replace all occurances of "from" in a string with "to" */
1008 static void replace_char(char *string, char from, char to, int maxlen)
1010 char *lastchar = string + maxlen;
1011 while (string) {
1012 string = strchr(string, from);
1013 if (string) {
1014 *string = to;
1015 if (string >= lastchar)
1016 return;
1021 /* Note that caller frees the returned buffer if necessary */
1022 static struct addrinfo *
1023 parse_server(char ** punc_name)
1025 char * unc_name = *punc_name;
1026 int length = strnlen(unc_name, MAX_UNC_LEN);
1027 char * share;
1028 struct addrinfo *addrlist;
1029 int rc;
1031 if(length > (MAX_UNC_LEN - 1)) {
1032 fprintf(stderr, "mount error: UNC name too long");
1033 return NULL;
1035 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1036 (strncasecmp("smb://", unc_name, 6) == 0)) {
1037 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1038 return NULL;
1041 if(length < 3) {
1042 /* BB add code to find DFS root here */
1043 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1044 return NULL;
1045 } else {
1046 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1047 /* check for nfs syntax ie server:share */
1048 share = strchr(unc_name,':');
1049 if(share) {
1050 *punc_name = (char *)malloc(length+3);
1051 if(*punc_name == NULL) {
1052 /* put the original string back if
1053 no memory left */
1054 *punc_name = unc_name;
1055 return NULL;
1057 *share = '/';
1058 strlcpy((*punc_name)+2,unc_name,length+1);
1059 SAFE_FREE(unc_name);
1060 unc_name = *punc_name;
1061 unc_name[length+2] = 0;
1062 goto continue_unc_parsing;
1063 } else {
1064 fprintf(stderr, "mount error: improperly formatted UNC name.");
1065 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1066 return NULL;
1068 } else {
1069 continue_unc_parsing:
1070 unc_name[0] = '/';
1071 unc_name[1] = '/';
1072 unc_name += 2;
1074 /* allow for either delimiter between host and sharename */
1075 if ((share = strpbrk(unc_name, "/\\"))) {
1076 *share = 0; /* temporarily terminate the string */
1077 share += 1;
1078 if(got_ip == 0) {
1079 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1080 if (rc != 0) {
1081 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1082 unc_name, gai_strerror(rc));
1083 addrlist = NULL;
1086 *(share - 1) = '/'; /* put delimiter back */
1088 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1089 if ((prefixpath = strpbrk(share, "/\\"))) {
1090 *prefixpath = 0; /* permanently terminate the string */
1091 if (!strlen(++prefixpath))
1092 prefixpath = NULL; /* this needs to be done explicitly */
1094 if(got_ip) {
1095 if(verboseflag)
1096 fprintf(stderr, "ip address specified explicitly\n");
1097 return NULL;
1099 /* BB should we pass an alternate version of the share name as Unicode */
1101 return addrlist;
1102 } else {
1103 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1104 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1105 return NULL;
1111 static struct option longopts[] = {
1112 { "all", 0, NULL, 'a' },
1113 { "help",0, NULL, 'h' },
1114 { "move",0, NULL, 'm' },
1115 { "bind",0, NULL, 'b' },
1116 { "read-only", 0, NULL, 'r' },
1117 { "ro", 0, NULL, 'r' },
1118 { "verbose", 0, NULL, 'v' },
1119 { "version", 0, NULL, 'V' },
1120 { "read-write", 0, NULL, 'w' },
1121 { "rw", 0, NULL, 'w' },
1122 { "options", 1, NULL, 'o' },
1123 { "type", 1, NULL, 't' },
1124 { "rsize",1, NULL, 'R' },
1125 { "wsize",1, NULL, 'W' },
1126 { "uid", 1, NULL, '1'},
1127 { "gid", 1, NULL, '2'},
1128 { "user",1,NULL,'u'},
1129 { "username",1,NULL,'u'},
1130 { "dom",1,NULL,'d'},
1131 { "domain",1,NULL,'d'},
1132 { "password",1,NULL,'p'},
1133 { "pass",1,NULL,'p'},
1134 { "credentials",1,NULL,'c'},
1135 { "port",1,NULL,'P'},
1136 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1137 { NULL, 0, NULL, 0 }
1140 /* convert a string to uppercase. return false if the string
1141 * wasn't ASCII. Return success on a NULL ptr */
1142 static int
1143 uppercase_string(char *string)
1145 if (!string)
1146 return 1;
1148 while (*string) {
1149 /* check for unicode */
1150 if ((unsigned char) string[0] & 0x80)
1151 return 0;
1152 *string = toupper((unsigned char) *string);
1153 string++;
1156 return 1;
1159 static void print_cifs_mount_version(void)
1161 printf("mount.cifs version: %s.%s%s\n",
1162 MOUNT_CIFS_VERSION_MAJOR,
1163 MOUNT_CIFS_VERSION_MINOR,
1164 MOUNT_CIFS_VENDOR_SUFFIX);
1167 int main(int argc, char ** argv)
1169 int c;
1170 unsigned long flags = MS_MANDLOCK;
1171 char * orgoptions = NULL;
1172 char * share_name = NULL;
1173 const char * ipaddr = NULL;
1174 char * uuid = NULL;
1175 char * mountpoint = NULL;
1176 char * options = NULL;
1177 char * optionstail;
1178 char * resolved_path = NULL;
1179 char * temp;
1180 char * dev_name;
1181 int rc = 0;
1182 int rsize = 0;
1183 int wsize = 0;
1184 int nomtab = 0;
1185 int uid = 0;
1186 int gid = 0;
1187 int optlen = 0;
1188 int orgoptlen = 0;
1189 size_t options_size = 0;
1190 size_t current_len;
1191 int retry = 0; /* set when we have to retry mount with uppercase */
1192 struct addrinfo *addrhead = NULL, *addr;
1193 struct utsname sysinfo;
1194 struct mntent mountent;
1195 struct sockaddr_in *addr4;
1196 struct sockaddr_in6 *addr6;
1197 FILE * pmntfile;
1199 /* setlocale(LC_ALL, "");
1200 bindtextdomain(PACKAGE, LOCALEDIR);
1201 textdomain(PACKAGE); */
1203 if(argc && argv)
1204 thisprogram = argv[0];
1205 else
1206 mount_cifs_usage(stderr);
1208 if(thisprogram == NULL)
1209 thisprogram = "mount.cifs";
1211 uname(&sysinfo);
1212 /* BB add workstation name and domain and pass down */
1214 /* #ifdef _GNU_SOURCE
1215 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1216 #endif */
1217 if(argc > 2) {
1218 dev_name = argv[1];
1219 share_name = strndup(argv[1], MAX_UNC_LEN);
1220 if (share_name == NULL) {
1221 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1222 exit(EX_SYSERR);
1224 mountpoint = argv[2];
1225 } else if (argc == 2) {
1226 if ((strcmp(argv[1], "-V") == 0) ||
1227 (strcmp(argv[1], "--version") == 0))
1229 print_cifs_mount_version();
1230 exit(0);
1233 if ((strcmp(argv[1], "-h") == 0) ||
1234 (strcmp(argv[1], "-?") == 0) ||
1235 (strcmp(argv[1], "--help") == 0))
1236 mount_cifs_usage(stdout);
1238 mount_cifs_usage(stderr);
1239 } else {
1240 mount_cifs_usage(stderr);
1244 /* add sharename in opts string as unc= parm */
1245 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1246 longopts, NULL)) != -1) {
1247 switch (c) {
1248 /* No code to do the following options yet */
1249 /* case 'l':
1250 list_with_volumelabel = 1;
1251 break;
1252 case 'L':
1253 volumelabel = optarg;
1254 break; */
1255 /* case 'a':
1256 ++mount_all;
1257 break; */
1259 case '?':
1260 case 'h': /* help */
1261 mount_cifs_usage(stdout);
1262 case 'n':
1263 ++nomtab;
1264 break;
1265 case 'b':
1266 #ifdef MS_BIND
1267 flags |= MS_BIND;
1268 #else
1269 fprintf(stderr,
1270 "option 'b' (MS_BIND) not supported\n");
1271 #endif
1272 break;
1273 case 'm':
1274 #ifdef MS_MOVE
1275 flags |= MS_MOVE;
1276 #else
1277 fprintf(stderr,
1278 "option 'm' (MS_MOVE) not supported\n");
1279 #endif
1280 break;
1281 case 'o':
1282 orgoptions = strdup(optarg);
1283 break;
1284 case 'r': /* mount readonly */
1285 flags |= MS_RDONLY;
1286 break;
1287 case 'U':
1288 uuid = optarg;
1289 break;
1290 case 'v':
1291 ++verboseflag;
1292 break;
1293 case 'V':
1294 print_cifs_mount_version();
1295 exit (0);
1296 case 'w':
1297 flags &= ~MS_RDONLY;
1298 break;
1299 case 'R':
1300 rsize = atoi(optarg) ;
1301 break;
1302 case 'W':
1303 wsize = atoi(optarg);
1304 break;
1305 case '1':
1306 if (isdigit(*optarg)) {
1307 char *ep;
1309 uid = strtoul(optarg, &ep, 10);
1310 if (*ep) {
1311 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1312 exit(EX_USAGE);
1314 } else {
1315 struct passwd *pw;
1317 if (!(pw = getpwnam(optarg))) {
1318 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1319 exit(EX_USAGE);
1321 uid = pw->pw_uid;
1322 endpwent();
1324 break;
1325 case '2':
1326 if (isdigit(*optarg)) {
1327 char *ep;
1329 gid = strtoul(optarg, &ep, 10);
1330 if (*ep) {
1331 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1332 exit(EX_USAGE);
1334 } else {
1335 struct group *gr;
1337 if (!(gr = getgrnam(optarg))) {
1338 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1339 exit(EX_USAGE);
1341 gid = gr->gr_gid;
1342 endpwent();
1344 break;
1345 case 'u':
1346 got_user = 1;
1347 user_name = optarg;
1348 break;
1349 case 'd':
1350 domain_name = optarg; /* BB fix this - currently ignored */
1351 got_domain = 1;
1352 break;
1353 case 'p':
1354 if(mountpassword == NULL)
1355 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1356 if(mountpassword) {
1357 got_password = 1;
1358 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1360 break;
1361 case 'S':
1362 get_password_from_file(0 /* stdin */,NULL);
1363 break;
1364 case 't':
1365 break;
1366 case 'f':
1367 ++fakemnt;
1368 break;
1369 default:
1370 fprintf(stderr, "unknown mount option %c\n",c);
1371 mount_cifs_usage(stderr);
1375 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1376 mount_cifs_usage(stderr);
1379 /* make sure mountpoint is legit */
1380 rc = chdir(mountpoint);
1381 if (rc) {
1382 fprintf(stderr, "Couldn't chdir to %s: %s\n", mountpoint,
1383 strerror(errno));
1384 rc = EX_USAGE;
1385 goto mount_exit;
1388 rc = check_mountpoint(thisprogram, mountpoint);
1389 if (rc)
1390 goto mount_exit;
1392 /* sanity check for unprivileged mounts */
1393 if (getuid()) {
1394 rc = check_fstab(thisprogram, mountpoint, dev_name,
1395 &orgoptions);
1396 if (rc)
1397 goto mount_exit;
1399 /* enable any default user mount flags */
1400 flags |= CIFS_SETUID_FLAGS;
1403 if (getenv("PASSWD")) {
1404 if(mountpassword == NULL)
1405 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1406 if(mountpassword) {
1407 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1408 got_password = 1;
1410 } else if (getenv("PASSWD_FD")) {
1411 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1412 } else if (getenv("PASSWD_FILE")) {
1413 get_password_from_file(0, getenv("PASSWD_FILE"));
1416 if (orgoptions && parse_options(&orgoptions, &flags)) {
1417 rc = EX_USAGE;
1418 goto mount_exit;
1421 if (getuid()) {
1422 #if !CIFS_LEGACY_SETUID_CHECK
1423 if (!(flags & (MS_USERS|MS_USER))) {
1424 fprintf(stderr, "%s: permission denied\n", thisprogram);
1425 rc = EX_USAGE;
1426 goto mount_exit;
1428 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1430 if (geteuid()) {
1431 fprintf(stderr, "%s: not installed setuid - \"user\" "
1432 "CIFS mounts not supported.",
1433 thisprogram);
1434 rc = EX_FAIL;
1435 goto mount_exit;
1439 flags &= ~(MS_USERS|MS_USER);
1441 addrhead = addr = parse_server(&share_name);
1442 if((addrhead == NULL) && (got_ip == 0)) {
1443 fprintf(stderr, "No ip address specified and hostname not found\n");
1444 rc = EX_USAGE;
1445 goto mount_exit;
1448 /* BB save off path and pop after mount returns? */
1449 resolved_path = (char *)malloc(PATH_MAX+1);
1450 if (!resolved_path) {
1451 fprintf(stderr, "Unable to allocate memory.\n");
1452 rc = EX_SYSERR;
1453 goto mount_exit;
1456 /* Note that if we can not canonicalize the name, we get
1457 another chance to see if it is valid when we chdir to it */
1458 if(!realpath(".", resolved_path)) {
1459 fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
1460 mountpoint, strerror(errno));
1461 rc = EX_SYSERR;
1462 goto mount_exit;
1465 mountpoint = resolved_path;
1467 if(got_user == 0) {
1468 /* Note that the password will not be retrieved from the
1469 USER env variable (ie user%password form) as there is
1470 already a PASSWD environment varaible */
1471 if (getenv("USER"))
1472 user_name = strdup(getenv("USER"));
1473 if (user_name == NULL)
1474 user_name = getusername();
1475 got_user = 1;
1478 if(got_password == 0) {
1479 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1480 no good replacement yet. */
1481 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1482 if (!tmp_pass || !mountpassword) {
1483 fprintf(stderr, "Password not entered, exiting\n");
1484 exit(EX_USAGE);
1486 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1487 got_password = 1;
1489 /* FIXME launch daemon (handles dfs name resolution and credential change)
1490 remember to clear parms and overwrite password field before launching */
1491 if(orgoptions) {
1492 optlen = strlen(orgoptions);
1493 orgoptlen = optlen;
1494 } else
1495 optlen = 0;
1496 if(share_name)
1497 optlen += strlen(share_name) + 4;
1498 else {
1499 fprintf(stderr, "No server share name specified\n");
1500 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1501 exit(EX_USAGE);
1503 if(user_name)
1504 optlen += strlen(user_name) + 6;
1505 optlen += MAX_ADDRESS_LEN + 4;
1506 if(mountpassword)
1507 optlen += strlen(mountpassword) + 6;
1508 mount_retry:
1509 SAFE_FREE(options);
1510 options_size = optlen + 10 + DOMAIN_SIZE;
1511 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 */);
1513 if(options == NULL) {
1514 fprintf(stderr, "Could not allocate memory for mount options\n");
1515 exit(EX_SYSERR);
1518 strlcpy(options, "unc=", options_size);
1519 strlcat(options,share_name,options_size);
1520 /* scan backwards and reverse direction of slash */
1521 temp = strrchr(options, '/');
1522 if(temp > options + 6)
1523 *temp = '\\';
1524 if(user_name) {
1525 /* check for syntax like user=domain\user */
1526 if(got_domain == 0)
1527 domain_name = check_for_domain(&user_name);
1528 strlcat(options,",user=",options_size);
1529 strlcat(options,user_name,options_size);
1531 if(retry == 0) {
1532 if(domain_name) {
1533 /* extra length accounted for in option string above */
1534 strlcat(options,",domain=",options_size);
1535 strlcat(options,domain_name,options_size);
1539 strlcat(options,",ver=",options_size);
1540 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1542 if(orgoptions) {
1543 strlcat(options,",",options_size);
1544 strlcat(options,orgoptions,options_size);
1546 if(prefixpath) {
1547 strlcat(options,",prefixpath=",options_size);
1548 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1551 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1552 replace_char(dev_name, '\\', '/', strlen(share_name));
1554 if (!got_ip && addr) {
1555 strlcat(options, ",ip=", options_size);
1556 current_len = strnlen(options, options_size);
1557 optionstail = options + current_len;
1558 switch (addr->ai_addr->sa_family) {
1559 case AF_INET6:
1560 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1561 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1562 options_size - current_len);
1563 break;
1564 case AF_INET:
1565 addr4 = (struct sockaddr_in *) addr->ai_addr;
1566 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1567 options_size - current_len);
1568 break;
1569 default:
1570 ipaddr = NULL;
1573 /* if the address looks bogus, try the next one */
1574 if (!ipaddr) {
1575 addr = addr->ai_next;
1576 if (addr)
1577 goto mount_retry;
1578 rc = EX_SYSERR;
1579 goto mount_exit;
1583 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1584 strlcat(options, "%", options_size);
1585 current_len = strnlen(options, options_size);
1586 optionstail = options + current_len;
1587 snprintf(optionstail, options_size - current_len, "%u",
1588 addr6->sin6_scope_id);
1591 if(verboseflag)
1592 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1594 if (mountpassword) {
1596 * Commas have to be doubled, or else they will
1597 * look like the parameter separator
1599 if(retry == 0)
1600 check_for_comma(&mountpassword);
1601 strlcat(options,",pass=",options_size);
1602 strlcat(options,mountpassword,options_size);
1603 if (verboseflag)
1604 fprintf(stderr, ",pass=********");
1607 if (verboseflag)
1608 fprintf(stderr, "\n");
1610 if (!fakemnt && mount(dev_name, ".", cifs_fstype, flags, options)) {
1611 switch (errno) {
1612 case ECONNREFUSED:
1613 case EHOSTUNREACH:
1614 if (addr) {
1615 addr = addr->ai_next;
1616 if (addr)
1617 goto mount_retry;
1619 break;
1620 case ENODEV:
1621 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1622 break;
1623 case ENXIO:
1624 if(retry == 0) {
1625 retry = 1;
1626 if (uppercase_string(dev_name) &&
1627 uppercase_string(share_name) &&
1628 uppercase_string(prefixpath)) {
1629 fprintf(stderr, "retrying with upper case share name\n");
1630 goto mount_retry;
1634 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1635 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1636 "mount.cifs)\n");
1637 rc = EX_FAIL;
1638 goto mount_exit;
1641 if (nomtab)
1642 goto mount_exit;
1643 atexit(unlock_mtab);
1644 rc = lock_mtab();
1645 if (rc) {
1646 fprintf(stderr, "cannot lock mtab");
1647 goto mount_exit;
1649 pmntfile = setmntent(MOUNTED, "a+");
1650 if (!pmntfile) {
1651 fprintf(stderr, "could not update mount table\n");
1652 unlock_mtab();
1653 rc = EX_FILEIO;
1654 goto mount_exit;
1656 mountent.mnt_fsname = dev_name;
1657 mountent.mnt_dir = mountpoint;
1658 mountent.mnt_type = (char *)(void *)cifs_fstype;
1659 mountent.mnt_opts = (char *)malloc(220);
1660 if(mountent.mnt_opts) {
1661 char * mount_user = getusername();
1662 memset(mountent.mnt_opts,0,200);
1663 if(flags & MS_RDONLY)
1664 strlcat(mountent.mnt_opts,"ro",220);
1665 else
1666 strlcat(mountent.mnt_opts,"rw",220);
1667 if(flags & MS_MANDLOCK)
1668 strlcat(mountent.mnt_opts,",mand",220);
1669 if(flags & MS_NOEXEC)
1670 strlcat(mountent.mnt_opts,",noexec",220);
1671 if(flags & MS_NOSUID)
1672 strlcat(mountent.mnt_opts,",nosuid",220);
1673 if(flags & MS_NODEV)
1674 strlcat(mountent.mnt_opts,",nodev",220);
1675 if(flags & MS_SYNCHRONOUS)
1676 strlcat(mountent.mnt_opts,",sync",220);
1677 if(mount_user) {
1678 if(getuid() != 0) {
1679 strlcat(mountent.mnt_opts,
1680 ",user=", 220);
1681 strlcat(mountent.mnt_opts,
1682 mount_user, 220);
1686 mountent.mnt_freq = 0;
1687 mountent.mnt_passno = 0;
1688 rc = addmntent(pmntfile,&mountent);
1689 endmntent(pmntfile);
1690 unlock_mtab();
1691 SAFE_FREE(mountent.mnt_opts);
1692 if (rc)
1693 rc = EX_FILEIO;
1694 mount_exit:
1695 if(mountpassword) {
1696 int len = strlen(mountpassword);
1697 memset(mountpassword,0,len);
1698 SAFE_FREE(mountpassword);
1701 if (addrhead)
1702 freeaddrinfo(addrhead);
1703 SAFE_FREE(options);
1704 SAFE_FREE(orgoptions);
1705 SAFE_FREE(resolved_path);
1706 SAFE_FREE(share_name);
1707 exit(rc);