Fix bug #5434 found by Ted Percival <ted@midg3t.net>.
[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,2008 Steve French  (sfrench@us.ibm.com)
4    Copyright (C) 2008 Jeremy Allison (jra@samba.org)
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/mount.h>
31 #include <sys/stat.h>
32 #include <sys/utsname.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <getopt.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <string.h>
39 #include <mntent.h>
40 #include <fcntl.h>
41 #include <limits.h>
42
43 #define MOUNT_CIFS_VERSION_MAJOR "1"
44 #define MOUNT_CIFS_VERSION_MINOR "11"
45
46 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
47  #ifdef _SAMBA_BUILD_
48   #include "include/version.h"
49   #ifdef SAMBA_VERSION_VENDOR_SUFFIX
50    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING"-"SAMBA_VERSION_VENDOR_SUFFIX
51   #else
52    #define MOUNT_CIFS_VENDOR_SUFFIX "-"SAMBA_VERSION_OFFICIAL_STRING
53   #endif /* SAMBA_VERSION_OFFICIAL_STRING and SAMBA_VERSION_VENDOR_SUFFIX */
54  #else
55    #define MOUNT_CIFS_VENDOR_SUFFIX ""
56  #endif /* _SAMBA_BUILD_ */
57 #endif /* MOUNT_CIFS_VENDOR_SUFFIX */
58
59 #ifndef MS_MOVE 
60 #define MS_MOVE 8192 
61 #endif 
62
63 #ifndef MS_BIND
64 #define MS_BIND 4096
65 #endif
66
67 #define MAX_UNC_LEN 1024
68
69 #define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
70
71 #ifndef SAFE_FREE
72 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
73 #endif
74
75 #define MOUNT_PASSWD_SIZE 64
76 #define DOMAIN_SIZE 64
77
78 const char *thisprogram;
79 int verboseflag = 0;
80 static int got_password = 0;
81 static int got_user = 0;
82 static int got_domain = 0;
83 static int got_ip = 0;
84 static int got_unc = 0;
85 static int got_uid = 0;
86 static int got_gid = 0;
87 static char * user_name = NULL;
88 static char * mountpassword = NULL;
89 char * domain_name = NULL;
90 char * prefixpath = NULL;
91
92 /* glibc doesn't have strlcpy, strlcat. Ensure we do. JRA. We
93  * don't link to libreplace so need them here. */
94
95 /* like strncpy but does not 0 fill the buffer and always null
96  *    terminates. bufsize is the size of the destination buffer */
97 size_t strlcpy(char *d, const char *s, size_t bufsize)
98 {
99         size_t len = strlen(s);
100         size_t ret = len;
101         if (bufsize <= 0) return 0;
102         if (len >= bufsize) len = bufsize-1;
103         memcpy(d, s, len);
104         d[len] = 0;
105         return ret;
106 }
107
108 /* like strncat but does not 0 fill the buffer and always null
109  *    terminates. bufsize is the length of the buffer, which should
110  *       be one more than the maximum resulting string length */
111 size_t strlcat(char *d, const char *s, size_t bufsize)
112 {
113         size_t len1 = strlen(d);
114         size_t len2 = strlen(s);
115         size_t ret = len1 + len2;
116
117         if (len1+len2 >= bufsize) {
118                 len2 = bufsize - (len1+1);
119         }
120         if (len2 > 0) {
121                 memcpy(d+len1, s, len2);
122                 d[len1+len2] = 0;
123         }
124         return ret;
125 }
126
127 /* BB finish BB
128
129         cifs_umount
130         open nofollow - avoid symlink exposure? 
131         get owner of dir see if matches self or if root
132         call system(umount argv) etc.
133                 
134 BB end finish BB */
135
136 static char * check_for_domain(char **);
137
138
139 static void mount_cifs_usage(void)
140 {
141         printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
142         printf("\nMount the remote target, specified as a UNC name,");
143         printf(" to a local directory.\n\nOptions:\n");
144         printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
145         printf("\nLess commonly used options:");
146         printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
147         printf("\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
148         printf("\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
149         printf("\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign");
150         printf("\n\nOptions not needed for servers supporting CIFS Unix extensions");
151         printf("\n\t(e.g. unneeded for mounts to most Samba versions):");
152         printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
153         printf("\n\nRarely used options:");
154         printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
155         printf("\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
156         printf("\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
157         printf("\n\tin6_addr");
158         printf("\n\nOptions are described in more detail in the manual page");
159         printf("\n\tman 8 mount.cifs\n");
160         printf("\nTo display the version number of the mount helper:");
161         printf("\n\t%s -V\n",thisprogram);
162
163         SAFE_FREE(mountpassword);
164         exit(1);
165 }
166
167 /* caller frees username if necessary */
168 static char * getusername(void) {
169         char *username = NULL;
170         struct passwd *password = getpwuid(getuid());
171
172         if (password) {
173                 username = password->pw_name;
174         }
175         return username;
176 }
177
178 static char * parse_cifs_url(char * unc_name)
179 {
180         printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
181         return NULL;
182 }
183
184 static int open_cred_file(char * file_name)
185 {
186         char * line_buf;
187         char * temp_val;
188         FILE * fs;
189         int i, length;
190         fs = fopen(file_name,"r");
191         if(fs == NULL)
192                 return errno;
193         line_buf = (char *)malloc(4096);
194         if(line_buf == NULL) {
195                 fclose(fs);
196                 return -ENOMEM;
197         }
198
199         while(fgets(line_buf,4096,fs)) {
200                 /* parse line from credential file */
201
202                 /* eat leading white space */
203                 for(i=0;i<4086;i++) {
204                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
205                                 break;
206                         /* if whitespace - skip past it */
207                 }
208                 if (strncasecmp("username",line_buf+i,8) == 0) {
209                         temp_val = strchr(line_buf + i,'=');
210                         if(temp_val) {
211                                 /* go past equals sign */
212                                 temp_val++;
213                                 for(length = 0;length<4087;length++) {
214                                         if ((temp_val[length] == '\n')
215                                             || (temp_val[length] == '\0')) {
216                                                 break;
217                                         }
218                                 }
219                                 if(length > 4086) {
220                                         printf("mount.cifs failed due to malformed username in credentials file");
221                                         memset(line_buf,0,4096);
222                                         exit(1);
223                                 } else {
224                                         got_user = 1;
225                                         user_name = (char *)calloc(1 + length,1);
226                                         /* BB adding free of user_name string before exit,
227                                                 not really necessary but would be cleaner */
228                                         strlcpy(user_name,temp_val, length+1);
229                                 }
230                         }
231                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
232                         temp_val = strchr(line_buf+i,'=');
233                         if(temp_val) {
234                                 /* go past equals sign */
235                                 temp_val++;
236                                 for(length = 0;length<MOUNT_PASSWD_SIZE+1;length++) {
237                                         if ((temp_val[length] == '\n')
238                                             || (temp_val[length] == '\0')) {
239                                                 temp_val[length] = '\0';
240                                                 break;
241                                         }
242                                 }
243                                 if(length > MOUNT_PASSWD_SIZE) {
244                                         printf("mount.cifs failed: password in credentials file too long\n");
245                                         memset(line_buf,0, 4096);
246                                         exit(1);
247                                 } else {
248                                         if(mountpassword == NULL) {
249                                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
250                                         } else
251                                                 memset(mountpassword,0,MOUNT_PASSWD_SIZE);
252                                         if(mountpassword) {
253                                                 strlcpy(mountpassword,temp_val,MOUNT_PASSWD_SIZE+1);
254                                                 got_password = 1;
255                                         }
256                                 }
257                         }
258                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
259                         temp_val = strchr(line_buf+i,'=');
260                         if(temp_val) {
261                                 /* go past equals sign */
262                                 temp_val++;
263                                 if(verboseflag)
264                                         printf("\nDomain %s\n",temp_val);
265                                 for(length = 0;length<DOMAIN_SIZE+1;length++) {
266                                         if ((temp_val[length] == '\n')
267                                             || (temp_val[length] == '\0')) {
268                                                 temp_val[length] = '\0';
269                                                 break;
270                                         }
271                                 }
272                                 if(length > DOMAIN_SIZE) {
273                                         printf("mount.cifs failed: domain in credentials file too long\n");
274                                         exit(1);
275                                 } else {
276                                         if(domain_name == NULL) {
277                                                 domain_name = (char *)calloc(DOMAIN_SIZE+1,1);
278                                         } else
279                                                 memset(domain_name,0,DOMAIN_SIZE);
280                                         if(domain_name) {
281                                                 strlcpy(domain_name,temp_val,DOMAIN_SIZE+1);
282                                                 got_domain = 1;
283                                         }
284                                 }
285                         }
286                 }
287
288         }
289         fclose(fs);
290         SAFE_FREE(line_buf);
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(MOUNT_PASSWD_SIZE+1,1);
302         else 
303                 memset(mountpassword, 0, MOUNT_PASSWD_SIZE);
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<MOUNT_PASSWD_SIZE;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                         if(filename != NULL)
325                                 close(file_descript);
326                         exit(1);
327                 } else if(rc == 0) {
328                         if(mountpassword[0] == 0) {
329                                 if(verboseflag)
330                                         printf("\nWarning: null password used since cifs password file empty");
331                         }
332                         break;
333                 } else /* read valid character */ {
334                         if((c == 0) || (c == '\n')) {
335                                 break;
336                         } else 
337                                 mountpassword[i] = c;
338                 }
339         }
340         if((i == MOUNT_PASSWD_SIZE) && (verboseflag)) {
341                 printf("\nWarning: password longer than %d characters specified in cifs password file",
342                         MOUNT_PASSWD_SIZE);
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(MOUNT_PASSWD_SIZE+1,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                                                         strlcpy(mountpassword, percent_char,MOUNT_PASSWD_SIZE+1);
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, DOMAIN_SIZE+1) < DOMAIN_SIZE+1) {
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         SAFE_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 > MOUNT_PASSWD_SIZE) {
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         SAFE_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 /* replace all occurances of "from" in a string with "to" */
837 static void replace_char(char *string, char from, char to, int maxlen)
838 {
839         char *lastchar = string + maxlen;
840         while (string) {
841                 string = strchr(string, from);
842                 if (string) {
843                         *string = to;
844                         if (string >= lastchar)
845                                 return;
846                 }
847         }
848 }
849
850 /* Note that caller frees the returned buffer if necessary */
851 static char * parse_server(char ** punc_name)
852 {
853         char * unc_name = *punc_name;
854         int length = strnlen(unc_name, MAX_UNC_LEN);
855         char * share;
856         char * ipaddress_string = NULL;
857         struct hostent * host_entry = NULL;
858         struct in_addr server_ipaddr;
859
860         if(length > (MAX_UNC_LEN - 1)) {
861                 printf("mount error: UNC name too long");
862                 return NULL;
863         }
864         if (strncasecmp("cifs://",unc_name,7) == 0)
865                 return parse_cifs_url(unc_name+7);
866         if (strncasecmp("smb://",unc_name,6) == 0) {
867                 return parse_cifs_url(unc_name+6);
868         }
869
870         if(length < 3) {
871                 /* BB add code to find DFS root here */
872                 printf("\nMounting the DFS root for domain not implemented yet\n");
873                 return NULL;
874         } else {
875                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
876                         /* check for nfs syntax ie server:share */
877                         share = strchr(unc_name,':');
878                         if(share) {
879                                 *punc_name = (char *)malloc(length+3);
880                                 if(*punc_name == NULL) {
881                                         /* put the original string back  if 
882                                            no memory left */
883                                         *punc_name = unc_name;
884                                         return NULL;
885                                 }
886                                 *share = '/';
887                                 strlcpy((*punc_name)+2,unc_name,length+1);
888                                 SAFE_FREE(unc_name);
889                                 unc_name = *punc_name;
890                                 unc_name[length+2] = 0;
891                                 goto continue_unc_parsing;
892                         } else {
893                                 printf("mount error: improperly formatted UNC name.");
894                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
895                                 return NULL;
896                         }
897                 } else {
898 continue_unc_parsing:
899                         unc_name[0] = '/';
900                         unc_name[1] = '/';
901                         unc_name += 2;
902
903                         /* allow for either delimiter between host and sharename */
904                         if ((share = strpbrk(unc_name, "/\\"))) {
905                                 *share = 0;  /* temporarily terminate the string */
906                                 share += 1;
907                                 if(got_ip == 0) {
908                                         host_entry = gethostbyname(unc_name);
909                                 }
910                                 *(share - 1) = '/'; /* put delimiter back */
911
912                                 /* we don't convert the prefixpath delimiters since '\\' is a valid char in posix paths */
913                                 if ((prefixpath = strpbrk(share, "/\\"))) {
914                                         *prefixpath = 0;  /* permanently terminate the string */
915                                         if (!strlen(++prefixpath))
916                                                 prefixpath = NULL; /* this needs to be done explicitly */
917                                 }
918                                 if(got_ip) {
919                                         if(verboseflag)
920                                                 printf("ip address specified explicitly\n");
921                                         return NULL;
922                                 }
923                                 if(host_entry == NULL) {
924                                         printf("mount error: could not find target server. TCP name %s not found\n", unc_name);
925                                         return NULL;
926                                 } else {
927                                         /* BB should we pass an alternate version of the share name as Unicode */
928                                         /* BB what about ipv6? BB */
929                                         /* BB add retries with alternate servers in list */
930
931                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
932
933                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
934                                         if(ipaddress_string == NULL) {
935                                                 printf("mount error: could not get valid ip address for target server\n");
936                                                 return NULL;
937                                         }
938                                         return ipaddress_string; 
939                                 }
940                         } else {
941                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
942                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
943                                 return NULL;
944                         }
945                 }
946         }
947 }
948
949 static struct option longopts[] = {
950         { "all", 0, NULL, 'a' },
951         { "help",0, NULL, 'h' },
952         { "move",0, NULL, 'm' },
953         { "bind",0, NULL, 'b' },
954         { "read-only", 0, NULL, 'r' },
955         { "ro", 0, NULL, 'r' },
956         { "verbose", 0, NULL, 'v' },
957         { "version", 0, NULL, 'V' },
958         { "read-write", 0, NULL, 'w' },
959         { "rw", 0, NULL, 'w' },
960         { "options", 1, NULL, 'o' },
961         { "type", 1, NULL, 't' },
962         { "rsize",1, NULL, 'R' },
963         { "wsize",1, NULL, 'W' },
964         { "uid", 1, NULL, '1'},
965         { "gid", 1, NULL, '2'},
966         { "user",1,NULL,'u'},
967         { "username",1,NULL,'u'},
968         { "dom",1,NULL,'d'},
969         { "domain",1,NULL,'d'},
970         { "password",1,NULL,'p'},
971         { "pass",1,NULL,'p'},
972         { "credentials",1,NULL,'c'},
973         { "port",1,NULL,'P'},
974         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
975         { NULL, 0, NULL, 0 }
976 };
977
978 /* convert a string to uppercase. return false if the string
979  * wasn't ASCII or was a NULL ptr */
980 static int
981 uppercase_string(char *string)
982 {
983         if (!string)
984                 return 0;
985
986         while (*string) {
987                 /* check for unicode */
988                 if ((unsigned char) string[0] & 0x80)
989                         return 0;
990                 *string = toupper((unsigned char) *string);
991                 string++;
992         }
993
994         return 1;
995 }
996
997 int main(int argc, char ** argv)
998 {
999         int c;
1000         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
1001         char * orgoptions = NULL;
1002         char * share_name = NULL;
1003         char * ipaddr = NULL;
1004         char * uuid = NULL;
1005         char * mountpoint = NULL;
1006         char * options = NULL;
1007         char * resolved_path = NULL;
1008         char * temp;
1009         char * dev_name;
1010         int rc;
1011         int rsize = 0;
1012         int wsize = 0;
1013         int nomtab = 0;
1014         int uid = 0;
1015         int gid = 0;
1016         int optlen = 0;
1017         int orgoptlen = 0;
1018         size_t options_size = 0;
1019         int retry = 0; /* set when we have to retry mount with uppercase */
1020         struct stat statbuf;
1021         struct utsname sysinfo;
1022         struct mntent mountent;
1023         FILE * pmntfile;
1024
1025         /* setlocale(LC_ALL, "");
1026         bindtextdomain(PACKAGE, LOCALEDIR);
1027         textdomain(PACKAGE); */
1028
1029         if(argc && argv) {
1030                 thisprogram = argv[0];
1031         } else {
1032                 mount_cifs_usage();
1033                 exit(1);
1034         }
1035
1036         if(thisprogram == NULL)
1037                 thisprogram = "mount.cifs";
1038
1039         uname(&sysinfo);
1040         /* BB add workstation name and domain and pass down */
1041
1042 /* #ifdef _GNU_SOURCE
1043         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
1044 #endif */
1045         if(argc > 2) {
1046                 dev_name = argv[1];
1047                 share_name = strndup(argv[1], MAX_UNC_LEN);
1048                 if (share_name == NULL) {
1049                         fprintf(stderr, "%s: %s", argv[0], strerror(ENOMEM));
1050                         exit(1);
1051                 }
1052                 mountpoint = argv[2];
1053         } else {
1054                 mount_cifs_usage();
1055                 exit(1);
1056         }
1057
1058         /* add sharename in opts string as unc= parm */
1059
1060         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
1061                          longopts, NULL)) != -1) {
1062                 switch (c) {
1063 /* No code to do the following  options yet */
1064 /*      case 'l':
1065                 list_with_volumelabel = 1;
1066                 break;
1067         case 'L':
1068                 volumelabel = optarg;
1069                 break; */
1070 /*      case 'a':              
1071                 ++mount_all;
1072                 break; */
1073
1074                 case '?':
1075                 case 'h':        /* help */
1076                         mount_cifs_usage ();
1077                         exit(1);
1078                 case 'n':
1079                     ++nomtab;
1080                     break;
1081                 case 'b':
1082 #ifdef MS_BIND
1083                         flags |= MS_BIND;
1084 #else
1085                         fprintf(stderr,
1086                                 "option 'b' (MS_BIND) not supported\n");
1087 #endif
1088                         break;
1089                 case 'm':
1090 #ifdef MS_MOVE                
1091                         flags |= MS_MOVE;
1092 #else
1093                         fprintf(stderr,
1094                                 "option 'm' (MS_MOVE) not supported\n");
1095 #endif
1096                         break;
1097                 case 'o':
1098                         orgoptions = strdup(optarg);
1099                     break;
1100                 case 'r':  /* mount readonly */
1101                         flags |= MS_RDONLY;
1102                         break;
1103                 case 'U':
1104                         uuid = optarg;
1105                         break;
1106                 case 'v':
1107                         ++verboseflag;
1108                         break;
1109                 case 'V':          
1110                         printf ("mount.cifs version: %s.%s%s\n",
1111                         MOUNT_CIFS_VERSION_MAJOR,
1112                         MOUNT_CIFS_VERSION_MINOR,
1113                         MOUNT_CIFS_VENDOR_SUFFIX);
1114                         exit (0);
1115                 case 'w':
1116                         flags &= ~MS_RDONLY;
1117                         break;
1118                 case 'R':
1119                         rsize = atoi(optarg) ;
1120                         break;
1121                 case 'W':
1122                         wsize = atoi(optarg);
1123                         break;
1124                 case '1':
1125                         if (isdigit(*optarg)) {
1126                                 char *ep;
1127
1128                                 uid = strtoul(optarg, &ep, 10);
1129                                 if (*ep) {
1130                                         printf("bad uid value \"%s\"\n", optarg);
1131                                         exit(1);
1132                                 }
1133                         } else {
1134                                 struct passwd *pw;
1135
1136                                 if (!(pw = getpwnam(optarg))) {
1137                                         printf("bad user name \"%s\"\n", optarg);
1138                                         exit(1);
1139                                 }
1140                                 uid = pw->pw_uid;
1141                                 endpwent();
1142                         }
1143                         break;
1144                 case '2':
1145                         if (isdigit(*optarg)) {
1146                                 char *ep;
1147
1148                                 gid = strtoul(optarg, &ep, 10);
1149                                 if (*ep) {
1150                                         printf("bad gid value \"%s\"\n", optarg);
1151                                         exit(1);
1152                                 }
1153                         } else {
1154                                 struct group *gr;
1155
1156                                 if (!(gr = getgrnam(optarg))) {
1157                                         printf("bad user name \"%s\"\n", optarg);
1158                                         exit(1);
1159                                 }
1160                                 gid = gr->gr_gid;
1161                                 endpwent();
1162                         }
1163                         break;
1164                 case 'u':
1165                         got_user = 1;
1166                         user_name = optarg;
1167                         break;
1168                 case 'd':
1169                         domain_name = optarg; /* BB fix this - currently ignored */
1170                         got_domain = 1;
1171                         break;
1172                 case 'p':
1173                         if(mountpassword == NULL)
1174                                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1175                         if(mountpassword) {
1176                                 got_password = 1;
1177                                 strlcpy(mountpassword,optarg,MOUNT_PASSWD_SIZE+1);
1178                         }
1179                         break;
1180                 case 'S':
1181                         get_password_from_file(0 /* stdin */,NULL);
1182                         break;
1183                 case 't':
1184                         break;
1185                 default:
1186                         printf("unknown mount option %c\n",c);
1187                         mount_cifs_usage();
1188                         exit(1);
1189                 }
1190         }
1191
1192         if((argc < 3) || (dev_name == NULL) || (mountpoint == NULL)) {
1193                 mount_cifs_usage();
1194                 exit(1);
1195         }
1196
1197         if (getenv("PASSWD")) {
1198                 if(mountpassword == NULL)
1199                         mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1200                 if(mountpassword) {
1201                         strlcpy(mountpassword,getenv("PASSWD"),MOUNT_PASSWD_SIZE+1);
1202                         got_password = 1;
1203                 }
1204         } else if (getenv("PASSWD_FD")) {
1205                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
1206         } else if (getenv("PASSWD_FILE")) {
1207                 get_password_from_file(0, getenv("PASSWD_FILE"));
1208         }
1209
1210         if (orgoptions && parse_options(&orgoptions, &flags)) {
1211                 rc = -1;
1212                 goto mount_exit;
1213         }
1214         ipaddr = parse_server(&share_name);
1215         if((ipaddr == NULL) && (got_ip == 0)) {
1216                 printf("No ip address specified and hostname not found\n");
1217                 rc = -1;
1218                 goto mount_exit;
1219         }
1220         
1221         /* BB save off path and pop after mount returns? */
1222         resolved_path = (char *)malloc(PATH_MAX+1);
1223         if(resolved_path) {
1224                 /* Note that if we can not canonicalize the name, we get
1225                 another chance to see if it is valid when we chdir to it */
1226                 if (realpath(mountpoint, resolved_path)) {
1227                         mountpoint = resolved_path; 
1228                 }
1229         }
1230         if(chdir(mountpoint)) {
1231                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
1232                 rc = -1;
1233                 goto mount_exit;
1234         }
1235
1236         if(stat (".", &statbuf)) {
1237                 printf("mount error: mount point %s does not exist\n",mountpoint);
1238                 rc = -1;
1239                 goto mount_exit;
1240         }
1241
1242         if (S_ISDIR(statbuf.st_mode) == 0) {
1243                 printf("mount error: mount point %s is not a directory\n",mountpoint);
1244                 rc = -1;
1245                 goto mount_exit;
1246         }
1247
1248         if((getuid() != 0) && (geteuid() == 0)) {
1249                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
1250 #ifndef CIFS_ALLOW_USR_SUID
1251                         /* Do not allow user mounts to control suid flag
1252                         for mount unless explicitly built that way */
1253                         flags |= MS_NOSUID | MS_NODEV;
1254 #endif                                          
1255                 } else {
1256                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
1257                         return -1;
1258                 }
1259         }
1260
1261         if(got_user == 0) {
1262                 user_name = getusername();
1263                 got_user = 1;
1264         }
1265        
1266         if(got_password == 0) {
1267                 char *tmp_pass = getpass("Password: "); /* BB obsolete sys call but
1268                                                            no good replacement yet. */
1269                 mountpassword = (char *)calloc(MOUNT_PASSWD_SIZE+1,1);
1270                 if (!tmp_pass || !mountpassword) {
1271                         printf("Password not entered, exiting\n");
1272                         return -1;
1273                 }
1274                 strlcpy(mountpassword, tmp_pass, MOUNT_PASSWD_SIZE+1);
1275                 got_password = 1;
1276         }
1277         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1278            remember to clear parms and overwrite password field before launching */
1279 mount_retry:
1280         if(orgoptions) {
1281                 optlen = strlen(orgoptions);
1282                 orgoptlen = optlen;
1283         } else
1284                 optlen = 0;
1285         if(share_name)
1286                 optlen += strlen(share_name) + 4;
1287         else {
1288                 printf("No server share name specified\n");
1289                 printf("\nMounting the DFS root for server not implemented yet\n");
1290                 exit(1);
1291         }
1292         if(user_name)
1293                 optlen += strlen(user_name) + 6;
1294         if(ipaddr)
1295                 optlen += strlen(ipaddr) + 4;
1296         if(mountpassword)
1297                 optlen += strlen(mountpassword) + 6;
1298         SAFE_FREE(options);
1299         options_size = optlen + 10 + DOMAIN_SIZE;
1300         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 */);
1301
1302         if(options == NULL) {
1303                 printf("Could not allocate memory for mount options\n");
1304                 return -1;
1305         }
1306
1307         options[0] = 0;
1308         strlcpy(options,"unc=",options_size);
1309         strlcat(options,share_name,options_size);
1310         /* scan backwards and reverse direction of slash */
1311         temp = strrchr(options, '/');
1312         if(temp > options + 6)
1313                 *temp = '\\';
1314         if(ipaddr) {
1315                 strlcat(options,",ip=",options_size);
1316                 strlcat(options,ipaddr,options_size);
1317         }
1318
1319         if(user_name) {
1320                 /* check for syntax like user=domain\user */
1321                 if(got_domain == 0)
1322                         domain_name = check_for_domain(&user_name);
1323                 strlcat(options,",user=",options_size);
1324                 strlcat(options,user_name,options_size);
1325         }
1326         if(retry == 0) {
1327                 if(domain_name) {
1328                         /* extra length accounted for in option string above */
1329                         strlcat(options,",domain=",options_size);
1330                         strlcat(options,domain_name,options_size);
1331                 }
1332         }
1333         if(mountpassword) {
1334                 /* Commas have to be doubled, or else they will
1335                 look like the parameter separator */
1336 /*              if(sep is not set)*/
1337                 if(retry == 0)
1338                         check_for_comma(&mountpassword);
1339                 strlcat(options,",pass=",options_size);
1340                 strlcat(options,mountpassword,options_size);
1341         }
1342
1343         strlcat(options,",ver=",options_size);
1344         strlcat(options,MOUNT_CIFS_VERSION_MAJOR,options_size);
1345
1346         if(orgoptions) {
1347                 strlcat(options,",",options_size);
1348                 strlcat(options,orgoptions,options_size);
1349         }
1350         if(prefixpath) {
1351                 strlcat(options,",prefixpath=",options_size);
1352                 strlcat(options,prefixpath,options_size); /* no need to cat the / */
1353         }
1354         if(verboseflag)
1355                 printf("\nmount.cifs kernel mount options %s \n",options);
1356
1357         /* convert all '\\' to '/' in share portion so that /proc/mounts looks pretty */
1358         replace_char(dev_name, '\\', '/', strlen(share_name));
1359
1360         if(mount(dev_name, mountpoint, "cifs", flags, options)) {
1361         /* remember to kill daemon on error */
1362                 switch (errno) {
1363                 case 0:
1364                         printf("mount failed but no error number set\n");
1365                         break;
1366                 case ENODEV:
1367                         printf("mount error: cifs filesystem not supported by the system\n");
1368                         break;
1369                 case ENXIO:
1370                         if(retry == 0) {
1371                                 retry = 1;
1372                                 if (uppercase_string(dev_name) &&
1373                                     uppercase_string(share_name) &&
1374                                     uppercase_string(prefixpath)) {
1375                                         printf("retrying with upper case share name\n");
1376                                         goto mount_retry;
1377                                 }
1378                         }
1379                 default:
1380                         printf("mount error %d = %s\n",errno,strerror(errno));
1381                 }
1382                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1383                 rc = -1;
1384                 goto mount_exit;
1385         } else {
1386                 pmntfile = setmntent(MOUNTED, "a+");
1387                 if(pmntfile) {
1388                         mountent.mnt_fsname = dev_name;
1389                         mountent.mnt_dir = mountpoint;
1390                         mountent.mnt_type = CONST_DISCARD(char *,"cifs");
1391                         mountent.mnt_opts = (char *)malloc(220);
1392                         if(mountent.mnt_opts) {
1393                                 char * mount_user = getusername();
1394                                 memset(mountent.mnt_opts,0,200);
1395                                 if(flags & MS_RDONLY)
1396                                         strlcat(mountent.mnt_opts,"ro",220);
1397                                 else
1398                                         strlcat(mountent.mnt_opts,"rw",220);
1399                                 if(flags & MS_MANDLOCK)
1400                                         strlcat(mountent.mnt_opts,",mand",220);
1401                                 if(flags & MS_NOEXEC)
1402                                         strlcat(mountent.mnt_opts,",noexec",220);
1403                                 if(flags & MS_NOSUID)
1404                                         strlcat(mountent.mnt_opts,",nosuid",220);
1405                                 if(flags & MS_NODEV)
1406                                         strlcat(mountent.mnt_opts,",nodev",220);
1407                                 if(flags & MS_SYNCHRONOUS)
1408                                         strlcat(mountent.mnt_opts,",synch",220);
1409                                 if(mount_user) {
1410                                         if(getuid() != 0) {
1411                                                 strlcat(mountent.mnt_opts,",user=",220);
1412                                                 strlcat(mountent.mnt_opts,mount_user,220);
1413                                         }
1414                                         /* free(mount_user); do not free static mem */
1415                                 }
1416                         }
1417                         mountent.mnt_freq = 0;
1418                         mountent.mnt_passno = 0;
1419                         rc = addmntent(pmntfile,&mountent);
1420                         endmntent(pmntfile);
1421                         SAFE_FREE(mountent.mnt_opts);
1422                 } else {
1423                     printf("could not update mount table\n");
1424                 }
1425         }
1426         rc = 0;
1427 mount_exit:
1428         if(mountpassword) {
1429                 int len = strlen(mountpassword);
1430                 memset(mountpassword,0,len);
1431                 SAFE_FREE(mountpassword);
1432         }
1433
1434         SAFE_FREE(options);
1435         SAFE_FREE(orgoptions);
1436         SAFE_FREE(resolved_path);
1437         SAFE_FREE(share_name);
1438         return rc;
1439 }