s3/docs: Fix typos.
[Samba/gebeck_regimport.git] / client / mount.cifs.c
blobf53bcf16071cb611ff155ceeecc4f0974ace610b
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 } else if (strncmp(data, "rw", 2) == 0) {
775 *filesys_flags &= ~MS_RDONLY;
776 } else if (strncmp(data, "remount", 7) == 0) {
777 *filesys_flags |= MS_REMOUNT;
778 } /* else if (strnicmp(data, "port", 4) == 0) {
779 if (value && *value) {
780 vol->port =
781 simple_strtoul(value, &value, 0);
783 } else if (strnicmp(data, "rsize", 5) == 0) {
784 if (value && *value) {
785 vol->rsize =
786 simple_strtoul(value, &value, 0);
788 } else if (strnicmp(data, "wsize", 5) == 0) {
789 if (value && *value) {
790 vol->wsize =
791 simple_strtoul(value, &value, 0);
793 } else if (strnicmp(data, "version", 3) == 0) {
794 } else {
795 printf("CIFS: Unknown mount option %s\n",data);
796 } */ /* nothing to do on those four mount options above.
797 Just pass to kernel and ignore them here */
799 /* Copy (possibly modified) option to out */
800 word_len = strlen(data);
801 if (value)
802 word_len += 1 + strlen(value);
804 out = (char *)realloc(out, out_len + word_len + 2);
805 if (out == NULL) {
806 perror("malloc");
807 exit(EX_SYSERR);
810 if (out_len) {
811 strlcat(out, ",", out_len + word_len + 2);
812 out_len++;
815 if (value)
816 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
817 else
818 snprintf(out + out_len, word_len + 1, "%s", data);
819 out_len = strlen(out);
821 nocopy:
822 data = next_keyword;
825 /* special-case the uid and gid */
826 if (got_uid) {
827 word_len = strlen(user);
829 out = (char *)realloc(out, out_len + word_len + 6);
830 if (out == NULL) {
831 perror("malloc");
832 exit(EX_SYSERR);
835 if (out_len) {
836 strlcat(out, ",", out_len + word_len + 6);
837 out_len++;
839 snprintf(out + out_len, word_len + 5, "uid=%s", user);
840 out_len = strlen(out);
842 if (got_gid) {
843 word_len = strlen(group);
845 out = (char *)realloc(out, out_len + 1 + word_len + 6);
846 if (out == NULL) {
847 perror("malloc");
848 exit(EX_SYSERR);
851 if (out_len) {
852 strlcat(out, ",", out_len + word_len + 6);
853 out_len++;
855 snprintf(out + out_len, word_len + 5, "gid=%s", group);
856 out_len = strlen(out);
859 SAFE_FREE(*optionsp);
860 *optionsp = out;
861 return 0;
864 /* replace all (one or more) commas with double commas */
865 static void check_for_comma(char ** ppasswrd)
867 char *new_pass_buf;
868 char *pass;
869 int i,j;
870 int number_of_commas = 0;
871 int len;
873 if(ppasswrd == NULL)
874 return;
875 else
876 (pass = *ppasswrd);
878 len = strlen(pass);
880 for(i=0;i<len;i++) {
881 if(pass[i] == ',')
882 number_of_commas++;
885 if(number_of_commas == 0)
886 return;
887 if(number_of_commas > MOUNT_PASSWD_SIZE) {
888 /* would otherwise overflow the mount options buffer */
889 printf("\nInvalid password. Password contains too many commas.\n");
890 return;
893 new_pass_buf = (char *)malloc(len+number_of_commas+1);
894 if(new_pass_buf == NULL)
895 return;
897 for(i=0,j=0;i<len;i++,j++) {
898 new_pass_buf[j] = pass[i];
899 if(pass[i] == ',') {
900 j++;
901 new_pass_buf[j] = pass[i];
904 new_pass_buf[len+number_of_commas] = 0;
906 SAFE_FREE(*ppasswrd);
907 *ppasswrd = new_pass_buf;
909 return;
912 /* Usernames can not have backslash in them and we use
913 [BB check if usernames can have forward slash in them BB]
914 backslash as domain\user separator character
916 static char * check_for_domain(char **ppuser)
918 char * original_string;
919 char * usernm;
920 char * domainnm;
921 int original_len;
922 int len;
923 int i;
925 if(ppuser == NULL)
926 return NULL;
928 original_string = *ppuser;
930 if (original_string == NULL)
931 return NULL;
933 original_len = strlen(original_string);
935 usernm = strchr(*ppuser,'/');
936 if (usernm == NULL) {
937 usernm = strchr(*ppuser,'\\');
938 if (usernm == NULL)
939 return NULL;
942 if(got_domain) {
943 printf("Domain name specified twice. Username probably malformed\n");
944 return NULL;
947 usernm[0] = 0;
948 domainnm = *ppuser;
949 if (domainnm[0] != 0) {
950 got_domain = 1;
951 } else {
952 printf("null domain\n");
954 len = strlen(domainnm);
955 /* reset domainm to new buffer, and copy
956 domain name into it */
957 domainnm = (char *)malloc(len+1);
958 if(domainnm == NULL)
959 return NULL;
961 strlcpy(domainnm,*ppuser,len+1);
963 /* move_string(*ppuser, usernm+1) */
964 len = strlen(usernm+1);
966 if(len >= original_len) {
967 /* should not happen */
968 return domainnm;
971 for(i=0;i<original_len;i++) {
972 if(i<len)
973 original_string[i] = usernm[i+1];
974 else /* stuff with commas to remove last parm */
975 original_string[i] = ',';
978 /* BB add check for more than one slash?
979 strchr(*ppuser,'/');
980 strchr(*ppuser,'\\')
983 return domainnm;
986 /* replace all occurances of "from" in a string with "to" */
987 static void replace_char(char *string, char from, char to, int maxlen)
989 char *lastchar = string + maxlen;
990 while (string) {
991 string = strchr(string, from);
992 if (string) {
993 *string = to;
994 if (string >= lastchar)
995 return;
1000 /* Note that caller frees the returned buffer if necessary */
1001 static struct addrinfo *
1002 parse_server(char ** punc_name)
1004 char * unc_name = *punc_name;
1005 int length = strnlen(unc_name, MAX_UNC_LEN);
1006 char * share;
1007 struct addrinfo *addrlist;
1008 int rc;
1010 if(length > (MAX_UNC_LEN - 1)) {
1011 printf("mount error: UNC name too long");
1012 return NULL;
1014 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
1015 (strncasecmp("smb://", unc_name, 6) == 0)) {
1016 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
1017 return NULL;
1020 if(length < 3) {
1021 /* BB add code to find DFS root here */
1022 printf("\nMounting the DFS root for domain not implemented yet\n");
1023 return NULL;
1024 } else {
1025 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
1026 /* check for nfs syntax ie server:share */
1027 share = strchr(unc_name,':');
1028 if(share) {
1029 *punc_name = (char *)malloc(length+3);
1030 if(*punc_name == NULL) {
1031 /* put the original string back if
1032 no memory left */
1033 *punc_name = unc_name;
1034 return NULL;
1036 *share = '/';
1037 strlcpy((*punc_name)+2,unc_name,length+1);
1038 SAFE_FREE(unc_name);
1039 unc_name = *punc_name;
1040 unc_name[length+2] = 0;
1041 goto continue_unc_parsing;
1042 } else {
1043 printf("mount error: improperly formatted UNC name.");
1044 printf(" %s does not begin with \\\\ or //\n",unc_name);
1045 return NULL;
1047 } else {
1048 continue_unc_parsing:
1049 unc_name[0] = '/';
1050 unc_name[1] = '/';
1051 unc_name += 2;
1053 /* allow for either delimiter between host and sharename */
1054 if ((share = strpbrk(unc_name, "/\\"))) {
1055 *share = 0; /* temporarily terminate the string */
1056 share += 1;
1057 if(got_ip == 0) {
1058 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
1059 if (rc != 0) {
1060 printf("mount error: could not resolve address for %s: %s\n",
1061 unc_name, gai_strerror(rc));
1062 addrlist = NULL;
1065 *(share - 1) = '/'; /* put delimiter back */
1067 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
1068 if ((prefixpath = strpbrk(share, "/\\"))) {
1069 *prefixpath = 0; /* permanently terminate the string */
1070 if (!strlen(++prefixpath))
1071 prefixpath = NULL; /* this needs to be done explicitly */
1073 if(got_ip) {
1074 if(verboseflag)
1075 printf("ip address specified explicitly\n");
1076 return NULL;
1078 /* BB should we pass an alternate version of the share name as Unicode */
1080 return addrlist;
1081 } else {
1082 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
1083 printf("Mounting the DFS root for a particular server not implemented yet\n");
1084 return NULL;
1090 static struct option longopts[] = {
1091 { "all", 0, NULL, 'a' },
1092 { "help",0, NULL, 'h' },
1093 { "move",0, NULL, 'm' },
1094 { "bind",0, NULL, 'b' },
1095 { "read-only", 0, NULL, 'r' },
1096 { "ro", 0, NULL, 'r' },
1097 { "verbose", 0, NULL, 'v' },
1098 { "version", 0, NULL, 'V' },
1099 { "read-write", 0, NULL, 'w' },
1100 { "rw", 0, NULL, 'w' },
1101 { "options", 1, NULL, 'o' },
1102 { "type", 1, NULL, 't' },
1103 { "rsize",1, NULL, 'R' },
1104 { "wsize",1, NULL, 'W' },
1105 { "uid", 1, NULL, '1'},
1106 { "gid", 1, NULL, '2'},
1107 { "user",1,NULL,'u'},
1108 { "username",1,NULL,'u'},
1109 { "dom",1,NULL,'d'},
1110 { "domain",1,NULL,'d'},
1111 { "password",1,NULL,'p'},
1112 { "pass",1,NULL,'p'},
1113 { "credentials",1,NULL,'c'},
1114 { "port",1,NULL,'P'},
1115 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1116 { NULL, 0, NULL, 0 }
1119 /* convert a string to uppercase. return false if the string
1120 * wasn't ASCII. Return success on a NULL ptr */
1121 static int
1122 uppercase_string(char *string)
1124 if (!string)
1125 return 1;
1127 while (*string) {
1128 /* check for unicode */
1129 if ((unsigned char) string[0] & 0x80)
1130 return 0;
1131 *string = toupper((unsigned char) *string);
1132 string++;
1135 return 1;
1138 static void print_cifs_mount_version(void)
1140 printf("mount.cifs version: %s.%s%s\n",
1141 MOUNT_CIFS_VERSION_MAJOR,
1142 MOUNT_CIFS_VERSION_MINOR,
1143 MOUNT_CIFS_VENDOR_SUFFIX);
1146 int main(int argc, char ** argv)
1148 int c;
1149 unsigned long flags = MS_MANDLOCK;
1150 char * orgoptions = NULL;
1151 char * share_name = NULL;
1152 const char * ipaddr = NULL;
1153 char * uuid = NULL;
1154 char * mountpoint = NULL;
1155 char * options = NULL;
1156 char * optionstail;
1157 char * resolved_path = NULL;
1158 char * temp;
1159 char * dev_name;
1160 int rc = 0;
1161 int rsize = 0;
1162 int wsize = 0;
1163 int nomtab = 0;
1164 int uid = 0;
1165 int gid = 0;
1166 int optlen = 0;
1167 int orgoptlen = 0;
1168 size_t options_size = 0;
1169 size_t current_len;
1170 int retry = 0; /* set when we have to retry mount with uppercase */
1171 struct addrinfo *addrhead = NULL, *addr;
1172 struct utsname sysinfo;
1173 struct mntent mountent;
1174 struct sockaddr_in *addr4;
1175 struct sockaddr_in6 *addr6;
1176 FILE * pmntfile;
1178 /* setlocale(LC_ALL, "");
1179 bindtextdomain(PACKAGE, LOCALEDIR);
1180 textdomain(PACKAGE); */
1182 if(argc && argv) {
1183 thisprogram = argv[0];
1184 } else {
1185 mount_cifs_usage();
1186 exit(EX_USAGE);
1189 if(thisprogram == NULL)
1190 thisprogram = "mount.cifs";
1192 uname(&sysinfo);
1193 /* BB add workstation name and domain and pass down */
1195 /* #ifdef _GNU_SOURCE
1196 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1197 #endif */
1198 if(argc > 2) {
1199 dev_name = argv[1];
1200 share_name = strndup(argv[1], MAX_UNC_LEN);
1201 if (share_name == NULL) {
1202 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1203 exit(EX_SYSERR);
1205 mountpoint = argv[2];
1206 } else if (argc == 2) {
1207 if ((strcmp(argv[1], "-V") == 0) ||
1208 (strcmp(argv[1], "--version") == 0))
1210 print_cifs_mount_version();
1211 exit(0);
1214 if ((strcmp(argv[1], "-h") == 0) ||
1215 (strcmp(argv[1], "-?") == 0) ||
1216 (strcmp(argv[1], "--help") == 0))
1218 mount_cifs_usage();
1219 exit(0);
1222 mount_cifs_usage();
1223 exit(EX_USAGE);
1224 } else {
1225 mount_cifs_usage();
1226 exit(EX_USAGE);
1230 /* add sharename in opts string as unc= parm */
1231 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1232 longopts, NULL)) != -1) {
1233 switch (c) {
1234 /* No code to do the following options yet */
1235 /* case 'l':
1236 list_with_volumelabel = 1;
1237 break;
1238 case 'L':
1239 volumelabel = optarg;
1240 break; */
1241 /* case 'a':
1242 ++mount_all;
1243 break; */
1245 case '?':
1246 case 'h': /* help */
1247 mount_cifs_usage ();
1248 exit(0);
1249 case 'n':
1250 ++nomtab;
1251 break;
1252 case 'b':
1253 #ifdef MS_BIND
1254 flags |= MS_BIND;
1255 #else
1256 fprintf(stderr,
1257 "option 'b' (MS_BIND) not supported\n");
1258 #endif
1259 break;
1260 case 'm':
1261 #ifdef MS_MOVE
1262 flags |= MS_MOVE;
1263 #else
1264 fprintf(stderr,
1265 "option 'm' (MS_MOVE) not supported\n");
1266 #endif
1267 break;
1268 case 'o':
1269 orgoptions = strdup(optarg);
1270 break;
1271 case 'r': /* mount readonly */
1272 flags |= MS_RDONLY;
1273 break;
1274 case 'U':
1275 uuid = optarg;
1276 break;
1277 case 'v':
1278 ++verboseflag;
1279 break;
1280 case 'V':
1281 print_cifs_mount_version();
1282 exit (0);
1283 case 'w':
1284 flags &= ~MS_RDONLY;
1285 break;
1286 case 'R':
1287 rsize = atoi(optarg) ;
1288 break;
1289 case 'W':
1290 wsize = atoi(optarg);
1291 break;
1292 case '1':
1293 if (isdigit(*optarg)) {
1294 char *ep;
1296 uid = strtoul(optarg, &ep, 10);
1297 if (*ep) {
1298 printf("bad uid value \"%s\"\n", optarg);
1299 exit(EX_USAGE);
1301 } else {
1302 struct passwd *pw;
1304 if (!(pw = getpwnam(optarg))) {
1305 printf("bad user name \"%s\"\n", optarg);
1306 exit(EX_USAGE);
1308 uid = pw->pw_uid;
1309 endpwent();
1311 break;
1312 case '2':
1313 if (isdigit(*optarg)) {
1314 char *ep;
1316 gid = strtoul(optarg, &ep, 10);
1317 if (*ep) {
1318 printf("bad gid value \"%s\"\n", optarg);
1319 exit(EX_USAGE);
1321 } else {
1322 struct group *gr;
1324 if (!(gr = getgrnam(optarg))) {
1325 printf("bad user name \"%s\"\n", optarg);
1326 exit(EX_USAGE);
1328 gid = gr->gr_gid;
1329 endpwent();
1331 break;
1332 case 'u':
1333 got_user = 1;
1334 user_name = optarg;
1335 break;
1336 case 'd':
1337 domain_name = optarg; /* BB fix this - currently ignored */
1338 got_domain = 1;
1339 break;
1340 case 'p':
1341 if(mountpassword == NULL)
1342 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1343 if(mountpassword) {
1344 got_password = 1;
1345 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1347 break;
1348 case 'S':
1349 get_password_from_file(0 /* stdin */,NULL);
1350 break;
1351 case 't':
1352 break;
1353 case 'f':
1354 ++fakemnt;
1355 break;
1356 default:
1357 printf("unknown mount option %c\n",c);
1358 mount_cifs_usage();
1359 exit(EX_USAGE);
1363 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1364 mount_cifs_usage();
1365 exit(EX_USAGE);
1368 /* make sure mountpoint is legit */
1369 rc = check_mountpoint(thisprogram, mountpoint);
1370 if (rc)
1371 goto mount_exit;
1373 /* sanity check for unprivileged mounts */
1374 if (getuid()) {
1375 rc = check_fstab(thisprogram, mountpoint, dev_name,
1376 &orgoptions);
1377 if (rc)
1378 goto mount_exit;
1380 /* enable any default user mount flags */
1381 flags |= CIFS_SETUID_FLAGS;
1384 if (getenv("PASSWD")) {
1385 if(mountpassword == NULL)
1386 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1387 if(mountpassword) {
1388 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1389 got_password = 1;
1391 } else if (getenv("PASSWD_FD")) {
1392 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1393 } else if (getenv("PASSWD_FILE")) {
1394 get_password_from_file(0, getenv("PASSWD_FILE"));
1397 if (orgoptions && parse_options(&orgoptions, &flags)) {
1398 rc = EX_USAGE;
1399 goto mount_exit;
1402 if (getuid()) {
1403 #if !CIFS_LEGACY_SETUID_CHECK
1404 if (!(flags & (MS_USERS|MS_USER))) {
1405 fprintf(stderr, "%s: permission denied\n", thisprogram);
1406 rc = EX_USAGE;
1407 goto mount_exit;
1409 #endif /* !CIFS_LEGACY_SETUID_CHECK */
1411 if (geteuid()) {
1412 fprintf(stderr, "%s: not installed setuid - \"user\" "
1413 "CIFS mounts not supported.",
1414 thisprogram);
1415 rc = EX_FAIL;
1416 goto mount_exit;
1420 flags &= ~(MS_USERS|MS_USER);
1422 addrhead = addr = parse_server(&share_name);
1423 if((addrhead == NULL) && (got_ip == 0)) {
1424 printf("No ip address specified and hostname not found\n");
1425 rc = EX_USAGE;
1426 goto mount_exit;
1429 /* BB save off path and pop after mount returns? */
1430 resolved_path = (char *)malloc(PATH_MAX+1);
1431 if(resolved_path) {
1432 /* Note that if we can not canonicalize the name, we get
1433 another chance to see if it is valid when we chdir to it */
1434 if (realpath(mountpoint, resolved_path)) {
1435 mountpoint = resolved_path;
1438 if(got_user == 0) {
1439 /* Note that the password will not be retrieved from the
1440 USER env variable (ie user%password form) as there is
1441 already a PASSWD environment varaible */
1442 if (getenv("USER"))
1443 user_name = strdup(getenv("USER"));
1444 if (user_name == NULL)
1445 user_name = getusername();
1446 got_user = 1;
1449 if(got_password == 0) {
1450 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1451 no good replacement yet. */
1452 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1453 if (!tmp_pass || !mountpassword) {
1454 printf("Password not entered, exiting\n");
1455 exit(EX_USAGE);
1457 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1458 got_password = 1;
1460 /* FIXME launch daemon (handles dfs name resolution and credential change)
1461 remember to clear parms and overwrite password field before launching */
1462 if(orgoptions) {
1463 optlen = strlen(orgoptions);
1464 orgoptlen = optlen;
1465 } else
1466 optlen = 0;
1467 if(share_name)
1468 optlen += strlen(share_name) + 4;
1469 else {
1470 printf("No server share name specified\n");
1471 printf("\nMounting the DFS root for server not implemented yet\n");
1472 exit(EX_USAGE);
1474 if(user_name)
1475 optlen += strlen(user_name) + 6;
1476 optlen += MAX_ADDRESS_LEN + 4;
1477 if(mountpassword)
1478 optlen += strlen(mountpassword) + 6;
1479 mount_retry:
1480 SAFE_FREE(options);
1481 options_size = optlen + 10 + DOMAIN_SIZE;
1482 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 */);
1484 if(options == NULL) {
1485 printf("Could not allocate memory for mount options\n");
1486 exit(EX_SYSERR);
1489 strlcpy(options, "unc=", options_size);
1490 strlcat(options,share_name,options_size);
1491 /* scan backwards and reverse direction of slash */
1492 temp = strrchr(options, '/');
1493 if(temp > options + 6)
1494 *temp = '\\';
1495 if(user_name) {
1496 /* check for syntax like user=domain\user */
1497 if(got_domain == 0)
1498 domain_name = check_for_domain(&user_name);
1499 strlcat(options,",user=",options_size);
1500 strlcat(options,user_name,options_size);
1502 if(retry == 0) {
1503 if(domain_name) {
1504 /* extra length accounted for in option string above */
1505 strlcat(options,",domain=",options_size);
1506 strlcat(options,domain_name,options_size);
1509 if(mountpassword) {
1510 /* Commas have to be doubled, or else they will
1511 look like the parameter separator */
1512 /* if(sep is not set)*/
1513 if(retry == 0)
1514 check_for_comma(&mountpassword);
1515 strlcat(options,",pass=",options_size);
1516 strlcat(options,mountpassword,options_size);
1519 strlcat(options,",ver=",options_size);
1520 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1522 if(orgoptions) {
1523 strlcat(options,",",options_size);
1524 strlcat(options,orgoptions,options_size);
1526 if(prefixpath) {
1527 strlcat(options,",prefixpath=",options_size);
1528 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1530 if(verboseflag)
1531 printf("\nmount.cifs kernel mount options %s \n",options);
1533 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1534 replace_char(dev_name, '\\', '/', strlen(share_name));
1536 if (!got_ip && addr) {
1537 strlcat(options, ",ip=", options_size);
1538 current_len = strnlen(options, options_size);
1539 optionstail = options + current_len;
1540 switch (addr->ai_addr->sa_family) {
1541 case AF_INET6:
1542 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1543 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1544 options_size - current_len);
1545 break;
1546 case AF_INET:
1547 addr4 = (struct sockaddr_in *) addr->ai_addr;
1548 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1549 options_size - current_len);
1550 break;
1553 /* if the address looks bogus, try the next one */
1554 if (!ipaddr) {
1555 addr = addr->ai_next;
1556 if (addr)
1557 goto mount_retry;
1558 rc = EX_SYSERR;
1559 goto mount_exit;
1563 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1564 switch (errno) {
1565 case ECONNREFUSED:
1566 case EHOSTUNREACH:
1567 if (addr) {
1568 addr = addr->ai_next;
1569 if (addr)
1570 goto mount_retry;
1572 break;
1573 case ENODEV:
1574 printf("mount error: cifs filesystem not supported by the system\n");
1575 break;
1576 case ENXIO:
1577 if(retry == 0) {
1578 retry = 1;
1579 if (uppercase_string(dev_name) &&
1580 uppercase_string(share_name) &&
1581 uppercase_string(prefixpath)) {
1582 printf("retrying with upper case share name\n");
1583 goto mount_retry;
1587 printf("mount error(%d): %s\n", errno, strerror(errno));
1588 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1589 "mount.cifs)\n");
1590 rc = EX_FAIL;
1591 goto mount_exit;
1594 if (nomtab)
1595 goto mount_exit;
1596 atexit(unlock_mtab);
1597 rc = lock_mtab();
1598 if (rc) {
1599 printf("cannot lock mtab");
1600 goto mount_exit;
1602 pmntfile = setmntent(MOUNTED, "a+");
1603 if (!pmntfile) {
1604 printf("could not update mount table\n");
1605 unlock_mtab();
1606 rc = EX_FILEIO;
1607 goto mount_exit;
1609 mountent.mnt_fsname = dev_name;
1610 mountent.mnt_dir = mountpoint;
1611 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1612 mountent.mnt_opts = (char *)malloc(220);
1613 if(mountent.mnt_opts) {
1614 char * mount_user = getusername();
1615 memset(mountent.mnt_opts,0,200);
1616 if(flags & MS_RDONLY)
1617 strlcat(mountent.mnt_opts,"ro",220);
1618 else
1619 strlcat(mountent.mnt_opts,"rw",220);
1620 if(flags & MS_MANDLOCK)
1621 strlcat(mountent.mnt_opts,",mand",220);
1622 if(flags & MS_NOEXEC)
1623 strlcat(mountent.mnt_opts,",noexec",220);
1624 if(flags & MS_NOSUID)
1625 strlcat(mountent.mnt_opts,",nosuid",220);
1626 if(flags & MS_NODEV)
1627 strlcat(mountent.mnt_opts,",nodev",220);
1628 if(flags & MS_SYNCHRONOUS)
1629 strlcat(mountent.mnt_opts,",sync",220);
1630 if(mount_user) {
1631 if(getuid() != 0) {
1632 strlcat(mountent.mnt_opts,
1633 ",user=", 220);
1634 strlcat(mountent.mnt_opts,
1635 mount_user, 220);
1639 mountent.mnt_freq = 0;
1640 mountent.mnt_passno = 0;
1641 rc = addmntent(pmntfile,&mountent);
1642 endmntent(pmntfile);
1643 unlock_mtab();
1644 SAFE_FREE(mountent.mnt_opts);
1645 if (rc)
1646 rc = EX_FILEIO;
1647 mount_exit:
1648 if(mountpassword) {
1649 int len = strlen(mountpassword);
1650 memset(mountpassword,0,len);
1651 SAFE_FREE(mountpassword);
1654 if (addrhead)
1655 freeaddrinfo(addrhead);
1656 SAFE_FREE(options);
1657 SAFE_FREE(orgoptions);
1658 SAFE_FREE(resolved_path);
1659 SAFE_FREE(share_name);
1660 exit(rc);