mount.cifs: check access of credential files before opening
[Samba.git] / source / client / mount.cifs.c
blob11019b7cde79519d716c1df452060b3799c8385d
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 "mount.h"
44 #define MOUNT_CIFS_VERSION_MAJOR "1"
45 #define MOUNT_CIFS_VERSION_MINOR "12"
47 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
48 #ifdef _SAMBA_BUILD_
49 #include "include/version.h"
50 #ifdef SAMBA_VERSION_VENDOR_SUFFIX
51 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
52 #else
53 #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
54 #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
55 #else
56 #define MOUNT_CIFS_VENDOR_SUFFIX ""
57 #endif /* _SAMBA_BUILD_ */
58 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
60 #ifdef _SAMBA_BUILD_
61 #include "include/config.h"
62 #endif
64 #ifndef MS_MOVE
65 #define MS_MOVE 8192
66 #endif
68 #ifndef MS_BIND
69 #define MS_BIND 4096
70 #endif
72 #define MAX_UNC_LEN 1024
74 #define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr)))
76 #ifndef SAFE_FREE
77 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
78 #endif
80 #define MOUNT_PASSWD_SIZE 64
81 #define DOMAIN_SIZE 64
83 /* currently maximum length of IPv6 address string */
84 #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN
86 const char *thisprogram;
87 int verboseflag = 0;
88 int fakemnt = 0;
89 static int got_password = 0;
90 static int got_user = 0;
91 static int got_domain = 0;
92 static int got_ip = 0;
93 static int got_unc = 0;
94 static int got_uid = 0;
95 static int got_gid = 0;
96 static char * user_name = NULL;
97 static char * mountpassword = NULL;
98 char * domain_name = NULL;
99 char * prefixpath = NULL;
101 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
102 * don't link to libreplace so need them here. */
104 /* like strncpy but does not 0 fill the buffer and always null
105 * terminates. bufsize is the size of the destination buffer */
107 #ifndef HAVE_STRLCPY
108 static size_t strlcpy(char *d, const char *s, size_t bufsize)
110 size_t len = strlen(s);
111 size_t ret = len;
112 if (bufsize <= 0) return 0;
113 if (len >= bufsize) len = bufsize-1;
114 memcpy(d, s, len);
115 d[len] = 0;
116 return ret;
118 #endif
120 /* like strncat but does not 0 fill the buffer and always null
121 * terminates. bufsize is the length of the buffer, which should
122 * be one more than the maximum resulting string length */
124 #ifndef HAVE_STRLCAT
125 static size_t strlcat(char *d, const char *s, size_t bufsize)
127 size_t len1 = strlen(d);
128 size_t len2 = strlen(s);
129 size_t ret = len1 + len2;
131 if (len1+len2 >= bufsize) {
132 if (bufsize < (len1+1)) {
133 return ret;
135 len2 = bufsize - (len1+1);
137 if (len2 > 0) {
138 memcpy(d+len1, s, len2);
139 d[len1+len2] = 0;
141 return ret;
143 #endif
145 /* BB finish BB
147 cifs_umount
148 open nofollow - avoid symlink exposure?
149 get owner of dir see if matches self or if root
150 call system(umount argv) etc.
152 BB end finish BB */
154 static char * check_for_domain(char **);
157 static void mount_cifs_usage(void)
159 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
160 printf("\nMount the remote target, specified as a UNC name,");
161 printf(" to a local directory.\n\nOptions:\n");
162 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
163 printf("\nLess commonly used options:");
164 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
165 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
166 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
167 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
168 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
169 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
170 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
171 printf("\n\nRarely used options:");
172 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
173 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
174 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
175 printf("\n\tin6_addr");
176 printf("\n\nOptions are described in more detail in the manual page");
177 printf("\n\tman 8 mount.cifs\n");
178 printf("\nTo display the version number of the mount helper:");
179 printf("\n\t%s -V\n",thisprogram);
181 SAFE_FREE(mountpassword);
182 exit(EX_USAGE);
185 /* caller frees username if necessary */
186 static char * getusername(void) {
187 char *username = NULL;
188 struct passwd *password = getpwuid(getuid());
190 if (password) {
191 username = password->pw_name;
193 return username;
196 static int open_cred_file(char * file_name)
198 char * line_buf;
199 char * temp_val;
200 FILE * fs;
201 int i, length;
203 i = access(file_name, R_OK);
204 if (i)
205 return i;
207 fs = fopen(file_name,"r");
208 if(fs == NULL)
209 return errno;
210 line_buf = (char *)malloc(4096);
211 if(line_buf == NULL) {
212 fclose(fs);
213 return ENOMEM;
216 while(fgets(line_buf,4096,fs)) {
217 /* parse line from credential file */
219 /* eat leading white space */
220 for(i=0;i<4086;i++) {
221 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
222 break;
223 /* if whitespace - skip past it */
225 if (strncasecmp("username",line_buf+i,8) == 0) {
226 temp_val = strchr(line_buf + i,'=');
227 if(temp_val) {
228 /* go past equals sign */
229 temp_val++;
230 for(length = 0;length<4087;length++) {
231 if ((temp_val[length] == '\n')
232 || (temp_val[length] == '\0')) {
233 temp_val[length] = '\0';
234 break;
237 if(length > 4086) {
238 printf("mount.cifs failed due to malformed username in credentials file");
239 memset(line_buf,0,4096);
240 exit(EX_USAGE);
241 } else {
242 got_user = 1;
243 user_name = (char *)calloc(1 + length,1);
244 /* BB adding free of user_name string before exit,
245 not really necessary but would be cleaner */
246 strlcpy(user_name,temp_val, length+1);
249 } else if (strncasecmp("password",line_buf+i,8) == 0) {
250 temp_val = strchr(line_buf+i,'=');
251 if(temp_val) {
252 /* go past equals sign */
253 temp_val++;
254 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
255 if ((temp_val[length] == '\n')
256 || (temp_val[length] == '\0')) {
257 temp_val[length] = '\0';
258 break;
261 if(length > MOUNT_PASSWD_SIZE) {
262 printf("mount.cifs failed: password in credentials file too long\n");
263 memset(line_buf,0, 4096);
264 exit(EX_USAGE);
265 } else {
266 if(mountpassword == NULL) {
267 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
268 } else
269 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
270 if(mountpassword) {
271 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
272 got_password = 1;
276 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
277 temp_val = strchr(line_buf+i,'=');
278 if(temp_val) {
279 /* go past equals sign */
280 temp_val++;
281 if(verboseflag)
282 printf("\nDomain %s\n",temp_val);
283 for(length = 0;length<DOMAIN_SIZE+1;length++) {
284 if ((temp_val[length] == '\n')
285 || (temp_val[length] == '\0')) {
286 temp_val[length] = '\0';
287 break;
290 if(length > DOMAIN_SIZE) {
291 printf("mount.cifs failed: domain in credentials file too long\n");
292 exit(EX_USAGE);
293 } else {
294 if(domain_name == NULL) {
295 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
296 } else
297 memset(domain_name,0,DOMAIN_SIZE);
298 if(domain_name) {
299 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
300 got_domain = 1;
307 fclose(fs);
308 SAFE_FREE(line_buf);
309 return 0;
312 static int get_password_from_file(int file_descript, char * filename)
314 int rc = 0;
315 int i;
316 char c;
318 if(mountpassword == NULL)
319 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
320 else
321 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
323 if (mountpassword == NULL) {
324 printf("malloc failed\n");
325 exit(EX_SYSERR);
328 if(filename != NULL) {
329 rc = access(filename, R_OK);
330 if (rc) {
331 fprintf(stderr, "mount.cifs failed: access check of %s failed: %s\n",
332 filename, strerror(errno));
333 exit(EX_SYSERR);
335 file_descript = open(filename, O_RDONLY);
336 if(file_descript < 0) {
337 printf("mount.cifs failed. %s attempting to open password file %s\n",
338 strerror(errno),filename);
339 exit(EX_SYSERR);
342 /* else file already open and fd provided */
344 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
345 rc = read(file_descript,&c,1);
346 if(rc < 0) {
347 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
348 if(filename != NULL)
349 close(file_descript);
350 exit(EX_SYSERR);
351 } else if(rc == 0) {
352 if(mountpassword[0] == 0) {
353 if(verboseflag)
354 printf("\nWarning: null password used since cifs password file empty");
356 break;
357 } else /* read valid character */ {
358 if((c == 0) || (c == '\n')) {
359 mountpassword[i] = '\0';
360 break;
361 } else
362 mountpassword[i] = c;
365 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
366 printf("\nWarning: password longer than %d characters specified in cifs password file",
367 MOUNT_PASSWD_SIZE);
369 got_password = 1;
370 if(filename != NULL) {
371 close(file_descript);
374 return rc;
377 static int parse_options(char ** optionsp, int * filesys_flags)
379 const char * data;
380 char * percent_char = NULL;
381 char * value = NULL;
382 char * next_keyword = NULL;
383 char * out = NULL;
384 int out_len = 0;
385 int word_len;
386 int rc = 0;
387 char user[32];
388 char group[32];
390 if (!optionsp || !*optionsp)
391 return 1;
392 data = *optionsp;
394 if(verboseflag)
395 printf("parsing options: %s\n", data);
397 /* BB fixme check for separator override BB */
399 if (getuid()) {
400 got_uid = 1;
401 snprintf(user,sizeof(user),"%u",getuid());
402 got_gid = 1;
403 snprintf(group,sizeof(group),"%u",getgid());
406 /* while ((data = strsep(&options, ",")) != NULL) { */
407 while(data != NULL) {
408 /* check if ends with trailing comma */
409 if(*data == 0)
410 break;
412 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
413 /* data = next keyword */
414 /* value = next value ie stuff after equal sign */
416 next_keyword = strchr(data,','); /* BB handle sep= */
418 /* temporarily null terminate end of keyword=value pair */
419 if(next_keyword)
420 *next_keyword++ = 0;
422 /* temporarily null terminate keyword to make keyword and value distinct */
423 if ((value = strchr(data, '=')) != NULL) {
424 *value = '\0';
425 value++;
428 if (strncmp(data, "users",5) == 0) {
429 if(!value || !*value) {
430 goto nocopy;
432 } else if (strncmp(data, "user_xattr",10) == 0) {
433 /* do nothing - need to skip so not parsed as user name */
434 } else if (strncmp(data, "user", 4) == 0) {
436 if (!value || !*value) {
437 if(data[4] == '\0') {
438 if(verboseflag)
439 printf("\nskipping empty user mount parameter\n");
440 /* remove the parm since it would otherwise be confusing
441 to the kernel code which would think it was a real username */
442 goto nocopy;
443 } else {
444 printf("username specified with no parameter\n");
445 SAFE_FREE(out);
446 return 1; /* needs_arg; */
448 } else {
449 if (strnlen(value, 260) < 260) {
450 got_user=1;
451 percent_char = strchr(value,'%');
452 if(percent_char) {
453 *percent_char = ',';
454 if(mountpassword == NULL)
455 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
456 if(mountpassword) {
457 if(got_password)
458 printf("\nmount.cifs warning - password specified twice\n");
459 got_password = 1;
460 percent_char++;
461 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
462 /* remove password from username */
463 while(*percent_char != 0) {
464 *percent_char = ',';
465 percent_char++;
469 /* this is only case in which the user
470 name buf is not malloc - so we have to
471 check for domain name embedded within
472 the user name here since the later
473 call to check_for_domain will not be
474 invoked */
475 domain_name = check_for_domain(&value);
476 } else {
477 printf("username too long\n");
478 SAFE_FREE(out);
479 return 1;
482 } else if (strncmp(data, "pass", 4) == 0) {
483 if (!value || !*value) {
484 if(got_password) {
485 printf("\npassword specified twice, ignoring second\n");
486 } else
487 got_password = 1;
488 } else if (strnlen(value, 17) < 17) {
489 if(got_password)
490 printf("\nmount.cifs warning - password specified twice\n");
491 got_password = 1;
492 } else {
493 printf("password too long\n");
494 SAFE_FREE(out);
495 return 1;
497 } else if (strncmp(data, "sec", 3) == 0) {
498 if (value) {
499 if (!strncmp(value, "none", 4) ||
500 !strncmp(value, "krb5", 4))
501 got_password = 1;
503 } else if (strncmp(data, "ip", 2) == 0) {
504 if (!value || !*value) {
505 printf("target ip address argument missing");
506 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
507 if(verboseflag)
508 printf("ip address %s override specified\n",value);
509 got_ip = 1;
510 } else {
511 printf("ip address too long\n");
512 SAFE_FREE(out);
513 return 1;
515 } else if ((strncmp(data, "unc", 3) == 0)
516 || (strncmp(data, "target", 6) == 0)
517 || (strncmp(data, "path", 4) == 0)) {
518 if (!value || !*value) {
519 printf("invalid path to network resource\n");
520 SAFE_FREE(out);
521 return 1; /* needs_arg; */
522 } else if(strnlen(value,5) < 5) {
523 printf("UNC name too short");
526 if (strnlen(value, 300) < 300) {
527 got_unc = 1;
528 if (strncmp(value, "//", 2) == 0) {
529 if(got_unc)
530 printf("unc name specified twice, ignoring second\n");
531 else
532 got_unc = 1;
533 } else if (strncmp(value, "\\\\", 2) != 0) {
534 printf("UNC Path does not begin with // or \\\\ \n");
535 SAFE_FREE(out);
536 return 1;
537 } else {
538 if(got_unc)
539 printf("unc name specified twice, ignoring second\n");
540 else
541 got_unc = 1;
543 } else {
544 printf("CIFS: UNC name too long\n");
545 SAFE_FREE(out);
546 return 1;
548 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
549 || (strncmp(data, "workg", 5) == 0)) {
550 /* note this allows for synonyms of "domain"
551 such as "DOM" and "dom" and "workgroup"
552 and "WORKGRP" etc. */
553 if (!value || !*value) {
554 printf("CIFS: invalid domain name\n");
555 SAFE_FREE(out);
556 return 1; /* needs_arg; */
558 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
559 got_domain = 1;
560 } else {
561 printf("domain name too long\n");
562 SAFE_FREE(out);
563 return 1;
565 } else if (strncmp(data, "cred", 4) == 0) {
566 if (value && *value) {
567 rc = open_cred_file(value);
568 if(rc) {
569 printf("error %d (%s) opening credential file %s\n",
570 rc, strerror(rc), value);
571 SAFE_FREE(out);
572 return 1;
574 } else {
575 printf("invalid credential file name specified\n");
576 SAFE_FREE(out);
577 return 1;
579 } else if (strncmp(data, "uid", 3) == 0) {
580 if (value && *value) {
581 got_uid = 1;
582 if (!isdigit(*value)) {
583 struct passwd *pw;
585 if (!(pw = getpwnam(value))) {
586 printf("bad user name \"%s\"\n", value);
587 exit(EX_USAGE);
589 snprintf(user, sizeof(user), "%u", pw->pw_uid);
590 } else {
591 strlcpy(user,value,sizeof(user));
594 goto nocopy;
595 } else if (strncmp(data, "gid", 3) == 0) {
596 if (value && *value) {
597 got_gid = 1;
598 if (!isdigit(*value)) {
599 struct group *gr;
601 if (!(gr = getgrnam(value))) {
602 printf("bad group name \"%s\"\n", value);
603 exit(EX_USAGE);
605 snprintf(group, sizeof(group), "%u", gr->gr_gid);
606 } else {
607 strlcpy(group,value,sizeof(group));
610 goto nocopy;
611 /* fmask and dmask synonyms for people used to smbfs syntax */
612 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
613 if (!value || !*value) {
614 printf ("Option '%s' requires a numerical argument\n", data);
615 SAFE_FREE(out);
616 return 1;
619 if (value[0] != '0') {
620 printf ("WARNING: '%s' not expressed in octal.\n", data);
623 if (strcmp (data, "fmask") == 0) {
624 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
625 data = "file_mode"; /* BB fix this */
627 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
628 if (!value || !*value) {
629 printf ("Option '%s' requires a numerical argument\n", data);
630 SAFE_FREE(out);
631 return 1;
634 if (value[0] != '0') {
635 printf ("WARNING: '%s' not expressed in octal.\n", data);
638 if (strcmp (data, "dmask") == 0) {
639 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
640 data = "dir_mode";
642 /* the following eight mount options should be
643 stripped out from what is passed into the kernel
644 since these eight options are best passed as the
645 mount flags rather than redundantly to the kernel
646 and could generate spurious warnings depending on the
647 level of the corresponding cifs vfs kernel code */
648 } else if (strncmp(data, "nosuid", 6) == 0) {
649 *filesys_flags |= MS_NOSUID;
650 } else if (strncmp(data, "suid", 4) == 0) {
651 *filesys_flags &= ~MS_NOSUID;
652 } else if (strncmp(data, "nodev", 5) == 0) {
653 *filesys_flags |= MS_NODEV;
654 } else if ((strncmp(data, "nobrl", 5) == 0) ||
655 (strncmp(data, "nolock", 6) == 0)) {
656 *filesys_flags &= ~MS_MANDLOCK;
657 } else if (strncmp(data, "dev", 3) == 0) {
658 *filesys_flags &= ~MS_NODEV;
659 } else if (strncmp(data, "noexec", 6) == 0) {
660 *filesys_flags |= MS_NOEXEC;
661 } else if (strncmp(data, "exec", 4) == 0) {
662 *filesys_flags &= ~MS_NOEXEC;
663 } else if (strncmp(data, "guest", 5) == 0) {
664 user_name = (char *)calloc(1, 1);
665 got_user = 1;
666 got_password = 1;
667 } else if (strncmp(data, "ro", 2) == 0) {
668 *filesys_flags |= MS_RDONLY;
669 } else if (strncmp(data, "rw", 2) == 0) {
670 *filesys_flags &= ~MS_RDONLY;
671 } else if (strncmp(data, "remount", 7) == 0) {
672 *filesys_flags |= MS_REMOUNT;
673 } /* else if (strnicmp(data, "port", 4) == 0) {
674 if (value && *value) {
675 vol->port =
676 simple_strtoul(value, &value, 0);
678 } else if (strnicmp(data, "rsize", 5) == 0) {
679 if (value && *value) {
680 vol->rsize =
681 simple_strtoul(value, &value, 0);
683 } else if (strnicmp(data, "wsize", 5) == 0) {
684 if (value && *value) {
685 vol->wsize =
686 simple_strtoul(value, &value, 0);
688 } else if (strnicmp(data, "version", 3) == 0) {
689 } else {
690 printf("CIFS: Unknown mount option %s\n",data);
691 } */ /* nothing to do on those four mount options above.
692 Just pass to kernel and ignore them here */
694 /* Copy (possibly modified) option to out */
695 word_len = strlen(data);
696 if (value)
697 word_len += 1 + strlen(value);
699 out = (char *)realloc(out, out_len + word_len + 2);
700 if (out == NULL) {
701 perror("malloc");
702 exit(EX_SYSERR);
705 if (out_len) {
706 strlcat(out, ",", out_len + word_len + 2);
707 out_len++;
710 if (value)
711 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
712 else
713 snprintf(out + out_len, word_len + 1, "%s", data);
714 out_len = strlen(out);
716 nocopy:
717 data = next_keyword;
720 /* special-case the uid and gid */
721 if (got_uid) {
722 word_len = strlen(user);
724 out = (char *)realloc(out, out_len + word_len + 6);
725 if (out == NULL) {
726 perror("malloc");
727 exit(EX_SYSERR);
730 if (out_len) {
731 strlcat(out, ",", out_len + word_len + 6);
732 out_len++;
734 snprintf(out + out_len, word_len + 5, "uid=%s", user);
735 out_len = strlen(out);
737 if (got_gid) {
738 word_len = strlen(group);
740 out = (char *)realloc(out, out_len + 1 + word_len + 6);
741 if (out == NULL) {
742 perror("malloc");
743 exit(EX_SYSERR);
746 if (out_len) {
747 strlcat(out, ",", out_len + word_len + 6);
748 out_len++;
750 snprintf(out + out_len, word_len + 5, "gid=%s", group);
751 out_len = strlen(out);
754 SAFE_FREE(*optionsp);
755 *optionsp = out;
756 return 0;
759 /* replace all (one or more) commas with double commas */
760 static void check_for_comma(char ** ppasswrd)
762 char *new_pass_buf;
763 char *pass;
764 int i,j;
765 int number_of_commas = 0;
766 int len;
768 if(ppasswrd == NULL)
769 return;
770 else
771 (pass = *ppasswrd);
773 len = strlen(pass);
775 for(i=0;i<len;i++) {
776 if(pass[i] == ',')
777 number_of_commas++;
780 if(number_of_commas == 0)
781 return;
782 if(number_of_commas > MOUNT_PASSWD_SIZE) {
783 /* would otherwise overflow the mount options buffer */
784 printf("\nInvalid password. Password contains too many commas.\n");
785 return;
788 new_pass_buf = (char *)malloc(len+number_of_commas+1);
789 if(new_pass_buf == NULL)
790 return;
792 for(i=0,j=0;i<len;i++,j++) {
793 new_pass_buf[j] = pass[i];
794 if(pass[i] == ',') {
795 j++;
796 new_pass_buf[j] = pass[i];
799 new_pass_buf[len+number_of_commas] = 0;
801 SAFE_FREE(*ppasswrd);
802 *ppasswrd = new_pass_buf;
804 return;
807 /* Usernames can not have backslash in them and we use
808 [BB check if usernames can have forward slash in them BB]
809 backslash as domain\user separator character
811 static char * check_for_domain(char **ppuser)
813 char * original_string;
814 char * usernm;
815 char * domainnm;
816 int original_len;
817 int len;
818 int i;
820 if(ppuser == NULL)
821 return NULL;
823 original_string = *ppuser;
825 if (original_string == NULL)
826 return NULL;
828 original_len = strlen(original_string);
830 usernm = strchr(*ppuser,'/');
831 if (usernm == NULL) {
832 usernm = strchr(*ppuser,'\\');
833 if (usernm == NULL)
834 return NULL;
837 if(got_domain) {
838 printf("Domain name specified twice. Username probably malformed\n");
839 return NULL;
842 usernm[0] = 0;
843 domainnm = *ppuser;
844 if (domainnm[0] != 0) {
845 got_domain = 1;
846 } else {
847 printf("null domain\n");
849 len = strlen(domainnm);
850 /* reset domainm to new buffer, and copy
851 domain name into it */
852 domainnm = (char *)malloc(len+1);
853 if(domainnm == NULL)
854 return NULL;
856 strlcpy(domainnm,*ppuser,len+1);
858 /* move_string(*ppuser, usernm+1) */
859 len = strlen(usernm+1);
861 if(len >= original_len) {
862 /* should not happen */
863 return domainnm;
866 for(i=0;i<original_len;i++) {
867 if(i<len)
868 original_string[i] = usernm[i+1];
869 else /* stuff with commas to remove last parm */
870 original_string[i] = ',';
873 /* BB add check for more than one slash?
874 strchr(*ppuser,'/');
875 strchr(*ppuser,'\\')
878 return domainnm;
881 /* replace all occurances of "from" in a string with "to" */
882 static void replace_char(char *string, char from, char to, int maxlen)
884 char *lastchar = string + maxlen;
885 while (string) {
886 string = strchr(string, from);
887 if (string) {
888 *string = to;
889 if (string >= lastchar)
890 return;
895 /* Note that caller frees the returned buffer if necessary */
896 static struct addrinfo *
897 parse_server(char ** punc_name)
899 char * unc_name = *punc_name;
900 int length = strnlen(unc_name, MAX_UNC_LEN);
901 char * share;
902 struct addrinfo *addrlist;
903 int rc;
905 if(length > (MAX_UNC_LEN - 1)) {
906 printf("mount error: UNC name too long");
907 return NULL;
909 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
910 (strncasecmp("smb://", unc_name, 6) == 0)) {
911 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
912 return NULL;
915 if(length < 3) {
916 /* BB add code to find DFS root here */
917 printf("\nMounting the DFS root for domain not implemented yet\n");
918 return NULL;
919 } else {
920 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
921 /* check for nfs syntax ie server:share */
922 share = strchr(unc_name,':');
923 if(share) {
924 *punc_name = (char *)malloc(length+3);
925 if(*punc_name == NULL) {
926 /* put the original string back if
927 no memory left */
928 *punc_name = unc_name;
929 return NULL;
931 *share = '/';
932 strlcpy((*punc_name)+2,unc_name,length+1);
933 SAFE_FREE(unc_name);
934 unc_name = *punc_name;
935 unc_name[length+2] = 0;
936 goto continue_unc_parsing;
937 } else {
938 printf("mount error: improperly formatted UNC name.");
939 printf(" %s does not begin with \\\\ or //\n",unc_name);
940 return NULL;
942 } else {
943 continue_unc_parsing:
944 unc_name[0] = '/';
945 unc_name[1] = '/';
946 unc_name += 2;
948 /* allow for either delimiter between host and sharename */
949 if ((share = strpbrk(unc_name, "/\\"))) {
950 *share = 0; /* temporarily terminate the string */
951 share += 1;
952 if(got_ip == 0) {
953 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
954 if (rc != 0) {
955 printf("mount error: could not resolve address for %s: %s\n",
956 unc_name, gai_strerror(rc));
957 addrlist = NULL;
960 *(share - 1) = '/'; /* put delimiter back */
962 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
963 if ((prefixpath = strpbrk(share, "/\\"))) {
964 *prefixpath = 0; /* permanently terminate the string */
965 if (!strlen(++prefixpath))
966 prefixpath = NULL; /* this needs to be done explicitly */
968 if(got_ip) {
969 if(verboseflag)
970 printf("ip address specified explicitly\n");
971 return NULL;
973 /* BB should we pass an alternate version of the share name as Unicode */
975 return addrlist;
976 } else {
977 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
978 printf("Mounting the DFS root for a particular server not implemented yet\n");
979 return NULL;
985 static struct option longopts[] = {
986 { "all", 0, NULL, 'a' },
987 { "help",0, NULL, 'h' },
988 { "move",0, NULL, 'm' },
989 { "bind",0, NULL, 'b' },
990 { "read-only", 0, NULL, 'r' },
991 { "ro", 0, NULL, 'r' },
992 { "verbose", 0, NULL, 'v' },
993 { "version", 0, NULL, 'V' },
994 { "read-write", 0, NULL, 'w' },
995 { "rw", 0, NULL, 'w' },
996 { "options", 1, NULL, 'o' },
997 { "type", 1, NULL, 't' },
998 { "rsize",1, NULL, 'R' },
999 { "wsize",1, NULL, 'W' },
1000 { "uid", 1, NULL, '1'},
1001 { "gid", 1, NULL, '2'},
1002 { "user",1,NULL,'u'},
1003 { "username",1,NULL,'u'},
1004 { "dom",1,NULL,'d'},
1005 { "domain",1,NULL,'d'},
1006 { "password",1,NULL,'p'},
1007 { "pass",1,NULL,'p'},
1008 { "credentials",1,NULL,'c'},
1009 { "port",1,NULL,'P'},
1010 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1011 { NULL, 0, NULL, 0 }
1014 /* convert a string to uppercase. return false if the string
1015 * wasn't ASCII. Return success on a NULL ptr */
1016 static int
1017 uppercase_string(char *string)
1019 if (!string)
1020 return 1;
1022 while (*string) {
1023 /* check for unicode */
1024 if ((unsigned char) string[0] & 0x80)
1025 return 0;
1026 *string = toupper((unsigned char) *string);
1027 string++;
1030 return 1;
1033 int main(int argc, char ** argv)
1035 int c;
1036 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1037 char * orgoptions = NULL;
1038 char * share_name = NULL;
1039 const char * ipaddr = NULL;
1040 char * uuid = NULL;
1041 char * mountpoint = NULL;
1042 char * options = NULL;
1043 char * optionstail;
1044 char * resolved_path = NULL;
1045 char * temp;
1046 char * dev_name;
1047 int rc = 0;
1048 int rsize = 0;
1049 int wsize = 0;
1050 int nomtab = 0;
1051 int uid = 0;
1052 int gid = 0;
1053 int optlen = 0;
1054 int orgoptlen = 0;
1055 size_t options_size = 0;
1056 size_t current_len;
1057 int retry = 0; /* set when we have to retry mount with uppercase */
1058 struct addrinfo *addrhead = NULL, *addr;
1059 struct stat statbuf;
1060 struct utsname sysinfo;
1061 struct mntent mountent;
1062 struct sockaddr_in *addr4;
1063 struct sockaddr_in6 *addr6;
1064 FILE * pmntfile;
1066 /* setlocale(LC_ALL, "");
1067 bindtextdomain(PACKAGE, LOCALEDIR);
1068 textdomain(PACKAGE); */
1070 if(argc && argv) {
1071 thisprogram = argv[0];
1072 } else {
1073 mount_cifs_usage();
1074 exit(EX_USAGE);
1077 if(thisprogram == NULL)
1078 thisprogram = "mount.cifs";
1080 uname(&sysinfo);
1081 /* BB add workstation name and domain and pass down */
1083 /* #ifdef _GNU_SOURCE
1084 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1085 #endif */
1086 if(argc > 2) {
1087 dev_name = argv[1];
1088 share_name = strndup(argv[1], MAX_UNC_LEN);
1089 if (share_name == NULL) {
1090 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1091 exit(EX_SYSERR);
1093 mountpoint = argv[2];
1094 } else {
1095 if ((strcmp (argv[1], "--version") == 0) ||
1096 ((strcmp (argv[1], "-V") == 0))) {
1097 printf ("mount.cifs version: %s.%s%s\n",
1098 MOUNT_CIFS_VERSION_MAJOR,
1099 MOUNT_CIFS_VERSION_MINOR,
1100 MOUNT_CIFS_VENDOR_SUFFIX);
1101 exit (0);
1103 mount_cifs_usage();
1104 exit(EX_USAGE);
1107 /* add sharename in opts string as unc= parm */
1109 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1110 longopts, NULL)) != -1) {
1111 switch (c) {
1112 /* No code to do the following options yet */
1113 /* case 'l':
1114 list_with_volumelabel = 1;
1115 break;
1116 case 'L':
1117 volumelabel = optarg;
1118 break; */
1119 /* case 'a':
1120 ++mount_all;
1121 break; */
1123 case '?':
1124 case 'h': /* help */
1125 mount_cifs_usage ();
1126 exit(EX_USAGE);
1127 case 'n':
1128 ++nomtab;
1129 break;
1130 case 'b':
1131 #ifdef MS_BIND
1132 flags |= MS_BIND;
1133 #else
1134 fprintf(stderr,
1135 "option 'b' (MS_BIND) not supported\n");
1136 #endif
1137 break;
1138 case 'm':
1139 #ifdef MS_MOVE
1140 flags |= MS_MOVE;
1141 #else
1142 fprintf(stderr,
1143 "option 'm' (MS_MOVE) not supported\n");
1144 #endif
1145 break;
1146 case 'o':
1147 orgoptions = strdup(optarg);
1148 break;
1149 case 'r': /* mount readonly */
1150 flags |= MS_RDONLY;
1151 break;
1152 case 'U':
1153 uuid = optarg;
1154 break;
1155 case 'v':
1156 ++verboseflag;
1157 break;
1158 case 'V':
1159 printf ("mount.cifs version: %s.%s%s\n",
1160 MOUNT_CIFS_VERSION_MAJOR,
1161 MOUNT_CIFS_VERSION_MINOR,
1162 MOUNT_CIFS_VENDOR_SUFFIX);
1163 exit (0);
1164 case 'w':
1165 flags &= ~MS_RDONLY;
1166 break;
1167 case 'R':
1168 rsize = atoi(optarg) ;
1169 break;
1170 case 'W':
1171 wsize = atoi(optarg);
1172 break;
1173 case '1':
1174 if (isdigit(*optarg)) {
1175 char *ep;
1177 uid = strtoul(optarg, &ep, 10);
1178 if (*ep) {
1179 printf("bad uid value \"%s\"\n", optarg);
1180 exit(EX_USAGE);
1182 } else {
1183 struct passwd *pw;
1185 if (!(pw = getpwnam(optarg))) {
1186 printf("bad user name \"%s\"\n", optarg);
1187 exit(EX_USAGE);
1189 uid = pw->pw_uid;
1190 endpwent();
1192 break;
1193 case '2':
1194 if (isdigit(*optarg)) {
1195 char *ep;
1197 gid = strtoul(optarg, &ep, 10);
1198 if (*ep) {
1199 printf("bad gid value \"%s\"\n", optarg);
1200 exit(EX_USAGE);
1202 } else {
1203 struct group *gr;
1205 if (!(gr = getgrnam(optarg))) {
1206 printf("bad user name \"%s\"\n", optarg);
1207 exit(EX_USAGE);
1209 gid = gr->gr_gid;
1210 endpwent();
1212 break;
1213 case 'u':
1214 got_user = 1;
1215 user_name = optarg;
1216 break;
1217 case 'd':
1218 domain_name = optarg; /* BB fix this - currently ignored */
1219 got_domain = 1;
1220 break;
1221 case 'p':
1222 if(mountpassword == NULL)
1223 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1224 if(mountpassword) {
1225 got_password = 1;
1226 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1228 break;
1229 case 'S':
1230 get_password_from_file(0 /* stdin */,NULL);
1231 break;
1232 case 't':
1233 break;
1234 case 'f':
1235 ++fakemnt;
1236 break;
1237 default:
1238 printf("unknown mount option %c\n",c);
1239 mount_cifs_usage();
1240 exit(EX_USAGE);
1244 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1245 mount_cifs_usage();
1246 exit(EX_USAGE);
1249 if (getenv("PASSWD")) {
1250 if(mountpassword == NULL)
1251 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1252 if(mountpassword) {
1253 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1254 got_password = 1;
1256 } else if (getenv("PASSWD_FD")) {
1257 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1258 } else if (getenv("PASSWD_FILE")) {
1259 get_password_from_file(0, getenv("PASSWD_FILE"));
1262 if (orgoptions && parse_options(&orgoptions, &flags)) {
1263 rc = EX_USAGE;
1264 goto mount_exit;
1266 addrhead = addr = parse_server(&share_name);
1267 if((addrhead == NULL) && (got_ip == 0)) {
1268 printf("No ip address specified and hostname not found\n");
1269 rc = EX_USAGE;
1270 goto mount_exit;
1273 /* BB save off path and pop after mount returns? */
1274 resolved_path = (char *)malloc(PATH_MAX+1);
1275 if(resolved_path) {
1276 /* Note that if we can not canonicalize the name, we get
1277 another chance to see if it is valid when we chdir to it */
1278 if (realpath(mountpoint, resolved_path)) {
1279 mountpoint = resolved_path;
1282 if(chdir(mountpoint)) {
1283 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1284 rc = EX_USAGE;
1285 goto mount_exit;
1288 if(stat (".", &statbuf)) {
1289 printf("mount error: mount point %s does not exist\n",mountpoint);
1290 rc = EX_USAGE;
1291 goto mount_exit;
1294 if (S_ISDIR(statbuf.st_mode) == 0) {
1295 printf("mount error: mount point %s is not a directory\n",mountpoint);
1296 rc = EX_USAGE;
1297 goto mount_exit;
1300 if((getuid() != 0) && (geteuid() == 0)) {
1301 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1302 #ifndef CIFS_ALLOW_USR_SUID
1303 /* Do not allow user mounts to control suid flag
1304 for mount unless explicitly built that way */
1305 flags |= MS_NOSUID | MS_NODEV;
1306 #endif
1307 } else {
1308 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1309 exit(EX_USAGE);
1313 if(got_user == 0) {
1314 /* Note that the password will not be retrieved from the
1315 USER env variable (ie user%password form) as there is
1316 already a PASSWD environment varaible */
1317 if (getenv("USER"))
1318 user_name = strdup(getenv("USER"));
1319 if (user_name == NULL)
1320 user_name = getusername();
1321 got_user = 1;
1324 if(got_password == 0) {
1325 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1326 no good replacement yet. */
1327 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1328 if (!tmp_pass || !mountpassword) {
1329 printf("Password not entered, exiting\n");
1330 exit(EX_USAGE);
1332 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1333 got_password = 1;
1335 /* FIXME launch daemon (handles dfs name resolution and credential change)
1336 remember to clear parms and overwrite password field before launching */
1337 if(orgoptions) {
1338 optlen = strlen(orgoptions);
1339 orgoptlen = optlen;
1340 } else
1341 optlen = 0;
1342 if(share_name)
1343 optlen += strlen(share_name) + 4;
1344 else {
1345 printf("No server share name specified\n");
1346 printf("\nMounting the DFS root for server not implemented yet\n");
1347 exit(EX_USAGE);
1349 if(user_name)
1350 optlen += strlen(user_name) + 6;
1351 optlen += MAX_ADDRESS_LEN + 4;
1352 if(mountpassword)
1353 optlen += strlen(mountpassword) + 6;
1354 mount_retry:
1355 SAFE_FREE(options);
1356 options_size = optlen + 10 + DOMAIN_SIZE;
1357 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 */);
1359 if(options == NULL) {
1360 printf("Could not allocate memory for mount options\n");
1361 exit(EX_SYSERR);
1364 strlcpy(options, "unc=", options_size);
1365 strlcat(options,share_name,options_size);
1366 /* scan backwards and reverse direction of slash */
1367 temp = strrchr(options, '/');
1368 if(temp > options + 6)
1369 *temp = '\\';
1370 if(user_name) {
1371 /* check for syntax like user=domain\user */
1372 if(got_domain == 0)
1373 domain_name = check_for_domain(&user_name);
1374 strlcat(options,",user=",options_size);
1375 strlcat(options,user_name,options_size);
1377 if(retry == 0) {
1378 if(domain_name) {
1379 /* extra length accounted for in option string above */
1380 strlcat(options,",domain=",options_size);
1381 strlcat(options,domain_name,options_size);
1384 if(mountpassword) {
1385 /* Commas have to be doubled, or else they will
1386 look like the parameter separator */
1387 /* if(sep is not set)*/
1388 if(retry == 0)
1389 check_for_comma(&mountpassword);
1390 strlcat(options,",pass=",options_size);
1391 strlcat(options,mountpassword,options_size);
1394 strlcat(options,",ver=",options_size);
1395 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1397 if(orgoptions) {
1398 strlcat(options,",",options_size);
1399 strlcat(options,orgoptions,options_size);
1401 if(prefixpath) {
1402 strlcat(options,",prefixpath=",options_size);
1403 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1405 if(verboseflag)
1406 printf("\nmount.cifs kernel mount options %s \n",options);
1408 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1409 replace_char(dev_name, '\\', '/', strlen(share_name));
1411 if (!got_ip && addr) {
1412 strlcat(options, ",ip=", options_size);
1413 current_len = strnlen(options, options_size);
1414 optionstail = options + current_len;
1415 switch (addr->ai_addr->sa_family) {
1416 case AF_INET6:
1417 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1418 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1419 options_size - current_len);
1420 break;
1421 case AF_INET:
1422 addr4 = (struct sockaddr_in *) addr->ai_addr;
1423 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1424 options_size - current_len);
1425 break;
1428 /* if the address looks bogus, try the next one */
1429 if (!ipaddr) {
1430 addr = addr->ai_next;
1431 if (addr)
1432 goto mount_retry;
1433 rc = EX_SYSERR;
1434 goto mount_exit;
1438 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1439 switch (errno) {
1440 case ECONNREFUSED:
1441 case EHOSTUNREACH:
1442 if (addr) {
1443 addr = addr->ai_next;
1444 if (addr)
1445 goto mount_retry;
1447 break;
1448 case ENODEV:
1449 printf("mount error: cifs filesystem not supported by the system\n");
1450 break;
1451 case ENXIO:
1452 if(retry == 0) {
1453 retry = 1;
1454 if (uppercase_string(dev_name) &&
1455 uppercase_string(share_name) &&
1456 uppercase_string(prefixpath)) {
1457 printf("retrying with upper case share name\n");
1458 goto mount_retry;
1462 printf("mount error(%d): %s\n", errno, strerror(errno));
1463 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1464 "mount.cifs)\n");
1465 rc = EX_FAIL;
1466 goto mount_exit;
1469 if (nomtab)
1470 goto mount_exit;
1471 atexit(unlock_mtab);
1472 rc = lock_mtab();
1473 if (rc) {
1474 printf("cannot lock mtab");
1475 goto mount_exit;
1477 pmntfile = setmntent(MOUNTED, "a+");
1478 if (!pmntfile) {
1479 printf("could not update mount table\n");
1480 unlock_mtab();
1481 rc = EX_FILEIO;
1482 goto mount_exit;
1484 mountent.mnt_fsname = dev_name;
1485 mountent.mnt_dir = mountpoint;
1486 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1487 mountent.mnt_opts = (char *)malloc(220);
1488 if(mountent.mnt_opts) {
1489 char * mount_user = getusername();
1490 memset(mountent.mnt_opts,0,200);
1491 if(flags & MS_RDONLY)
1492 strlcat(mountent.mnt_opts,"ro",220);
1493 else
1494 strlcat(mountent.mnt_opts,"rw",220);
1495 if(flags & MS_MANDLOCK)
1496 strlcat(mountent.mnt_opts,",mand",220);
1497 if(flags & MS_NOEXEC)
1498 strlcat(mountent.mnt_opts,",noexec",220);
1499 if(flags & MS_NOSUID)
1500 strlcat(mountent.mnt_opts,",nosuid",220);
1501 if(flags & MS_NODEV)
1502 strlcat(mountent.mnt_opts,",nodev",220);
1503 if(flags & MS_SYNCHRONOUS)
1504 strlcat(mountent.mnt_opts,",sync",220);
1505 if(mount_user) {
1506 if(getuid() != 0) {
1507 strlcat(mountent.mnt_opts,
1508 ",user=", 220);
1509 strlcat(mountent.mnt_opts,
1510 mount_user, 220);
1514 mountent.mnt_freq = 0;
1515 mountent.mnt_passno = 0;
1516 rc = addmntent(pmntfile,&mountent);
1517 endmntent(pmntfile);
1518 unlock_mtab();
1519 SAFE_FREE(mountent.mnt_opts);
1520 if (rc)
1521 rc = EX_FILEIO;
1522 mount_exit:
1523 if(mountpassword) {
1524 int len = strlen(mountpassword);
1525 memset(mountpassword,0,len);
1526 SAFE_FREE(mountpassword);
1529 if (addrhead)
1530 freeaddrinfo(addrhead);
1531 SAFE_FREE(options);
1532 SAFE_FREE(orgoptions);
1533 SAFE_FREE(resolved_path);
1534 SAFE_FREE(share_name);
1535 exit(rc);