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