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