Add "store create time" parameter (docs to follow)
[Samba/gbeck.git] / client / mount.cifs.c
blob4387f5945a916d4d6b9216538c45c6015e49f35a
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(void)
278 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
279 printf("\nMount the remote target, specified as a UNC name,");
280 printf(" to a local directory.\n\nOptions:\n");
281 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
282 printf("\nLess commonly used options:");
283 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
284 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
285 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
286 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
287 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
288 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
289 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
290 printf("\n\nRarely used options:");
291 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
292 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
293 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
294 printf("\n\tin6_addr");
295 printf("\n\nOptions are described in more detail in the manual page");
296 printf("\n\tman 8 mount.cifs\n");
297 printf("\nTo display the version number of the mount helper:");
298 printf("\n\t%s -V\n",thisprogram);
300 SAFE_FREE(mountpassword);
303 /* caller frees username if necessary */
304 static char * getusername(void) {
305 char *username = NULL;
306 struct passwd *password = getpwuid(getuid());
308 if (password) {
309 username = password->pw_name;
311 return username;
314 static int open_cred_file(char * file_name)
316 char * line_buf;
317 char * temp_val;
318 FILE * fs;
319 int i, length;
320 fs = fopen(file_name,"r");
321 if(fs == NULL)
322 return errno;
323 line_buf = (char *)malloc(4096);
324 if(line_buf == NULL) {
325 fclose(fs);
326 return ENOMEM;
329 while(fgets(line_buf,4096,fs)) {
330 /* parse line from credential file */
332 /* eat leading white space */
333 for(i=0;i<4086;i++) {
334 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
335 break;
336 /* if whitespace - skip past it */
338 if (strncasecmp("username",line_buf+i,8) == 0) {
339 temp_val = strchr(line_buf + i,'=');
340 if(temp_val) {
341 /* go past equals sign */
342 temp_val++;
343 for(length = 0;length<4087;length++) {
344 if ((temp_val[length] == '\n')
345 || (temp_val[length] == '\0')) {
346 temp_val[length] = '\0';
347 break;
350 if(length > 4086) {
351 printf("mount.cifs failed due to malformed username in credentials file");
352 memset(line_buf,0,4096);
353 exit(EX_USAGE);
354 } else {
355 got_user = 1;
356 user_name = (char *)calloc(1 + length,1);
357 /* BB adding free of user_name string before exit,
358 not really necessary but would be cleaner */
359 strlcpy(user_name,temp_val, length+1);
362 } else if (strncasecmp("password",line_buf+i,8) == 0) {
363 temp_val = strchr(line_buf+i,'=');
364 if(temp_val) {
365 /* go past equals sign */
366 temp_val++;
367 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
368 if ((temp_val[length] == '\n')
369 || (temp_val[length] == '\0')) {
370 temp_val[length] = '\0';
371 break;
374 if(length > MOUNT_PASSWD_SIZE) {
375 printf("mount.cifs failed: password in credentials file too long\n");
376 memset(line_buf,0, 4096);
377 exit(EX_USAGE);
378 } else {
379 if(mountpassword == NULL) {
380 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
381 } else
382 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
383 if(mountpassword) {
384 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
385 got_password = 1;
389 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
390 temp_val = strchr(line_buf+i,'=');
391 if(temp_val) {
392 /* go past equals sign */
393 temp_val++;
394 if(verboseflag)
395 printf("\nDomain %s\n",temp_val);
396 for(length = 0;length<DOMAIN_SIZE+1;length++) {
397 if ((temp_val[length] == '\n')
398 || (temp_val[length] == '\0')) {
399 temp_val[length] = '\0';
400 break;
403 if(length > DOMAIN_SIZE) {
404 printf("mount.cifs failed: domain in credentials file too long\n");
405 exit(EX_USAGE);
406 } else {
407 if(domain_name == NULL) {
408 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
409 } else
410 memset(domain_name,0,DOMAIN_SIZE);
411 if(domain_name) {
412 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
413 got_domain = 1;
420 fclose(fs);
421 SAFE_FREE(line_buf);
422 return 0;
425 static int get_password_from_file(int file_descript, char * filename)
427 int rc = 0;
428 int i;
429 char c;
431 if(mountpassword == NULL)
432 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
433 else
434 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
436 if (mountpassword == NULL) {
437 printf("malloc failed\n");
438 exit(EX_SYSERR);
441 if(filename != NULL) {
442 file_descript = open(filename, O_RDONLY);
443 if(file_descript < 0) {
444 printf("mount.cifs failed. %s attempting to open password file %s\n",
445 strerror(errno),filename);
446 exit(EX_SYSERR);
449 /* else file already open and fd provided */
451 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
452 rc = read(file_descript,&c,1);
453 if(rc < 0) {
454 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
455 if(filename != NULL)
456 close(file_descript);
457 exit(EX_SYSERR);
458 } else if(rc == 0) {
459 if(mountpassword[0] == 0) {
460 if(verboseflag)
461 printf("\nWarning: null password used since cifs password file empty");
463 break;
464 } else /* read valid character */ {
465 if((c == 0) || (c == '\n')) {
466 mountpassword[i] = '\0';
467 break;
468 } else
469 mountpassword[i] = c;
472 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
473 printf("\nWarning: password longer than %d characters specified in cifs password file",
474 MOUNT_PASSWD_SIZE);
476 got_password = 1;
477 if(filename != NULL) {
478 close(file_descript);
481 return rc;
484 static int parse_options(char ** optionsp, unsigned long * filesys_flags)
486 const char * data;
487 char * percent_char = NULL;
488 char * value = NULL;
489 char * next_keyword = NULL;
490 char * out = NULL;
491 int out_len = 0;
492 int word_len;
493 int rc = 0;
494 char user[32];
495 char group[32];
497 if (!optionsp || !*optionsp)
498 return 1;
499 data = *optionsp;
501 if(verboseflag)
502 printf("parsing options: %s\n", data);
504 /* BB fixme check for separator override BB */
506 if (getuid()) {
507 got_uid = 1;
508 snprintf(user,sizeof(user),"%u",getuid());
509 got_gid = 1;
510 snprintf(group,sizeof(group),"%u",getgid());
513 /* while ((data = strsep(&options, ",")) != NULL) { */
514 while(data != NULL) {
515 /* check if ends with trailing comma */
516 if(*data == 0)
517 break;
519 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
520 /* data = next keyword */
521 /* value = next value ie stuff after equal sign */
523 next_keyword = strchr(data,','); /* BB handle sep= */
525 /* temporarily null terminate end of keyword=value pair */
526 if(next_keyword)
527 *next_keyword++ = 0;
529 /* temporarily null terminate keyword to make keyword and value distinct */
530 if ((value = strchr(data, '=')) != NULL) {
531 *value = '\0';
532 value++;
535 if (strncmp(data, "users",5) == 0) {
536 if(!value || !*value) {
537 *filesys_flags |= MS_USERS;
538 goto nocopy;
540 } else if (strncmp(data, "user_xattr",10) == 0) {
541 /* do nothing - need to skip so not parsed as user name */
542 } else if (strncmp(data, "user", 4) == 0) {
544 if (!value || !*value) {
545 if(data[4] == '\0') {
546 *filesys_flags |= MS_USER;
547 goto nocopy;
548 } else {
549 printf("username specified with no parameter\n");
550 SAFE_FREE(out);
551 return 1; /* needs_arg; */
553 } else {
554 if (strnlen(value, 260) < 260) {
555 got_user=1;
556 percent_char = strchr(value,'%');
557 if(percent_char) {
558 *percent_char = ',';
559 if(mountpassword == NULL)
560 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
561 if(mountpassword) {
562 if(got_password)
563 printf("\nmount.cifs warning - password specified twice\n");
564 got_password = 1;
565 percent_char++;
566 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
567 /* remove password from username */
568 while(*percent_char != 0) {
569 *percent_char = ',';
570 percent_char++;
574 /* this is only case in which the user
575 name buf is not malloc - so we have to
576 check for domain name embedded within
577 the user name here since the later
578 call to check_for_domain will not be
579 invoked */
580 domain_name = check_for_domain(&value);
581 } else {
582 printf("username too long\n");
583 SAFE_FREE(out);
584 return 1;
587 } else if (strncmp(data, "pass", 4) == 0) {
588 if (!value || !*value) {
589 if(got_password) {
590 printf("\npassword specified twice, ignoring second\n");
591 } else
592 got_password = 1;
593 } else if (strnlen(value, MOUNT_PASSWD_SIZE) < MOUNT_PASSWD_SIZE) {
594 if(got_password)
595 printf("\nmount.cifs warning - password specified twice\n");
596 got_password = 1;
597 } else {
598 printf("password too long\n");
599 SAFE_FREE(out);
600 return 1;
602 } else if (strncmp(data, "sec", 3) == 0) {
603 if (value) {
604 if (!strncmp(value, "none", 4) ||
605 !strncmp(value, "krb5", 4))
606 got_password = 1;
608 } else if (strncmp(data, "ip", 2) == 0) {
609 if (!value || !*value) {
610 printf("target ip address argument missing");
611 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
612 if(verboseflag)
613 printf("ip address %s override specified\n",value);
614 got_ip = 1;
615 } else {
616 printf("ip address too long\n");
617 SAFE_FREE(out);
618 return 1;
620 } else if ((strncmp(data, "unc", 3) == 0)
621 || (strncmp(data, "target", 6) == 0)
622 || (strncmp(data, "path", 4) == 0)) {
623 if (!value || !*value) {
624 printf("invalid path to network resource\n");
625 SAFE_FREE(out);
626 return 1; /* needs_arg; */
627 } else if(strnlen(value,5) < 5) {
628 printf("UNC name too short");
631 if (strnlen(value, 300) < 300) {
632 got_unc = 1;
633 if (strncmp(value, "//", 2) == 0) {
634 if(got_unc)
635 printf("unc name specified twice, ignoring second\n");
636 else
637 got_unc = 1;
638 } else if (strncmp(value, "\\\\", 2) != 0) {
639 printf("UNC Path does not begin with // or \\\\ \n");
640 SAFE_FREE(out);
641 return 1;
642 } else {
643 if(got_unc)
644 printf("unc name specified twice, ignoring second\n");
645 else
646 got_unc = 1;
648 } else {
649 printf("CIFS: UNC name too long\n");
650 SAFE_FREE(out);
651 return 1;
653 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
654 || (strncmp(data, "workg", 5) == 0)) {
655 /* note this allows for synonyms of "domain"
656 such as "DOM" and "dom" and "workgroup"
657 and "WORKGRP" etc. */
658 if (!value || !*value) {
659 printf("CIFS: invalid domain name\n");
660 SAFE_FREE(out);
661 return 1; /* needs_arg; */
663 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
664 got_domain = 1;
665 } else {
666 printf("domain name too long\n");
667 SAFE_FREE(out);
668 return 1;
670 } else if (strncmp(data, "cred", 4) == 0) {
671 if (value && *value) {
672 rc = open_cred_file(value);
673 if(rc) {
674 printf("error %d (%s) opening credential file %s\n",
675 rc, strerror(rc), value);
676 SAFE_FREE(out);
677 return 1;
679 } else {
680 printf("invalid credential file name specified\n");
681 SAFE_FREE(out);
682 return 1;
684 } else if (strncmp(data, "uid", 3) == 0) {
685 if (value && *value) {
686 got_uid = 1;
687 if (!isdigit(*value)) {
688 struct passwd *pw;
690 if (!(pw = getpwnam(value))) {
691 printf("bad user name \"%s\"\n", value);
692 exit(EX_USAGE);
694 snprintf(user, sizeof(user), "%u", pw->pw_uid);
695 } else {
696 strlcpy(user,value,sizeof(user));
699 goto nocopy;
700 } else if (strncmp(data, "gid", 3) == 0) {
701 if (value && *value) {
702 got_gid = 1;
703 if (!isdigit(*value)) {
704 struct group *gr;
706 if (!(gr = getgrnam(value))) {
707 printf("bad group name \"%s\"\n", value);
708 exit(EX_USAGE);
710 snprintf(group, sizeof(group), "%u", gr->gr_gid);
711 } else {
712 strlcpy(group,value,sizeof(group));
715 goto nocopy;
716 /* fmask and dmask synonyms for people used to smbfs syntax */
717 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
718 if (!value || !*value) {
719 printf ("Option '%s' requires a numerical argument\n", data);
720 SAFE_FREE(out);
721 return 1;
724 if (value[0] != '0') {
725 printf ("WARNING: '%s' not expressed in octal.\n", data);
728 if (strcmp (data, "fmask") == 0) {
729 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
730 data = "file_mode"; /* BB fix this */
732 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
733 if (!value || !*value) {
734 printf ("Option '%s' requires a numerical argument\n", data);
735 SAFE_FREE(out);
736 return 1;
739 if (value[0] != '0') {
740 printf ("WARNING: '%s' not expressed in octal.\n", data);
743 if (strcmp (data, "dmask") == 0) {
744 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
745 data = "dir_mode";
747 /* the following eight mount options should be
748 stripped out from what is passed into the kernel
749 since these eight options are best passed as the
750 mount flags rather than redundantly to the kernel
751 and could generate spurious warnings depending on the
752 level of the corresponding cifs vfs kernel code */
753 } else if (strncmp(data, "nosuid", 6) == 0) {
754 *filesys_flags |= MS_NOSUID;
755 } else if (strncmp(data, "suid", 4) == 0) {
756 *filesys_flags &= ~MS_NOSUID;
757 } else if (strncmp(data, "nodev", 5) == 0) {
758 *filesys_flags |= MS_NODEV;
759 } else if ((strncmp(data, "nobrl", 5) == 0) ||
760 (strncmp(data, "nolock", 6) == 0)) {
761 *filesys_flags &= ~MS_MANDLOCK;
762 } else if (strncmp(data, "dev", 3) == 0) {
763 *filesys_flags &= ~MS_NODEV;
764 } else if (strncmp(data, "noexec", 6) == 0) {
765 *filesys_flags |= MS_NOEXEC;
766 } else if (strncmp(data, "exec", 4) == 0) {
767 *filesys_flags &= ~MS_NOEXEC;
768 } else if (strncmp(data, "guest", 5) == 0) {
769 user_name = (char *)calloc(1, 1);
770 got_user = 1;
771 got_password = 1;
772 } else if (strncmp(data, "ro", 2) == 0) {
773 *filesys_flags |= MS_RDONLY;
774 goto nocopy;
775 } else if (strncmp(data, "rw", 2) == 0) {
776 *filesys_flags &= ~MS_RDONLY;
777 goto nocopy;
778 } else if (strncmp(data, "remount", 7) == 0) {
779 *filesys_flags |= MS_REMOUNT;
780 } /* else if (strnicmp(data, "port", 4) == 0) {
781 if (value && *value) {
782 vol->port =
783 simple_strtoul(value, &value, 0);
785 } else if (strnicmp(data, "rsize", 5) == 0) {
786 if (value && *value) {
787 vol->rsize =
788 simple_strtoul(value, &value, 0);
790 } else if (strnicmp(data, "wsize", 5) == 0) {
791 if (value && *value) {
792 vol->wsize =
793 simple_strtoul(value, &value, 0);
795 } else if (strnicmp(data, "version", 3) == 0) {
796 } else {
797 printf("CIFS: Unknown mount option %s\n",data);
798 } */ /* nothing to do on those four mount options above.
799 Just pass to kernel and ignore them here */
801 /* Copy (possibly modified) option to out */
802 word_len = strlen(data);
803 if (value)
804 word_len += 1 + strlen(value);
806 out = (char *)realloc(out, out_len + word_len + 2);
807 if (out == NULL) {
808 perror("malloc");
809 exit(EX_SYSERR);
812 if (out_len) {
813 strlcat(out, ",", out_len + word_len + 2);
814 out_len++;
817 if (value)
818 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
819 else
820 snprintf(out + out_len, word_len + 1, "%s", data);
821 out_len = strlen(out);
823 nocopy:
824 data = next_keyword;
827 /* special-case the uid and gid */
828 if (got_uid) {
829 word_len = strlen(user);
831 out = (char *)realloc(out, out_len + word_len + 6);
832 if (out == NULL) {
833 perror("malloc");
834 exit(EX_SYSERR);
837 if (out_len) {
838 strlcat(out, ",", out_len + word_len + 6);
839 out_len++;
841 snprintf(out + out_len, word_len + 5, "uid=%s", user);
842 out_len = strlen(out);
844 if (got_gid) {
845 word_len = strlen(group);
847 out = (char *)realloc(out, out_len + 1 + word_len + 6);
848 if (out == NULL) {
849 perror("malloc");
850 exit(EX_SYSERR);
853 if (out_len) {
854 strlcat(out, ",", out_len + word_len + 6);
855 out_len++;
857 snprintf(out + out_len, word_len + 5, "gid=%s", group);
858 out_len = strlen(out);
861 SAFE_FREE(*optionsp);
862 *optionsp = out;
863 return 0;
866 /* replace all (one or more) commas with double commas */
867 static void check_for_comma(char ** ppasswrd)
869 char *new_pass_buf;
870 char *pass;
871 int i,j;
872 int number_of_commas = 0;
873 int len;
875 if(ppasswrd == NULL)
876 return;
877 else
878 (pass = *ppasswrd);
880 len = strlen(pass);
882 for(i=0;i<len;i++) {
883 if(pass[i] == ',')
884 number_of_commas++;
887 if(number_of_commas == 0)
888 return;
889 if(number_of_commas > MOUNT_PASSWD_SIZE) {
890 /* would otherwise overflow the mount options buffer */
891 printf("\nInvalid password. Password contains too many commas.\n");
892 return;
895 new_pass_buf = (char *)malloc(len+number_of_commas+1);
896 if(new_pass_buf == NULL)
897 return;
899 for(i=0,j=0;i<len;i++,j++) {
900 new_pass_buf[j] = pass[i];
901 if(pass[i] == ',') {
902 j++;
903 new_pass_buf[j] = pass[i];
906 new_pass_buf[len+number_of_commas] = 0;
908 SAFE_FREE(*ppasswrd);
909 *ppasswrd = new_pass_buf;
911 return;
914 /* Usernames can not have backslash in them and we use
915 [BB check if usernames can have forward slash in them BB]
916 backslash as domain\user separator character
918 static char * check_for_domain(char **ppuser)
920 char * original_string;
921 char * usernm;
922 char * domainnm;
923 int original_len;
924 int len;
925 int i;
927 if(ppuser == NULL)
928 return NULL;
930 original_string = *ppuser;
932 if (original_string == NULL)
933 return NULL;
935 original_len = strlen(original_string);
937 usernm = strchr(*ppuser,'/');
938 if (usernm == NULL) {
939 usernm = strchr(*ppuser,'\\');
940 if (usernm == NULL)
941 return NULL;
944 if(got_domain) {
945 printf("Domain name specified twice. Username probably malformed\n");
946 return NULL;
949 usernm[0] = 0;
950 domainnm = *ppuser;
951 if (domainnm[0] != 0) {
952 got_domain = 1;
953 } else {
954 printf("null domain\n");
956 len = strlen(domainnm);
957 /* reset domainm to new buffer, and copy
958 domain name into it */
959 domainnm = (char *)malloc(len+1);
960 if(domainnm == NULL)
961 return NULL;
963 strlcpy(domainnm,*ppuser,len+1);
965 /* move_string(*ppuser, usernm+1) */
966 len = strlen(usernm+1);
968 if(len >= original_len) {
969 /* should not happen */
970 return domainnm;
973 for(i=0;i<original_len;i++) {
974 if(i<len)
975 original_string[i] = usernm[i+1];
976 else /* stuff with commas to remove last parm */
977 original_string[i] = ',';
980 /* BB add check for more than one slash?
981 strchr(*ppuser,'/');
982 strchr(*ppuser,'\\')
985 return domainnm;
988 /* replace all occurances of "from" in a string with "to" */
989 static void replace_char(char *string, char from, char to, int maxlen)
991 char *lastchar = string + maxlen;
992 while (string) {
993 string = strchr(string, from);
994 if (string) {
995 *string = to;
996 if (string >= lastchar)
997 return;
1002 /* Note that caller frees the returned buffer if necessary */
1003 static struct addrinfo *
1004 parse_server(char ** punc_name)
1006 char * unc_name = *punc_name;
1007 int length = strnlen(unc_name, MAX_UNC_LEN);
1008 char * share;
1009 struct addrinfo *addrlist;
1010 int rc;
1012 if(length > (MAX_UNC_LEN - 1)) {
1013 printf("mount error: UNC name too long");
1014 return NULL;
1016 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1017 (strncasecmp("smb://", unc_name, 6) == 0)) {
1018 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1019 return NULL;
1022 if(length < 3) {
1023 /* BB add code to find DFS root here */
1024 printf("\nMounting the DFS root for domain not implemented yet\n");
1025 return NULL;
1026 } else {
1027 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1028 /* check for nfs syntax ie server:share */
1029 share = strchr(unc_name,':');
1030 if(share) {
1031 *punc_name = (char *)malloc(length+3);
1032 if(*punc_name == NULL) {
1033 /* put the original string back if
1034 no memory left */
1035 *punc_name = unc_name;
1036 return NULL;
1038 *share = '/';
1039 strlcpy((*punc_name)+2,unc_name,length+1);
1040 SAFE_FREE(unc_name);
1041 unc_name = *punc_name;
1042 unc_name[length+2] = 0;
1043 goto continue_unc_parsing;
1044 } else {
1045 printf("mount error: improperly formatted UNC name.");
1046 printf(" %s does not begin with \\\\ or //\n",unc_name);
1047 return NULL;
1049 } else {
1050 continue_unc_parsing:
1051 unc_name[0] = '/';
1052 unc_name[1] = '/';
1053 unc_name += 2;
1055 /* allow for either delimiter between host and sharename */
1056 if ((share = strpbrk(unc_name, "/\\"))) {
1057 *share = 0; /* temporarily terminate the string */
1058 share += 1;
1059 if(got_ip == 0) {
1060 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1061 if (rc != 0) {
1062 printf("mount error: could not resolve address for %s: %s\n",
1063 unc_name, gai_strerror(rc));
1064 addrlist = NULL;
1067 *(share - 1) = '/'; /* put delimiter back */
1069 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1070 if ((prefixpath = strpbrk(share, "/\\"))) {
1071 *prefixpath = 0; /* permanently terminate the string */
1072 if (!strlen(++prefixpath))
1073 prefixpath = NULL; /* this needs to be done explicitly */
1075 if(got_ip) {
1076 if(verboseflag)
1077 printf("ip address specified explicitly\n");
1078 return NULL;
1080 /* BB should we pass an alternate version of the share name as Unicode */
1082 return addrlist;
1083 } else {
1084 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1085 printf("Mounting the DFS root for a particular server not implemented yet\n");
1086 return NULL;
1092 static struct option longopts[] = {
1093 { "all", 0, NULL, 'a' },
1094 { "help",0, NULL, 'h' },
1095 { "move",0, NULL, 'm' },
1096 { "bind",0, NULL, 'b' },
1097 { "read-only", 0, NULL, 'r' },
1098 { "ro", 0, NULL, 'r' },
1099 { "verbose", 0, NULL, 'v' },
1100 { "version", 0, NULL, 'V' },
1101 { "read-write", 0, NULL, 'w' },
1102 { "rw", 0, NULL, 'w' },
1103 { "options", 1, NULL, 'o' },
1104 { "type", 1, NULL, 't' },
1105 { "rsize",1, NULL, 'R' },
1106 { "wsize",1, NULL, 'W' },
1107 { "uid", 1, NULL, '1'},
1108 { "gid", 1, NULL, '2'},
1109 { "user",1,NULL,'u'},
1110 { "username",1,NULL,'u'},
1111 { "dom",1,NULL,'d'},
1112 { "domain",1,NULL,'d'},
1113 { "password",1,NULL,'p'},
1114 { "pass",1,NULL,'p'},
1115 { "credentials",1,NULL,'c'},
1116 { "port",1,NULL,'P'},
1117 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1118 { NULL, 0, NULL, 0 }
1121 /* convert a string to uppercase. return false if the string
1122 * wasn't ASCII. Return success on a NULL ptr */
1123 static int
1124 uppercase_string(char *string)
1126 if (!string)
1127 return 1;
1129 while (*string) {
1130 /* check for unicode */
1131 if ((unsigned char) string[0] & 0x80)
1132 return 0;
1133 *string = toupper((unsigned char) *string);
1134 string++;
1137 return 1;
1140 static void print_cifs_mount_version(void)
1142 printf("mount.cifs version: %s.%s%s\n",
1143 MOUNT_CIFS_VERSION_MAJOR,
1144 MOUNT_CIFS_VERSION_MINOR,
1145 MOUNT_CIFS_VENDOR_SUFFIX);
1148 int main(int argc, char ** argv)
1150 int c;
1151 unsigned long flags = MS_MANDLOCK;
1152 char * orgoptions = NULL;
1153 char * share_name = NULL;
1154 const char * ipaddr = NULL;
1155 char * uuid = NULL;
1156 char * mountpoint = NULL;
1157 char * options = NULL;
1158 char * optionstail;
1159 char * resolved_path = NULL;
1160 char * temp;
1161 char * dev_name;
1162 int rc = 0;
1163 int rsize = 0;
1164 int wsize = 0;
1165 int nomtab = 0;
1166 int uid = 0;
1167 int gid = 0;
1168 int optlen = 0;
1169 int orgoptlen = 0;
1170 size_t options_size = 0;
1171 size_t current_len;
1172 int retry = 0; /* set when we have to retry mount with uppercase */
1173 struct addrinfo *addrhead = NULL, *addr;
1174 struct utsname sysinfo;
1175 struct mntent mountent;
1176 struct sockaddr_in *addr4;
1177 struct sockaddr_in6 *addr6;
1178 FILE * pmntfile;
1180 /* setlocale(LC_ALL, "");
1181 bindtextdomain(PACKAGE, LOCALEDIR);
1182 textdomain(PACKAGE); */
1184 if(argc && argv) {
1185 thisprogram = argv[0];
1186 } else {
1187 mount_cifs_usage();
1188 exit(EX_USAGE);
1191 if(thisprogram == NULL)
1192 thisprogram = "mount.cifs";
1194 uname(&sysinfo);
1195 /* BB add workstation name and domain and pass down */
1197 /* #ifdef _GNU_SOURCE
1198 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1199 #endif */
1200 if(argc > 2) {
1201 dev_name = argv[1];
1202 share_name = strndup(argv[1], MAX_UNC_LEN);
1203 if (share_name == NULL) {
1204 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1205 exit(EX_SYSERR);
1207 mountpoint = argv[2];
1208 } else if (argc == 2) {
1209 if ((strcmp(argv[1], "-V") == 0) ||
1210 (strcmp(argv[1], "--version") == 0))
1212 print_cifs_mount_version();
1213 exit(0);
1216 if ((strcmp(argv[1], "-h") == 0) ||
1217 (strcmp(argv[1], "-?") == 0) ||
1218 (strcmp(argv[1], "--help") == 0))
1220 mount_cifs_usage();
1221 exit(0);
1224 mount_cifs_usage();
1225 exit(EX_USAGE);
1226 } else {
1227 mount_cifs_usage();
1228 exit(EX_USAGE);
1232 /* add sharename in opts string as unc= parm */
1233 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1234 longopts, NULL)) != -1) {
1235 switch (c) {
1236 /* No code to do the following options yet */
1237 /* case 'l':
1238 list_with_volumelabel = 1;
1239 break;
1240 case 'L':
1241 volumelabel = optarg;
1242 break; */
1243 /* case 'a':
1244 ++mount_all;
1245 break; */
1247 case '?':
1248 case 'h': /* help */
1249 mount_cifs_usage ();
1250 exit(0);
1251 case 'n':
1252 ++nomtab;
1253 break;
1254 case 'b':
1255 #ifdef MS_BIND
1256 flags |= MS_BIND;
1257 #else
1258 fprintf(stderr,
1259 "option 'b' (MS_BIND) not supported\n");
1260 #endif
1261 break;
1262 case 'm':
1263 #ifdef MS_MOVE
1264 flags |= MS_MOVE;
1265 #else
1266 fprintf(stderr,
1267 "option 'm' (MS_MOVE) not supported\n");
1268 #endif
1269 break;
1270 case 'o':
1271 orgoptions = strdup(optarg);
1272 break;
1273 case 'r': /* mount readonly */
1274 flags |= MS_RDONLY;
1275 break;
1276 case 'U':
1277 uuid = optarg;
1278 break;
1279 case 'v':
1280 ++verboseflag;
1281 break;
1282 case 'V':
1283 print_cifs_mount_version();
1284 exit (0);
1285 case 'w':
1286 flags &= ~MS_RDONLY;
1287 break;
1288 case 'R':
1289 rsize = atoi(optarg) ;
1290 break;
1291 case 'W':
1292 wsize = atoi(optarg);
1293 break;
1294 case '1':
1295 if (isdigit(*optarg)) {
1296 char *ep;
1298 uid = strtoul(optarg, &ep, 10);
1299 if (*ep) {
1300 printf("bad uid value \"%s\"\n", optarg);
1301 exit(EX_USAGE);
1303 } else {
1304 struct passwd *pw;
1306 if (!(pw = getpwnam(optarg))) {
1307 printf("bad user name \"%s\"\n", optarg);
1308 exit(EX_USAGE);
1310 uid = pw->pw_uid;
1311 endpwent();
1313 break;
1314 case '2':
1315 if (isdigit(*optarg)) {
1316 char *ep;
1318 gid = strtoul(optarg, &ep, 10);
1319 if (*ep) {
1320 printf("bad gid value \"%s\"\n", optarg);
1321 exit(EX_USAGE);
1323 } else {
1324 struct group *gr;
1326 if (!(gr = getgrnam(optarg))) {
1327 printf("bad user name \"%s\"\n", optarg);
1328 exit(EX_USAGE);
1330 gid = gr->gr_gid;
1331 endpwent();
1333 break;
1334 case 'u':
1335 got_user = 1;
1336 user_name = optarg;
1337 break;
1338 case 'd':
1339 domain_name = optarg; /* BB fix this - currently ignored */
1340 got_domain = 1;
1341 break;
1342 case 'p':
1343 if(mountpassword == NULL)
1344 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1345 if(mountpassword) {
1346 got_password = 1;
1347 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1349 break;
1350 case 'S':
1351 get_password_from_file(0 /* stdin */,NULL);
1352 break;
1353 case 't':
1354 break;
1355 case 'f':
1356 ++fakemnt;
1357 break;
1358 default:
1359 printf("unknown mount option %c\n",c);
1360 mount_cifs_usage();
1361 exit(EX_USAGE);
1365 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1366 mount_cifs_usage();
1367 exit(EX_USAGE);
1370 /* make sure mountpoint is legit */
1371 rc = check_mountpoint(thisprogram, mountpoint);
1372 if (rc)
1373 goto mount_exit;
1375 /* sanity check for unprivileged mounts */
1376 if (getuid()) {
1377 rc = check_fstab(thisprogram, mountpoint, dev_name,
1378 &orgoptions);
1379 if (rc)
1380 goto mount_exit;
1382 /* enable any default user mount flags */
1383 flags |= CIFS_SETUID_FLAGS;
1386 if (getenv("PASSWD")) {
1387 if(mountpassword == NULL)
1388 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1389 if(mountpassword) {
1390 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1391 got_password = 1;
1393 } else if (getenv("PASSWD_FD")) {
1394 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1395 } else if (getenv("PASSWD_FILE")) {
1396 get_password_from_file(0, getenv("PASSWD_FILE"));
1399 if (orgoptions && parse_options(&orgoptions, &flags)) {
1400 rc = EX_USAGE;
1401 goto mount_exit;
1404 if (getuid()) {
1405 #if !CIFS_LEGACY_SETUID_CHECK
1406 if (!(flags & (MS_USERS|MS_USER))) {
1407 fprintf(stderr, "%s: permission denied\n", thisprogram);
1408 rc = EX_USAGE;
1409 goto mount_exit;
1411 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1413 if (geteuid()) {
1414 fprintf(stderr, "%s: not installed setuid - \"user\" "
1415 "CIFS mounts not supported.",
1416 thisprogram);
1417 rc = EX_FAIL;
1418 goto mount_exit;
1422 flags &= ~(MS_USERS|MS_USER);
1424 addrhead = addr = parse_server(&share_name);
1425 if((addrhead == NULL) && (got_ip == 0)) {
1426 printf("No ip address specified and hostname not found\n");
1427 rc = EX_USAGE;
1428 goto mount_exit;
1431 /* BB save off path and pop after mount returns? */
1432 resolved_path = (char *)malloc(PATH_MAX+1);
1433 if(resolved_path) {
1434 /* Note that if we can not canonicalize the name, we get
1435 another chance to see if it is valid when we chdir to it */
1436 if (realpath(mountpoint, resolved_path)) {
1437 mountpoint = resolved_path;
1440 if(got_user == 0) {
1441 /* Note that the password will not be retrieved from the
1442 USER env variable (ie user%password form) as there is
1443 already a PASSWD environment varaible */
1444 if (getenv("USER"))
1445 user_name = strdup(getenv("USER"));
1446 if (user_name == NULL)
1447 user_name = getusername();
1448 got_user = 1;
1451 if(got_password == 0) {
1452 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1453 no good replacement yet. */
1454 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1455 if (!tmp_pass || !mountpassword) {
1456 printf("Password not entered, exiting\n");
1457 exit(EX_USAGE);
1459 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1460 got_password = 1;
1462 /* FIXME launch daemon (handles dfs name resolution and credential change)
1463 remember to clear parms and overwrite password field before launching */
1464 if(orgoptions) {
1465 optlen = strlen(orgoptions);
1466 orgoptlen = optlen;
1467 } else
1468 optlen = 0;
1469 if(share_name)
1470 optlen += strlen(share_name) + 4;
1471 else {
1472 printf("No server share name specified\n");
1473 printf("\nMounting the DFS root for server not implemented yet\n");
1474 exit(EX_USAGE);
1476 if(user_name)
1477 optlen += strlen(user_name) + 6;
1478 optlen += MAX_ADDRESS_LEN + 4;
1479 if(mountpassword)
1480 optlen += strlen(mountpassword) + 6;
1481 mount_retry:
1482 SAFE_FREE(options);
1483 options_size = optlen + 10 + DOMAIN_SIZE;
1484 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 */);
1486 if(options == NULL) {
1487 printf("Could not allocate memory for mount options\n");
1488 exit(EX_SYSERR);
1491 strlcpy(options, "unc=", options_size);
1492 strlcat(options,share_name,options_size);
1493 /* scan backwards and reverse direction of slash */
1494 temp = strrchr(options, '/');
1495 if(temp > options + 6)
1496 *temp = '\\';
1497 if(user_name) {
1498 /* check for syntax like user=domain\user */
1499 if(got_domain == 0)
1500 domain_name = check_for_domain(&user_name);
1501 strlcat(options,",user=",options_size);
1502 strlcat(options,user_name,options_size);
1504 if(retry == 0) {
1505 if(domain_name) {
1506 /* extra length accounted for in option string above */
1507 strlcat(options,",domain=",options_size);
1508 strlcat(options,domain_name,options_size);
1511 if(mountpassword) {
1512 /* Commas have to be doubled, or else they will
1513 look like the parameter separator */
1514 /* if(sep is not set)*/
1515 if(retry == 0)
1516 check_for_comma(&mountpassword);
1517 strlcat(options,",pass=",options_size);
1518 strlcat(options,mountpassword,options_size);
1521 strlcat(options,",ver=",options_size);
1522 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1524 if(orgoptions) {
1525 strlcat(options,",",options_size);
1526 strlcat(options,orgoptions,options_size);
1528 if(prefixpath) {
1529 strlcat(options,",prefixpath=",options_size);
1530 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1532 if(verboseflag)
1533 printf("\nmount.cifs kernel mount options %s \n",options);
1535 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1536 replace_char(dev_name, '\\', '/', strlen(share_name));
1538 if (!got_ip && addr) {
1539 strlcat(options, ",ip=", options_size);
1540 current_len = strnlen(options, options_size);
1541 optionstail = options + current_len;
1542 switch (addr->ai_addr->sa_family) {
1543 case AF_INET6:
1544 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1545 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1546 options_size - current_len);
1547 break;
1548 case AF_INET:
1549 addr4 = (struct sockaddr_in *) addr->ai_addr;
1550 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1551 options_size - current_len);
1552 break;
1553 default:
1554 ipaddr = NULL;
1557 /* if the address looks bogus, try the next one */
1558 if (!ipaddr) {
1559 addr = addr->ai_next;
1560 if (addr)
1561 goto mount_retry;
1562 rc = EX_SYSERR;
1563 goto mount_exit;
1567 if (addr->ai_addr->sa_family == AF_INET6 && addr6->sin6_scope_id) {
1568 strlcat(options, "%", options_size);
1569 current_len = strnlen(options, options_size);
1570 optionstail = options + current_len;
1571 snprintf(optionstail, options_size - current_len, "%u",
1572 addr6->sin6_scope_id);
1575 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1576 switch (errno) {
1577 case ECONNREFUSED:
1578 case EHOSTUNREACH:
1579 if (addr) {
1580 addr = addr->ai_next;
1581 if (addr)
1582 goto mount_retry;
1584 break;
1585 case ENODEV:
1586 printf("mount error: cifs filesystem not supported by the system\n");
1587 break;
1588 case ENXIO:
1589 if(retry == 0) {
1590 retry = 1;
1591 if (uppercase_string(dev_name) &&
1592 uppercase_string(share_name) &&
1593 uppercase_string(prefixpath)) {
1594 printf("retrying with upper case share name\n");
1595 goto mount_retry;
1599 printf("mount error(%d): %s\n", errno, strerror(errno));
1600 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1601 "mount.cifs)\n");
1602 rc = EX_FAIL;
1603 goto mount_exit;
1606 if (nomtab)
1607 goto mount_exit;
1608 atexit(unlock_mtab);
1609 rc = lock_mtab();
1610 if (rc) {
1611 printf("cannot lock mtab");
1612 goto mount_exit;
1614 pmntfile = setmntent(MOUNTED, "a+");
1615 if (!pmntfile) {
1616 printf("could not update mount table\n");
1617 unlock_mtab();
1618 rc = EX_FILEIO;
1619 goto mount_exit;
1621 mountent.mnt_fsname = dev_name;
1622 mountent.mnt_dir = mountpoint;
1623 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1624 mountent.mnt_opts = (char *)malloc(220);
1625 if(mountent.mnt_opts) {
1626 char * mount_user = getusername();
1627 memset(mountent.mnt_opts,0,200);
1628 if(flags & MS_RDONLY)
1629 strlcat(mountent.mnt_opts,"ro",220);
1630 else
1631 strlcat(mountent.mnt_opts,"rw",220);
1632 if(flags & MS_MANDLOCK)
1633 strlcat(mountent.mnt_opts,",mand",220);
1634 if(flags & MS_NOEXEC)
1635 strlcat(mountent.mnt_opts,",noexec",220);
1636 if(flags & MS_NOSUID)
1637 strlcat(mountent.mnt_opts,",nosuid",220);
1638 if(flags & MS_NODEV)
1639 strlcat(mountent.mnt_opts,",nodev",220);
1640 if(flags & MS_SYNCHRONOUS)
1641 strlcat(mountent.mnt_opts,",sync",220);
1642 if(mount_user) {
1643 if(getuid() != 0) {
1644 strlcat(mountent.mnt_opts,
1645 ",user=", 220);
1646 strlcat(mountent.mnt_opts,
1647 mount_user, 220);
1651 mountent.mnt_freq = 0;
1652 mountent.mnt_passno = 0;
1653 rc = addmntent(pmntfile,&mountent);
1654 endmntent(pmntfile);
1655 unlock_mtab();
1656 SAFE_FREE(mountent.mnt_opts);
1657 if (rc)
1658 rc = EX_FILEIO;
1659 mount_exit:
1660 if(mountpassword) {
1661 int len = strlen(mountpassword);
1662 memset(mountpassword,0,len);
1663 SAFE_FREE(mountpassword);
1666 if (addrhead)
1667 freeaddrinfo(addrhead);
1668 SAFE_FREE(options);
1669 SAFE_FREE(orgoptions);
1670 SAFE_FREE(resolved_path);
1671 SAFE_FREE(share_name);
1672 exit(rc);