s3/popt.h: Fix typo.
[Samba/bb.git] / source / client / mount.cifs.c
blob8b62ef73fe8407b9a1782b768498284a9d308f40
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;
202 fs = fopen(file_name,"r");
203 if(fs == NULL)
204 return errno;
205 line_buf = (char *)malloc(4096);
206 if(line_buf == NULL) {
207 fclose(fs);
208 return ENOMEM;
211 while(fgets(line_buf,4096,fs)) {
212 /* parse line from credential file */
214 /* eat leading white space */
215 for(i=0;i<4086;i++) {
216 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
217 break;
218 /* if whitespace - skip past it */
220 if (strncasecmp("username",line_buf+i,8) == 0) {
221 temp_val = strchr(line_buf + i,'=');
222 if(temp_val) {
223 /* go past equals sign */
224 temp_val++;
225 for(length = 0;length<4087;length++) {
226 if ((temp_val[length] == '\n')
227 || (temp_val[length] == '\0')) {
228 temp_val[length] = '\0';
229 break;
232 if(length > 4086) {
233 printf("mount.cifs failed due to malformed username in credentials file");
234 memset(line_buf,0,4096);
235 exit(EX_USAGE);
236 } else {
237 got_user = 1;
238 user_name = (char *)calloc(1 + length,1);
239 /* BB adding free of user_name string before exit,
240 not really necessary but would be cleaner */
241 strlcpy(user_name,temp_val, length+1);
244 } else if (strncasecmp("password",line_buf+i,8) == 0) {
245 temp_val = strchr(line_buf+i,'=');
246 if(temp_val) {
247 /* go past equals sign */
248 temp_val++;
249 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
250 if ((temp_val[length] == '\n')
251 || (temp_val[length] == '\0')) {
252 temp_val[length] = '\0';
253 break;
256 if(length > MOUNT_PASSWD_SIZE) {
257 printf("mount.cifs failed: password in credentials file too long\n");
258 memset(line_buf,0, 4096);
259 exit(EX_USAGE);
260 } else {
261 if(mountpassword == NULL) {
262 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
263 } else
264 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
265 if(mountpassword) {
266 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
267 got_password = 1;
271 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
272 temp_val = strchr(line_buf+i,'=');
273 if(temp_val) {
274 /* go past equals sign */
275 temp_val++;
276 if(verboseflag)
277 printf("\nDomain %s\n",temp_val);
278 for(length = 0;length<DOMAIN_SIZE+1;length++) {
279 if ((temp_val[length] == '\n')
280 || (temp_val[length] == '\0')) {
281 temp_val[length] = '\0';
282 break;
285 if(length > DOMAIN_SIZE) {
286 printf("mount.cifs failed: domain in credentials file too long\n");
287 exit(EX_USAGE);
288 } else {
289 if(domain_name == NULL) {
290 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
291 } else
292 memset(domain_name,0,DOMAIN_SIZE);
293 if(domain_name) {
294 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
295 got_domain = 1;
302 fclose(fs);
303 SAFE_FREE(line_buf);
304 return 0;
307 static int get_password_from_file(int file_descript, char * filename)
309 int rc = 0;
310 int i;
311 char c;
313 if(mountpassword == NULL)
314 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
315 else
316 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
318 if (mountpassword == NULL) {
319 printf("malloc failed\n");
320 exit(EX_SYSERR);
323 if(filename != NULL) {
324 file_descript = open(filename, O_RDONLY);
325 if(file_descript < 0) {
326 printf("mount.cifs failed. %s attempting to open password file %s\n",
327 strerror(errno),filename);
328 exit(EX_SYSERR);
331 /* else file already open and fd provided */
333 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
334 rc = read(file_descript,&c,1);
335 if(rc < 0) {
336 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
337 if(filename != NULL)
338 close(file_descript);
339 exit(EX_SYSERR);
340 } else if(rc == 0) {
341 if(mountpassword[0] == 0) {
342 if(verboseflag)
343 printf("\nWarning: null password used since cifs password file empty");
345 break;
346 } else /* read valid character */ {
347 if((c == 0) || (c == '\n')) {
348 mountpassword[i] = '\0';
349 break;
350 } else
351 mountpassword[i] = c;
354 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
355 printf("\nWarning: password longer than %d characters specified in cifs password file",
356 MOUNT_PASSWD_SIZE);
358 got_password = 1;
359 if(filename != NULL) {
360 close(file_descript);
363 return rc;
366 static int parse_options(char ** optionsp, int * filesys_flags)
368 const char * data;
369 char * percent_char = NULL;
370 char * value = NULL;
371 char * next_keyword = NULL;
372 char * out = NULL;
373 int out_len = 0;
374 int word_len;
375 int rc = 0;
376 char user[32];
377 char group[32];
379 if (!optionsp || !*optionsp)
380 return 1;
381 data = *optionsp;
383 if(verboseflag)
384 printf("parsing options: %s\n", data);
386 /* BB fixme check for separator override BB */
388 if (getuid()) {
389 got_uid = 1;
390 snprintf(user,sizeof(user),"%u",getuid());
391 got_gid = 1;
392 snprintf(group,sizeof(group),"%u",getgid());
395 /* while ((data = strsep(&options, ",")) != NULL) { */
396 while(data != NULL) {
397 /* check if ends with trailing comma */
398 if(*data == 0)
399 break;
401 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
402 /* data = next keyword */
403 /* value = next value ie stuff after equal sign */
405 next_keyword = strchr(data,','); /* BB handle sep= */
407 /* temporarily null terminate end of keyword=value pair */
408 if(next_keyword)
409 *next_keyword++ = 0;
411 /* temporarily null terminate keyword to make keyword and value distinct */
412 if ((value = strchr(data, '=')) != NULL) {
413 *value = '\0';
414 value++;
417 if (strncmp(data, "users",5) == 0) {
418 if(!value || !*value) {
419 goto nocopy;
421 } else if (strncmp(data, "user_xattr",10) == 0) {
422 /* do nothing - need to skip so not parsed as user name */
423 } else if (strncmp(data, "user", 4) == 0) {
425 if (!value || !*value) {
426 if(data[4] == '\0') {
427 if(verboseflag)
428 printf("\nskipping empty user mount parameter\n");
429 /* remove the parm since it would otherwise be confusing
430 to the kernel code which would think it was a real username */
431 goto nocopy;
432 } else {
433 printf("username specified with no parameter\n");
434 SAFE_FREE(out);
435 return 1; /* needs_arg; */
437 } else {
438 if (strnlen(value, 260) < 260) {
439 got_user=1;
440 percent_char = strchr(value,'%');
441 if(percent_char) {
442 *percent_char = ',';
443 if(mountpassword == NULL)
444 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
445 if(mountpassword) {
446 if(got_password)
447 printf("\nmount.cifs warning - password specified twice\n");
448 got_password = 1;
449 percent_char++;
450 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
451 /* remove password from username */
452 while(*percent_char != 0) {
453 *percent_char = ',';
454 percent_char++;
458 /* this is only case in which the user
459 name buf is not malloc - so we have to
460 check for domain name embedded within
461 the user name here since the later
462 call to check_for_domain will not be
463 invoked */
464 domain_name = check_for_domain(&value);
465 } else {
466 printf("username too long\n");
467 SAFE_FREE(out);
468 return 1;
471 } else if (strncmp(data, "pass", 4) == 0) {
472 if (!value || !*value) {
473 if(got_password) {
474 printf("\npassword specified twice, ignoring second\n");
475 } else
476 got_password = 1;
477 } else if (strnlen(value, 17) < 17) {
478 if(got_password)
479 printf("\nmount.cifs warning - password specified twice\n");
480 got_password = 1;
481 } else {
482 printf("password too long\n");
483 SAFE_FREE(out);
484 return 1;
486 } else if (strncmp(data, "sec", 3) == 0) {
487 if (value) {
488 if (!strncmp(value, "none", 4) ||
489 !strncmp(value, "krb5", 4))
490 got_password = 1;
492 } else if (strncmp(data, "ip", 2) == 0) {
493 if (!value || !*value) {
494 printf("target ip address argument missing");
495 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
496 if(verboseflag)
497 printf("ip address %s override specified\n",value);
498 got_ip = 1;
499 } else {
500 printf("ip address too long\n");
501 SAFE_FREE(out);
502 return 1;
504 } else if ((strncmp(data, "unc", 3) == 0)
505 || (strncmp(data, "target", 6) == 0)
506 || (strncmp(data, "path", 4) == 0)) {
507 if (!value || !*value) {
508 printf("invalid path to network resource\n");
509 SAFE_FREE(out);
510 return 1; /* needs_arg; */
511 } else if(strnlen(value,5) < 5) {
512 printf("UNC name too short");
515 if (strnlen(value, 300) < 300) {
516 got_unc = 1;
517 if (strncmp(value, "//", 2) == 0) {
518 if(got_unc)
519 printf("unc name specified twice, ignoring second\n");
520 else
521 got_unc = 1;
522 } else if (strncmp(value, "\\\\", 2) != 0) {
523 printf("UNC Path does not begin with // or \\\\ \n");
524 SAFE_FREE(out);
525 return 1;
526 } else {
527 if(got_unc)
528 printf("unc name specified twice, ignoring second\n");
529 else
530 got_unc = 1;
532 } else {
533 printf("CIFS: UNC name too long\n");
534 SAFE_FREE(out);
535 return 1;
537 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
538 || (strncmp(data, "workg", 5) == 0)) {
539 /* note this allows for synonyms of "domain"
540 such as "DOM" and "dom" and "workgroup"
541 and "WORKGRP" etc. */
542 if (!value || !*value) {
543 printf("CIFS: invalid domain name\n");
544 SAFE_FREE(out);
545 return 1; /* needs_arg; */
547 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
548 got_domain = 1;
549 } else {
550 printf("domain name too long\n");
551 SAFE_FREE(out);
552 return 1;
554 } else if (strncmp(data, "cred", 4) == 0) {
555 if (value && *value) {
556 rc = open_cred_file(value);
557 if(rc) {
558 printf("error %d (%s) opening credential file %s\n",
559 rc, strerror(rc), value);
560 SAFE_FREE(out);
561 return 1;
563 } else {
564 printf("invalid credential file name specified\n");
565 SAFE_FREE(out);
566 return 1;
568 } else if (strncmp(data, "uid", 3) == 0) {
569 if (value && *value) {
570 got_uid = 1;
571 if (!isdigit(*value)) {
572 struct passwd *pw;
574 if (!(pw = getpwnam(value))) {
575 printf("bad user name \"%s\"\n", value);
576 exit(EX_USAGE);
578 snprintf(user, sizeof(user), "%u", pw->pw_uid);
579 } else {
580 strlcpy(user,value,sizeof(user));
583 goto nocopy;
584 } else if (strncmp(data, "gid", 3) == 0) {
585 if (value && *value) {
586 got_gid = 1;
587 if (!isdigit(*value)) {
588 struct group *gr;
590 if (!(gr = getgrnam(value))) {
591 printf("bad group name \"%s\"\n", value);
592 exit(EX_USAGE);
594 snprintf(group, sizeof(group), "%u", gr->gr_gid);
595 } else {
596 strlcpy(group,value,sizeof(group));
599 goto nocopy;
600 /* fmask and dmask synonyms for people used to smbfs syntax */
601 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
602 if (!value || !*value) {
603 printf ("Option '%s' requires a numerical argument\n", data);
604 SAFE_FREE(out);
605 return 1;
608 if (value[0] != '0') {
609 printf ("WARNING: '%s' not expressed in octal.\n", data);
612 if (strcmp (data, "fmask") == 0) {
613 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
614 data = "file_mode"; /* BB fix this */
616 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
617 if (!value || !*value) {
618 printf ("Option '%s' requires a numerical argument\n", data);
619 SAFE_FREE(out);
620 return 1;
623 if (value[0] != '0') {
624 printf ("WARNING: '%s' not expressed in octal.\n", data);
627 if (strcmp (data, "dmask") == 0) {
628 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
629 data = "dir_mode";
631 /* the following eight mount options should be
632 stripped out from what is passed into the kernel
633 since these eight options are best passed as the
634 mount flags rather than redundantly to the kernel
635 and could generate spurious warnings depending on the
636 level of the corresponding cifs vfs kernel code */
637 } else if (strncmp(data, "nosuid", 6) == 0) {
638 *filesys_flags |= MS_NOSUID;
639 } else if (strncmp(data, "suid", 4) == 0) {
640 *filesys_flags &= ~MS_NOSUID;
641 } else if (strncmp(data, "nodev", 5) == 0) {
642 *filesys_flags |= MS_NODEV;
643 } else if ((strncmp(data, "nobrl", 5) == 0) ||
644 (strncmp(data, "nolock", 6) == 0)) {
645 *filesys_flags &= ~MS_MANDLOCK;
646 } else if (strncmp(data, "dev", 3) == 0) {
647 *filesys_flags &= ~MS_NODEV;
648 } else if (strncmp(data, "noexec", 6) == 0) {
649 *filesys_flags |= MS_NOEXEC;
650 } else if (strncmp(data, "exec", 4) == 0) {
651 *filesys_flags &= ~MS_NOEXEC;
652 } else if (strncmp(data, "guest", 5) == 0) {
653 user_name = (char *)calloc(1, 1);
654 got_user = 1;
655 got_password = 1;
656 } else if (strncmp(data, "ro", 2) == 0) {
657 *filesys_flags |= MS_RDONLY;
658 } else if (strncmp(data, "rw", 2) == 0) {
659 *filesys_flags &= ~MS_RDONLY;
660 } else if (strncmp(data, "remount", 7) == 0) {
661 *filesys_flags |= MS_REMOUNT;
662 } /* else if (strnicmp(data, "port", 4) == 0) {
663 if (value && *value) {
664 vol->port =
665 simple_strtoul(value, &value, 0);
667 } else if (strnicmp(data, "rsize", 5) == 0) {
668 if (value && *value) {
669 vol->rsize =
670 simple_strtoul(value, &value, 0);
672 } else if (strnicmp(data, "wsize", 5) == 0) {
673 if (value && *value) {
674 vol->wsize =
675 simple_strtoul(value, &value, 0);
677 } else if (strnicmp(data, "version", 3) == 0) {
678 } else {
679 printf("CIFS: Unknown mount option %s\n",data);
680 } */ /* nothing to do on those four mount options above.
681 Just pass to kernel and ignore them here */
683 /* Copy (possibly modified) option to out */
684 word_len = strlen(data);
685 if (value)
686 word_len += 1 + strlen(value);
688 out = (char *)realloc(out, out_len + word_len + 2);
689 if (out == NULL) {
690 perror("malloc");
691 exit(EX_SYSERR);
694 if (out_len) {
695 strlcat(out, ",", out_len + word_len + 2);
696 out_len++;
699 if (value)
700 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
701 else
702 snprintf(out + out_len, word_len + 1, "%s", data);
703 out_len = strlen(out);
705 nocopy:
706 data = next_keyword;
709 /* special-case the uid and gid */
710 if (got_uid) {
711 word_len = strlen(user);
713 out = (char *)realloc(out, out_len + word_len + 6);
714 if (out == NULL) {
715 perror("malloc");
716 exit(EX_SYSERR);
719 if (out_len) {
720 strlcat(out, ",", out_len + word_len + 6);
721 out_len++;
723 snprintf(out + out_len, word_len + 5, "uid=%s", user);
724 out_len = strlen(out);
726 if (got_gid) {
727 word_len = strlen(group);
729 out = (char *)realloc(out, out_len + 1 + word_len + 6);
730 if (out == NULL) {
731 perror("malloc");
732 exit(EX_SYSERR);
735 if (out_len) {
736 strlcat(out, ",", out_len + word_len + 6);
737 out_len++;
739 snprintf(out + out_len, word_len + 5, "gid=%s", group);
740 out_len = strlen(out);
743 SAFE_FREE(*optionsp);
744 *optionsp = out;
745 return 0;
748 /* replace all (one or more) commas with double commas */
749 static void check_for_comma(char ** ppasswrd)
751 char *new_pass_buf;
752 char *pass;
753 int i,j;
754 int number_of_commas = 0;
755 int len;
757 if(ppasswrd == NULL)
758 return;
759 else
760 (pass = *ppasswrd);
762 len = strlen(pass);
764 for(i=0;i<len;i++) {
765 if(pass[i] == ',')
766 number_of_commas++;
769 if(number_of_commas == 0)
770 return;
771 if(number_of_commas > MOUNT_PASSWD_SIZE) {
772 /* would otherwise overflow the mount options buffer */
773 printf("\nInvalid password. Password contains too many commas.\n");
774 return;
777 new_pass_buf = (char *)malloc(len+number_of_commas+1);
778 if(new_pass_buf == NULL)
779 return;
781 for(i=0,j=0;i<len;i++,j++) {
782 new_pass_buf[j] = pass[i];
783 if(pass[i] == ',') {
784 j++;
785 new_pass_buf[j] = pass[i];
788 new_pass_buf[len+number_of_commas] = 0;
790 SAFE_FREE(*ppasswrd);
791 *ppasswrd = new_pass_buf;
793 return;
796 /* Usernames can not have backslash in them and we use
797 [BB check if usernames can have forward slash in them BB]
798 backslash as domain\user separator character
800 static char * check_for_domain(char **ppuser)
802 char * original_string;
803 char * usernm;
804 char * domainnm;
805 int original_len;
806 int len;
807 int i;
809 if(ppuser == NULL)
810 return NULL;
812 original_string = *ppuser;
814 if (original_string == NULL)
815 return NULL;
817 original_len = strlen(original_string);
819 usernm = strchr(*ppuser,'/');
820 if (usernm == NULL) {
821 usernm = strchr(*ppuser,'\\');
822 if (usernm == NULL)
823 return NULL;
826 if(got_domain) {
827 printf("Domain name specified twice. Username probably malformed\n");
828 return NULL;
831 usernm[0] = 0;
832 domainnm = *ppuser;
833 if (domainnm[0] != 0) {
834 got_domain = 1;
835 } else {
836 printf("null domain\n");
838 len = strlen(domainnm);
839 /* reset domainm to new buffer, and copy
840 domain name into it */
841 domainnm = (char *)malloc(len+1);
842 if(domainnm == NULL)
843 return NULL;
845 strlcpy(domainnm,*ppuser,len+1);
847 /* move_string(*ppuser, usernm+1) */
848 len = strlen(usernm+1);
850 if(len >= original_len) {
851 /* should not happen */
852 return domainnm;
855 for(i=0;i<original_len;i++) {
856 if(i<len)
857 original_string[i] = usernm[i+1];
858 else /* stuff with commas to remove last parm */
859 original_string[i] = ',';
862 /* BB add check for more than one slash?
863 strchr(*ppuser,'/');
864 strchr(*ppuser,'\\')
867 return domainnm;
870 /* replace all occurances of "from" in a string with "to" */
871 static void replace_char(char *string, char from, char to, int maxlen)
873 char *lastchar = string + maxlen;
874 while (string) {
875 string = strchr(string, from);
876 if (string) {
877 *string = to;
878 if (string >= lastchar)
879 return;
884 /* Note that caller frees the returned buffer if necessary */
885 static struct addrinfo *
886 parse_server(char ** punc_name)
888 char * unc_name = *punc_name;
889 int length = strnlen(unc_name, MAX_UNC_LEN);
890 char * share;
891 struct addrinfo *addrlist;
892 int rc;
894 if(length > (MAX_UNC_LEN - 1)) {
895 printf("mount error: UNC name too long");
896 return NULL;
898 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
899 (strncasecmp("smb://", unc_name, 6) == 0)) {
900 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
901 return NULL;
904 if(length < 3) {
905 /* BB add code to find DFS root here */
906 printf("\nMounting the DFS root for domain not implemented yet\n");
907 return NULL;
908 } else {
909 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
910 /* check for nfs syntax ie server:share */
911 share = strchr(unc_name,':');
912 if(share) {
913 *punc_name = (char *)malloc(length+3);
914 if(*punc_name == NULL) {
915 /* put the original string back if
916 no memory left */
917 *punc_name = unc_name;
918 return NULL;
920 *share = '/';
921 strlcpy((*punc_name)+2,unc_name,length+1);
922 SAFE_FREE(unc_name);
923 unc_name = *punc_name;
924 unc_name[length+2] = 0;
925 goto continue_unc_parsing;
926 } else {
927 printf("mount error: improperly formatted UNC name.");
928 printf(" %s does not begin with \\\\ or //\n",unc_name);
929 return NULL;
931 } else {
932 continue_unc_parsing:
933 unc_name[0] = '/';
934 unc_name[1] = '/';
935 unc_name += 2;
937 /* allow for either delimiter between host and sharename */
938 if ((share = strpbrk(unc_name, "/\\"))) {
939 *share = 0; /* temporarily terminate the string */
940 share += 1;
941 if(got_ip == 0) {
942 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
943 if (rc != 0) {
944 printf("mount error: could not resolve address for %s: %s\n",
945 unc_name, gai_strerror(rc));
946 addrlist = NULL;
949 *(share - 1) = '/'; /* put delimiter back */
951 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
952 if ((prefixpath = strpbrk(share, "/\\"))) {
953 *prefixpath = 0; /* permanently terminate the string */
954 if (!strlen(++prefixpath))
955 prefixpath = NULL; /* this needs to be done explicitly */
957 if(got_ip) {
958 if(verboseflag)
959 printf("ip address specified explicitly\n");
960 return NULL;
962 /* BB should we pass an alternate version of the share name as Unicode */
964 return addrlist;
965 } else {
966 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
967 printf("Mounting the DFS root for a particular server not implemented yet\n");
968 return NULL;
974 static struct option longopts[] = {
975 { "all", 0, NULL, 'a' },
976 { "help",0, NULL, 'h' },
977 { "move",0, NULL, 'm' },
978 { "bind",0, NULL, 'b' },
979 { "read-only", 0, NULL, 'r' },
980 { "ro", 0, NULL, 'r' },
981 { "verbose", 0, NULL, 'v' },
982 { "version", 0, NULL, 'V' },
983 { "read-write", 0, NULL, 'w' },
984 { "rw", 0, NULL, 'w' },
985 { "options", 1, NULL, 'o' },
986 { "type", 1, NULL, 't' },
987 { "rsize",1, NULL, 'R' },
988 { "wsize",1, NULL, 'W' },
989 { "uid", 1, NULL, '1'},
990 { "gid", 1, NULL, '2'},
991 { "user",1,NULL,'u'},
992 { "username",1,NULL,'u'},
993 { "dom",1,NULL,'d'},
994 { "domain",1,NULL,'d'},
995 { "password",1,NULL,'p'},
996 { "pass",1,NULL,'p'},
997 { "credentials",1,NULL,'c'},
998 { "port",1,NULL,'P'},
999 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
1000 { NULL, 0, NULL, 0 }
1003 /* convert a string to uppercase. return false if the string
1004 * wasn't ASCII. Return success on a NULL ptr */
1005 static int
1006 uppercase_string(char *string)
1008 if (!string)
1009 return 1;
1011 while (*string) {
1012 /* check for unicode */
1013 if ((unsigned char) string[0] & 0x80)
1014 return 0;
1015 *string = toupper((unsigned char) *string);
1016 string++;
1019 return 1;
1022 int main(int argc, char ** argv)
1024 int c;
1025 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1026 char * orgoptions = NULL;
1027 char * share_name = NULL;
1028 const char * ipaddr = NULL;
1029 char * uuid = NULL;
1030 char * mountpoint = NULL;
1031 char * options = NULL;
1032 char * optionstail;
1033 char * resolved_path = NULL;
1034 char * temp;
1035 char * dev_name;
1036 int rc = 0;
1037 int rsize = 0;
1038 int wsize = 0;
1039 int nomtab = 0;
1040 int uid = 0;
1041 int gid = 0;
1042 int optlen = 0;
1043 int orgoptlen = 0;
1044 size_t options_size = 0;
1045 size_t current_len;
1046 int retry = 0; /* set when we have to retry mount with uppercase */
1047 struct addrinfo *addrhead = NULL, *addr;
1048 struct stat statbuf;
1049 struct utsname sysinfo;
1050 struct mntent mountent;
1051 struct sockaddr_in *addr4;
1052 struct sockaddr_in6 *addr6;
1053 FILE * pmntfile;
1055 /* setlocale(LC_ALL, "");
1056 bindtextdomain(PACKAGE, LOCALEDIR);
1057 textdomain(PACKAGE); */
1059 if(argc && argv) {
1060 thisprogram = argv[0];
1061 } else {
1062 mount_cifs_usage();
1063 exit(EX_USAGE);
1066 if(thisprogram == NULL)
1067 thisprogram = "mount.cifs";
1069 uname(&sysinfo);
1070 /* BB add workstation name and domain and pass down */
1072 /* #ifdef _GNU_SOURCE
1073 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1074 #endif */
1075 if(argc > 2) {
1076 dev_name = argv[1];
1077 share_name = strndup(argv[1], MAX_UNC_LEN);
1078 if (share_name == NULL) {
1079 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1080 exit(EX_SYSERR);
1082 mountpoint = argv[2];
1083 } else {
1084 if ((strcmp (argv[1], "--version") == 0) ||
1085 ((strcmp (argv[1], "-V") == 0))) {
1086 printf ("mount.cifs version: %s.%s%s\n",
1087 MOUNT_CIFS_VERSION_MAJOR,
1088 MOUNT_CIFS_VERSION_MINOR,
1089 MOUNT_CIFS_VENDOR_SUFFIX);
1090 exit (0);
1092 mount_cifs_usage();
1093 exit(EX_USAGE);
1096 /* add sharename in opts string as unc= parm */
1098 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1099 longopts, NULL)) != -1) {
1100 switch (c) {
1101 /* No code to do the following options yet */
1102 /* case 'l':
1103 list_with_volumelabel = 1;
1104 break;
1105 case 'L':
1106 volumelabel = optarg;
1107 break; */
1108 /* case 'a':
1109 ++mount_all;
1110 break; */
1112 case '?':
1113 case 'h': /* help */
1114 mount_cifs_usage ();
1115 exit(EX_USAGE);
1116 case 'n':
1117 ++nomtab;
1118 break;
1119 case 'b':
1120 #ifdef MS_BIND
1121 flags |= MS_BIND;
1122 #else
1123 fprintf(stderr,
1124 "option 'b' (MS_BIND) not supported\n");
1125 #endif
1126 break;
1127 case 'm':
1128 #ifdef MS_MOVE
1129 flags |= MS_MOVE;
1130 #else
1131 fprintf(stderr,
1132 "option 'm' (MS_MOVE) not supported\n");
1133 #endif
1134 break;
1135 case 'o':
1136 orgoptions = strdup(optarg);
1137 break;
1138 case 'r': /* mount readonly */
1139 flags |= MS_RDONLY;
1140 break;
1141 case 'U':
1142 uuid = optarg;
1143 break;
1144 case 'v':
1145 ++verboseflag;
1146 break;
1147 case 'V':
1148 printf ("mount.cifs version: %s.%s%s\n",
1149 MOUNT_CIFS_VERSION_MAJOR,
1150 MOUNT_CIFS_VERSION_MINOR,
1151 MOUNT_CIFS_VENDOR_SUFFIX);
1152 exit (0);
1153 case 'w':
1154 flags &= ~MS_RDONLY;
1155 break;
1156 case 'R':
1157 rsize = atoi(optarg) ;
1158 break;
1159 case 'W':
1160 wsize = atoi(optarg);
1161 break;
1162 case '1':
1163 if (isdigit(*optarg)) {
1164 char *ep;
1166 uid = strtoul(optarg, &ep, 10);
1167 if (*ep) {
1168 printf("bad uid value \"%s\"\n", optarg);
1169 exit(EX_USAGE);
1171 } else {
1172 struct passwd *pw;
1174 if (!(pw = getpwnam(optarg))) {
1175 printf("bad user name \"%s\"\n", optarg);
1176 exit(EX_USAGE);
1178 uid = pw->pw_uid;
1179 endpwent();
1181 break;
1182 case '2':
1183 if (isdigit(*optarg)) {
1184 char *ep;
1186 gid = strtoul(optarg, &ep, 10);
1187 if (*ep) {
1188 printf("bad gid value \"%s\"\n", optarg);
1189 exit(EX_USAGE);
1191 } else {
1192 struct group *gr;
1194 if (!(gr = getgrnam(optarg))) {
1195 printf("bad user name \"%s\"\n", optarg);
1196 exit(EX_USAGE);
1198 gid = gr->gr_gid;
1199 endpwent();
1201 break;
1202 case 'u':
1203 got_user = 1;
1204 user_name = optarg;
1205 break;
1206 case 'd':
1207 domain_name = optarg; /* BB fix this - currently ignored */
1208 got_domain = 1;
1209 break;
1210 case 'p':
1211 if(mountpassword == NULL)
1212 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1213 if(mountpassword) {
1214 got_password = 1;
1215 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1217 break;
1218 case 'S':
1219 get_password_from_file(0 /* stdin */,NULL);
1220 break;
1221 case 't':
1222 break;
1223 case 'f':
1224 ++fakemnt;
1225 break;
1226 default:
1227 printf("unknown mount option %c\n",c);
1228 mount_cifs_usage();
1229 exit(EX_USAGE);
1233 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1234 mount_cifs_usage();
1235 exit(EX_USAGE);
1238 if (getenv("PASSWD")) {
1239 if(mountpassword == NULL)
1240 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1241 if(mountpassword) {
1242 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1243 got_password = 1;
1245 } else if (getenv("PASSWD_FD")) {
1246 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1247 } else if (getenv("PASSWD_FILE")) {
1248 get_password_from_file(0, getenv("PASSWD_FILE"));
1251 if (orgoptions && parse_options(&orgoptions, &flags)) {
1252 rc = EX_USAGE;
1253 goto mount_exit;
1255 addrhead = addr = parse_server(&share_name);
1256 if((addrhead == NULL) && (got_ip == 0)) {
1257 printf("No ip address specified and hostname not found\n");
1258 rc = EX_USAGE;
1259 goto mount_exit;
1262 /* BB save off path and pop after mount returns? */
1263 resolved_path = (char *)malloc(PATH_MAX+1);
1264 if(resolved_path) {
1265 /* Note that if we can not canonicalize the name, we get
1266 another chance to see if it is valid when we chdir to it */
1267 if (realpath(mountpoint, resolved_path)) {
1268 mountpoint = resolved_path;
1271 if(chdir(mountpoint)) {
1272 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1273 rc = EX_USAGE;
1274 goto mount_exit;
1277 if(stat (".", &statbuf)) {
1278 printf("mount error: mount point %s does not exist\n",mountpoint);
1279 rc = EX_USAGE;
1280 goto mount_exit;
1283 if (S_ISDIR(statbuf.st_mode) == 0) {
1284 printf("mount error: mount point %s is not a directory\n",mountpoint);
1285 rc = EX_USAGE;
1286 goto mount_exit;
1289 if((getuid() != 0) && (geteuid() == 0)) {
1290 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1291 #ifndef CIFS_ALLOW_USR_SUID
1292 /* Do not allow user mounts to control suid flag
1293 for mount unless explicitly built that way */
1294 flags |= MS_NOSUID | MS_NODEV;
1295 #endif
1296 } else {
1297 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1298 exit(EX_USAGE);
1302 if(got_user == 0) {
1303 /* Note that the password will not be retrieved from the
1304 USER env variable (ie user%password form) as there is
1305 already a PASSWD environment varaible */
1306 if (getenv("USER"))
1307 user_name = strdup(getenv("USER"));
1308 if (user_name == NULL)
1309 user_name = getusername();
1310 got_user = 1;
1313 if(got_password == 0) {
1314 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1315 no good replacement yet. */
1316 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1317 if (!tmp_pass || !mountpassword) {
1318 printf("Password not entered, exiting\n");
1319 exit(EX_USAGE);
1321 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1322 got_password = 1;
1324 /* FIXME launch daemon (handles dfs name resolution and credential change)
1325 remember to clear parms and overwrite password field before launching */
1326 if(orgoptions) {
1327 optlen = strlen(orgoptions);
1328 orgoptlen = optlen;
1329 } else
1330 optlen = 0;
1331 if(share_name)
1332 optlen += strlen(share_name) + 4;
1333 else {
1334 printf("No server share name specified\n");
1335 printf("\nMounting the DFS root for server not implemented yet\n");
1336 exit(EX_USAGE);
1338 if(user_name)
1339 optlen += strlen(user_name) + 6;
1340 optlen += MAX_ADDRESS_LEN + 4;
1341 if(mountpassword)
1342 optlen += strlen(mountpassword) + 6;
1343 mount_retry:
1344 SAFE_FREE(options);
1345 options_size = optlen + 10 + DOMAIN_SIZE;
1346 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 */);
1348 if(options == NULL) {
1349 printf("Could not allocate memory for mount options\n");
1350 exit(EX_SYSERR);
1353 strlcpy(options, "unc=", options_size);
1354 strlcat(options,share_name,options_size);
1355 /* scan backwards and reverse direction of slash */
1356 temp = strrchr(options, '/');
1357 if(temp > options + 6)
1358 *temp = '\\';
1359 if(user_name) {
1360 /* check for syntax like user=domain\user */
1361 if(got_domain == 0)
1362 domain_name = check_for_domain(&user_name);
1363 strlcat(options,",user=",options_size);
1364 strlcat(options,user_name,options_size);
1366 if(retry == 0) {
1367 if(domain_name) {
1368 /* extra length accounted for in option string above */
1369 strlcat(options,",domain=",options_size);
1370 strlcat(options,domain_name,options_size);
1373 if(mountpassword) {
1374 /* Commas have to be doubled, or else they will
1375 look like the parameter separator */
1376 /* if(sep is not set)*/
1377 if(retry == 0)
1378 check_for_comma(&mountpassword);
1379 strlcat(options,",pass=",options_size);
1380 strlcat(options,mountpassword,options_size);
1383 strlcat(options,",ver=",options_size);
1384 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1386 if(orgoptions) {
1387 strlcat(options,",",options_size);
1388 strlcat(options,orgoptions,options_size);
1390 if(prefixpath) {
1391 strlcat(options,",prefixpath=",options_size);
1392 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1394 if(verboseflag)
1395 printf("\nmount.cifs kernel mount options %s \n",options);
1397 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1398 replace_char(dev_name, '\\', '/', strlen(share_name));
1400 if (!got_ip && addr) {
1401 strlcat(options, ",ip=", options_size);
1402 current_len = strnlen(options, options_size);
1403 optionstail = options + current_len;
1404 switch (addr->ai_addr->sa_family) {
1405 case AF_INET6:
1406 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1407 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1408 options_size - current_len);
1409 break;
1410 case AF_INET:
1411 addr4 = (struct sockaddr_in *) addr->ai_addr;
1412 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1413 options_size - current_len);
1414 break;
1417 /* if the address looks bogus, try the next one */
1418 if (!ipaddr) {
1419 addr = addr->ai_next;
1420 if (addr)
1421 goto mount_retry;
1422 rc = EX_SYSERR;
1423 goto mount_exit;
1427 if (!fakemnt && mount(dev_name, mountpoint, "cifs", flags, options)) {
1428 switch (errno) {
1429 case ECONNREFUSED:
1430 case EHOSTUNREACH:
1431 if (addr) {
1432 addr = addr->ai_next;
1433 if (addr)
1434 goto mount_retry;
1436 break;
1437 case ENODEV:
1438 printf("mount error: cifs filesystem not supported by the system\n");
1439 break;
1440 case ENXIO:
1441 if(retry == 0) {
1442 retry = 1;
1443 if (uppercase_string(dev_name) &&
1444 uppercase_string(share_name) &&
1445 uppercase_string(prefixpath)) {
1446 printf("retrying with upper case share name\n");
1447 goto mount_retry;
1451 printf("mount error(%d): %s\n", errno, strerror(errno));
1452 printf("Refer to the mount.cifs(8) manual page (e.g. man "
1453 "mount.cifs)\n");
1454 rc = EX_FAIL;
1455 goto mount_exit;
1458 if (nomtab)
1459 goto mount_exit;
1460 atexit(unlock_mtab);
1461 rc = lock_mtab();
1462 if (rc) {
1463 printf("cannot lock mtab");
1464 goto mount_exit;
1466 pmntfile = setmntent(MOUNTED, "a+");
1467 if (!pmntfile) {
1468 printf("could not update mount table\n");
1469 unlock_mtab();
1470 rc = EX_FILEIO;
1471 goto mount_exit;
1473 mountent.mnt_fsname = dev_name;
1474 mountent.mnt_dir = mountpoint;
1475 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1476 mountent.mnt_opts = (char *)malloc(220);
1477 if(mountent.mnt_opts) {
1478 char * mount_user = getusername();
1479 memset(mountent.mnt_opts,0,200);
1480 if(flags & MS_RDONLY)
1481 strlcat(mountent.mnt_opts,"ro",220);
1482 else
1483 strlcat(mountent.mnt_opts,"rw",220);
1484 if(flags & MS_MANDLOCK)
1485 strlcat(mountent.mnt_opts,",mand",220);
1486 if(flags & MS_NOEXEC)
1487 strlcat(mountent.mnt_opts,",noexec",220);
1488 if(flags & MS_NOSUID)
1489 strlcat(mountent.mnt_opts,",nosuid",220);
1490 if(flags & MS_NODEV)
1491 strlcat(mountent.mnt_opts,",nodev",220);
1492 if(flags & MS_SYNCHRONOUS)
1493 strlcat(mountent.mnt_opts,",sync",220);
1494 if(mount_user) {
1495 if(getuid() != 0) {
1496 strlcat(mountent.mnt_opts,
1497 ",user=", 220);
1498 strlcat(mountent.mnt_opts,
1499 mount_user, 220);
1503 mountent.mnt_freq = 0;
1504 mountent.mnt_passno = 0;
1505 rc = addmntent(pmntfile,&mountent);
1506 endmntent(pmntfile);
1507 unlock_mtab();
1508 SAFE_FREE(mountent.mnt_opts);
1509 if (rc)
1510 rc = EX_FILEIO;
1511 mount_exit:
1512 if(mountpassword) {
1513 int len = strlen(mountpassword);
1514 memset(mountpassword,0,len);
1515 SAFE_FREE(mountpassword);
1518 if (addrhead)
1519 freeaddrinfo(addrhead);
1520 SAFE_FREE(options);
1521 SAFE_FREE(orgoptions);
1522 SAFE_FREE(resolved_path);
1523 SAFE_FREE(share_name);
1524 exit(rc);