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