s3/VERSION: Raise version up to 3.5.0.
[Samba/gebeck_regimport.git] / client / mount.cifs.c
blob1c04e13c8d63aa30657d79f9a55d65d959e23af7
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;
323 fs = fopen(file_name,"r");
324 if(fs == NULL)
325 return errno;
326 line_buf = (char *)malloc(4096);
327 if(line_buf == NULL) {
328 fclose(fs);
329 return ENOMEM;
332 while(fgets(line_buf,4096,fs)) {
333 /* parse line from credential file */
335 /* eat leading white space */
336 for(i=0;i<4086;i++) {
337 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
338 break;
339 /* if whitespace - skip past it */
341 if (strncasecmp("username",line_buf+i,8) == 0) {
342 temp_val = strchr(line_buf + i,'=');
343 if(temp_val) {
344 /* go past equals sign */
345 temp_val++;
346 for(length = 0;length<4087;length++) {
347 if ((temp_val[length] == '\n')
348 || (temp_val[length] == '\0')) {
349 temp_val[length] = '\0';
350 break;
353 if(length > 4086) {
354 fprintf(stderr, "mount.cifs failed due to malformed username in credentials file\n");
355 memset(line_buf,0,4096);
356 exit(EX_USAGE);
357 } else {
358 got_user = 1;
359 user_name = (char *)calloc(1 + length,1);
360 /* BB adding free of user_name string before exit,
361 not really necessary but would be cleaner */
362 strlcpy(user_name,temp_val, length+1);
365 } else if (strncasecmp("password",line_buf+i,8) == 0) {
366 temp_val = strchr(line_buf+i,'=');
367 if(temp_val) {
368 /* go past equals sign */
369 temp_val++;
370 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
371 if ((temp_val[length] == '\n')
372 || (temp_val[length] == '\0')) {
373 temp_val[length] = '\0';
374 break;
377 if(length > MOUNT_PASSWD_SIZE) {
378 fprintf(stderr, "mount.cifs failed: password in credentials file too long\n");
379 memset(line_buf,0, 4096);
380 exit(EX_USAGE);
381 } else {
382 if(mountpassword == NULL) {
383 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
384 } else
385 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
386 if(mountpassword) {
387 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
388 got_password = 1;
392 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
393 temp_val = strchr(line_buf+i,'=');
394 if(temp_val) {
395 /* go past equals sign */
396 temp_val++;
397 if(verboseflag)
398 fprintf(stderr, "\nDomain %s\n",temp_val);
399 for(length = 0;length<DOMAIN_SIZE+1;length++) {
400 if ((temp_val[length] == '\n')
401 || (temp_val[length] == '\0')) {
402 temp_val[length] = '\0';
403 break;
406 if(length > DOMAIN_SIZE) {
407 fprintf(stderr, "mount.cifs failed: domain in credentials file too long\n");
408 exit(EX_USAGE);
409 } else {
410 if(domain_name == NULL) {
411 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
412 } else
413 memset(domain_name,0,DOMAIN_SIZE);
414 if(domain_name) {
415 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
416 got_domain = 1;
423 fclose(fs);
424 SAFE_FREE(line_buf);
425 return 0;
428 static int get_password_from_file(int file_descript, char * filename)
430 int rc = 0;
431 int i;
432 char c;
434 if(mountpassword == NULL)
435 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
436 else
437 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
439 if (mountpassword == NULL) {
440 fprintf(stderr, "malloc failed\n");
441 exit(EX_SYSERR);
444 if(filename != NULL) {
445 file_descript = open(filename, O_RDONLY);
446 if(file_descript < 0) {
447 fprintf(stderr, "mount.cifs failed. %s attempting to open password file %s\n",
448 strerror(errno),filename);
449 exit(EX_SYSERR);
452 /* else file already open and fd provided */
454 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
455 rc = read(file_descript,&c,1);
456 if(rc < 0) {
457 fprintf(stderr, "mount.cifs failed. Error %s reading password file\n",strerror(errno));
458 if(filename != NULL)
459 close(file_descript);
460 exit(EX_SYSERR);
461 } else if(rc == 0) {
462 if(mountpassword[0] == 0) {
463 if(verboseflag)
464 fprintf(stderr, "\nWarning: null password used since cifs password file empty");
466 break;
467 } else /* read valid character */ {
468 if((c == 0) || (c == '\n')) {
469 mountpassword[i] = '\0';
470 break;
471 } else
472 mountpassword[i] = c;
475 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
476 fprintf(stderr, "\nWarning: password longer than %d characters specified in cifs password file",
477 MOUNT_PASSWD_SIZE);
479 got_password = 1;
480 if(filename != NULL) {
481 close(file_descript);
484 return rc;
487 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
489 const char * data;
490 char * percent_char = NULL;
491 char * value = NULL;
492 char * next_keyword = NULL;
493 char * out = NULL;
494 int out_len = 0;
495 int word_len;
496 int rc = 0;
497 char user[32];
498 char group[32];
500 if (!optionsp || !*optionsp)
501 return 1;
502 data = *optionsp;
504 if(verboseflag)
505 fprintf(stderr, "parsing options: %s\n", data);
507 /* BB fixme check for separator override BB */
509 if (getuid()) {
510 got_uid = 1;
511 snprintf(user,sizeof(user),"%u",getuid());
512 got_gid = 1;
513 snprintf(group,sizeof(group),"%u",getgid());
516 /* while ((data = strsep(&options, ",")) != NULL) { */
517 while(data != NULL) {
518 /* check if ends with trailing comma */
519 if(*data == 0)
520 break;
522 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
523 /* data = next keyword */
524 /* value = next value ie stuff after equal sign */
526 next_keyword = strchr(data,','); /* BB handle sep= */
528 /* temporarily null terminate end of keyword=value pair */
529 if(next_keyword)
530 *next_keyword++ = 0;
532 /* temporarily null terminate keyword to make keyword and value distinct */
533 if ((value = strchr(data, '=')) != NULL) {
534 *value = '\0';
535 value++;
538 if (strncmp(data, "users",5) == 0) {
539 if(!value || !*value) {
540 *filesys_flags |= MS_USERS;
541 goto nocopy;
543 } else if (strncmp(data, "user_xattr",10) == 0) {
544 /* do nothing - need to skip so not parsed as user name */
545 } else if (strncmp(data, "user", 4) == 0) {
547 if (!value || !*value) {
548 if(data[4] == '\0') {
549 *filesys_flags |= MS_USER;
550 goto nocopy;
551 } else {
552 fprintf(stderr, "username specified with no parameter\n");
553 SAFE_FREE(out);
554 return 1; /* needs_arg; */
556 } else {
557 if (strnlen(value, 260) < 260) {
558 got_user=1;
559 percent_char = strchr(value,'%');
560 if(percent_char) {
561 *percent_char = ',';
562 if(mountpassword == NULL)
563 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
564 if(mountpassword) {
565 if(got_password)
566 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
567 got_password = 1;
568 percent_char++;
569 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
570 /* remove password from username */
571 while(*percent_char != 0) {
572 *percent_char = ',';
573 percent_char++;
577 /* this is only case in which the user
578 name buf is not malloc - so we have to
579 check for domain name embedded within
580 the user name here since the later
581 call to check_for_domain will not be
582 invoked */
583 domain_name = check_for_domain(&value);
584 } else {
585 fprintf(stderr, "username too long\n");
586 SAFE_FREE(out);
587 return 1;
590 } else if (strncmp(data, "pass", 4) == 0) {
591 if (!value || !*value) {
592 if(got_password) {
593 fprintf(stderr, "\npassword specified twice, ignoring second\n");
594 } else
595 got_password = 1;
596 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
597 if(got_password)
598 fprintf(stderr, "\nmount.cifs warning - password specified twice\n");
599 got_password = 1;
600 } else {
601 fprintf(stderr, "password too long\n");
602 SAFE_FREE(out);
603 return 1;
605 } else if (strncmp(data, "sec", 3) == 0) {
606 if (value) {
607 if (!strncmp(value, "none", 4) ||
608 !strncmp(value, "krb5", 4))
609 got_password = 1;
611 } else if (strncmp(data, "ip", 2) == 0) {
612 if (!value || !*value) {
613 fprintf(stderr, "target ip address argument missing");
614 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
615 if(verboseflag)
616 fprintf(stderr, "ip address %s override specified\n",value);
617 got_ip = 1;
618 } else {
619 fprintf(stderr, "ip address too long\n");
620 SAFE_FREE(out);
621 return 1;
623 } else if ((strncmp(data, "unc", 3) == 0)
624 || (strncmp(data, "target", 6) == 0)
625 || (strncmp(data, "path", 4) == 0)) {
626 if (!value || !*value) {
627 fprintf(stderr, "invalid path to network resource\n");
628 SAFE_FREE(out);
629 return 1; /* needs_arg; */
630 } else if(strnlen(value,5) < 5) {
631 fprintf(stderr, "UNC name too short");
634 if (strnlen(value, 300) < 300) {
635 got_unc = 1;
636 if (strncmp(value, "//", 2) == 0) {
637 if(got_unc)
638 fprintf(stderr, "unc name specified twice, ignoring second\n");
639 else
640 got_unc = 1;
641 } else if (strncmp(value, "\\\\", 2) != 0) {
642 fprintf(stderr, "UNC Path does not begin with // or \\\\ \n");
643 SAFE_FREE(out);
644 return 1;
645 } else {
646 if(got_unc)
647 fprintf(stderr, "unc name specified twice, ignoring second\n");
648 else
649 got_unc = 1;
651 } else {
652 fprintf(stderr, "CIFS: UNC name too long\n");
653 SAFE_FREE(out);
654 return 1;
656 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
657 || (strncmp(data, "workg", 5) == 0)) {
658 /* note this allows for synonyms of "domain"
659 such as "DOM" and "dom" and "workgroup"
660 and "WORKGRP" etc. */
661 if (!value || !*value) {
662 fprintf(stderr, "CIFS: invalid domain name\n");
663 SAFE_FREE(out);
664 return 1; /* needs_arg; */
666 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
667 got_domain = 1;
668 } else {
669 fprintf(stderr, "domain name too long\n");
670 SAFE_FREE(out);
671 return 1;
673 } else if (strncmp(data, "cred", 4) == 0) {
674 if (value && *value) {
675 rc = open_cred_file(value);
676 if(rc) {
677 fprintf(stderr, "error %d (%s) opening credential file %s\n",
678 rc, strerror(rc), value);
679 SAFE_FREE(out);
680 return 1;
682 } else {
683 fprintf(stderr, "invalid credential file name specified\n");
684 SAFE_FREE(out);
685 return 1;
687 } else if (strncmp(data, "uid", 3) == 0) {
688 if (value && *value) {
689 got_uid = 1;
690 if (!isdigit(*value)) {
691 struct passwd *pw;
693 if (!(pw = getpwnam(value))) {
694 fprintf(stderr, "bad user name \"%s\"\n", value);
695 exit(EX_USAGE);
697 snprintf(user, sizeof(user), "%u", pw->pw_uid);
698 } else {
699 strlcpy(user,value,sizeof(user));
702 goto nocopy;
703 } else if (strncmp(data, "gid", 3) == 0) {
704 if (value && *value) {
705 got_gid = 1;
706 if (!isdigit(*value)) {
707 struct group *gr;
709 if (!(gr = getgrnam(value))) {
710 fprintf(stderr, "bad group name \"%s\"\n", value);
711 exit(EX_USAGE);
713 snprintf(group, sizeof(group), "%u", gr->gr_gid);
714 } else {
715 strlcpy(group,value,sizeof(group));
718 goto nocopy;
719 /* fmask and dmask synonyms for people used to smbfs syntax */
720 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
721 if (!value || !*value) {
722 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
723 SAFE_FREE(out);
724 return 1;
727 if (value[0] != '0') {
728 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
731 if (strcmp (data, "fmask") == 0) {
732 fprintf(stderr, "WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
733 data = "file_mode"; /* BB fix this */
735 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
736 if (!value || !*value) {
737 fprintf(stderr, "Option '%s' requires a numerical argument\n", data);
738 SAFE_FREE(out);
739 return 1;
742 if (value[0] != '0') {
743 fprintf(stderr, "WARNING: '%s' not expressed in octal.\n", data);
746 if (strcmp (data, "dmask") == 0) {
747 fprintf(stderr, "WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
748 data = "dir_mode";
750 /* the following eight mount options should be
751 stripped out from what is passed into the kernel
752 since these eight options are best passed as the
753 mount flags rather than redundantly to the kernel
754 and could generate spurious warnings depending on the
755 level of the corresponding cifs vfs kernel code */
756 } else if (strncmp(data, "nosuid", 6) == 0) {
757 *filesys_flags |= MS_NOSUID;
758 } else if (strncmp(data, "suid", 4) == 0) {
759 *filesys_flags &= ~MS_NOSUID;
760 } else if (strncmp(data, "nodev", 5) == 0) {
761 *filesys_flags |= MS_NODEV;
762 } else if ((strncmp(data, "nobrl", 5) == 0) ||
763 (strncmp(data, "nolock", 6) == 0)) {
764 *filesys_flags &= ~MS_MANDLOCK;
765 } else if (strncmp(data, "dev", 3) == 0) {
766 *filesys_flags &= ~MS_NODEV;
767 } else if (strncmp(data, "noexec", 6) == 0) {
768 *filesys_flags |= MS_NOEXEC;
769 } else if (strncmp(data, "exec", 4) == 0) {
770 *filesys_flags &= ~MS_NOEXEC;
771 } else if (strncmp(data, "guest", 5) == 0) {
772 user_name = (char *)calloc(1, 1);
773 got_user = 1;
774 got_password = 1;
775 } else if (strncmp(data, "ro", 2) == 0) {
776 *filesys_flags |= MS_RDONLY;
777 goto nocopy;
778 } else if (strncmp(data, "rw", 2) == 0) {
779 *filesys_flags &= ~MS_RDONLY;
780 goto nocopy;
781 } else if (strncmp(data, "remount", 7) == 0) {
782 *filesys_flags |= MS_REMOUNT;
783 } /* else if (strnicmp(data, "port", 4) == 0) {
784 if (value && *value) {
785 vol->port =
786 simple_strtoul(value, &value, 0);
788 } else if (strnicmp(data, "rsize", 5) == 0) {
789 if (value && *value) {
790 vol->rsize =
791 simple_strtoul(value, &value, 0);
793 } else if (strnicmp(data, "wsize", 5) == 0) {
794 if (value && *value) {
795 vol->wsize =
796 simple_strtoul(value, &value, 0);
798 } else if (strnicmp(data, "version", 3) == 0) {
799 } else {
800 fprintf(stderr, "CIFS: Unknown mount option %s\n",data);
801 } */ /* nothing to do on those four mount options above.
802 Just pass to kernel and ignore them here */
804 /* Copy (possibly modified) option to out */
805 word_len = strlen(data);
806 if (value)
807 word_len += 1 + strlen(value);
809 out = (char *)realloc(out, out_len + word_len + 2);
810 if (out == NULL) {
811 perror("malloc");
812 exit(EX_SYSERR);
815 if (out_len) {
816 strlcat(out, ",", out_len + word_len + 2);
817 out_len++;
820 if (value)
821 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
822 else
823 snprintf(out + out_len, word_len + 1, "%s", data);
824 out_len = strlen(out);
826 nocopy:
827 data = next_keyword;
830 /* special-case the uid and gid */
831 if (got_uid) {
832 word_len = strlen(user);
834 out = (char *)realloc(out, out_len + word_len + 6);
835 if (out == NULL) {
836 perror("malloc");
837 exit(EX_SYSERR);
840 if (out_len) {
841 strlcat(out, ",", out_len + word_len + 6);
842 out_len++;
844 snprintf(out + out_len, word_len + 5, "uid=%s", user);
845 out_len = strlen(out);
847 if (got_gid) {
848 word_len = strlen(group);
850 out = (char *)realloc(out, out_len + 1 + 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, "gid=%s", group);
861 out_len = strlen(out);
864 SAFE_FREE(*optionsp);
865 *optionsp = out;
866 return 0;
869 /* replace all (one or more) commas with double commas */
870 static void check_for_comma(char ** ppasswrd)
872 char *new_pass_buf;
873 char *pass;
874 int i,j;
875 int number_of_commas = 0;
876 int len;
878 if(ppasswrd == NULL)
879 return;
880 else
881 (pass = *ppasswrd);
883 len = strlen(pass);
885 for(i=0;i<len;i++) {
886 if(pass[i] == ',')
887 number_of_commas++;
890 if(number_of_commas == 0)
891 return;
892 if(number_of_commas > MOUNT_PASSWD_SIZE) {
893 /* would otherwise overflow the mount options buffer */
894 fprintf(stderr, "\nInvalid password. Password contains too many commas.\n");
895 return;
898 new_pass_buf = (char *)malloc(len+number_of_commas+1);
899 if(new_pass_buf == NULL)
900 return;
902 for(i=0,j=0;i<len;i++,j++) {
903 new_pass_buf[j] = pass[i];
904 if(pass[i] == ',') {
905 j++;
906 new_pass_buf[j] = pass[i];
909 new_pass_buf[len+number_of_commas] = 0;
911 SAFE_FREE(*ppasswrd);
912 *ppasswrd = new_pass_buf;
914 return;
917 /* Usernames can not have backslash in them and we use
918 [BB check if usernames can have forward slash in them BB]
919 backslash as domain\user separator character
921 static char * check_for_domain(char **ppuser)
923 char * original_string;
924 char * usernm;
925 char * domainnm;
926 int original_len;
927 int len;
928 int i;
930 if(ppuser == NULL)
931 return NULL;
933 original_string = *ppuser;
935 if (original_string == NULL)
936 return NULL;
938 original_len = strlen(original_string);
940 usernm = strchr(*ppuser,'/');
941 if (usernm == NULL) {
942 usernm = strchr(*ppuser,'\\');
943 if (usernm == NULL)
944 return NULL;
947 if(got_domain) {
948 fprintf(stderr, "Domain name specified twice. Username probably malformed\n");
949 return NULL;
952 usernm[0] = 0;
953 domainnm = *ppuser;
954 if (domainnm[0] != 0) {
955 got_domain = 1;
956 } else {
957 fprintf(stderr, "null domain\n");
959 len = strlen(domainnm);
960 /* reset domainm to new buffer, and copy
961 domain name into it */
962 domainnm = (char *)malloc(len+1);
963 if(domainnm == NULL)
964 return NULL;
966 strlcpy(domainnm,*ppuser,len+1);
968 /* move_string(*ppuser, usernm+1) */
969 len = strlen(usernm+1);
971 if(len >= original_len) {
972 /* should not happen */
973 return domainnm;
976 for(i=0;i<original_len;i++) {
977 if(i<len)
978 original_string[i] = usernm[i+1];
979 else /* stuff with commas to remove last parm */
980 original_string[i] = ',';
983 /* BB add check for more than one slash?
984 strchr(*ppuser,'/');
985 strchr(*ppuser,'\\')
988 return domainnm;
991 /* replace all occurances of "from" in a string with "to" */
992 static void replace_char(char *string, char from, char to, int maxlen)
994 char *lastchar = string + maxlen;
995 while (string) {
996 string = strchr(string, from);
997 if (string) {
998 *string = to;
999 if (string >= lastchar)
1000 return;
1005 /* Note that caller frees the returned buffer if necessary */
1006 static struct addrinfo *
1007 parse_server(char ** punc_name)
1009 char * unc_name = *punc_name;
1010 int length = strnlen(unc_name, MAX_UNC_LEN);
1011 char * share;
1012 struct addrinfo *addrlist;
1013 int rc;
1015 if(length > (MAX_UNC_LEN - 1)) {
1016 fprintf(stderr, "mount error: UNC name too long");
1017 return NULL;
1019 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1020 (strncasecmp("smb://", unc_name, 6) == 0)) {
1021 fprintf(stderr, "\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1022 return NULL;
1025 if(length < 3) {
1026 /* BB add code to find DFS root here */
1027 fprintf(stderr, "\nMounting the DFS root for domain not implemented yet\n");
1028 return NULL;
1029 } else {
1030 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1031 /* check for nfs syntax ie server:share */
1032 share = strchr(unc_name,':');
1033 if(share) {
1034 *punc_name = (char *)malloc(length+3);
1035 if(*punc_name == NULL) {
1036 /* put the original string back if
1037 no memory left */
1038 *punc_name = unc_name;
1039 return NULL;
1041 *share = '/';
1042 strlcpy((*punc_name)+2,unc_name,length+1);
1043 SAFE_FREE(unc_name);
1044 unc_name = *punc_name;
1045 unc_name[length+2] = 0;
1046 goto continue_unc_parsing;
1047 } else {
1048 fprintf(stderr, "mount error: improperly formatted UNC name.");
1049 fprintf(stderr, " %s does not begin with \\\\ or //\n",unc_name);
1050 return NULL;
1052 } else {
1053 continue_unc_parsing:
1054 unc_name[0] = '/';
1055 unc_name[1] = '/';
1056 unc_name += 2;
1058 /* allow for either delimiter between host and sharename */
1059 if ((share = strpbrk(unc_name, "/\\"))) {
1060 *share = 0; /* temporarily terminate the string */
1061 share += 1;
1062 if(got_ip == 0) {
1063 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1064 if (rc != 0) {
1065 fprintf(stderr, "mount error: could not resolve address for %s: %s\n",
1066 unc_name, gai_strerror(rc));
1067 addrlist = NULL;
1070 *(share - 1) = '/'; /* put delimiter back */
1072 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1073 if ((prefixpath = strpbrk(share, "/\\"))) {
1074 *prefixpath = 0; /* permanently terminate the string */
1075 if (!strlen(++prefixpath))
1076 prefixpath = NULL; /* this needs to be done explicitly */
1078 if(got_ip) {
1079 if(verboseflag)
1080 fprintf(stderr, "ip address specified explicitly\n");
1081 return NULL;
1083 /* BB should we pass an alternate version of the share name as Unicode */
1085 return addrlist;
1086 } else {
1087 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1088 fprintf(stderr, "Mounting the DFS root for a particular server not implemented yet\n");
1089 return NULL;
1095 static struct option longopts[] = {
1096 { "all", 0, NULL, 'a' },
1097 { "help",0, NULL, 'h' },
1098 { "move",0, NULL, 'm' },
1099 { "bind",0, NULL, 'b' },
1100 { "read-only", 0, NULL, 'r' },
1101 { "ro", 0, NULL, 'r' },
1102 { "verbose", 0, NULL, 'v' },
1103 { "version", 0, NULL, 'V' },
1104 { "read-write", 0, NULL, 'w' },
1105 { "rw", 0, NULL, 'w' },
1106 { "options", 1, NULL, 'o' },
1107 { "type", 1, NULL, 't' },
1108 { "rsize",1, NULL, 'R' },
1109 { "wsize",1, NULL, 'W' },
1110 { "uid", 1, NULL, '1'},
1111 { "gid", 1, NULL, '2'},
1112 { "user",1,NULL,'u'},
1113 { "username",1,NULL,'u'},
1114 { "dom",1,NULL,'d'},
1115 { "domain",1,NULL,'d'},
1116 { "password",1,NULL,'p'},
1117 { "pass",1,NULL,'p'},
1118 { "credentials",1,NULL,'c'},
1119 { "port",1,NULL,'P'},
1120 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1121 { NULL, 0, NULL, 0 }
1124 /* convert a string to uppercase. return false if the string
1125 * wasn't ASCII. Return success on a NULL ptr */
1126 static int
1127 uppercase_string(char *string)
1129 if (!string)
1130 return 1;
1132 while (*string) {
1133 /* check for unicode */
1134 if ((unsigned char) string[0] & 0x80)
1135 return 0;
1136 *string = toupper((unsigned char) *string);
1137 string++;
1140 return 1;
1143 static void print_cifs_mount_version(void)
1145 printf("mount.cifs version: %s.%s%s\n",
1146 MOUNT_CIFS_VERSION_MAJOR,
1147 MOUNT_CIFS_VERSION_MINOR,
1148 MOUNT_CIFS_VENDOR_SUFFIX);
1151 int main(int argc, char ** argv)
1153 int c;
1154 unsigned long flags = MS_MANDLOCK;
1155 char * orgoptions = NULL;
1156 char * share_name = NULL;
1157 const char * ipaddr = NULL;
1158 char * uuid = NULL;
1159 char * mountpoint = NULL;
1160 char * options = NULL;
1161 char * optionstail;
1162 char * resolved_path = NULL;
1163 char * temp;
1164 char * dev_name;
1165 int rc = 0;
1166 int rsize = 0;
1167 int wsize = 0;
1168 int nomtab = 0;
1169 int uid = 0;
1170 int gid = 0;
1171 int optlen = 0;
1172 int orgoptlen = 0;
1173 size_t options_size = 0;
1174 size_t current_len;
1175 int retry = 0; /* set when we have to retry mount with uppercase */
1176 struct addrinfo *addrhead = NULL, *addr;
1177 struct utsname sysinfo;
1178 struct mntent mountent;
1179 struct sockaddr_in *addr4;
1180 struct sockaddr_in6 *addr6;
1181 FILE * pmntfile;
1183 /* setlocale(LC_ALL, "");
1184 bindtextdomain(PACKAGE, LOCALEDIR);
1185 textdomain(PACKAGE); */
1187 if(argc && argv)
1188 thisprogram = argv[0];
1189 else
1190 mount_cifs_usage(stderr);
1192 if(thisprogram == NULL)
1193 thisprogram = "mount.cifs";
1195 uname(&sysinfo);
1196 /* BB add workstation name and domain and pass down */
1198 /* #ifdef _GNU_SOURCE
1199 fprintf(stderr, " node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1200 #endif */
1201 if(argc > 2) {
1202 dev_name = argv[1];
1203 share_name = strndup(argv[1], MAX_UNC_LEN);
1204 if (share_name == NULL) {
1205 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1206 exit(EX_SYSERR);
1208 mountpoint = argv[2];
1209 } else if (argc == 2) {
1210 if ((strcmp(argv[1], "-V") == 0) ||
1211 (strcmp(argv[1], "--version") == 0))
1213 print_cifs_mount_version();
1214 exit(0);
1217 if ((strcmp(argv[1], "-h") == 0) ||
1218 (strcmp(argv[1], "-?") == 0) ||
1219 (strcmp(argv[1], "--help") == 0))
1220 mount_cifs_usage(stdout);
1222 mount_cifs_usage(stderr);
1223 } else {
1224 mount_cifs_usage(stderr);
1228 /* add sharename in opts string as unc= parm */
1229 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1230 longopts, NULL)) != -1) {
1231 switch (c) {
1232 /* No code to do the following options yet */
1233 /* case 'l':
1234 list_with_volumelabel = 1;
1235 break;
1236 case 'L':
1237 volumelabel = optarg;
1238 break; */
1239 /* case 'a':
1240 ++mount_all;
1241 break; */
1243 case '?':
1244 case 'h': /* help */
1245 mount_cifs_usage(stdout);
1246 case 'n':
1247 ++nomtab;
1248 break;
1249 case 'b':
1250 #ifdef MS_BIND
1251 flags |= MS_BIND;
1252 #else
1253 fprintf(stderr,
1254 "option 'b' (MS_BIND) not supported\n");
1255 #endif
1256 break;
1257 case 'm':
1258 #ifdef MS_MOVE
1259 flags |= MS_MOVE;
1260 #else
1261 fprintf(stderr,
1262 "option 'm' (MS_MOVE) not supported\n");
1263 #endif
1264 break;
1265 case 'o':
1266 orgoptions = strdup(optarg);
1267 break;
1268 case 'r': /* mount readonly */
1269 flags |= MS_RDONLY;
1270 break;
1271 case 'U':
1272 uuid = optarg;
1273 break;
1274 case 'v':
1275 ++verboseflag;
1276 break;
1277 case 'V':
1278 print_cifs_mount_version();
1279 exit (0);
1280 case 'w':
1281 flags &= ~MS_RDONLY;
1282 break;
1283 case 'R':
1284 rsize = atoi(optarg) ;
1285 break;
1286 case 'W':
1287 wsize = atoi(optarg);
1288 break;
1289 case '1':
1290 if (isdigit(*optarg)) {
1291 char *ep;
1293 uid = strtoul(optarg, &ep, 10);
1294 if (*ep) {
1295 fprintf(stderr, "bad uid value \"%s\"\n", optarg);
1296 exit(EX_USAGE);
1298 } else {
1299 struct passwd *pw;
1301 if (!(pw = getpwnam(optarg))) {
1302 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1303 exit(EX_USAGE);
1305 uid = pw->pw_uid;
1306 endpwent();
1308 break;
1309 case '2':
1310 if (isdigit(*optarg)) {
1311 char *ep;
1313 gid = strtoul(optarg, &ep, 10);
1314 if (*ep) {
1315 fprintf(stderr, "bad gid value \"%s\"\n", optarg);
1316 exit(EX_USAGE);
1318 } else {
1319 struct group *gr;
1321 if (!(gr = getgrnam(optarg))) {
1322 fprintf(stderr, "bad user name \"%s\"\n", optarg);
1323 exit(EX_USAGE);
1325 gid = gr->gr_gid;
1326 endpwent();
1328 break;
1329 case 'u':
1330 got_user = 1;
1331 user_name = optarg;
1332 break;
1333 case 'd':
1334 domain_name = optarg; /* BB fix this - currently ignored */
1335 got_domain = 1;
1336 break;
1337 case 'p':
1338 if(mountpassword == NULL)
1339 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1340 if(mountpassword) {
1341 got_password = 1;
1342 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1344 break;
1345 case 'S':
1346 get_password_from_file(0 /* stdin */,NULL);
1347 break;
1348 case 't':
1349 break;
1350 case 'f':
1351 ++fakemnt;
1352 break;
1353 default:
1354 fprintf(stderr, "unknown mount option %c\n",c);
1355 mount_cifs_usage(stderr);
1359 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1360 mount_cifs_usage(stderr);
1363 /* make sure mountpoint is legit */
1364 rc = check_mountpoint(thisprogram, mountpoint);
1365 if (rc)
1366 goto mount_exit;
1368 /* sanity check for unprivileged mounts */
1369 if (getuid()) {
1370 rc = check_fstab(thisprogram, mountpoint, dev_name,
1371 &orgoptions);
1372 if (rc)
1373 goto mount_exit;
1375 /* enable any default user mount flags */
1376 flags |= CIFS_SETUID_FLAGS;
1379 if (getenv("PASSWD")) {
1380 if(mountpassword == NULL)
1381 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1382 if(mountpassword) {
1383 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1384 got_password = 1;
1386 } else if (getenv("PASSWD_FD")) {
1387 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1388 } else if (getenv("PASSWD_FILE")) {
1389 get_password_from_file(0, getenv("PASSWD_FILE"));
1392 if (orgoptions && parse_options(&orgoptions, &flags)) {
1393 rc = EX_USAGE;
1394 goto mount_exit;
1397 if (getuid()) {
1398 #if !CIFS_LEGACY_SETUID_CHECK
1399 if (!(flags & (MS_USERS|MS_USER))) {
1400 fprintf(stderr, "%s: permission denied\n", thisprogram);
1401 rc = EX_USAGE;
1402 goto mount_exit;
1404 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1406 if (geteuid()) {
1407 fprintf(stderr, "%s: not installed setuid - \"user\" "
1408 "CIFS mounts not supported.",
1409 thisprogram);
1410 rc = EX_FAIL;
1411 goto mount_exit;
1415 flags &= ~(MS_USERS|MS_USER);
1417 addrhead = addr = parse_server(&share_name);
1418 if((addrhead == NULL) && (got_ip == 0)) {
1419 fprintf(stderr, "No ip address specified and hostname not found\n");
1420 rc = EX_USAGE;
1421 goto mount_exit;
1424 /* BB save off path and pop after mount returns? */
1425 resolved_path = (char *)malloc(PATH_MAX+1);
1426 if(resolved_path) {
1427 /* Note that if we can not canonicalize the name, we get
1428 another chance to see if it is valid when we chdir to it */
1429 if (realpath(mountpoint, resolved_path)) {
1430 mountpoint = resolved_path;
1433 if(got_user == 0) {
1434 /* Note that the password will not be retrieved from the
1435 USER env variable (ie user%password form) as there is
1436 already a PASSWD environment varaible */
1437 if (getenv("USER"))
1438 user_name = strdup(getenv("USER"));
1439 if (user_name == NULL)
1440 user_name = getusername();
1441 got_user = 1;
1444 if(got_password == 0) {
1445 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1446 no good replacement yet. */
1447 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1448 if (!tmp_pass || !mountpassword) {
1449 fprintf(stderr, "Password not entered, exiting\n");
1450 exit(EX_USAGE);
1452 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1453 got_password = 1;
1455 /* FIXME launch daemon (handles dfs name resolution and credential change)
1456 remember to clear parms and overwrite password field before launching */
1457 if(orgoptions) {
1458 optlen = strlen(orgoptions);
1459 orgoptlen = optlen;
1460 } else
1461 optlen = 0;
1462 if(share_name)
1463 optlen += strlen(share_name) + 4;
1464 else {
1465 fprintf(stderr, "No server share name specified\n");
1466 fprintf(stderr, "\nMounting the DFS root for server not implemented yet\n");
1467 exit(EX_USAGE);
1469 if(user_name)
1470 optlen += strlen(user_name) + 6;
1471 optlen += MAX_ADDRESS_LEN + 4;
1472 if(mountpassword)
1473 optlen += strlen(mountpassword) + 6;
1474 mount_retry:
1475 SAFE_FREE(options);
1476 options_size = optlen + 10 + DOMAIN_SIZE;
1477 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 */);
1479 if(options == NULL) {
1480 fprintf(stderr, "Could not allocate memory for mount options\n");
1481 exit(EX_SYSERR);
1484 strlcpy(options, "unc=", options_size);
1485 strlcat(options,share_name,options_size);
1486 /* scan backwards and reverse direction of slash */
1487 temp = strrchr(options, '/');
1488 if(temp > options + 6)
1489 *temp = '\\';
1490 if(user_name) {
1491 /* check for syntax like user=domain\user */
1492 if(got_domain == 0)
1493 domain_name = check_for_domain(&user_name);
1494 strlcat(options,",user=",options_size);
1495 strlcat(options,user_name,options_size);
1497 if(retry == 0) {
1498 if(domain_name) {
1499 /* extra length accounted for in option string above */
1500 strlcat(options,",domain=",options_size);
1501 strlcat(options,domain_name,options_size);
1504 if(mountpassword) {
1505 /* Commas have to be doubled, or else they will
1506 look like the parameter separator */
1507 /* if(sep is not set)*/
1508 if(retry == 0)
1509 check_for_comma(&mountpassword);
1510 strlcat(options,",pass=",options_size);
1511 strlcat(options,mountpassword,options_size);
1514 strlcat(options,",ver=",options_size);
1515 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1517 if(orgoptions) {
1518 strlcat(options,",",options_size);
1519 strlcat(options,orgoptions,options_size);
1521 if(prefixpath) {
1522 strlcat(options,",prefixpath=",options_size);
1523 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1525 if(verboseflag)
1526 fprintf(stderr, "\nmount.cifs kernel mount options %s \n",options);
1528 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1529 replace_char(dev_name, '\\', '/', strlen(share_name));
1531 if (!got_ip && addr) {
1532 strlcat(options, ",ip=", options_size);
1533 current_len = strnlen(options, options_size);
1534 optionstail = options + current_len;
1535 switch (addr->ai_addr->sa_family) {
1536 case AF_INET6:
1537 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1538 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1539 options_size - current_len);
1540 break;
1541 case AF_INET:
1542 addr4 = (struct sockaddr_in *) addr->ai_addr;
1543 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1544 options_size - current_len);
1545 break;
1546 default:
1547 ipaddr = NULL;
1550 /* if the address looks bogus, try the next one */
1551 if (!ipaddr) {
1552 addr = addr->ai_next;
1553 if (addr)
1554 goto mount_retry;
1555 rc = EX_SYSERR;
1556 goto mount_exit;
1560 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1561 strlcat(options, "%", options_size);
1562 current_len = strnlen(options, options_size);
1563 optionstail = options + current_len;
1564 snprintf(optionstail, options_size - current_len, "%u",
1565 addr6->sin6_scope_id);
1568 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1569 switch (errno) {
1570 case ECONNREFUSED:
1571 case EHOSTUNREACH:
1572 if (addr) {
1573 addr = addr->ai_next;
1574 if (addr)
1575 goto mount_retry;
1577 break;
1578 case ENODEV:
1579 fprintf(stderr, "mount error: cifs filesystem not supported by the system\n");
1580 break;
1581 case ENXIO:
1582 if(retry == 0) {
1583 retry = 1;
1584 if (uppercase_string(dev_name) &&
1585 uppercase_string(share_name) &&
1586 uppercase_string(prefixpath)) {
1587 fprintf(stderr, "retrying with upper case share name\n");
1588 goto mount_retry;
1592 fprintf(stderr, "mount error(%d): %s\n", errno, strerror(errno));
1593 fprintf(stderr, "Refer to the mount.cifs(8) manual page (e.g. man "
1594 "mount.cifs)\n");
1595 rc = EX_FAIL;
1596 goto mount_exit;
1599 if (nomtab)
1600 goto mount_exit;
1601 atexit(unlock_mtab);
1602 rc = lock_mtab();
1603 if (rc) {
1604 fprintf(stderr, "cannot lock mtab");
1605 goto mount_exit;
1607 pmntfile = setmntent(MOUNTED, "a+");
1608 if (!pmntfile) {
1609 fprintf(stderr, "could not update mount table\n");
1610 unlock_mtab();
1611 rc = EX_FILEIO;
1612 goto mount_exit;
1614 mountent.mnt_fsname = dev_name;
1615 mountent.mnt_dir = mountpoint;
1616 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1617 mountent.mnt_opts = (char *)malloc(220);
1618 if(mountent.mnt_opts) {
1619 char * mount_user = getusername();
1620 memset(mountent.mnt_opts,0,200);
1621 if(flags & MS_RDONLY)
1622 strlcat(mountent.mnt_opts,"ro",220);
1623 else
1624 strlcat(mountent.mnt_opts,"rw",220);
1625 if(flags & MS_MANDLOCK)
1626 strlcat(mountent.mnt_opts,",mand",220);
1627 if(flags & MS_NOEXEC)
1628 strlcat(mountent.mnt_opts,",noexec",220);
1629 if(flags & MS_NOSUID)
1630 strlcat(mountent.mnt_opts,",nosuid",220);
1631 if(flags & MS_NODEV)
1632 strlcat(mountent.mnt_opts,",nodev",220);
1633 if(flags & MS_SYNCHRONOUS)
1634 strlcat(mountent.mnt_opts,",sync",220);
1635 if(mount_user) {
1636 if(getuid() != 0) {
1637 strlcat(mountent.mnt_opts,
1638 ",user=", 220);
1639 strlcat(mountent.mnt_opts,
1640 mount_user, 220);
1644 mountent.mnt_freq = 0;
1645 mountent.mnt_passno = 0;
1646 rc = addmntent(pmntfile,&mountent);
1647 endmntent(pmntfile);
1648 unlock_mtab();
1649 SAFE_FREE(mountent.mnt_opts);
1650 if (rc)
1651 rc = EX_FILEIO;
1652 mount_exit:
1653 if(mountpassword) {
1654 int len = strlen(mountpassword);
1655 memset(mountpassword,0,len);
1656 SAFE_FREE(mountpassword);
1659 if (addrhead)
1660 freeaddrinfo(addrhead);
1661 SAFE_FREE(options);
1662 SAFE_FREE(orgoptions);
1663 SAFE_FREE(resolved_path);
1664 SAFE_FREE(share_name);
1665 exit(rc);