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