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