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