Fix confusing error message -- bug 5252
[abartlet/samba.git/.git] / source3 / client / mount.cifs.c
1 /* 
2    Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2003,2005 Steve French  (sfrench@us.ibm.com)
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <sys/stat.h>
31 #include <sys/utsname.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <getopt.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <string.h>
38 #include <mntent.h>
39 #include <fcntl.h>
40 #include <limits.h>
41
42 #define MOUNT_CIFS_VERSION_MAJOR "1"
43 #define MOUNT_CIFS_VERSION_MINOR "11"
44
45 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
46  #ifdef _SAMBA_BUILD_
47   #include "include/version.h"
48   #ifdef SAMBA_VERSION_VENDOR_SUFFIX
49    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
50   #else
51    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
52   #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
53  #else
54    #define MOUNT_CIFS_VENDOR_SUFFIX ""
55  #endif /* _SAMBA_BUILD_ */
56 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
57
58 #ifndef MS_MOVE 
59 #define MS_MOVE 8192 
60 #endif 
61
62 #ifndef MS_BIND
63 #define MS_BIND 4096
64 #endif
65
66 #define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
67
68 const char *thisprogram;
69 int verboseflag = 0;
70 static int got_password = 0;
71 static int got_user = 0;
72 static int got_domain = 0;
73 static int got_ip = 0;
74 static int got_unc = 0;
75 static int got_uid = 0;
76 static int got_gid = 0;
77 static int free_share_name = 0;
78 static char * user_name = NULL;
79 static char * mountpassword = NULL;
80 char * domain_name = NULL;
81 char * prefixpath = NULL;
82
83 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
84  * don't link to libreplace so need them here. */
85
86 /* like strncpy but does not 0 fill the buffer and always null
87  *    terminates. bufsize is the size of the destination buffer */
88 size_t strlcpy(char *d, const char *s, size_t bufsize)
89 {
90         size_t len = strlen(s);
91         size_t ret = len;
92         if (bufsize <= 0) return 0;
93         if (len >= bufsize) len = bufsize-1;
94         memcpy(d, s, len);
95         d[len] = 0;
96         return ret;
97 }
98
99 /* like strncat but does not 0 fill the buffer and always null
100  *    terminates. bufsize is the length of the buffer, which should
101  *       be one more than the maximum resulting string length */
102 size_t strlcat(char *d, const char *s, size_t bufsize)
103 {
104         size_t len1 = strlen(d);
105         size_t len2 = strlen(s);
106         size_t ret = len1 + len2;
107
108         if (len1+len2 >= bufsize) {
109                 len2 = bufsize - (len1+1);
110         }
111         if (len2 > 0) {
112                 memcpy(d+len1, s, len2);
113                 d[len1+len2] = 0;
114         }
115         return ret;
116 }
117
118 /* BB finish BB
119
120         cifs_umount
121         open nofollow - avoid symlink exposure? 
122         get owner of dir see if matches self or if root
123         call system(umount argv) etc.
124                 
125 BB end finish BB */
126
127 static char * check_for_domain(char **);
128
129
130 static void mount_cifs_usage(void)
131 {
132         printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
133         printf("\nMount the remote target, specified as a UNC name,");
134         printf(" to a local directory.\n\nOptions:\n");
135         printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
136         printf("\nLess commonly used options:");
137         printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
138         printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
139         printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
140         printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
141         printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
142         printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
143         printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
144         printf("\n\nRarely used options:");
145         printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
146         printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
147         printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
148         printf("\n\tin6_addr");
149         printf("\n\nOptions are described in more detail in the manual page");
150         printf("\n\tman 8 mount.cifs\n");
151         printf("\nTo display the version number of the mount helper:");
152         printf("\n\t%s -V\n",thisprogram);
153
154         if(mountpassword) {
155                 memset(mountpassword,0,64);
156                 free(mountpassword);
157         }
158         exit(1);
159 }
160
161 /* caller frees username if necessary */
162 static char * getusername(void) {
163         char *username = NULL;
164         struct passwd *password = getpwuid(getuid());
165
166         if (password) {
167                 username = password->pw_name;
168         }
169         return username;
170 }
171
172 static char * parse_cifs_url(char * unc_name)
173 {
174         printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
175         return NULL;
176 }
177
178 static int open_cred_file(char * file_name)
179 {
180         char * line_buf;
181         char * temp_val;
182         FILE * fs;
183         int i, length;
184         fs = fopen(file_name,"r");
185         if(fs == NULL)
186                 return errno;
187         line_buf = (char *)malloc(4096);
188         if(line_buf == NULL) {
189                 fclose(fs);
190                 return -ENOMEM;
191         }
192
193         while(fgets(line_buf,4096,fs)) {
194                 /* parse line from credential file */
195
196                 /* eat leading white space */
197                 for(i=0;i<4086;i++) {
198                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
199                                 break;
200                         /* if whitespace - skip past it */
201                 }
202                 if (strncasecmp("username",line_buf+i,8) == 0) {
203                         temp_val = strchr(line_buf + i,'=');
204                         if(temp_val) {
205                                 /* go past equals sign */
206                                 temp_val++;
207                                 for(length = 0;length<4087;length++) {
208                                         if ((temp_val[length] == '\n')
209                                             || (temp_val[length] == '\0')) {
210                                                 break;
211                                         }
212                                 }
213                                 if(length > 4086) {
214                                         printf("mount.cifs failed due to malformed username in credentials file");
215                                         memset(line_buf,0,4096);
216                                         if(mountpassword) {
217                                                 memset(mountpassword,0,64);
218                                         }
219                                         exit(1);
220                                 } else {
221                                         got_user = 1;
222                                         user_name = (char *)calloc(1 + length,1);
223                                         /* BB adding free of user_name string before exit,
224                                                 not really necessary but would be cleaner */
225                                         strncpy(user_name,temp_val, length);
226                                 }
227                         }
228                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
229                         temp_val = strchr(line_buf+i,'=');
230                         if(temp_val) {
231                                 /* go past equals sign */
232                                 temp_val++;
233                                 for(length = 0;length<65;length++) {
234                                         if(temp_val[length] == '\n' || temp_val[length] == '\0')
235                                                 break;
236                                 }
237                                 if(length > 64) {
238                                         printf("mount.cifs failed: password in credentials file too long\n");
239                                         memset(line_buf,0, 4096);
240                                         if(mountpassword) {
241                                                 memset(mountpassword,0,64);
242                                         }
243                                         exit(1);
244                                 } else {
245                                         if(mountpassword == NULL) {
246                                                 mountpassword = (char *)calloc(65,1);
247                                         } else
248                                                 memset(mountpassword,0,64);
249                                         if(mountpassword) {
250                                                 strncpy(mountpassword,temp_val,length);
251                                                 got_password = 1;
252                                         }
253                                 }
254                         }
255                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
256                         temp_val = strchr(line_buf+i,'=');
257                         if(temp_val) {
258                                 /* go past equals sign */
259                                 temp_val++;
260                                 if(verboseflag)
261                                         printf("\nDomain %s\n",temp_val);
262                                 for(length = 0;length<65;length++) {
263                                         if(temp_val[length] == '\n' || temp_val[length] == '\0')
264                                                 break;
265                                 }
266                                 if(length > 64) {
267                                         printf("mount.cifs failed: domain in credentials file too long\n");
268                                         if(mountpassword) {
269                                                 memset(mountpassword,0,64);
270                                         }
271                                         exit(1);
272                                 } else {
273                                         if(domain_name == NULL) {
274                                                 domain_name = (char *)calloc(65,1);
275                                         } else
276                                                 memset(domain_name,0,64);
277                                         if(domain_name) {
278                                                 strncpy(domain_name,temp_val,length);
279                                                 got_domain = 1;
280                                         }
281                                 }
282                         }
283                 }
284
285         }
286         fclose(fs);
287         if(line_buf) {
288                 memset(line_buf,0,4096);
289                 free(line_buf);
290         }
291         return 0;
292 }
293
294 static int get_password_from_file(int file_descript, char * filename)
295 {
296         int rc = 0;
297         int i;
298         char c;
299
300         if(mountpassword == NULL)
301                 mountpassword = (char *)calloc(65,1);
302         else 
303                 memset(mountpassword, 0, 64);
304
305         if (mountpassword == NULL) {
306                 printf("malloc failed\n");
307                 exit(1);
308         }
309
310         if(filename != NULL) {
311                 file_descript = open(filename, O_RDONLY);
312                 if(file_descript < 0) {
313                         printf("mount.cifs failed. %s attempting to open password file %s\n",
314                                    strerror(errno),filename);
315                         exit(1);
316                 }
317         }
318         /* else file already open and fd provided */
319
320         for(i=0;i<64;i++) {
321                 rc = read(file_descript,&c,1);
322                 if(rc < 0) {
323                         printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
324                         memset(mountpassword,0,64);
325                         if(filename != NULL)
326                                 close(file_descript);
327                         exit(1);
328                 } else if(rc == 0) {
329                         if(mountpassword[0] == 0) {
330                                 if(verboseflag)
331                                         printf("\nWarning: null password used since cifs password file empty");
332                         }
333                         break;
334                 } else /* read valid character */ {
335                         if((c == 0) || (c == '\n')) {
336                                 break;
337                         } else 
338                                 mountpassword[i] = c;
339                 }
340         }
341         if((i == 64) && (verboseflag)) {
342                 printf("\nWarning: password longer than 64 characters specified in cifs password file");
343         }
344         got_password = 1;
345         if(filename != NULL) {
346                 close(file_descript);
347         }
348
349         return rc;
350 }
351
352 static int parse_options(char ** optionsp, int * filesys_flags)
353 {
354         const char * data;
355         char * percent_char = NULL;
356         char * value = NULL;
357         char * next_keyword = NULL;
358         char * out = NULL;
359         int out_len = 0;
360         int word_len;
361         int rc = 0;
362         char user[32];
363         char group[32];
364
365         if (!optionsp || !*optionsp)
366                 return 1;
367         data = *optionsp;
368
369         if(verboseflag)
370                 printf("parsing options: %s\n", data);
371
372         /* BB fixme check for separator override BB */
373
374         if (getuid()) {
375                 got_uid = 1;
376                 snprintf(user,sizeof(user),"%u",getuid());
377                 got_gid = 1;
378                 snprintf(group,sizeof(group),"%u",getgid());
379         }
380
381 /* while ((data = strsep(&options, ",")) != NULL) { */
382         while(data != NULL) {
383                 /*  check if ends with trailing comma */
384                 if(*data == 0)
385                         break;
386
387                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
388                 /* data  = next keyword */
389                 /* value = next value ie stuff after equal sign */
390
391                 next_keyword = strchr(data,','); /* BB handle sep= */
392         
393                 /* temporarily null terminate end of keyword=value pair */
394                 if(next_keyword)
395                         *next_keyword++ = 0;
396
397                 /* temporarily null terminate keyword to make keyword and value distinct */
398                 if ((value = strchr(data, '=')) != NULL) {
399                         *value = '\0';
400                         value++;
401                 }
402
403                 if (strncmp(data, "users",5) == 0) {
404                         if(!value || !*value) {
405                                 goto nocopy;
406                         }
407                 } else if (strncmp(data, "user_xattr",10) == 0) {
408                    /* do nothing - need to skip so not parsed as user name */
409                 } else if (strncmp(data, "user", 4) == 0) {
410
411                         if (!value || !*value) {
412                                 if(data[4] == '\0') {
413                                         if(verboseflag)
414                                                 printf("\nskipping empty user mount parameter\n");
415                                         /* remove the parm since it would otherwise be confusing
416                                         to the kernel code which would think it was a real username */
417                                         goto nocopy;
418                                 } else {
419                                         printf("username specified with no parameter\n");
420                                         return 1;       /* needs_arg; */
421                                 }
422                         } else {
423                                 if (strnlen(value, 260) < 260) {
424                                         got_user=1;
425                                         percent_char = strchr(value,'%');
426                                         if(percent_char) {
427                                                 *percent_char = ',';
428                                                 if(mountpassword == NULL)
429                                                         mountpassword = (char *)calloc(65,1);
430                                                 if(mountpassword) {
431                                                         if(got_password)
432                                                                 printf("\nmount.cifs warning - password specified twice\n");
433                                                         got_password = 1;
434                                                         percent_char++;
435                                                         strncpy(mountpassword, percent_char,64);
436                                                 /*  remove password from username */
437                                                         while(*percent_char != 0) {
438                                                                 *percent_char = ',';
439                                                                 percent_char++;
440                                                         }
441                                                 }
442                                         }
443                                         /* this is only case in which the user
444                                         name buf is not malloc - so we have to
445                                         check for domain name embedded within
446                                         the user name here since the later
447                                         call to check_for_domain will not be
448                                         invoked */
449                                         domain_name = check_for_domain(&value);
450                                 } else {
451                                         printf("username too long\n");
452                                         return 1;
453                                 }
454                         }
455                 } else if (strncmp(data, "pass", 4) == 0) {
456                         if (!value || !*value) {
457                                 if(got_password) {
458                                         printf("\npassword specified twice, ignoring second\n");
459                                 } else
460                                         got_password = 1;
461                         } else if (strnlen(value, 17) < 17) {
462                                 if(got_password)
463                                         printf("\nmount.cifs warning - password specified twice\n");
464                                 got_password = 1;
465                         } else {
466                                 printf("password too long\n");
467                                 return 1;
468                         }
469                 } else if (strncmp(data, "sec", 3) == 0) {
470                         if (value) {
471                                 if (!strcmp(value, "none"))
472                                         got_password = 1;
473                         }
474                 } else if (strncmp(data, "ip", 2) == 0) {
475                         if (!value || !*value) {
476                                 printf("target ip address argument missing");
477                         } else if (strnlen(value, 35) < 35) {
478                                 if(verboseflag)
479                                         printf("ip address %s override specified\n",value);
480                                 got_ip = 1;
481                         } else {
482                                 printf("ip address too long\n");
483                                 return 1;
484                         }
485                 } else if ((strncmp(data, "unc", 3) == 0)
486                    || (strncmp(data, "target", 6) == 0)
487                    || (strncmp(data, "path", 4) == 0)) {
488                         if (!value || !*value) {
489                                 printf("invalid path to network resource\n");
490                                 return 1;  /* needs_arg; */
491                         } else if(strnlen(value,5) < 5) {
492                                 printf("UNC name too short");
493                         }
494
495                         if (strnlen(value, 300) < 300) {
496                                 got_unc = 1;
497                                 if (strncmp(value, "//", 2) == 0) {
498                                         if(got_unc)
499                                                 printf("unc name specified twice, ignoring second\n");
500                                         else
501                                                 got_unc = 1;
502                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
503                                         printf("UNC Path does not begin with // or \\\\ \n");
504                                         return 1;
505                                 } else {
506                                         if(got_unc)
507                                                 printf("unc name specified twice, ignoring second\n");
508                                         else
509                                                 got_unc = 1;
510                                 }
511                         } else {
512                                 printf("CIFS: UNC name too long\n");
513                                 return 1;
514                         }
515                 } else if ((strncmp(data, "domain", 3) == 0)
516                            || (strncmp(data, "workgroup", 5) == 0)) {
517                         if (!value || !*value) {
518                                 printf("CIFS: invalid domain name\n");
519                                 return 1;       /* needs_arg; */
520                         }
521                         if (strnlen(value, 65) < 65) {
522                                 got_domain = 1;
523                         } else {
524                                 printf("domain name too long\n");
525                                 return 1;
526                         }
527                 } else if (strncmp(data, "cred", 4) == 0) {
528                         if (value && *value) {
529                                 rc = open_cred_file(value);
530                                 if(rc) {
531                                         printf("error %d opening credential file %s\n",rc, value);
532                                         return 1;
533                                 }
534                         } else {
535                                 printf("invalid credential file name specified\n");
536                                 return 1;
537                         }
538                 } else if (strncmp(data, "uid", 3) == 0) {
539                         if (value && *value) {
540                                 got_uid = 1;
541                                 if (!isdigit(*value)) {
542                                         struct passwd *pw;
543
544                                         if (!(pw = getpwnam(value))) {
545                                                 printf("bad user name \"%s\"\n", value);
546                                                 exit(1);
547                                         }
548                                         snprintf(user, sizeof(user), "%u", pw->pw_uid);
549                                 } else {
550                                         strlcpy(user,value,sizeof(user));
551                                 }
552                         }
553                         goto nocopy;
554                 } else if (strncmp(data, "gid", 3) == 0) {
555                         if (value && *value) {
556                                 got_gid = 1;
557                                 if (!isdigit(*value)) {
558                                         struct group *gr;
559
560                                         if (!(gr = getgrnam(value))) {
561                                                 printf("bad group name \"%s\"\n", value);
562                                                 exit(1);
563                                         }
564                                         snprintf(group, sizeof(group), "%u", gr->gr_gid);
565                                 } else {
566                                         strlcpy(group,value,sizeof(group));
567                                 }
568                         }
569                         goto nocopy;
570        /* fmask and dmask synonyms for people used to smbfs syntax */
571                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
572                         if (!value || !*value) {
573                                 printf ("Option '%s' requires a numerical argument\n", data);
574                                 return 1;
575                         }
576
577                         if (value[0] != '0') {
578                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
579                         }
580
581                         if (strcmp (data, "fmask") == 0) {
582                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
583                                 data = "file_mode"; /* BB fix this */
584                         }
585                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
586                         if (!value || !*value) {
587                                 printf ("Option '%s' requires a numerical argument\n", data);
588                                 return 1;
589                         }
590
591                         if (value[0] != '0') {
592                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
593                         }
594
595                         if (strcmp (data, "dmask") == 0) {
596                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
597                                 data = "dir_mode";
598                         }
599                         /* the following eight mount options should be
600                         stripped out from what is passed into the kernel
601                         since these eight options are best passed as the
602                         mount flags rather than redundantly to the kernel 
603                         and could generate spurious warnings depending on the
604                         level of the corresponding cifs vfs kernel code */
605                 } else if (strncmp(data, "nosuid", 6) == 0) {
606                         *filesys_flags |= MS_NOSUID;
607                 } else if (strncmp(data, "suid", 4) == 0) {
608                         *filesys_flags &= ~MS_NOSUID;
609                 } else if (strncmp(data, "nodev", 5) == 0) {
610                         *filesys_flags |= MS_NODEV;
611                 } else if ((strncmp(data, "nobrl", 5) == 0) || 
612                            (strncmp(data, "nolock", 6) == 0)) {
613                         *filesys_flags &= ~MS_MANDLOCK;
614                 } else if (strncmp(data, "dev", 3) == 0) {
615                         *filesys_flags &= ~MS_NODEV;
616                 } else if (strncmp(data, "noexec", 6) == 0) {
617                         *filesys_flags |= MS_NOEXEC;
618                 } else if (strncmp(data, "exec", 4) == 0) {
619                         *filesys_flags &= ~MS_NOEXEC;
620                 } else if (strncmp(data, "guest", 5) == 0) {
621                         got_password=1;
622                 } else if (strncmp(data, "ro", 2) == 0) {
623                         *filesys_flags |= MS_RDONLY;
624                 } else if (strncmp(data, "rw", 2) == 0) {
625                         *filesys_flags &= ~MS_RDONLY;
626                 } else if (strncmp(data, "remount", 7) == 0) {
627                         *filesys_flags |= MS_REMOUNT;
628                 } /* else if (strnicmp(data, "port", 4) == 0) {
629                         if (value && *value) {
630                                 vol->port =
631                                         simple_strtoul(value, &value, 0);
632                         }
633                 } else if (strnicmp(data, "rsize", 5) == 0) {
634                         if (value && *value) {
635                                 vol->rsize =
636                                         simple_strtoul(value, &value, 0);
637                         }
638                 } else if (strnicmp(data, "wsize", 5) == 0) {
639                         if (value && *value) {
640                                 vol->wsize =
641                                         simple_strtoul(value, &value, 0);
642                         }
643                 } else if (strnicmp(data, "version", 3) == 0) {
644                 } else {
645                         printf("CIFS: Unknown mount option %s\n",data);
646                 } */ /* nothing to do on those four mount options above.
647                         Just pass to kernel and ignore them here */
648
649                 /* Copy (possibly modified) option to out */
650                 word_len = strlen(data);
651                 if (value)
652                         word_len += 1 + strlen(value);
653
654                 out = (char *)realloc(out, out_len + word_len + 2);
655                 if (out == NULL) {
656                         perror("malloc");
657                         exit(1);
658                 }
659
660                 if (out_len) {
661                         strlcat(out, ",", out_len + word_len + 2);
662                         out_len++;
663                 }
664
665                 if (value)
666                         snprintf(out + out_len, word_len + 1, "%s=%s", data, value);
667                 else
668                         snprintf(out + out_len, word_len + 1, "%s", data);
669                 out_len = strlen(out);
670
671 nocopy:
672                 data = next_keyword;
673         }
674
675         /* special-case the uid and gid */
676         if (got_uid) {
677                 word_len = strlen(user);
678
679                 out = (char *)realloc(out, out_len + word_len + 6);
680                 if (out == NULL) {
681                         perror("malloc");
682                         exit(1);
683                 }
684
685                 if (out_len) {
686                         strlcat(out, ",", out_len + word_len + 6);
687                         out_len++;
688                 }
689                 snprintf(out + out_len, word_len + 5, "uid=%s", user);
690                 out_len = strlen(out);
691         }
692         if (got_gid) {
693                 word_len = strlen(group);
694
695                 out = (char *)realloc(out, out_len + 1 + word_len + 6);
696                 if (out == NULL) {
697                 perror("malloc");
698                         exit(1);
699                 }
700
701                 if (out_len) {
702                         strlcat(out, ",", out_len + word_len + 6);
703                         out_len++;
704                 }
705                 snprintf(out + out_len, word_len + 5, "gid=%s", group);
706                 out_len = strlen(out);
707         }
708
709         free(*optionsp);
710         *optionsp = out;
711         return 0;
712 }
713
714 /* replace all (one or more) commas with double commas */
715 static void check_for_comma(char ** ppasswrd)
716 {
717         char *new_pass_buf;
718         char *pass;
719         int i,j;
720         int number_of_commas = 0;
721         int len;
722
723         if(ppasswrd == NULL)
724                 return;
725         else 
726                 (pass = *ppasswrd);
727
728         len = strlen(pass);
729
730         for(i=0;i<len;i++)  {
731                 if(pass[i] == ',')
732                         number_of_commas++;
733         }
734
735         if(number_of_commas == 0)
736                 return;
737         if(number_of_commas > 64) {
738                 /* would otherwise overflow the mount options buffer */
739                 printf("\nInvalid password. Password contains too many commas.\n");
740                 return;
741         }
742
743         new_pass_buf = (char *)malloc(len+number_of_commas+1);
744         if(new_pass_buf == NULL)
745                 return;
746
747         for(i=0,j=0;i<len;i++,j++) {
748                 new_pass_buf[j] = pass[i];
749                 if(pass[i] == ',') {
750                         j++;
751                         new_pass_buf[j] = pass[i];
752                 }
753         }
754         new_pass_buf[len+number_of_commas] = 0;
755
756         free(*ppasswrd);
757         *ppasswrd = new_pass_buf;
758         
759         return;
760 }
761
762 /* Usernames can not have backslash in them and we use
763    [BB check if usernames can have forward slash in them BB] 
764    backslash as domain\user separator character
765 */
766 static char * check_for_domain(char **ppuser)
767 {
768         char * original_string;
769         char * usernm;
770         char * domainnm;
771         int    original_len;
772         int    len;
773         int    i;
774
775         if(ppuser == NULL)
776                 return NULL;
777
778         original_string = *ppuser;
779
780         if (original_string == NULL)
781                 return NULL;
782         
783         original_len = strlen(original_string);
784
785         usernm = strchr(*ppuser,'/');
786         if (usernm == NULL) {
787                 usernm = strchr(*ppuser,'\\');
788                 if (usernm == NULL)
789                         return NULL;
790         }
791
792         if(got_domain) {
793                 printf("Domain name specified twice. Username probably malformed\n");
794                 return NULL;
795         }
796
797         usernm[0] = 0;
798         domainnm = *ppuser;
799         if (domainnm[0] != 0) {
800                 got_domain = 1;
801         } else {
802                 printf("null domain\n");
803         }
804         len = strlen(domainnm);
805         /* reset domainm to new buffer, and copy
806         domain name into it */
807         domainnm = (char *)malloc(len+1);
808         if(domainnm == NULL)
809                 return NULL;
810
811         strlcpy(domainnm,*ppuser,len+1);
812
813 /*      move_string(*ppuser, usernm+1) */
814         len = strlen(usernm+1);
815
816         if(len >= original_len) {
817                 /* should not happen */
818                 return domainnm;
819         }
820
821         for(i=0;i<original_len;i++) {
822                 if(i<len)
823                         original_string[i] = usernm[i+1];
824                 else /* stuff with commas to remove last parm */
825                         original_string[i] = ',';
826         }
827
828         /* BB add check for more than one slash? 
829           strchr(*ppuser,'/');
830           strchr(*ppuser,'\\') 
831         */
832         
833         return domainnm;
834 }
835
836 /* Note that caller frees the returned buffer if necessary */
837 static char * parse_server(char ** punc_name)
838 {
839         char * unc_name = *punc_name;
840         int length = strnlen(unc_name,1024);
841         char * share;
842         char * ipaddress_string = NULL;
843         struct hostent * host_entry = NULL;
844         struct in_addr server_ipaddr;
845
846         if(length > 1023) {
847                 printf("mount error: UNC name too long");
848                 return NULL;
849         }
850         if (strncasecmp("cifs://",unc_name,7) == 0)
851                 return parse_cifs_url(unc_name+7);
852         if (strncasecmp("smb://",unc_name,6) == 0) {
853                 return parse_cifs_url(unc_name+6);
854         }
855
856         if(length < 3) {
857                 /* BB add code to find DFS root here */
858                 printf("\nMounting the DFS root for domain not implemented yet\n");
859                 return NULL;
860         } else {
861                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
862                         /* check for nfs syntax ie server:share */
863                         share = strchr(unc_name,':');
864                         if(share) {
865                                 free_share_name = 1;
866                                 *punc_name = (char *)malloc(length+3);
867                                 if(*punc_name == NULL) {
868                                         /* put the original string back  if 
869                                            no memory left */
870                                         *punc_name = unc_name;
871                                         return NULL;
872                                 }
873                                         
874                                 *share = '/';
875                                 strncpy((*punc_name)+2,unc_name,length);
876                                 unc_name = *punc_name;
877                                 unc_name[length+2] = 0;
878                                 goto continue_unc_parsing;
879                         } else {
880                                 printf("mount error: improperly formatted UNC name.");
881                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
882                                 return NULL;
883                         }
884                 } else {
885 continue_unc_parsing:
886                         unc_name[0] = '/';
887                         unc_name[1] = '/';
888                         unc_name += 2;
889                         if ((share = strchr(unc_name, '/')) || 
890                                 (share = strchr(unc_name,'\\'))) {
891                                 *share = 0;  /* temporarily terminate the string */
892                                 share += 1;
893                                 if(got_ip == 0) {
894                                         host_entry = gethostbyname(unc_name);
895                                 }
896                                 *(share - 1) = '/'; /* put the slash back */
897                                 if ((prefixpath = strchr(share, '/'))) {
898                                         *prefixpath = 0;  /* permanently terminate the string */
899                                         if (!strlen(++prefixpath))
900                                                 prefixpath = NULL; /* this needs to be done explicitly */
901                                 }
902                                 if(got_ip) {
903                                         if(verboseflag)
904                                                 printf("ip address specified explicitly\n");
905                                         return NULL;
906                                 }
907                                 if(host_entry == NULL) {
908                                         printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
909                                         return NULL;
910                                 } else {
911                                         /* BB should we pass an alternate version of the share name as Unicode */
912                                         /* BB what about ipv6? BB */
913                                         /* BB add retries with alternate servers in list */
914
915                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
916
917                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
918                                         if(ipaddress_string == NULL) {
919                                                 printf("mount error: could not get valid ip address for target server\n");
920                                                 return NULL;
921                                         }
922                                         return ipaddress_string; 
923                                 }
924                         } else {
925                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
926                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
927                                 return NULL;
928                         }
929                 }
930         }
931 }
932
933 static struct option longopts[] = {
934         { "all", 0, NULL, 'a' },
935         { "help",0, NULL, 'h' },
936         { "move",0, NULL, 'm' },
937         { "bind",0, NULL, 'b' },
938         { "read-only", 0, NULL, 'r' },
939         { "ro", 0, NULL, 'r' },
940         { "verbose", 0, NULL, 'v' },
941         { "version", 0, NULL, 'V' },
942         { "read-write", 0, NULL, 'w' },
943         { "rw", 0, NULL, 'w' },
944         { "options", 1, NULL, 'o' },
945         { "type", 1, NULL, 't' },
946         { "rsize",1, NULL, 'R' },
947         { "wsize",1, NULL, 'W' },
948         { "uid", 1, NULL, '1'},
949         { "gid", 1, NULL, '2'},
950         { "user",1,NULL,'u'},
951         { "username",1,NULL,'u'},
952         { "dom",1,NULL,'d'},
953         { "domain",1,NULL,'d'},
954         { "password",1,NULL,'p'},
955         { "pass",1,NULL,'p'},
956         { "credentials",1,NULL,'c'},
957         { "port",1,NULL,'P'},
958         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
959         { NULL, 0, NULL, 0 }
960 };
961
962 int main(int argc, char ** argv)
963 {
964         int c;
965         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
966         char * orgoptions = NULL;
967         char * share_name = NULL;
968         char * ipaddr = NULL;
969         char * uuid = NULL;
970         char * mountpoint = NULL;
971         char * options = NULL;
972         char * resolved_path = NULL;
973         char * temp;
974         int rc;
975         int rsize = 0;
976         int wsize = 0;
977         int nomtab = 0;
978         int uid = 0;
979         int gid = 0;
980         int optlen = 0;
981         int orgoptlen = 0;
982         size_t options_size = 0;
983         int retry = 0; /* set when we have to retry mount with uppercase */
984         struct stat statbuf;
985         struct utsname sysinfo;
986         struct mntent mountent;
987         FILE * pmntfile;
988
989         /* setlocale(LC_ALL, "");
990         bindtextdomain(PACKAGE, LOCALEDIR);
991         textdomain(PACKAGE); */
992
993         if(argc && argv) {
994                 thisprogram = argv[0];
995         } else {
996                 mount_cifs_usage();
997                 exit(1);
998         }
999
1000         if(thisprogram == NULL)
1001                 thisprogram = "mount.cifs";
1002
1003         uname(&sysinfo);
1004         /* BB add workstation name and domain and pass down */
1005
1006 /* #ifdef _GNU_SOURCE
1007         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1008 #endif */
1009         if(argc > 2) {
1010                 share_name = argv[1];
1011                 mountpoint = argv[2];
1012         }
1013
1014         /* add sharename in opts string as unc= parm */
1015
1016         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1017                          longopts, NULL)) != -1) {
1018                 switch (c) {
1019 /* No code to do the following  options yet */
1020 /*      case 'l':
1021                 list_with_volumelabel = 1;
1022                 break;
1023         case 'L':
1024                 volumelabel = optarg;
1025                 break; */
1026 /*      case 'a':              
1027                 ++mount_all;
1028                 break; */
1029
1030                 case '?':
1031                 case 'h':        /* help */
1032                         mount_cifs_usage ();
1033                         exit(1);
1034                 case 'n':
1035                     ++nomtab;
1036                     break;
1037                 case 'b':
1038 #ifdef MS_BIND
1039                         flags |= MS_BIND;
1040 #else
1041                         fprintf(stderr,
1042                                 "option 'b' (MS_BIND) not supported\n");
1043 #endif
1044                         break;
1045                 case 'm':
1046 #ifdef MS_MOVE                
1047                         flags |= MS_MOVE;
1048 #else
1049                         fprintf(stderr,
1050                                 "option 'm' (MS_MOVE) not supported\n");
1051 #endif
1052                         break;
1053                 case 'o':
1054                         orgoptions = strdup(optarg);
1055                     break;
1056                 case 'r':  /* mount readonly */
1057                         flags |= MS_RDONLY;
1058                         break;
1059                 case 'U':
1060                         uuid = optarg;
1061                         break;
1062                 case 'v':
1063                         ++verboseflag;
1064                         break;
1065                 case 'V':          
1066                         printf ("mount.cifs version: %s.%s%s\n",
1067                         MOUNT_CIFS_VERSION_MAJOR,
1068                         MOUNT_CIFS_VERSION_MINOR,
1069                         MOUNT_CIFS_VENDOR_SUFFIX);
1070                         if(mountpassword) {
1071                                 memset(mountpassword,0,64);
1072                         }
1073                         exit (0);
1074                 case 'w':
1075                         flags &= ~MS_RDONLY;
1076                         break;
1077                 case 'R':
1078                         rsize = atoi(optarg) ;
1079                         break;
1080                 case 'W':
1081                         wsize = atoi(optarg);
1082                         break;
1083                 case '1':
1084                         if (isdigit(*optarg)) {
1085                                 char *ep;
1086
1087                                 uid = strtoul(optarg, &ep, 10);
1088                                 if (*ep) {
1089                                         printf("bad uid value \"%s\"\n", optarg);
1090                                         exit(1);
1091                                 }
1092                         } else {
1093                                 struct passwd *pw;
1094
1095                                 if (!(pw = getpwnam(optarg))) {
1096                                         printf("bad user name \"%s\"\n", optarg);
1097                                         exit(1);
1098                                 }
1099                                 uid = pw->pw_uid;
1100                                 endpwent();
1101                         }
1102                         break;
1103                 case '2':
1104                         if (isdigit(*optarg)) {
1105                                 char *ep;
1106
1107                                 gid = strtoul(optarg, &ep, 10);
1108                                 if (*ep) {
1109                                         printf("bad gid value \"%s\"\n", optarg);
1110                                         exit(1);
1111                                 }
1112                         } else {
1113                                 struct group *gr;
1114
1115                                 if (!(gr = getgrnam(optarg))) {
1116                                         printf("bad user name \"%s\"\n", optarg);
1117                                         exit(1);
1118                                 }
1119                                 gid = gr->gr_gid;
1120                                 endpwent();
1121                         }
1122                         break;
1123                 case 'u':
1124                         got_user = 1;
1125                         user_name = optarg;
1126                         break;
1127                 case 'd':
1128                         domain_name = optarg; /* BB fix this - currently ignored */
1129                         got_domain = 1;
1130                         break;
1131                 case 'p':
1132                         if(mountpassword == NULL)
1133                                 mountpassword = (char *)calloc(65,1);
1134                         if(mountpassword) {
1135                                 got_password = 1;
1136                                 strncpy(mountpassword,optarg,64);
1137                         }
1138                         break;
1139                 case 'S':
1140                         get_password_from_file(0 /* stdin */,NULL);
1141                         break;
1142                 case 't':
1143                         break;
1144                 default:
1145                         printf("unknown mount option %c\n",c);
1146                         mount_cifs_usage();
1147                         exit(1);
1148                 }
1149         }
1150
1151         if((argc < 3) || (share_name == NULL) || (mountpoint == NULL)) {
1152                 mount_cifs_usage();
1153                 exit(1);
1154         }
1155
1156         if (getenv("PASSWD")) {
1157                 if(mountpassword == NULL)
1158                         mountpassword = (char *)calloc(65,1);
1159                 if(mountpassword) {
1160                         strncpy(mountpassword,getenv("PASSWD"),64);
1161                         got_password = 1;
1162                 }
1163         } else if (getenv("PASSWD_FD")) {
1164                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1165         } else if (getenv("PASSWD_FILE")) {
1166                 get_password_from_file(0, getenv("PASSWD_FILE"));
1167         }
1168
1169         if (orgoptions && parse_options(&orgoptions, &flags)) {
1170                 rc = -1;
1171                 goto mount_exit;
1172         }
1173         ipaddr = parse_server(&share_name);
1174         if((ipaddr == NULL) && (got_ip == 0)) {
1175                 printf("No ip address specified and hostname not found\n");
1176                 rc = -1;
1177                 goto mount_exit;
1178         }
1179         
1180         /* BB save off path and pop after mount returns? */
1181         resolved_path = (char *)malloc(PATH_MAX+1);
1182         if(resolved_path) {
1183                 /* Note that if we can not canonicalize the name, we get
1184                 another chance to see if it is valid when we chdir to it */
1185                 if (realpath(mountpoint, resolved_path)) {
1186                         mountpoint = resolved_path; 
1187                 }
1188         }
1189         if(chdir(mountpoint)) {
1190                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1191                 rc = -1;
1192                 goto mount_exit;
1193         }
1194
1195         if(stat (".", &statbuf)) {
1196                 printf("mount error: mount point %s does not exist\n",mountpoint);
1197                 rc = -1;
1198                 goto mount_exit;
1199         }
1200
1201         if (S_ISDIR(statbuf.st_mode) == 0) {
1202                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1203                 rc = -1;
1204                 goto mount_exit;
1205         }
1206
1207         if((getuid() != 0) && (geteuid() == 0)) {
1208                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1209 #ifndef CIFS_ALLOW_USR_SUID
1210                         /* Do not allow user mounts to control suid flag
1211                         for mount unless explicitly built that way */
1212                         flags |= MS_NOSUID | MS_NODEV;
1213 #endif                                          
1214                 } else {
1215                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1216                         return -1;
1217                 }
1218         }
1219
1220         if(got_user == 0) {
1221                 user_name = getusername();
1222                 got_user = 1;
1223         }
1224        
1225         if(got_password == 0) {
1226                 mountpassword = getpass("Password: "); /* BB obsolete */
1227                 got_password = 1;
1228         }
1229         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1230            remember to clear parms and overwrite password field before launching */
1231 mount_retry:
1232         if(orgoptions) {
1233                 optlen = strlen(orgoptions);
1234                 orgoptlen = optlen;
1235         } else
1236                 optlen = 0;
1237         if(share_name)
1238                 optlen += strlen(share_name) + 4;
1239         else {
1240                 printf("No server share name specified\n");
1241                 printf("\nMounting the DFS root for server not implemented yet\n");
1242                 exit(1);
1243         }
1244         if(user_name)
1245                 optlen += strlen(user_name) + 6;
1246         if(ipaddr)
1247                 optlen += strlen(ipaddr) + 4;
1248         if(mountpassword)
1249                 optlen += strlen(mountpassword) + 6;
1250         if(options)
1251                 free(options);
1252         options_size = optlen + 10 + 64;
1253         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 */);
1254
1255         if(options == NULL) {
1256                 printf("Could not allocate memory for mount options\n");
1257                 return -1;
1258         }
1259
1260         options[0] = 0;
1261         strlcpy(options,"unc=",options_size);
1262         strlcat(options,share_name,options_size);
1263         /* scan backwards and reverse direction of slash */
1264         temp = strrchr(options, '/');
1265         if(temp > options + 6)
1266                 *temp = '\\';
1267         if(ipaddr) {
1268                 strlcat(options,",ip=",options_size);
1269                 strlcat(options,ipaddr,options_size);
1270         }
1271
1272         if(user_name) {
1273                 /* check for syntax like user=domain\user */
1274                 if(got_domain == 0)
1275                         domain_name = check_for_domain(&user_name);
1276                 strlcat(options,",user=",options_size);
1277                 strlcat(options,user_name,options_size);
1278         }
1279         if(retry == 0) {
1280                 if(domain_name) {
1281                         /* extra length accounted for in option string above */
1282                         strlcat(options,",domain=",options_size);
1283                         strlcat(options,domain_name,options_size);
1284                 }
1285         }
1286         if(mountpassword) {
1287                 /* Commas have to be doubled, or else they will
1288                 look like the parameter separator */
1289 /*              if(sep is not set)*/
1290                 if(retry == 0)
1291                         check_for_comma(&mountpassword);
1292                 strlcat(options,",pass=",options_size);
1293                 strlcat(options,mountpassword,options_size);
1294         }
1295
1296         strlcat(options,",ver=",options_size);
1297         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1298
1299         if(orgoptions) {
1300                 strlcat(options,",",options_size);
1301                 strlcat(options,orgoptions,options_size);
1302         }
1303         if(prefixpath) {
1304                 strlcat(options,",prefixpath=",options_size);
1305                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1306         }
1307         if(verboseflag)
1308                 printf("\nmount.cifs kernel mount options %s \n",options);
1309         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1310         /* remember to kill daemon on error */
1311                 char * tmp;
1312
1313                 switch (errno) {
1314                 case 0:
1315                         printf("mount failed but no error number set\n");
1316                         break;
1317                 case ENODEV:
1318                         printf("mount error: cifs filesystem not supported by the system\n");
1319                         break;
1320                 case ENXIO:
1321                         if(retry == 0) {
1322                                 retry = 1;
1323                                 tmp = share_name;
1324                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1325                                         *tmp = toupper((unsigned char)*tmp);
1326                                         tmp++;
1327                                 }
1328                                 if(!*tmp) {
1329                                         printf("retrying with upper case share name\n");
1330                                         goto mount_retry;
1331                                 }
1332                         }
1333                 default:
1334                         printf("mount error %d = %s\n",errno,strerror(errno));
1335                 }
1336                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1337                 rc = -1;
1338                 goto mount_exit;
1339         } else {
1340                 pmntfile = setmntent(MOUNTED, "a+");
1341                 if(pmntfile) {
1342                         mountent.mnt_fsname = share_name;
1343                         mountent.mnt_dir = mountpoint;
1344                         mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1345                         mountent.mnt_opts = (char *)malloc(220);
1346                         if(mountent.mnt_opts) {
1347                                 char * mount_user = getusername();
1348                                 memset(mountent.mnt_opts,0,200);
1349                                 if(flags & MS_RDONLY)
1350                                         strlcat(mountent.mnt_opts,"ro",220);
1351                                 else
1352                                         strlcat(mountent.mnt_opts,"rw",220);
1353                                 if(flags & MS_MANDLOCK)
1354                                         strlcat(mountent.mnt_opts,",mand",220);
1355                                 if(flags & MS_NOEXEC)
1356                                         strlcat(mountent.mnt_opts,",noexec",220);
1357                                 if(flags & MS_NOSUID)
1358                                         strlcat(mountent.mnt_opts,",nosuid",220);
1359                                 if(flags & MS_NODEV)
1360                                         strlcat(mountent.mnt_opts,",nodev",220);
1361                                 if(flags & MS_SYNCHRONOUS)
1362                                         strlcat(mountent.mnt_opts,",synch",220);
1363                                 if(mount_user) {
1364                                         if(getuid() != 0) {
1365                                                 strlcat(mountent.mnt_opts,",user=",220);
1366                                                 strlcat(mountent.mnt_opts,mount_user,220);
1367                                         }
1368                                         /* free(mount_user); do not free static mem */
1369                                 }
1370                         }
1371                         mountent.mnt_freq = 0;
1372                         mountent.mnt_passno = 0;
1373                         rc = addmntent(pmntfile,&mountent);
1374                         endmntent(pmntfile);
1375                         if(mountent.mnt_opts)
1376                                 free(mountent.mnt_opts);
1377                 } else {
1378                     printf("could not update mount table\n");
1379                 }
1380         }
1381         rc = 0;
1382 mount_exit:
1383         if(mountpassword) {
1384                 int len = strlen(mountpassword);
1385                 memset(mountpassword,0,len);
1386                 free(mountpassword);
1387         }
1388
1389         if(options) {
1390                 memset(options,0,optlen);
1391                 free(options);
1392         }
1393
1394         if(orgoptions) {
1395                 memset(orgoptions,0,orgoptlen);
1396                 free(orgoptions);
1397         }
1398         if(resolved_path) {
1399                 free(resolved_path);
1400         }
1401
1402         if(free_share_name) {
1403                 free(share_name);
1404                 }
1405         return rc;
1406 }