s3-nsstest: drastically shrink size and dependencies of nsstest binary.
[Samba.git] / client / mount.cifs.c
blob3baaad793713dbbe224348126cd57042f6d002f6
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 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
81 #ifndef SAFE_FREE
82 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
83 #endif
85 #define MOUNT_PASSWD_SIZE 128
86 #define DOMAIN_SIZE 64
88 /* currently maximum length of IPv6 address string */
89 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
92 * By default, mount.cifs follows the conventions set forth by /bin/mount
93 * for user mounts. That is, it requires that the mount be listed in
94 * /etc/fstab with the "user" option when run as an unprivileged user and
95 * mount.cifs is setuid root.
97 * Older versions of mount.cifs however were "looser" in this regard. When
98 * made setuid root, a user could run mount.cifs directly and mount any share
99 * on a directory owned by that user.
101 * The legacy behavior is now disabled by default. To reenable it, set the
102 * following #define to true.
104 #define CIFS_LEGACY_SETUID_CHECK 0
107 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
108 * flags by default. These defaults can be changed here.
110 #define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
112 const char *thisprogram;
113 int verboseflag = 0;
114 int fakemnt = 0;
115 static int got_password = 0;
116 static int got_user = 0;
117 static int got_domain = 0;
118 static int got_ip = 0;
119 static int got_unc = 0;
120 static int got_uid = 0;
121 static int got_gid = 0;
122 static char * user_name = NULL;
123 static char * mountpassword = NULL;
124 char * domain_name = NULL;
125 char * prefixpath = NULL;
127 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
128 * don't link to libreplace so need them here. */
130 /* like strncpy but does not 0 fill the buffer and always null
131 * terminates. bufsize is the size of the destination buffer */
133 #ifndef HAVE_STRLCPY
134 static size_t strlcpy(char *d, const char *s, size_t bufsize)
136 size_t len = strlen(s);
137 size_t ret = len;
138 if (bufsize <= 0) return 0;
139 if (len >= bufsize) len = bufsize-1;
140 memcpy(d, s, len);
141 d[len] = 0;
142 return ret;
144 #endif
146 /* like strncat but does not 0 fill the buffer and always null
147 * terminates. bufsize is the length of the buffer, which should
148 * be one more than the maximum resulting string length */
150 #ifndef HAVE_STRLCAT
151 static size_t strlcat(char *d, const char *s, size_t bufsize)
153 size_t len1 = strlen(d);
154 size_t len2 = strlen(s);
155 size_t ret = len1 + len2;
157 if (len1+len2 >= bufsize) {
158 if (bufsize < (len1+1)) {
159 return ret;
161 len2 = bufsize - (len1+1);
163 if (len2 > 0) {
164 memcpy(d+len1, s, len2);
165 d[len1+len2] = 0;
167 return ret;
169 #endif
172 * If an unprivileged user is doing the mounting then we need to ensure
173 * that the entry is in /etc/fstab.
175 static int
176 check_mountpoint(const char *progname, char *mountpoint)
178 int err;
179 struct stat statbuf;
181 /* does mountpoint exist and is it a directory? */
182 err = stat(mountpoint, &statbuf);
183 if (err) {
184 fprintf(stderr, "%s: failed to stat %s: %s\n", progname,
185 mountpoint, strerror(errno));
186 return EX_USAGE;
189 if (!S_ISDIR(statbuf.st_mode)) {
190 fprintf(stderr, "%s: %s is not a directory!", progname,
191 mountpoint);
192 return EX_USAGE;
195 #if CIFS_LEGACY_SETUID_CHECK
196 /* do extra checks on mountpoint for legacy setuid behavior */
197 if (!getuid() || geteuid())
198 return 0;
200 if (statbuf.st_uid != getuid()) {
201 fprintf(stderr, "%s: %s is not owned by user\n", progname,
202 mountpoint);
203 return EX_USAGE;
206 if ((statbuf.st_mode & S_IRWXU) != S_IRWXU) {
207 fprintf(stderr, "%s: invalid permissions on %s\n", progname,
208 mountpoint);
209 return EX_USAGE;
211 #endif /* CIFS_LEGACY_SETUID_CHECK */
213 return 0;
216 #if CIFS_LEGACY_SETUID_CHECK
217 static int
218 check_fstab(const char *progname, char *mountpoint, char *devname,
219 char **options)
221 return 0;
223 #else /* CIFS_LEGACY_SETUID_CHECK */
224 static int
225 check_fstab(const char *progname, char *mountpoint, char *devname,
226 char **options)
228 FILE *fstab;
229 struct mntent *mnt;
231 /* make sure this mount is listed in /etc/fstab */
232 fstab = setmntent(_PATH_FSTAB, "r");
233 if (!fstab) {
234 fprintf(stderr, "Couldn't open %s for reading!\n",
235 _PATH_FSTAB);
236 return EX_FILEIO;
239 while((mnt = getmntent(fstab))) {
240 if (!strcmp(mountpoint, mnt->mnt_dir))
241 break;
243 endmntent(fstab);
245 if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
246 fprintf(stderr, "%s: permission denied: no match for "
247 "%s found in %s\n", progname, mountpoint,
248 _PATH_FSTAB);
249 return EX_USAGE;
253 * 'mount' munges the options from fstab before passing them
254 * to us. It is non-trivial to test that we have the correct
255 * set of options. We don't want to trust what the user
256 * gave us, so just take whatever is in /etc/fstab.
258 free(*options);
259 *options = strdup(mnt->mnt_opts);
260 return 0;
262 #endif /* CIFS_LEGACY_SETUID_CHECK */
264 /* BB finish BB
266 cifs_umount
267 open nofollow - avoid symlink exposure?
268 get owner of dir see if matches self or if root
269 call system(umount argv) etc.
271 BB end finish BB */
273 static char * check_for_domain(char **);
276 static void mount_cifs_usage(FILE *stream)
278 fprintf(stream, "\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
279 fprintf(stream, "\nMount the remote target, specified as a UNC name,");
280 fprintf(stream, " to a local directory.\n\nOptions:\n");
281 fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 fprintf(stream, "\nLess commonly used options:");
283 fprintf(stream, "\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 fprintf(stream, "\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 fprintf(stream, "\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 fprintf(stream, "\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 fprintf(stream, "\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 fprintf(stream, "\n\t(e.g. unneeded for mounts to most Samba versions):");
289 fprintf(stream, "\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 fprintf(stream, "\n\nRarely used options:");
291 fprintf(stream, "\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 fprintf(stream, "\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 fprintf(stream, "\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 fprintf(stream, "\n\nOptions are described in more detail in the manual page");
295 fprintf(stream, "\n\tman 8 mount.cifs\n");
296 fprintf(stream, "\nTo display the version number of the mount helper:");
297 fprintf(stream, "\n\t%s -V\n",thisprogram);
299 SAFE_FREE(mountpassword);
301 if (stream == stderr)
302 exit(EX_USAGE);
303 exit(0);
306 /* caller frees username if necessary */
307 static char * getusername(void) {
308 char *username = NULL;
309 struct passwd *password = getpwuid(getuid());
311 if (password) {
312 username = password->pw_name;
314 return username;
317 static int open_cred_file(char * file_name)
319 char * line_buf;
320 char * temp_val;
321 FILE * fs;
322 int i, length;
324 i = access(file_name, R_OK);
325 if (i)
326 return i;
328 fs = fopen(file_name,"r");
329 if(fs == NULL)
330 return errno;
331 line_buf = (char *)malloc(4096);
332 if(line_buf == NULL) {
333 fclose(fs);
334 return ENOMEM;
337 while(fgets(line_buf,4096,fs)) {
338 /* parse line from credential file */
340 /* eat leading white space */
341 for(i=0;i<4086;i++) {
342 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
343 break;
344 /* if whitespace - skip past it */
346 if (strncasecmp("username",line_buf+i,8) == 0) {
347 temp_val = strchr(line_buf + i,'=');
348 if(temp_val) {
349 /* go past equals sign */
350 temp_val++;
351 for(length = 0;length<4087;length++) {
352 if ((temp_val[length] == '\n')
353 || (temp_val[length] == '\0')) {
354 temp_val[length] = '\0';
355 break;
358 if(length > 4086) {
359 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
360 memset(line_buf,0,4096);
361 exit(EX_USAGE);
362 } else {
363 got_user = 1;
364 user_name = (char *)calloc(1 + length,1);
365 /* BB adding free of user_name string before exit,
366 not really necessary but would be cleaner */
367 strlcpy(user_name,temp_val, length+1);
370 } else if (strncasecmp("password",line_buf+i,8) == 0) {
371 temp_val = strchr(line_buf+i,'=');
372 if(temp_val) {
373 /* go past equals sign */
374 temp_val++;
375 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
376 if ((temp_val[length] == '\n')
377 || (temp_val[length] == '\0')) {
378 temp_val[length] = '\0';
379 break;
382 if(length > MOUNT_PASSWD_SIZE) {
383 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
384 memset(line_buf,0, 4096);
385 exit(EX_USAGE);
386 } else {
387 if(mountpassword == NULL) {
388 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
389 } else
390 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
391 if(mountpassword) {
392 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
393 got_password = 1;
397 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
398 temp_val = strchr(line_buf+i,'=');
399 if(temp_val) {
400 /* go past equals sign */
401 temp_val++;
402 if(verboseflag)
403 fprintf(stderr, "\nDomain %s\n",temp_val);
404 for(length = 0;length<DOMAIN_SIZE+1;length++) {
405 if ((temp_val[length] == '\n')
406 || (temp_val[length] == '\0')) {
407 temp_val[length] = '\0';
408 break;
411 if(length > DOMAIN_SIZE) {
412 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
413 exit(EX_USAGE);
414 } else {
415 if(domain_name == NULL) {
416 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
417 } else
418 memset(domain_name,0,DOMAIN_SIZE);
419 if(domain_name) {
420 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
421 got_domain = 1;
428 fclose(fs);
429 SAFE_FREE(line_buf);
430 return 0;
433 static int get_password_from_file(int file_descript, char * filename)
435 int rc = 0;
436 int i;
437 char c;
439 if(mountpassword == NULL)
440 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
441 else
442 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
444 if (mountpassword == NULL) {
445 fprintf(stderr, "malloc failed\n");
446 exit(EX_SYSERR);
449 if(filename != NULL) {
450 rc = access(filename, R_OK);
451 if (rc) {
452 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
453 filename, strerror(errno));
454 exit(EX_SYSERR);
456 file_descript = open(filename, O_RDONLY);
457 if(file_descript < 0) {
458 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
459 strerror(errno),filename);
460 exit(EX_SYSERR);
463 /* else file already open and fd provided */
465 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
466 rc = read(file_descript,&c,1);
467 if(rc < 0) {
468 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
469 if(filename != NULL)
470 close(file_descript);
471 exit(EX_SYSERR);
472 } else if(rc == 0) {
473 if(mountpassword[0] == 0) {
474 if(verboseflag)
475 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
477 break;
478 } else /* read valid character */ {
479 if((c == 0) || (c == '\n')) {
480 mountpassword[i] = '\0';
481 break;
482 } else
483 mountpassword[i] = c;
486 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
487 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
488 MOUNT_PASSWD_SIZE);
490 got_password = 1;
491 if(filename != NULL) {
492 close(file_descript);
495 return rc;
498 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
500 const char * data;
501 char * percent_char = NULL;
502 char * value = NULL;
503 char * next_keyword = NULL;
504 char * out = NULL;
505 int out_len = 0;
506 int word_len;
507 int rc = 0;
508 char user[32];
509 char group[32];
511 if (!optionsp || !*optionsp)
512 return 1;
513 data = *optionsp;
515 /* BB fixme check for separator override BB */
517 if (getuid()) {
518 got_uid = 1;
519 snprintf(user,sizeof(user),"%u",getuid());
520 got_gid = 1;
521 snprintf(group,sizeof(group),"%u",getgid());
524 /* while ((data = strsep(&options, ",")) != NULL) { */
525 while(data != NULL) {
526 /* check if ends with trailing comma */
527 if(*data == 0)
528 break;
530 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
531 /* data = next keyword */
532 /* value = next value ie stuff after equal sign */
534 next_keyword = strchr(data,','); /* BB handle sep= */
536 /* temporarily null terminate end of keyword=value pair */
537 if(next_keyword)
538 *next_keyword++ = 0;
540 /* temporarily null terminate keyword to make keyword and value distinct */
541 if ((value = strchr(data, '=')) != NULL) {
542 *value = '\0';
543 value++;
546 if (strncmp(data, "users",5) == 0) {
547 if(!value || !*value) {
548 *filesys_flags |= MS_USERS;
549 goto nocopy;
551 } else if (strncmp(data, "user_xattr",10) == 0) {
552 /* do nothing - need to skip so not parsed as user name */
553 } else if (strncmp(data, "user", 4) == 0) {
555 if (!value || !*value) {
556 if(data[4] == '\0') {
557 *filesys_flags |= MS_USER;
558 goto nocopy;
559 } else {
560 fprintf(stderr, "username specified with no parameter\n");
561 SAFE_FREE(out);
562 return 1; /* needs_arg; */
564 } else {
565 if (strnlen(value, 260) < 260) {
566 got_user=1;
567 percent_char = strchr(value,'%');
568 if(percent_char) {
569 *percent_char = ',';
570 if(mountpassword == NULL)
571 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
572 if(mountpassword) {
573 if(got_password)
574 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
575 got_password = 1;
576 percent_char++;
577 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
578 /* remove password from username */
579 while(*percent_char != 0) {
580 *percent_char = ',';
581 percent_char++;
585 /* this is only case in which the user
586 name buf is not malloc - so we have to
587 check for domain name embedded within
588 the user name here since the later
589 call to check_for_domain will not be
590 invoked */
591 domain_name = check_for_domain(&value);
592 } else {
593 fprintf(stderr, "username too long\n");
594 SAFE_FREE(out);
595 return 1;
598 } else if (strncmp(data, "pass", 4) == 0) {
599 if (!value || !*value) {
600 if(got_password) {
601 fprintf(stderr, "\npassword specified twice, ignoring second\n");
602 } else
603 got_password = 1;
604 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
605 if (got_password) {
606 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
607 } else {
608 mountpassword = strndup(value, MOUNT_PASSWD_SIZE);
609 if (!mountpassword) {
610 fprintf(stderr, "mount.cifs error: %s", strerror(ENOMEM));
611 SAFE_FREE(out);
612 return 1;
614 got_password = 1;
616 } else {
617 fprintf(stderr, "password too long\n");
618 SAFE_FREE(out);
619 return 1;
621 goto nocopy;
622 } else if (strncmp(data, "sec", 3) == 0) {
623 if (value) {
624 if (!strncmp(value, "none", 4) ||
625 !strncmp(value, "krb5", 4))
626 got_password = 1;
628 } else if (strncmp(data, "ip", 2) == 0) {
629 if (!value || !*value) {
630 fprintf(stderr, "target ip address argument missing");
631 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
632 if(verboseflag)
633 fprintf(stderr, "ip address %s override specified\n",value);
634 got_ip = 1;
635 } else {
636 fprintf(stderr, "ip address too long\n");
637 SAFE_FREE(out);
638 return 1;
640 } else if ((strncmp(data, "unc", 3) == 0)
641 || (strncmp(data, "target", 6) == 0)
642 || (strncmp(data, "path", 4) == 0)) {
643 if (!value || !*value) {
644 fprintf(stderr, "invalid path to network resource\n");
645 SAFE_FREE(out);
646 return 1; /* needs_arg; */
647 } else if(strnlen(value,5) < 5) {
648 fprintf(stderr, "UNC name too short");
651 if (strnlen(value, 300) < 300) {
652 got_unc = 1;
653 if (strncmp(value, "//", 2) == 0) {
654 if(got_unc)
655 fprintf(stderr, "unc name specified twice, ignoring second\n");
656 else
657 got_unc = 1;
658 } else if (strncmp(value, "\\\\", 2) != 0) {
659 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
660 SAFE_FREE(out);
661 return 1;
662 } else {
663 if(got_unc)
664 fprintf(stderr, "unc name specified twice, ignoring second\n");
665 else
666 got_unc = 1;
668 } else {
669 fprintf(stderr, "CIFS: UNC name too long\n");
670 SAFE_FREE(out);
671 return 1;
673 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
674 || (strncmp(data, "workg", 5) == 0)) {
675 /* note this allows for synonyms of "domain"
676 such as "DOM" and "dom" and "workgroup"
677 and "WORKGRP" etc. */
678 if (!value || !*value) {
679 fprintf(stderr, "CIFS: invalid domain name\n");
680 SAFE_FREE(out);
681 return 1; /* needs_arg; */
683 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
684 got_domain = 1;
685 } else {
686 fprintf(stderr, "domain name too long\n");
687 SAFE_FREE(out);
688 return 1;
690 } else if (strncmp(data, "cred", 4) == 0) {
691 if (value && *value) {
692 rc = open_cred_file(value);
693 if(rc) {
694 fprintf(stderr, "error %d (%s) opening credential file %s\n",
695 rc, strerror(rc), value);
696 SAFE_FREE(out);
697 return 1;
699 } else {
700 fprintf(stderr, "invalid credential file name specified\n");
701 SAFE_FREE(out);
702 return 1;
704 } else if (strncmp(data, "uid", 3) == 0) {
705 if (value && *value) {
706 got_uid = 1;
707 if (!isdigit(*value)) {
708 struct passwd *pw;
710 if (!(pw = getpwnam(value))) {
711 fprintf(stderr, "bad user name \"%s\"\n", value);
712 exit(EX_USAGE);
714 snprintf(user, sizeof(user), "%u", pw->pw_uid);
715 } else {
716 strlcpy(user,value,sizeof(user));
719 goto nocopy;
720 } else if (strncmp(data, "gid", 3) == 0) {
721 if (value && *value) {
722 got_gid = 1;
723 if (!isdigit(*value)) {
724 struct group *gr;
726 if (!(gr = getgrnam(value))) {
727 fprintf(stderr, "bad group name \"%s\"\n", value);
728 exit(EX_USAGE);
730 snprintf(group, sizeof(group), "%u", gr->gr_gid);
731 } else {
732 strlcpy(group,value,sizeof(group));
735 goto nocopy;
736 /* fmask and dmask synonyms for people used to smbfs syntax */
737 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
738 if (!value || !*value) {
739 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
740 SAFE_FREE(out);
741 return 1;
744 if (value[0] != '0') {
745 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
748 if (strcmp (data, "fmask") == 0) {
749 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
750 data = "file_mode"; /* BB fix this */
752 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
753 if (!value || !*value) {
754 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
755 SAFE_FREE(out);
756 return 1;
759 if (value[0] != '0') {
760 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
763 if (strcmp (data, "dmask") == 0) {
764 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
765 data = "dir_mode";
767 /* the following eight mount options should be
768 stripped out from what is passed into the kernel
769 since these eight options are best passed as the
770 mount flags rather than redundantly to the kernel
771 and could generate spurious warnings depending on the
772 level of the corresponding cifs vfs kernel code */
773 } else if (strncmp(data, "nosuid", 6) == 0) {
774 *filesys_flags |= MS_NOSUID;
775 } else if (strncmp(data, "suid", 4) == 0) {
776 *filesys_flags &= ~MS_NOSUID;
777 } else if (strncmp(data, "nodev", 5) == 0) {
778 *filesys_flags |= MS_NODEV;
779 } else if ((strncmp(data, "nobrl", 5) == 0) ||
780 (strncmp(data, "nolock", 6) == 0)) {
781 *filesys_flags &= ~MS_MANDLOCK;
782 } else if (strncmp(data, "dev", 3) == 0) {
783 *filesys_flags &= ~MS_NODEV;
784 } else if (strncmp(data, "noexec", 6) == 0) {
785 *filesys_flags |= MS_NOEXEC;
786 } else if (strncmp(data, "exec", 4) == 0) {
787 *filesys_flags &= ~MS_NOEXEC;
788 } else if (strncmp(data, "guest", 5) == 0) {
789 user_name = (char *)calloc(1, 1);
790 got_user = 1;
791 got_password = 1;
792 } else if (strncmp(data, "ro", 2) == 0) {
793 *filesys_flags |= MS_RDONLY;
794 goto nocopy;
795 } else if (strncmp(data, "rw", 2) == 0) {
796 *filesys_flags &= ~MS_RDONLY;
797 goto nocopy;
798 } else if (strncmp(data, "remount", 7) == 0) {
799 *filesys_flags |= MS_REMOUNT;
800 } /* else if (strnicmp(data, "port", 4) == 0) {
801 if (value && *value) {
802 vol->port =
803 simple_strtoul(value, &value, 0);
805 } else if (strnicmp(data, "rsize", 5) == 0) {
806 if (value && *value) {
807 vol->rsize =
808 simple_strtoul(value, &value, 0);
810 } else if (strnicmp(data, "wsize", 5) == 0) {
811 if (value && *value) {
812 vol->wsize =
813 simple_strtoul(value, &value, 0);
815 } else if (strnicmp(data, "version", 3) == 0) {
816 } else {
817 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
818 } */ /* nothing to do on those four mount options above.
819 Just pass to kernel and ignore them here */
821 /* Copy (possibly modified) option to out */
822 word_len = strlen(data);
823 if (value)
824 word_len += 1 + strlen(value);
826 out = (char *)realloc(out, out_len + word_len + 2);
827 if (out == NULL) {
828 perror("malloc");
829 exit(EX_SYSERR);
832 if (out_len) {
833 strlcat(out, ",", out_len + word_len + 2);
834 out_len++;
837 if (value)
838 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
839 else
840 snprintf(out + out_len, word_len + 1, "%s", data);
841 out_len = strlen(out);
843 nocopy:
844 data = next_keyword;
847 /* special-case the uid and gid */
848 if (got_uid) {
849 word_len = strlen(user);
851 out = (char *)realloc(out, out_len + word_len + 6);
852 if (out == NULL) {
853 perror("malloc");
854 exit(EX_SYSERR);
857 if (out_len) {
858 strlcat(out, ",", out_len + word_len + 6);
859 out_len++;
861 snprintf(out + out_len, word_len + 5, "uid=%s", user);
862 out_len = strlen(out);
864 if (got_gid) {
865 word_len = strlen(group);
867 out = (char *)realloc(out, out_len + 1 + word_len + 6);
868 if (out == NULL) {
869 perror("malloc");
870 exit(EX_SYSERR);
873 if (out_len) {
874 strlcat(out, ",", out_len + word_len + 6);
875 out_len++;
877 snprintf(out + out_len, word_len + 5, "gid=%s", group);
878 out_len = strlen(out);
881 SAFE_FREE(*optionsp);
882 *optionsp = out;
883 return 0;
886 /* replace all (one or more) commas with double commas */
887 static void check_for_comma(char ** ppasswrd)
889 char *new_pass_buf;
890 char *pass;
891 int i,j;
892 int number_of_commas = 0;
893 int len;
895 if(ppasswrd == NULL)
896 return;
897 else
898 (pass = *ppasswrd);
900 len = strlen(pass);
902 for(i=0;i<len;i++) {
903 if(pass[i] == ',')
904 number_of_commas++;
907 if(number_of_commas == 0)
908 return;
909 if(number_of_commas > MOUNT_PASSWD_SIZE) {
910 /* would otherwise overflow the mount options buffer */
911 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
912 return;
915 new_pass_buf = (char *)malloc(len+number_of_commas+1);
916 if(new_pass_buf == NULL)
917 return;
919 for(i=0,j=0;i<len;i++,j++) {
920 new_pass_buf[j] = pass[i];
921 if(pass[i] == ',') {
922 j++;
923 new_pass_buf[j] = pass[i];
926 new_pass_buf[len+number_of_commas] = 0;
928 SAFE_FREE(*ppasswrd);
929 *ppasswrd = new_pass_buf;
931 return;
934 /* Usernames can not have backslash in them and we use
935 [BB check if usernames can have forward slash in them BB]
936 backslash as domain\user separator character
938 static char * check_for_domain(char **ppuser)
940 char * original_string;
941 char * usernm;
942 char * domainnm;
943 int original_len;
944 int len;
945 int i;
947 if(ppuser == NULL)
948 return NULL;
950 original_string = *ppuser;
952 if (original_string == NULL)
953 return NULL;
955 original_len = strlen(original_string);
957 usernm = strchr(*ppuser,'/');
958 if (usernm == NULL) {
959 usernm = strchr(*ppuser,'\\');
960 if (usernm == NULL)
961 return NULL;
964 if(got_domain) {
965 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
966 return NULL;
969 usernm[0] = 0;
970 domainnm = *ppuser;
971 if (domainnm[0] != 0) {
972 got_domain = 1;
973 } else {
974 fprintf(stderr, "null domain\n");
976 len = strlen(domainnm);
977 /* reset domainm to new buffer, and copy
978 domain name into it */
979 domainnm = (char *)malloc(len+1);
980 if(domainnm == NULL)
981 return NULL;
983 strlcpy(domainnm,*ppuser,len+1);
985 /* move_string(*ppuser, usernm+1) */
986 len = strlen(usernm+1);
988 if(len >= original_len) {
989 /* should not happen */
990 return domainnm;
993 for(i=0;i<original_len;i++) {
994 if(i<len)
995 original_string[i] = usernm[i+1];
996 else /* stuff with commas to remove last parm */
997 original_string[i] = ',';
1000 /* BB add check for more than one slash?
1001 strchr(*ppuser,'/');
1002 strchr(*ppuser,'\\')
1005 return domainnm;
1008 /* replace all occurances of "from" in a string with "to" */
1009 static void replace_char(char *string, char from, char to, int maxlen)
1011 char *lastchar = string + maxlen;
1012 while (string) {
1013 string = strchr(string, from);
1014 if (string) {
1015 *string = to;
1016 if (string >= lastchar)
1017 return;
1022 /* Note that caller frees the returned buffer if necessary */
1023 static struct addrinfo *
1024 parse_server(char ** punc_name)
1026 char * unc_name = *punc_name;
1027 int length = strnlen(unc_name, MAX_UNC_LEN);
1028 char * share;
1029 struct addrinfo *addrlist;
1030 int rc;
1032 if(length > (MAX_UNC_LEN - 1)) {
1033 fprintf(stderr, "mount error: UNC name too long");
1034 return NULL;
1036 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1037 (strncasecmp("smb://", unc_name, 6) == 0)) {
1038 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1039 return NULL;
1042 if(length < 3) {
1043 /* BB add code to find DFS root here */
1044 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1045 return NULL;
1046 } else {
1047 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1048 /* check for nfs syntax ie server:share */
1049 share = strchr(unc_name,':');
1050 if(share) {
1051 *punc_name = (char *)malloc(length+3);
1052 if(*punc_name == NULL) {
1053 /* put the original string back if
1054 no memory left */
1055 *punc_name = unc_name;
1056 return NULL;
1058 *share = '/';
1059 strlcpy((*punc_name)+2,unc_name,length+1);
1060 SAFE_FREE(unc_name);
1061 unc_name = *punc_name;
1062 unc_name[length+2] = 0;
1063 goto continue_unc_parsing;
1064 } else {
1065 fprintf(stderr, "mount error: improperly formatted UNC name.");
1066 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1067 return NULL;
1069 } else {
1070 continue_unc_parsing:
1071 unc_name[0] = '/';
1072 unc_name[1] = '/';
1073 unc_name += 2;
1075 /* allow for either delimiter between host and sharename */
1076 if ((share = strpbrk(unc_name, "/\\"))) {
1077 *share = 0; /* temporarily terminate the string */
1078 share += 1;
1079 if(got_ip == 0) {
1080 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1081 if (rc != 0) {
1082 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1083 unc_name, gai_strerror(rc));
1084 addrlist = NULL;
1087 *(share - 1) = '/'; /* put delimiter back */
1089 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1090 if ((prefixpath = strpbrk(share, "/\\"))) {
1091 *prefixpath = 0; /* permanently terminate the string */
1092 if (!strlen(++prefixpath))
1093 prefixpath = NULL; /* this needs to be done explicitly */
1095 if(got_ip) {
1096 if(verboseflag)
1097 fprintf(stderr, "ip address specified explicitly\n");
1098 return NULL;
1100 /* BB should we pass an alternate version of the share name as Unicode */
1102 return addrlist;
1103 } else {
1104 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1105 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1106 return NULL;
1112 static struct option longopts[] = {
1113 { "all", 0, NULL, 'a' },
1114 { "help",0, NULL, 'h' },
1115 { "move",0, NULL, 'm' },
1116 { "bind",0, NULL, 'b' },
1117 { "read-only", 0, NULL, 'r' },
1118 { "ro", 0, NULL, 'r' },
1119 { "verbose", 0, NULL, 'v' },
1120 { "version", 0, NULL, 'V' },
1121 { "read-write", 0, NULL, 'w' },
1122 { "rw", 0, NULL, 'w' },
1123 { "options", 1, NULL, 'o' },
1124 { "type", 1, NULL, 't' },
1125 { "rsize",1, NULL, 'R' },
1126 { "wsize",1, NULL, 'W' },
1127 { "uid", 1, NULL, '1'},
1128 { "gid", 1, NULL, '2'},
1129 { "user",1,NULL,'u'},
1130 { "username",1,NULL,'u'},
1131 { "dom",1,NULL,'d'},
1132 { "domain",1,NULL,'d'},
1133 { "password",1,NULL,'p'},
1134 { "pass",1,NULL,'p'},
1135 { "credentials",1,NULL,'c'},
1136 { "port",1,NULL,'P'},
1137 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1138 { NULL, 0, NULL, 0 }
1141 /* convert a string to uppercase. return false if the string
1142 * wasn't ASCII. Return success on a NULL ptr */
1143 static int
1144 uppercase_string(char *string)
1146 if (!string)
1147 return 1;
1149 while (*string) {
1150 /* check for unicode */
1151 if ((unsigned char) string[0] & 0x80)
1152 return 0;
1153 *string = toupper((unsigned char) *string);
1154 string++;
1157 return 1;
1160 static void print_cifs_mount_version(void)
1162 printf("mount.cifs version: %s.%s%s\n",
1163 MOUNT_CIFS_VERSION_MAJOR,
1164 MOUNT_CIFS_VERSION_MINOR,
1165 MOUNT_CIFS_VENDOR_SUFFIX);
1168 int main(int argc, char ** argv)
1170 int c;
1171 unsigned long flags = MS_MANDLOCK;
1172 char * orgoptions = NULL;
1173 char * share_name = NULL;
1174 const char * ipaddr = NULL;
1175 char * uuid = NULL;
1176 char * mountpoint = NULL;
1177 char * options = NULL;
1178 char * optionstail;
1179 char * resolved_path = NULL;
1180 char * temp;
1181 char * dev_name;
1182 int rc = 0;
1183 int rsize = 0;
1184 int wsize = 0;
1185 int nomtab = 0;
1186 int uid = 0;
1187 int gid = 0;
1188 int optlen = 0;
1189 int orgoptlen = 0;
1190 size_t options_size = 0;
1191 size_t current_len;
1192 int retry = 0; /* set when we have to retry mount with uppercase */
1193 struct addrinfo *addrhead = NULL, *addr;
1194 struct utsname sysinfo;
1195 struct mntent mountent;
1196 struct sockaddr_in *addr4;
1197 struct sockaddr_in6 *addr6;
1198 FILE * pmntfile;
1200 /* setlocale(LC_ALL, "");
1201 bindtextdomain(PACKAGE, LOCALEDIR);
1202 textdomain(PACKAGE); */
1204 if(argc && argv)
1205 thisprogram = argv[0];
1206 else
1207 mount_cifs_usage(stderr);
1209 if(thisprogram == NULL)
1210 thisprogram = "mount.cifs";
1212 uname(&sysinfo);
1213 /* BB add workstation name and domain and pass down */
1215 /* #ifdef _GNU_SOURCE
1216 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1217 #endif */
1218 if(argc > 2) {
1219 dev_name = argv[1];
1220 share_name = strndup(argv[1], MAX_UNC_LEN);
1221 if (share_name == NULL) {
1222 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1223 exit(EX_SYSERR);
1225 mountpoint = argv[2];
1226 } else if (argc == 2) {
1227 if ((strcmp(argv[1], "-V") == 0) ||
1228 (strcmp(argv[1], "--version") == 0))
1230 print_cifs_mount_version();
1231 exit(0);
1234 if ((strcmp(argv[1], "-h") == 0) ||
1235 (strcmp(argv[1], "-?") == 0) ||
1236 (strcmp(argv[1], "--help") == 0))
1237 mount_cifs_usage(stdout);
1239 mount_cifs_usage(stderr);
1240 } else {
1241 mount_cifs_usage(stderr);
1245 /* add sharename in opts string as unc= parm */
1246 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1247 longopts, NULL)) != -1) {
1248 switch (c) {
1249 /* No code to do the following options yet */
1250 /* case 'l':
1251 list_with_volumelabel = 1;
1252 break;
1253 case 'L':
1254 volumelabel = optarg;
1255 break; */
1256 /* case 'a':
1257 ++mount_all;
1258 break; */
1260 case '?':
1261 case 'h': /* help */
1262 mount_cifs_usage(stdout);
1263 case 'n':
1264 ++nomtab;
1265 break;
1266 case 'b':
1267 #ifdef MS_BIND
1268 flags |= MS_BIND;
1269 #else
1270 fprintf(stderr,
1271 "option 'b' (MS_BIND) not supported\n");
1272 #endif
1273 break;
1274 case 'm':
1275 #ifdef MS_MOVE
1276 flags |= MS_MOVE;
1277 #else
1278 fprintf(stderr,
1279 "option 'm' (MS_MOVE) not supported\n");
1280 #endif
1281 break;
1282 case 'o':
1283 orgoptions = strdup(optarg);
1284 break;
1285 case 'r': /* mount readonly */
1286 flags |= MS_RDONLY;
1287 break;
1288 case 'U':
1289 uuid = optarg;
1290 break;
1291 case 'v':
1292 ++verboseflag;
1293 break;
1294 case 'V':
1295 print_cifs_mount_version();
1296 exit (0);
1297 case 'w':
1298 flags &= ~MS_RDONLY;
1299 break;
1300 case 'R':
1301 rsize = atoi(optarg) ;
1302 break;
1303 case 'W':
1304 wsize = atoi(optarg);
1305 break;
1306 case '1':
1307 if (isdigit(*optarg)) {
1308 char *ep;
1310 uid = strtoul(optarg, &ep, 10);
1311 if (*ep) {
1312 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1313 exit(EX_USAGE);
1315 } else {
1316 struct passwd *pw;
1318 if (!(pw = getpwnam(optarg))) {
1319 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1320 exit(EX_USAGE);
1322 uid = pw->pw_uid;
1323 endpwent();
1325 break;
1326 case '2':
1327 if (isdigit(*optarg)) {
1328 char *ep;
1330 gid = strtoul(optarg, &ep, 10);
1331 if (*ep) {
1332 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1333 exit(EX_USAGE);
1335 } else {
1336 struct group *gr;
1338 if (!(gr = getgrnam(optarg))) {
1339 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1340 exit(EX_USAGE);
1342 gid = gr->gr_gid;
1343 endpwent();
1345 break;
1346 case 'u':
1347 got_user = 1;
1348 user_name = optarg;
1349 break;
1350 case 'd':
1351 domain_name = optarg; /* BB fix this - currently ignored */
1352 got_domain = 1;
1353 break;
1354 case 'p':
1355 if(mountpassword == NULL)
1356 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1357 if(mountpassword) {
1358 got_password = 1;
1359 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1361 break;
1362 case 'S':
1363 get_password_from_file(0 /* stdin */,NULL);
1364 break;
1365 case 't':
1366 break;
1367 case 'f':
1368 ++fakemnt;
1369 break;
1370 default:
1371 fprintf(stderr, "unknown mount option %c\n",c);
1372 mount_cifs_usage(stderr);
1376 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1377 mount_cifs_usage(stderr);
1380 /* make sure mountpoint is legit */
1381 rc = check_mountpoint(thisprogram, mountpoint);
1382 if (rc)
1383 goto mount_exit;
1385 /* sanity check for unprivileged mounts */
1386 if (getuid()) {
1387 rc = check_fstab(thisprogram, mountpoint, dev_name,
1388 &orgoptions);
1389 if (rc)
1390 goto mount_exit;
1392 /* enable any default user mount flags */
1393 flags |= CIFS_SETUID_FLAGS;
1396 if (getenv("PASSWD")) {
1397 if(mountpassword == NULL)
1398 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1399 if(mountpassword) {
1400 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1401 got_password = 1;
1403 } else if (getenv("PASSWD_FD")) {
1404 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1405 } else if (getenv("PASSWD_FILE")) {
1406 get_password_from_file(0, getenv("PASSWD_FILE"));
1409 if (orgoptions && parse_options(&orgoptions, &flags)) {
1410 rc = EX_USAGE;
1411 goto mount_exit;
1414 if (getuid()) {
1415 #if !CIFS_LEGACY_SETUID_CHECK
1416 if (!(flags & (MS_USERS|MS_USER))) {
1417 fprintf(stderr, "%s: permission denied\n", thisprogram);
1418 rc = EX_USAGE;
1419 goto mount_exit;
1421 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1423 if (geteuid()) {
1424 fprintf(stderr, "%s: not installed setuid - \"user\" "
1425 "CIFS mounts not supported.",
1426 thisprogram);
1427 rc = EX_FAIL;
1428 goto mount_exit;
1432 flags &= ~(MS_USERS|MS_USER);
1434 addrhead = addr = parse_server(&share_name);
1435 if((addrhead == NULL) && (got_ip == 0)) {
1436 fprintf(stderr, "No ip address specified and hostname not found\n");
1437 rc = EX_USAGE;
1438 goto mount_exit;
1441 /* BB save off path and pop after mount returns? */
1442 resolved_path = (char *)malloc(PATH_MAX+1);
1443 if(resolved_path) {
1444 /* Note that if we can not canonicalize the name, we get
1445 another chance to see if it is valid when we chdir to it */
1446 if (realpath(mountpoint, resolved_path)) {
1447 mountpoint = resolved_path;
1450 if(got_user == 0) {
1451 /* Note that the password will not be retrieved from the
1452 USER env variable (ie user%password form) as there is
1453 already a PASSWD environment varaible */
1454 if (getenv("USER"))
1455 user_name = strdup(getenv("USER"));
1456 if (user_name == NULL)
1457 user_name = getusername();
1458 got_user = 1;
1461 if(got_password == 0) {
1462 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1463 no good replacement yet. */
1464 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1465 if (!tmp_pass || !mountpassword) {
1466 fprintf(stderr, "Password not entered, exiting\n");
1467 exit(EX_USAGE);
1469 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1470 got_password = 1;
1472 /* FIXME launch daemon (handles dfs name resolution and credential change)
1473 remember to clear parms and overwrite password field before launching */
1474 if(orgoptions) {
1475 optlen = strlen(orgoptions);
1476 orgoptlen = optlen;
1477 } else
1478 optlen = 0;
1479 if(share_name)
1480 optlen += strlen(share_name) + 4;
1481 else {
1482 fprintf(stderr, "No server share name specified\n");
1483 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1484 exit(EX_USAGE);
1486 if(user_name)
1487 optlen += strlen(user_name) + 6;
1488 optlen += MAX_ADDRESS_LEN + 4;
1489 if(mountpassword)
1490 optlen += strlen(mountpassword) + 6;
1491 mount_retry:
1492 SAFE_FREE(options);
1493 options_size = optlen + 10 + DOMAIN_SIZE;
1494 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 */);
1496 if(options == NULL) {
1497 fprintf(stderr, "Could not allocate memory for mount options\n");
1498 exit(EX_SYSERR);
1501 strlcpy(options, "unc=", options_size);
1502 strlcat(options,share_name,options_size);
1503 /* scan backwards and reverse direction of slash */
1504 temp = strrchr(options, '/');
1505 if(temp > options + 6)
1506 *temp = '\\';
1507 if(user_name) {
1508 /* check for syntax like user=domain\user */
1509 if(got_domain == 0)
1510 domain_name = check_for_domain(&user_name);
1511 strlcat(options,",user=",options_size);
1512 strlcat(options,user_name,options_size);
1514 if(retry == 0) {
1515 if(domain_name) {
1516 /* extra length accounted for in option string above */
1517 strlcat(options,",domain=",options_size);
1518 strlcat(options,domain_name,options_size);
1522 strlcat(options,",ver=",options_size);
1523 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1525 if(orgoptions) {
1526 strlcat(options,",",options_size);
1527 strlcat(options,orgoptions,options_size);
1529 if(prefixpath) {
1530 strlcat(options,",prefixpath=",options_size);
1531 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1534 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1535 replace_char(dev_name, '\\', '/', strlen(share_name));
1537 if (!got_ip && addr) {
1538 strlcat(options, ",ip=", options_size);
1539 current_len = strnlen(options, options_size);
1540 optionstail = options + current_len;
1541 switch (addr->ai_addr->sa_family) {
1542 case AF_INET6:
1543 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1544 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1545 options_size - current_len);
1546 break;
1547 case AF_INET:
1548 addr4 = (struct sockaddr_in *) addr->ai_addr;
1549 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1550 options_size - current_len);
1551 break;
1552 default:
1553 ipaddr = NULL;
1556 /* if the address looks bogus, try the next one */
1557 if (!ipaddr) {
1558 addr = addr->ai_next;
1559 if (addr)
1560 goto mount_retry;
1561 rc = EX_SYSERR;
1562 goto mount_exit;
1566 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1567 strlcat(options, "%", options_size);
1568 current_len = strnlen(options, options_size);
1569 optionstail = options + current_len;
1570 snprintf(optionstail, options_size - current_len, "%u",
1571 addr6->sin6_scope_id);
1574 if(verboseflag)
1575 fprintf(stderr, "\nmount.cifs kernel mount options: %s", options);
1577 if (mountpassword) {
1579 * Commas have to be doubled, or else they will
1580 * look like the parameter separator
1582 if(retry == 0)
1583 check_for_comma(&mountpassword);
1584 strlcat(options,",pass=",options_size);
1585 strlcat(options,mountpassword,options_size);
1586 if (verboseflag)
1587 fprintf(stderr, ",pass=********");
1590 if (verboseflag)
1591 fprintf(stderr, "\n");
1593 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1594 switch (errno) {
1595 case ECONNREFUSED:
1596 case EHOSTUNREACH:
1597 if (addr) {
1598 addr = addr->ai_next;
1599 if (addr)
1600 goto mount_retry;
1602 break;
1603 case ENODEV:
1604 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1605 break;
1606 case ENXIO:
1607 if(retry == 0) {
1608 retry = 1;
1609 if (uppercase_string(dev_name) &&
1610 uppercase_string(share_name) &&
1611 uppercase_string(prefixpath)) {
1612 fprintf(stderr, "retrying with upper case share name\n");
1613 goto mount_retry;
1617 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1618 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1619 "mount.cifs)\n");
1620 rc = EX_FAIL;
1621 goto mount_exit;
1624 if (nomtab)
1625 goto mount_exit;
1626 atexit(unlock_mtab);
1627 rc = lock_mtab();
1628 if (rc) {
1629 fprintf(stderr, "cannot lock mtab");
1630 goto mount_exit;
1632 pmntfile = setmntent(MOUNTED, "a+");
1633 if (!pmntfile) {
1634 fprintf(stderr, "could not update mount table\n");
1635 unlock_mtab();
1636 rc = EX_FILEIO;
1637 goto mount_exit;
1639 mountent.mnt_fsname = dev_name;
1640 mountent.mnt_dir = mountpoint;
1641 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1642 mountent.mnt_opts = (char *)malloc(220);
1643 if(mountent.mnt_opts) {
1644 char * mount_user = getusername();
1645 memset(mountent.mnt_opts,0,200);
1646 if(flags & MS_RDONLY)
1647 strlcat(mountent.mnt_opts,"ro",220);
1648 else
1649 strlcat(mountent.mnt_opts,"rw",220);
1650 if(flags & MS_MANDLOCK)
1651 strlcat(mountent.mnt_opts,",mand",220);
1652 if(flags & MS_NOEXEC)
1653 strlcat(mountent.mnt_opts,",noexec",220);
1654 if(flags & MS_NOSUID)
1655 strlcat(mountent.mnt_opts,",nosuid",220);
1656 if(flags & MS_NODEV)
1657 strlcat(mountent.mnt_opts,",nodev",220);
1658 if(flags & MS_SYNCHRONOUS)
1659 strlcat(mountent.mnt_opts,",sync",220);
1660 if(mount_user) {
1661 if(getuid() != 0) {
1662 strlcat(mountent.mnt_opts,
1663 ",user=", 220);
1664 strlcat(mountent.mnt_opts,
1665 mount_user, 220);
1669 mountent.mnt_freq = 0;
1670 mountent.mnt_passno = 0;
1671 rc = addmntent(pmntfile,&mountent);
1672 endmntent(pmntfile);
1673 unlock_mtab();
1674 SAFE_FREE(mountent.mnt_opts);
1675 if (rc)
1676 rc = EX_FILEIO;
1677 mount_exit:
1678 if(mountpassword) {
1679 int len = strlen(mountpassword);
1680 memset(mountpassword,0,len);
1681 SAFE_FREE(mountpassword);
1684 if (addrhead)
1685 freeaddrinfo(addrhead);
1686 SAFE_FREE(options);
1687 SAFE_FREE(orgoptions);
1688 SAFE_FREE(resolved_path);
1689 SAFE_FREE(share_name);
1690 exit(rc);