Add an entry for the "check" command to the tdbtool manpage.
[Samba/gebeck_regimport.git] / source3 / client / mount.cifs.c
blob9f4d1d3fd06abc558fed15d959af6cfe4f0334d2
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 static int got_password = 0;
89 static int got_user = 0;
90 static int got_domain = 0;
91 static int got_ip = 0;
92 static int got_unc = 0;
93 static int got_uid = 0;
94 static int got_gid = 0;
95 static char * user_name = NULL;
96 static char * mountpassword = NULL;
97 char * domain_name = NULL;
98 char * prefixpath = NULL;
100 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
101 * don't link to libreplace so need them here. */
103 /* like strncpy but does not 0 fill the buffer and always null
104 * terminates. bufsize is the size of the destination buffer */
106 #ifndef HAVE_STRLCPY
107 static size_t strlcpy(char *d, const char *s, size_t bufsize)
109 size_t len = strlen(s);
110 size_t ret = len;
111 if (bufsize <= 0) return 0;
112 if (len >= bufsize) len = bufsize-1;
113 memcpy(d, s, len);
114 d[len] = 0;
115 return ret;
117 #endif
119 /* like strncat but does not 0 fill the buffer and always null
120 * terminates. bufsize is the length of the buffer, which should
121 * be one more than the maximum resulting string length */
123 #ifndef HAVE_STRLCAT
124 static size_t strlcat(char *d, const char *s, size_t bufsize)
126 size_t len1 = strlen(d);
127 size_t len2 = strlen(s);
128 size_t ret = len1 + len2;
130 if (len1+len2 >= bufsize) {
131 if (bufsize < (len1+1)) {
132 return ret;
134 len2 = bufsize - (len1+1);
136 if (len2 > 0) {
137 memcpy(d+len1, s, len2);
138 d[len1+len2] = 0;
140 return ret;
142 #endif
144 /* BB finish BB
146 cifs_umount
147 open nofollow - avoid symlink exposure?
148 get owner of dir see if matches self or if root
149 call system(umount argv) etc.
151 BB end finish BB */
153 static char * check_for_domain(char **);
156 static void mount_cifs_usage(void)
158 printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
159 printf("\nMount the remote target, specified as a UNC name,");
160 printf(" to a local directory.\n\nOptions:\n");
161 printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
162 printf("\nLess commonly used options:");
163 printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
164 printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
165 printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
166 printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
167 printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
168 printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
169 printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
170 printf("\n\nRarely used options:");
171 printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
172 printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
173 printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
174 printf("\n\tin6_addr");
175 printf("\n\nOptions are described in more detail in the manual page");
176 printf("\n\tman 8 mount.cifs\n");
177 printf("\nTo display the version number of the mount helper:");
178 printf("\n\t%s -V\n",thisprogram);
180 SAFE_FREE(mountpassword);
181 exit(EX_USAGE);
184 /* caller frees username if necessary */
185 static char * getusername(void) {
186 char *username = NULL;
187 struct passwd *password = getpwuid(getuid());
189 if (password) {
190 username = password->pw_name;
192 return username;
195 static int open_cred_file(char * file_name)
197 char * line_buf;
198 char * temp_val;
199 FILE * fs;
200 int i, length;
201 fs = fopen(file_name,"r");
202 if(fs == NULL)
203 return errno;
204 line_buf = (char *)malloc(4096);
205 if(line_buf == NULL) {
206 fclose(fs);
207 return ENOMEM;
210 while(fgets(line_buf,4096,fs)) {
211 /* parse line from credential file */
213 /* eat leading white space */
214 for(i=0;i<4086;i++) {
215 if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
216 break;
217 /* if whitespace - skip past it */
219 if (strncasecmp("username",line_buf+i,8) == 0) {
220 temp_val = strchr(line_buf + i,'=');
221 if(temp_val) {
222 /* go past equals sign */
223 temp_val++;
224 for(length = 0;length<4087;length++) {
225 if ((temp_val[length] == '\n')
226 || (temp_val[length] == '\0')) {
227 temp_val[length] = '\0';
228 break;
231 if(length > 4086) {
232 printf("mount.cifs failed due to malformed username in credentials file");
233 memset(line_buf,0,4096);
234 exit(EX_USAGE);
235 } else {
236 got_user = 1;
237 user_name = (char *)calloc(1 + length,1);
238 /* BB adding free of user_name string before exit,
239 not really necessary but would be cleaner */
240 strlcpy(user_name,temp_val, length+1);
243 } else if (strncasecmp("password",line_buf+i,8) == 0) {
244 temp_val = strchr(line_buf+i,'=');
245 if(temp_val) {
246 /* go past equals sign */
247 temp_val++;
248 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
249 if ((temp_val[length] == '\n')
250 || (temp_val[length] == '\0')) {
251 temp_val[length] = '\0';
252 break;
255 if(length > MOUNT_PASSWD_SIZE) {
256 printf("mount.cifs failed: password in credentials file too long\n");
257 memset(line_buf,0, 4096);
258 exit(EX_USAGE);
259 } else {
260 if(mountpassword == NULL) {
261 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
262 } else
263 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
264 if(mountpassword) {
265 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
266 got_password = 1;
270 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
271 temp_val = strchr(line_buf+i,'=');
272 if(temp_val) {
273 /* go past equals sign */
274 temp_val++;
275 if(verboseflag)
276 printf("\nDomain %s\n",temp_val);
277 for(length = 0;length<DOMAIN_SIZE+1;length++) {
278 if ((temp_val[length] == '\n')
279 || (temp_val[length] == '\0')) {
280 temp_val[length] = '\0';
281 break;
284 if(length > DOMAIN_SIZE) {
285 printf("mount.cifs failed: domain in credentials file too long\n");
286 exit(EX_USAGE);
287 } else {
288 if(domain_name == NULL) {
289 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
290 } else
291 memset(domain_name,0,DOMAIN_SIZE);
292 if(domain_name) {
293 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
294 got_domain = 1;
301 fclose(fs);
302 SAFE_FREE(line_buf);
303 return 0;
306 static int get_password_from_file(int file_descript, char * filename)
308 int rc = 0;
309 int i;
310 char c;
312 if(mountpassword == NULL)
313 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
314 else
315 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
317 if (mountpassword == NULL) {
318 printf("malloc failed\n");
319 exit(EX_SYSERR);
322 if(filename != NULL) {
323 file_descript = open(filename, O_RDONLY);
324 if(file_descript < 0) {
325 printf("mount.cifs failed. %s attempting to open password file %s\n",
326 strerror(errno),filename);
327 exit(EX_SYSERR);
330 /* else file already open and fd provided */
332 for(i=0;i<MOUNT_PASSWD_SIZE;i++) {
333 rc = read(file_descript,&c,1);
334 if(rc < 0) {
335 printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
336 if(filename != NULL)
337 close(file_descript);
338 exit(EX_SYSERR);
339 } else if(rc == 0) {
340 if(mountpassword[0] == 0) {
341 if(verboseflag)
342 printf("\nWarning: null password used since cifs password file empty");
344 break;
345 } else /* read valid character */ {
346 if((c == 0) || (c == '\n')) {
347 mountpassword[i] = '\0';
348 break;
349 } else
350 mountpassword[i] = c;
353 if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
354 printf("\nWarning: password longer than %d characters specified in cifs password file",
355 MOUNT_PASSWD_SIZE);
357 got_password = 1;
358 if(filename != NULL) {
359 close(file_descript);
362 return rc;
365 static int parse_options(char ** optionsp, int * filesys_flags)
367 const char * data;
368 char * percent_char = NULL;
369 char * value = NULL;
370 char * next_keyword = NULL;
371 char * out = NULL;
372 int out_len = 0;
373 int word_len;
374 int rc = 0;
375 char user[32];
376 char group[32];
378 if (!optionsp || !*optionsp)
379 return 1;
380 data = *optionsp;
382 if(verboseflag)
383 printf("parsing options: %s\n", data);
385 /* BB fixme check for separator override BB */
387 if (getuid()) {
388 got_uid = 1;
389 snprintf(user,sizeof(user),"%u",getuid());
390 got_gid = 1;
391 snprintf(group,sizeof(group),"%u",getgid());
394 /* while ((data = strsep(&options, ",")) != NULL) { */
395 while(data != NULL) {
396 /* check if ends with trailing comma */
397 if(*data == 0)
398 break;
400 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
401 /* data = next keyword */
402 /* value = next value ie stuff after equal sign */
404 next_keyword = strchr(data,','); /* BB handle sep= */
406 /* temporarily null terminate end of keyword=value pair */
407 if(next_keyword)
408 *next_keyword++ = 0;
410 /* temporarily null terminate keyword to make keyword and value distinct */
411 if ((value = strchr(data, '=')) != NULL) {
412 *value = '\0';
413 value++;
416 if (strncmp(data, "users",5) == 0) {
417 if(!value || !*value) {
418 goto nocopy;
420 } else if (strncmp(data, "user_xattr",10) == 0) {
421 /* do nothing - need to skip so not parsed as user name */
422 } else if (strncmp(data, "user", 4) == 0) {
424 if (!value || !*value) {
425 if(data[4] == '\0') {
426 if(verboseflag)
427 printf("\nskipping empty user mount parameter\n");
428 /* remove the parm since it would otherwise be confusing
429 to the kernel code which would think it was a real username */
430 goto nocopy;
431 } else {
432 printf("username specified with no parameter\n");
433 SAFE_FREE(out);
434 return 1; /* needs_arg; */
436 } else {
437 if (strnlen(value, 260) < 260) {
438 got_user=1;
439 percent_char = strchr(value,'%');
440 if(percent_char) {
441 *percent_char = ',';
442 if(mountpassword == NULL)
443 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
444 if(mountpassword) {
445 if(got_password)
446 printf("\nmount.cifs warning - password specified twice\n");
447 got_password = 1;
448 percent_char++;
449 strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
450 /* remove password from username */
451 while(*percent_char != 0) {
452 *percent_char = ',';
453 percent_char++;
457 /* this is only case in which the user
458 name buf is not malloc - so we have to
459 check for domain name embedded within
460 the user name here since the later
461 call to check_for_domain will not be
462 invoked */
463 domain_name = check_for_domain(&value);
464 } else {
465 printf("username too long\n");
466 SAFE_FREE(out);
467 return 1;
470 } else if (strncmp(data, "pass", 4) == 0) {
471 if (!value || !*value) {
472 if(got_password) {
473 printf("\npassword specified twice, ignoring second\n");
474 } else
475 got_password = 1;
476 } else if (strnlen(value, 17) < 17) {
477 if(got_password)
478 printf("\nmount.cifs warning - password specified twice\n");
479 got_password = 1;
480 } else {
481 printf("password too long\n");
482 SAFE_FREE(out);
483 return 1;
485 } else if (strncmp(data, "sec", 3) == 0) {
486 if (value) {
487 if (!strncmp(value, "none", 4) ||
488 !strncmp(value, "krb5", 4))
489 got_password = 1;
491 } else if (strncmp(data, "ip", 2) == 0) {
492 if (!value || !*value) {
493 printf("target ip address argument missing");
494 } else if (strnlen(value, MAX_ADDRESS_LEN) <= MAX_ADDRESS_LEN) {
495 if(verboseflag)
496 printf("ip address %s override specified\n",value);
497 got_ip = 1;
498 } else {
499 printf("ip address too long\n");
500 SAFE_FREE(out);
501 return 1;
503 } else if ((strncmp(data, "unc", 3) == 0)
504 || (strncmp(data, "target", 6) == 0)
505 || (strncmp(data, "path", 4) == 0)) {
506 if (!value || !*value) {
507 printf("invalid path to network resource\n");
508 SAFE_FREE(out);
509 return 1; /* needs_arg; */
510 } else if(strnlen(value,5) < 5) {
511 printf("UNC name too short");
514 if (strnlen(value, 300) < 300) {
515 got_unc = 1;
516 if (strncmp(value, "//", 2) == 0) {
517 if(got_unc)
518 printf("unc name specified twice, ignoring second\n");
519 else
520 got_unc = 1;
521 } else if (strncmp(value, "\\\\", 2) != 0) {
522 printf("UNC Path does not begin with // or \\\\ \n");
523 SAFE_FREE(out);
524 return 1;
525 } else {
526 if(got_unc)
527 printf("unc name specified twice, ignoring second\n");
528 else
529 got_unc = 1;
531 } else {
532 printf("CIFS: UNC name too long\n");
533 SAFE_FREE(out);
534 return 1;
536 } else if ((strncmp(data, "dom" /* domain */, 3) == 0)
537 || (strncmp(data, "workg", 5) == 0)) {
538 /* note this allows for synonyms of "domain"
539 such as "DOM" and "dom" and "workgroup"
540 and "WORKGRP" etc. */
541 if (!value || !*value) {
542 printf("CIFS: invalid domain name\n");
543 SAFE_FREE(out);
544 return 1; /* needs_arg; */
546 if (strnlen(value, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
547 got_domain = 1;
548 } else {
549 printf("domain name too long\n");
550 SAFE_FREE(out);
551 return 1;
553 } else if (strncmp(data, "cred", 4) == 0) {
554 if (value && *value) {
555 rc = open_cred_file(value);
556 if(rc) {
557 printf("error %d (%s) opening credential file %s\n",
558 rc, strerror(rc), value);
559 SAFE_FREE(out);
560 return 1;
562 } else {
563 printf("invalid credential file name specified\n");
564 SAFE_FREE(out);
565 return 1;
567 } else if (strncmp(data, "uid", 3) == 0) {
568 if (value && *value) {
569 got_uid = 1;
570 if (!isdigit(*value)) {
571 struct passwd *pw;
573 if (!(pw = getpwnam(value))) {
574 printf("bad user name \"%s\"\n", value);
575 exit(EX_USAGE);
577 snprintf(user, sizeof(user), "%u", pw->pw_uid);
578 } else {
579 strlcpy(user,value,sizeof(user));
582 goto nocopy;
583 } else if (strncmp(data, "gid", 3) == 0) {
584 if (value && *value) {
585 got_gid = 1;
586 if (!isdigit(*value)) {
587 struct group *gr;
589 if (!(gr = getgrnam(value))) {
590 printf("bad group name \"%s\"\n", value);
591 exit(EX_USAGE);
593 snprintf(group, sizeof(group), "%u", gr->gr_gid);
594 } else {
595 strlcpy(group,value,sizeof(group));
598 goto nocopy;
599 /* fmask and dmask synonyms for people used to smbfs syntax */
600 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
601 if (!value || !*value) {
602 printf ("Option '%s' requires a numerical argument\n", data);
603 SAFE_FREE(out);
604 return 1;
607 if (value[0] != '0') {
608 printf ("WARNING: '%s' not expressed in octal.\n", data);
611 if (strcmp (data, "fmask") == 0) {
612 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
613 data = "file_mode"; /* BB fix this */
615 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
616 if (!value || !*value) {
617 printf ("Option '%s' requires a numerical argument\n", data);
618 SAFE_FREE(out);
619 return 1;
622 if (value[0] != '0') {
623 printf ("WARNING: '%s' not expressed in octal.\n", data);
626 if (strcmp (data, "dmask") == 0) {
627 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
628 data = "dir_mode";
630 /* the following eight mount options should be
631 stripped out from what is passed into the kernel
632 since these eight options are best passed as the
633 mount flags rather than redundantly to the kernel
634 and could generate spurious warnings depending on the
635 level of the corresponding cifs vfs kernel code */
636 } else if (strncmp(data, "nosuid", 6) == 0) {
637 *filesys_flags |= MS_NOSUID;
638 } else if (strncmp(data, "suid", 4) == 0) {
639 *filesys_flags &= ~MS_NOSUID;
640 } else if (strncmp(data, "nodev", 5) == 0) {
641 *filesys_flags |= MS_NODEV;
642 } else if ((strncmp(data, "nobrl", 5) == 0) ||
643 (strncmp(data, "nolock", 6) == 0)) {
644 *filesys_flags &= ~MS_MANDLOCK;
645 } else if (strncmp(data, "dev", 3) == 0) {
646 *filesys_flags &= ~MS_NODEV;
647 } else if (strncmp(data, "noexec", 6) == 0) {
648 *filesys_flags |= MS_NOEXEC;
649 } else if (strncmp(data, "exec", 4) == 0) {
650 *filesys_flags &= ~MS_NOEXEC;
651 } else if (strncmp(data, "guest", 5) == 0) {
652 got_password=1;
653 } else if (strncmp(data, "ro", 2) == 0) {
654 *filesys_flags |= MS_RDONLY;
655 } else if (strncmp(data, "rw", 2) == 0) {
656 *filesys_flags &= ~MS_RDONLY;
657 } else if (strncmp(data, "remount", 7) == 0) {
658 *filesys_flags |= MS_REMOUNT;
659 } /* else if (strnicmp(data, "port", 4) == 0) {
660 if (value && *value) {
661 vol->port =
662 simple_strtoul(value, &value, 0);
664 } else if (strnicmp(data, "rsize", 5) == 0) {
665 if (value && *value) {
666 vol->rsize =
667 simple_strtoul(value, &value, 0);
669 } else if (strnicmp(data, "wsize", 5) == 0) {
670 if (value && *value) {
671 vol->wsize =
672 simple_strtoul(value, &value, 0);
674 } else if (strnicmp(data, "version", 3) == 0) {
675 } else {
676 printf("CIFS: Unknown mount option %s\n",data);
677 } */ /* nothing to do on those four mount options above.
678 Just pass to kernel and ignore them here */
680 /* Copy (possibly modified) option to out */
681 word_len = strlen(data);
682 if (value)
683 word_len += 1 + strlen(value);
685 out = (char *)realloc(out, out_len + word_len + 2);
686 if (out == NULL) {
687 perror("malloc");
688 exit(EX_SYSERR);
691 if (out_len) {
692 strlcat(out, ",", out_len + word_len + 2);
693 out_len++;
696 if (value)
697 snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
698 else
699 snprintf(out + out_len, word_len + 1, "%s", data);
700 out_len = strlen(out);
702 nocopy:
703 data = next_keyword;
706 /* special-case the uid and gid */
707 if (got_uid) {
708 word_len = strlen(user);
710 out = (char *)realloc(out, out_len + word_len + 6);
711 if (out == NULL) {
712 perror("malloc");
713 exit(EX_SYSERR);
716 if (out_len) {
717 strlcat(out, ",", out_len + word_len + 6);
718 out_len++;
720 snprintf(out + out_len, word_len + 5, "uid=%s", user);
721 out_len = strlen(out);
723 if (got_gid) {
724 word_len = strlen(group);
726 out = (char *)realloc(out, out_len + 1 + word_len + 6);
727 if (out == NULL) {
728 perror("malloc");
729 exit(EX_SYSERR);
732 if (out_len) {
733 strlcat(out, ",", out_len + word_len + 6);
734 out_len++;
736 snprintf(out + out_len, word_len + 5, "gid=%s", group);
737 out_len = strlen(out);
740 SAFE_FREE(*optionsp);
741 *optionsp = out;
742 return 0;
745 /* replace all (one or more) commas with double commas */
746 static void check_for_comma(char ** ppasswrd)
748 char *new_pass_buf;
749 char *pass;
750 int i,j;
751 int number_of_commas = 0;
752 int len;
754 if(ppasswrd == NULL)
755 return;
756 else
757 (pass = *ppasswrd);
759 len = strlen(pass);
761 for(i=0;i<len;i++) {
762 if(pass[i] == ',')
763 number_of_commas++;
766 if(number_of_commas == 0)
767 return;
768 if(number_of_commas > MOUNT_PASSWD_SIZE) {
769 /* would otherwise overflow the mount options buffer */
770 printf("\nInvalid password. Password contains too many commas.\n");
771 return;
774 new_pass_buf = (char *)malloc(len+number_of_commas+1);
775 if(new_pass_buf == NULL)
776 return;
778 for(i=0,j=0;i<len;i++,j++) {
779 new_pass_buf[j] = pass[i];
780 if(pass[i] == ',') {
781 j++;
782 new_pass_buf[j] = pass[i];
785 new_pass_buf[len+number_of_commas] = 0;
787 SAFE_FREE(*ppasswrd);
788 *ppasswrd = new_pass_buf;
790 return;
793 /* Usernames can not have backslash in them and we use
794 [BB check if usernames can have forward slash in them BB]
795 backslash as domain\user separator character
797 static char * check_for_domain(char **ppuser)
799 char * original_string;
800 char * usernm;
801 char * domainnm;
802 int original_len;
803 int len;
804 int i;
806 if(ppuser == NULL)
807 return NULL;
809 original_string = *ppuser;
811 if (original_string == NULL)
812 return NULL;
814 original_len = strlen(original_string);
816 usernm = strchr(*ppuser,'/');
817 if (usernm == NULL) {
818 usernm = strchr(*ppuser,'\\');
819 if (usernm == NULL)
820 return NULL;
823 if(got_domain) {
824 printf("Domain name specified twice. Username probably malformed\n");
825 return NULL;
828 usernm[0] = 0;
829 domainnm = *ppuser;
830 if (domainnm[0] != 0) {
831 got_domain = 1;
832 } else {
833 printf("null domain\n");
835 len = strlen(domainnm);
836 /* reset domainm to new buffer, and copy
837 domain name into it */
838 domainnm = (char *)malloc(len+1);
839 if(domainnm == NULL)
840 return NULL;
842 strlcpy(domainnm,*ppuser,len+1);
844 /* move_string(*ppuser, usernm+1) */
845 len = strlen(usernm+1);
847 if(len >= original_len) {
848 /* should not happen */
849 return domainnm;
852 for(i=0;i<original_len;i++) {
853 if(i<len)
854 original_string[i] = usernm[i+1];
855 else /* stuff with commas to remove last parm */
856 original_string[i] = ',';
859 /* BB add check for more than one slash?
860 strchr(*ppuser,'/');
861 strchr(*ppuser,'\\')
864 return domainnm;
867 /* replace all occurances of "from" in a string with "to" */
868 static void replace_char(char *string, char from, char to, int maxlen)
870 char *lastchar = string + maxlen;
871 while (string) {
872 string = strchr(string, from);
873 if (string) {
874 *string = to;
875 if (string >= lastchar)
876 return;
881 /* Note that caller frees the returned buffer if necessary */
882 static struct addrinfo *
883 parse_server(char ** punc_name)
885 char * unc_name = *punc_name;
886 int length = strnlen(unc_name, MAX_UNC_LEN);
887 char * share;
888 struct addrinfo *addrlist;
889 int rc;
891 if(length > (MAX_UNC_LEN - 1)) {
892 printf("mount error: UNC name too long");
893 return NULL;
895 if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
896 (strncasecmp("smb://", unc_name, 6) == 0)) {
897 printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n", unc_name);
898 return NULL;
901 if(length < 3) {
902 /* BB add code to find DFS root here */
903 printf("\nMounting the DFS root for domain not implemented yet\n");
904 return NULL;
905 } else {
906 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
907 /* check for nfs syntax ie server:share */
908 share = strchr(unc_name,':');
909 if(share) {
910 *punc_name = (char *)malloc(length+3);
911 if(*punc_name == NULL) {
912 /* put the original string back if
913 no memory left */
914 *punc_name = unc_name;
915 return NULL;
917 *share = '/';
918 strlcpy((*punc_name)+2,unc_name,length+1);
919 SAFE_FREE(unc_name);
920 unc_name = *punc_name;
921 unc_name[length+2] = 0;
922 goto continue_unc_parsing;
923 } else {
924 printf("mount error: improperly formatted UNC name.");
925 printf(" %s does not begin with \\\\ or //\n",unc_name);
926 return NULL;
928 } else {
929 continue_unc_parsing:
930 unc_name[0] = '/';
931 unc_name[1] = '/';
932 unc_name += 2;
934 /* allow for either delimiter between host and sharename */
935 if ((share = strpbrk(unc_name, "/\\"))) {
936 *share = 0; /* temporarily terminate the string */
937 share += 1;
938 if(got_ip == 0) {
939 rc = getaddrinfo(unc_name, NULL, NULL, &addrlist);
940 if (rc != 0) {
941 printf("mount error: could not resolve address for %s: %s\n",
942 unc_name, gai_strerror(rc));
943 addrlist = NULL;
946 *(share - 1) = '/'; /* put delimiter back */
948 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
949 if ((prefixpath = strpbrk(share, "/\\"))) {
950 *prefixpath = 0; /* permanently terminate the string */
951 if (!strlen(++prefixpath))
952 prefixpath = NULL; /* this needs to be done explicitly */
954 if(got_ip) {
955 if(verboseflag)
956 printf("ip address specified explicitly\n");
957 return NULL;
959 /* BB should we pass an alternate version of the share name as Unicode */
961 return addrlist;
962 } else {
963 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
964 printf("Mounting the DFS root for a particular server not implemented yet\n");
965 return NULL;
971 static struct option longopts[] = {
972 { "all", 0, NULL, 'a' },
973 { "help",0, NULL, 'h' },
974 { "move",0, NULL, 'm' },
975 { "bind",0, NULL, 'b' },
976 { "read-only", 0, NULL, 'r' },
977 { "ro", 0, NULL, 'r' },
978 { "verbose", 0, NULL, 'v' },
979 { "version", 0, NULL, 'V' },
980 { "read-write", 0, NULL, 'w' },
981 { "rw", 0, NULL, 'w' },
982 { "options", 1, NULL, 'o' },
983 { "type", 1, NULL, 't' },
984 { "rsize",1, NULL, 'R' },
985 { "wsize",1, NULL, 'W' },
986 { "uid", 1, NULL, '1'},
987 { "gid", 1, NULL, '2'},
988 { "user",1,NULL,'u'},
989 { "username",1,NULL,'u'},
990 { "dom",1,NULL,'d'},
991 { "domain",1,NULL,'d'},
992 { "password",1,NULL,'p'},
993 { "pass",1,NULL,'p'},
994 { "credentials",1,NULL,'c'},
995 { "port",1,NULL,'P'},
996 /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
997 { NULL, 0, NULL, 0 }
1000 /* convert a string to uppercase. return false if the string
1001 * wasn't ASCII. Return success on a NULL ptr */
1002 static int
1003 uppercase_string(char *string)
1005 if (!string)
1006 return 1;
1008 while (*string) {
1009 /* check for unicode */
1010 if ((unsigned char) string[0] & 0x80)
1011 return 0;
1012 *string = toupper((unsigned char) *string);
1013 string++;
1016 return 1;
1019 int main(int argc, char ** argv)
1021 int c;
1022 int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1023 char * orgoptions = NULL;
1024 char * share_name = NULL;
1025 const char * ipaddr = NULL;
1026 char * uuid = NULL;
1027 char * mountpoint = NULL;
1028 char * options = NULL;
1029 char * optionstail;
1030 char * resolved_path = NULL;
1031 char * temp;
1032 char * dev_name;
1033 int rc;
1034 int rsize = 0;
1035 int wsize = 0;
1036 int nomtab = 0;
1037 int uid = 0;
1038 int gid = 0;
1039 int optlen = 0;
1040 int orgoptlen = 0;
1041 size_t options_size = 0;
1042 size_t current_len;
1043 int retry = 0; /* set when we have to retry mount with uppercase */
1044 struct addrinfo *addrhead = NULL, *addr;
1045 struct stat statbuf;
1046 struct utsname sysinfo;
1047 struct mntent mountent;
1048 struct sockaddr_in *addr4;
1049 struct sockaddr_in6 *addr6;
1050 FILE * pmntfile;
1052 /* setlocale(LC_ALL, "");
1053 bindtextdomain(PACKAGE, LOCALEDIR);
1054 textdomain(PACKAGE); */
1056 if(argc && argv) {
1057 thisprogram = argv[0];
1058 } else {
1059 mount_cifs_usage();
1060 exit(EX_USAGE);
1063 if(thisprogram == NULL)
1064 thisprogram = "mount.cifs";
1066 uname(&sysinfo);
1067 /* BB add workstation name and domain and pass down */
1069 /* #ifdef _GNU_SOURCE
1070 printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1071 #endif */
1072 if(argc > 2) {
1073 dev_name = argv[1];
1074 share_name = strndup(argv[1], MAX_UNC_LEN);
1075 if (share_name == NULL) {
1076 fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1077 exit(EX_SYSERR);
1079 mountpoint = argv[2];
1080 } else {
1081 mount_cifs_usage();
1082 exit(EX_USAGE);
1085 /* add sharename in opts string as unc= parm */
1087 while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1088 longopts, NULL)) != -1) {
1089 switch (c) {
1090 /* No code to do the following options yet */
1091 /* case 'l':
1092 list_with_volumelabel = 1;
1093 break;
1094 case 'L':
1095 volumelabel = optarg;
1096 break; */
1097 /* case 'a':
1098 ++mount_all;
1099 break; */
1101 case '?':
1102 case 'h': /* help */
1103 mount_cifs_usage ();
1104 exit(EX_USAGE);
1105 case 'n':
1106 ++nomtab;
1107 break;
1108 case 'b':
1109 #ifdef MS_BIND
1110 flags |= MS_BIND;
1111 #else
1112 fprintf(stderr,
1113 "option 'b' (MS_BIND) not supported\n");
1114 #endif
1115 break;
1116 case 'm':
1117 #ifdef MS_MOVE
1118 flags |= MS_MOVE;
1119 #else
1120 fprintf(stderr,
1121 "option 'm' (MS_MOVE) not supported\n");
1122 #endif
1123 break;
1124 case 'o':
1125 orgoptions = strdup(optarg);
1126 break;
1127 case 'r': /* mount readonly */
1128 flags |= MS_RDONLY;
1129 break;
1130 case 'U':
1131 uuid = optarg;
1132 break;
1133 case 'v':
1134 ++verboseflag;
1135 break;
1136 case 'V':
1137 printf ("mount.cifs version: %s.%s%s\n",
1138 MOUNT_CIFS_VERSION_MAJOR,
1139 MOUNT_CIFS_VERSION_MINOR,
1140 MOUNT_CIFS_VENDOR_SUFFIX);
1141 exit (0);
1142 case 'w':
1143 flags &= ~MS_RDONLY;
1144 break;
1145 case 'R':
1146 rsize = atoi(optarg) ;
1147 break;
1148 case 'W':
1149 wsize = atoi(optarg);
1150 break;
1151 case '1':
1152 if (isdigit(*optarg)) {
1153 char *ep;
1155 uid = strtoul(optarg, &ep, 10);
1156 if (*ep) {
1157 printf("bad uid value \"%s\"\n", optarg);
1158 exit(EX_USAGE);
1160 } else {
1161 struct passwd *pw;
1163 if (!(pw = getpwnam(optarg))) {
1164 printf("bad user name \"%s\"\n", optarg);
1165 exit(EX_USAGE);
1167 uid = pw->pw_uid;
1168 endpwent();
1170 break;
1171 case '2':
1172 if (isdigit(*optarg)) {
1173 char *ep;
1175 gid = strtoul(optarg, &ep, 10);
1176 if (*ep) {
1177 printf("bad gid value \"%s\"\n", optarg);
1178 exit(EX_USAGE);
1180 } else {
1181 struct group *gr;
1183 if (!(gr = getgrnam(optarg))) {
1184 printf("bad user name \"%s\"\n", optarg);
1185 exit(EX_USAGE);
1187 gid = gr->gr_gid;
1188 endpwent();
1190 break;
1191 case 'u':
1192 got_user = 1;
1193 user_name = optarg;
1194 break;
1195 case 'd':
1196 domain_name = optarg; /* BB fix this - currently ignored */
1197 got_domain = 1;
1198 break;
1199 case 'p':
1200 if(mountpassword == NULL)
1201 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1202 if(mountpassword) {
1203 got_password = 1;
1204 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1206 break;
1207 case 'S':
1208 get_password_from_file(0 /* stdin */,NULL);
1209 break;
1210 case 't':
1211 break;
1212 default:
1213 printf("unknown mount option %c\n",c);
1214 mount_cifs_usage();
1215 exit(EX_USAGE);
1219 if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1220 mount_cifs_usage();
1221 exit(EX_USAGE);
1224 if (getenv("PASSWD")) {
1225 if(mountpassword == NULL)
1226 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1227 if(mountpassword) {
1228 strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1229 got_password = 1;
1231 } else if (getenv("PASSWD_FD")) {
1232 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1233 } else if (getenv("PASSWD_FILE")) {
1234 get_password_from_file(0, getenv("PASSWD_FILE"));
1237 if (orgoptions && parse_options(&orgoptions, &flags)) {
1238 rc = EX_USAGE;
1239 goto mount_exit;
1241 addrhead = addr = parse_server(&share_name);
1242 if((addrhead == NULL) && (got_ip == 0)) {
1243 printf("No ip address specified and hostname not found\n");
1244 rc = EX_USAGE;
1245 goto mount_exit;
1248 /* BB save off path and pop after mount returns? */
1249 resolved_path = (char *)malloc(PATH_MAX+1);
1250 if(resolved_path) {
1251 /* Note that if we can not canonicalize the name, we get
1252 another chance to see if it is valid when we chdir to it */
1253 if (realpath(mountpoint, resolved_path)) {
1254 mountpoint = resolved_path;
1257 if(chdir(mountpoint)) {
1258 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1259 rc = EX_USAGE;
1260 goto mount_exit;
1263 if(stat (".", &statbuf)) {
1264 printf("mount error: mount point %s does not exist\n",mountpoint);
1265 rc = EX_USAGE;
1266 goto mount_exit;
1269 if (S_ISDIR(statbuf.st_mode) == 0) {
1270 printf("mount error: mount point %s is not a directory\n",mountpoint);
1271 rc = EX_USAGE;
1272 goto mount_exit;
1275 if((getuid() != 0) && (geteuid() == 0)) {
1276 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1277 #ifndef CIFS_ALLOW_USR_SUID
1278 /* Do not allow user mounts to control suid flag
1279 for mount unless explicitly built that way */
1280 flags |= MS_NOSUID | MS_NODEV;
1281 #endif
1282 } else {
1283 printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n");
1284 exit(EX_USAGE);
1288 if(got_user == 0) {
1289 /* Note that the password will not be retrieved from the
1290 USER env variable (ie user%password form) as there is
1291 already a PASSWD environment varaible */
1292 if (getenv("USER"))
1293 user_name = strdup(getenv("USER"));
1294 if (user_name == NULL)
1295 user_name = getusername();
1296 got_user = 1;
1299 if(got_password == 0) {
1300 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1301 no good replacement yet. */
1302 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1303 if (!tmp_pass || !mountpassword) {
1304 printf("Password not entered, exiting\n");
1305 exit(EX_USAGE);
1307 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1308 got_password = 1;
1310 /* FIXME launch daemon (handles dfs name resolution and credential change)
1311 remember to clear parms and overwrite password field before launching */
1312 if(orgoptions) {
1313 optlen = strlen(orgoptions);
1314 orgoptlen = optlen;
1315 } else
1316 optlen = 0;
1317 if(share_name)
1318 optlen += strlen(share_name) + 4;
1319 else {
1320 printf("No server share name specified\n");
1321 printf("\nMounting the DFS root for server not implemented yet\n");
1322 exit(EX_USAGE);
1324 if(user_name)
1325 optlen += strlen(user_name) + 6;
1326 optlen += MAX_ADDRESS_LEN + 4;
1327 if(mountpassword)
1328 optlen += strlen(mountpassword) + 6;
1329 mount_retry:
1330 SAFE_FREE(options);
1331 options_size = optlen + 10 + DOMAIN_SIZE;
1332 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 */);
1334 if(options == NULL) {
1335 printf("Could not allocate memory for mount options\n");
1336 exit(EX_SYSERR);
1339 strlcpy(options, "unc=", options_size);
1340 strlcat(options,share_name,options_size);
1341 /* scan backwards and reverse direction of slash */
1342 temp = strrchr(options, '/');
1343 if(temp > options + 6)
1344 *temp = '\\';
1345 if(user_name) {
1346 /* check for syntax like user=domain\user */
1347 if(got_domain == 0)
1348 domain_name = check_for_domain(&user_name);
1349 strlcat(options,",user=",options_size);
1350 strlcat(options,user_name,options_size);
1352 if(retry == 0) {
1353 if(domain_name) {
1354 /* extra length accounted for in option string above */
1355 strlcat(options,",domain=",options_size);
1356 strlcat(options,domain_name,options_size);
1359 if(mountpassword) {
1360 /* Commas have to be doubled, or else they will
1361 look like the parameter separator */
1362 /* if(sep is not set)*/
1363 if(retry == 0)
1364 check_for_comma(&mountpassword);
1365 strlcat(options,",pass=",options_size);
1366 strlcat(options,mountpassword,options_size);
1369 strlcat(options,",ver=",options_size);
1370 strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1372 if(orgoptions) {
1373 strlcat(options,",",options_size);
1374 strlcat(options,orgoptions,options_size);
1376 if(prefixpath) {
1377 strlcat(options,",prefixpath=",options_size);
1378 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1380 if(verboseflag)
1381 printf("\nmount.cifs kernel mount options %s \n",options);
1383 /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1384 replace_char(dev_name, '\\', '/', strlen(share_name));
1386 if (!got_ip && addr) {
1387 strlcat(options, ",ip=", options_size);
1388 current_len = strnlen(options, options_size);
1389 optionstail = options + current_len;
1390 switch (addr->ai_addr->sa_family) {
1391 case AF_INET6:
1392 addr6 = (struct sockaddr_in6 *) addr->ai_addr;
1393 ipaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, optionstail,
1394 options_size - current_len);
1395 break;
1396 case AF_INET:
1397 addr4 = (struct sockaddr_in *) addr->ai_addr;
1398 ipaddr = inet_ntop(AF_INET, &addr4->sin_addr, optionstail,
1399 options_size - current_len);
1400 break;
1403 /* if the address looks bogus, try the next one */
1404 if (!ipaddr) {
1405 addr = addr->ai_next;
1406 if (addr)
1407 goto mount_retry;
1408 rc = EX_SYSERR;
1409 goto mount_exit;
1413 if (mount(dev_name, mountpoint, "cifs", flags, options)) {
1414 switch (errno) {
1415 case ECONNREFUSED:
1416 case EHOSTUNREACH:
1417 if (addr) {
1418 addr = addr->ai_next;
1419 if (addr)
1420 goto mount_retry;
1422 break;
1423 case ENODEV:
1424 printf("mount error: cifs filesystem not supported by the system\n");
1425 break;
1426 case ENXIO:
1427 if(retry == 0) {
1428 retry = 1;
1429 if (uppercase_string(dev_name) &&
1430 uppercase_string(share_name) &&
1431 uppercase_string(prefixpath)) {
1432 printf("retrying with upper case share name\n");
1433 goto mount_retry;
1437 printf("mount error(%d): %s\n", errno, strerror(errno));
1438 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1439 rc = EX_FAIL;
1440 goto mount_exit;
1443 atexit(unlock_mtab);
1444 rc = lock_mtab();
1445 if (rc) {
1446 printf("cannot lock mtab");
1447 goto mount_exit;
1449 pmntfile = setmntent(MOUNTED, "a+");
1450 if (!pmntfile) {
1451 printf("could not update mount table\n");
1452 unlock_mtab();
1453 rc = EX_FILEIO;
1454 goto mount_exit;
1456 mountent.mnt_fsname = dev_name;
1457 mountent.mnt_dir = mountpoint;
1458 mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1459 mountent.mnt_opts = (char *)malloc(220);
1460 if(mountent.mnt_opts) {
1461 char * mount_user = getusername();
1462 memset(mountent.mnt_opts,0,200);
1463 if(flags & MS_RDONLY)
1464 strlcat(mountent.mnt_opts,"ro",220);
1465 else
1466 strlcat(mountent.mnt_opts,"rw",220);
1467 if(flags & MS_MANDLOCK)
1468 strlcat(mountent.mnt_opts,",mand",220);
1469 if(flags & MS_NOEXEC)
1470 strlcat(mountent.mnt_opts,",noexec",220);
1471 if(flags & MS_NOSUID)
1472 strlcat(mountent.mnt_opts,",nosuid",220);
1473 if(flags & MS_NODEV)
1474 strlcat(mountent.mnt_opts,",nodev",220);
1475 if(flags & MS_SYNCHRONOUS)
1476 strlcat(mountent.mnt_opts,",sync",220);
1477 if(mount_user) {
1478 if(getuid() != 0) {
1479 strlcat(mountent.mnt_opts,
1480 ",user=", 220);
1481 strlcat(mountent.mnt_opts,
1482 mount_user, 220);
1486 mountent.mnt_freq = 0;
1487 mountent.mnt_passno = 0;
1488 rc = addmntent(pmntfile,&mountent);
1489 endmntent(pmntfile);
1490 unlock_mtab();
1491 SAFE_FREE(mountent.mnt_opts);
1492 if (rc)
1493 rc = EX_FILEIO;
1494 mount_exit:
1495 if(mountpassword) {
1496 int len = strlen(mountpassword);
1497 memset(mountpassword,0,len);
1498 SAFE_FREE(mountpassword);
1501 if (addrhead)
1502 freeaddrinfo(addrhead);
1503 SAFE_FREE(options);
1504 SAFE_FREE(orgoptions);
1505 SAFE_FREE(resolved_path);
1506 SAFE_FREE(share_name);
1507 exit(rc);