r10656: BIG merge from trunk. Features not copied over
[sfrench/samba-autobuild/.git] / source / client / mount.cifs.c
1 /* 
2    Mount helper utility for Linux CIFS VFS (virtual filesystem) client
3    Copyright (C) 2003,2005 Steve French  (sfrench@us.ibm.com)
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
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 <ctype.h>
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <sys/stat.h>
31 #include <sys/utsname.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <getopt.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <string.h>
38 #include <mntent.h>
39 #include <fcntl.h>
40
41 #define MOUNT_CIFS_VERSION_MAJOR "1"
42 #define MOUNT_CIFS_VERSION_MINOR "8"
43
44 #ifndef MOUNT_CIFS_VENDOR_SUFFIX
45 #define MOUNT_CIFS_VENDOR_SUFFIX ""
46 #endif
47
48 #ifndef MS_MOVE 
49 #define MS_MOVE 8192 
50 #endif 
51
52 char * thisprogram;
53 int verboseflag = 0;
54 static int got_password = 0;
55 static int got_user = 0;
56 static int got_domain = 0;
57 static int got_ip = 0;
58 static int got_unc = 0;
59 static int got_uid = 0;
60 static int got_gid = 0;
61 static int free_share_name = 0;
62 static char * user_name = NULL;
63 static char * mountpassword = NULL;
64 char * domain_name = NULL;
65
66
67 /* BB finish BB
68
69         cifs_umount
70         open nofollow - avoid symlink exposure? 
71         get owner of dir see if matches self or if root
72         call system(umount argv) etc.
73                 
74 BB end finish BB */
75
76 static char * check_for_domain(char **);
77
78
79 static void mount_cifs_usage(void)
80 {
81         printf("\nUsage:  %s <remotetarget> <dir> -o <options>\n", thisprogram);
82         printf("\nMount the remote target, specified as a UNC name,");
83         printf(" to a local directory.\n\nOptions:\n");
84         printf("\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
85         printf("\nLess commonly used options:");
86         printf("\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,\n\tdirectio,mapchars,nomapchars");
87         printf("\n\nOptions not needed for servers supporting CIFS Unix extensions\n\t(e.g. most Samba versions):");
88         printf("\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>");
89         printf("\n\nRarely used options:");
90         printf("\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,\n\tdev,nodev,nouser_xattr,netbiosname,hard,soft,intr,nointr,noacl");
91         printf("\n\nOptions are described in more detail in the manual page");
92         printf("\n\tman 8 mount.cifs\n");
93         printf("\nTo display the version number of the mount helper:");
94         printf("\n\t%s -V\n",thisprogram);
95
96         if(mountpassword) {
97                 memset(mountpassword,0,64);
98                 free(mountpassword);
99         }
100         exit(1);
101 }
102
103 /* caller frees username if necessary */
104 static char * getusername(void) {
105         char *username = NULL;
106         struct passwd *password = getpwuid(getuid());
107
108         if (password) {
109                 username = password->pw_name;
110         }
111         return username;
112 }
113
114 static char * parse_cifs_url(char * unc_name)
115 {
116         printf("\nMounting cifs URL not implemented yet. Attempt to mount %s\n",unc_name);
117         return NULL;
118 }
119
120 static int open_cred_file(char * file_name)
121 {
122         char * line_buf;
123         char * temp_val;
124         FILE * fs;
125         int i, length;
126         fs = fopen(file_name,"r");
127         if(fs == NULL)
128                 return errno;
129         line_buf = malloc(4096);
130         if(line_buf == NULL)
131                 return -ENOMEM;
132
133         while(fgets(line_buf,4096,fs)) {
134                 /* parse line from credential file */
135
136                 /* eat leading white space */
137                 for(i=0;i<4086;i++) {
138                         if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
139                                 break;
140                         /* if whitespace - skip past it */
141                 }
142                 if (strncasecmp("username",line_buf+i,8) == 0) {
143                         temp_val = strchr(line_buf + i,'=');
144                         if(temp_val) {
145                                 /* go past equals sign */
146                                 temp_val++;
147                                 for(length = 0;length<4087;length++) {
148                                         if(temp_val[length] == '\n')
149                                                 break;
150                                 }
151                                 if(length > 4086) {
152                                         printf("mount.cifs failed due to malformed username in credentials file");
153                                         memset(line_buf,0,4096);
154                                         if(mountpassword) {
155                                                 memset(mountpassword,0,64);
156                                         }
157                                         exit(1);
158                                 } else {
159                                         got_user = 1;
160                                         user_name = calloc(1 + length,1);
161                                         /* BB adding free of user_name string before exit,
162                                                 not really necessary but would be cleaner */
163                                         strncpy(user_name,temp_val, length);
164                                 }
165                         }
166                 } else if (strncasecmp("password",line_buf+i,8) == 0) {
167                         temp_val = strchr(line_buf+i,'=');
168                         if(temp_val) {
169                                 /* go past equals sign */
170                                 temp_val++;
171                                 for(length = 0;length<65;length++) {
172                                         if(temp_val[length] == '\n')
173                                                 break;
174                                 }
175                                 if(length > 64) {
176                                         printf("mount.cifs failed: password in credentials file too long\n");
177                                         memset(line_buf,0, 4096);
178                                         if(mountpassword) {
179                                                 memset(mountpassword,0,64);
180                                         }
181                                         exit(1);
182                                 } else {
183                                         if(mountpassword == NULL) {
184                                                 mountpassword = calloc(65,1);
185                                         } else
186                                                 memset(mountpassword,0,64);
187                                         if(mountpassword) {
188                                                 strncpy(mountpassword,temp_val,length);
189                                                 got_password = 1;
190                                         }
191                                 }
192                         }
193                 } else if (strncasecmp("domain",line_buf+i,6) == 0) {
194                         temp_val = strchr(line_buf+i,'=');
195                         if(temp_val) {
196                                 /* go past equals sign */
197                                 temp_val++;
198                                 if(verboseflag)
199                                         printf("\nDomain %s\n",temp_val);
200                                 for(length = 0;length<65;length++) {
201                                         if(temp_val[length] == '\n')
202                                                 break;
203                                 }
204                                 if(length > 64) {
205                                         printf("mount.cifs failed: domain in credentials file too long\n");
206                                         if(mountpassword) {
207                                                 memset(mountpassword,0,64);
208                                         }
209                                         exit(1);
210                                 } else {
211                                         if(domain_name == NULL) {
212                                                 domain_name = calloc(65,1);
213                                         } else
214                                                 memset(domain_name,0,64);
215                                         if(domain_name) {
216                                                 strncpy(domain_name,temp_val,length);
217                                                 got_domain = 1;
218                                         }
219                                 }
220                         }
221                 }
222
223         }
224         fclose(fs);
225         if(line_buf) {
226                 memset(line_buf,0,4096);
227                 free(line_buf);
228         }
229         return 0;
230 }
231
232 static int get_password_from_file(int file_descript, char * filename)
233 {
234         int rc = 0;
235         int i;
236         char c;
237
238         if(mountpassword == NULL)
239                 mountpassword = calloc(65,1);
240         else 
241                 memset(mountpassword, 0, 64);
242
243         if(filename != NULL) {
244                 file_descript = open(filename, O_RDONLY);
245                 if(file_descript < 0) {
246                         printf("mount.cifs failed. %s attempting to open password file %s\n",
247                                    strerror(errno),filename);
248                         exit(1);
249                 }
250         }
251         /* else file already open and fd provided */
252
253         for(i=0;i<64;i++) {
254                 rc = read(file_descript,&c,1);
255                 if(rc < 0) {
256                         printf("mount.cifs failed. Error %s reading password file\n",strerror(errno));
257                         memset(mountpassword,0,64);
258                         if(filename != NULL)
259                                 close(file_descript);
260                         exit(1);
261                 } else if(rc == 0) {
262                         if(mountpassword[0] == 0) {
263                                 if(verboseflag)
264                                         printf("\nWarning: null password used since cifs password file empty");
265                         }
266                         break;
267                 } else /* read valid character */ {
268                         if((c == 0) || (c == '\n')) {
269                                 break;
270                         } else 
271                                 mountpassword[i] = c;
272                 }
273         }
274         if((i == 64) && (verboseflag)) {
275                 printf("\nWarning: password longer than 64 characters specified in cifs password file");
276         }
277         got_password = 1;
278         if(filename != NULL) {
279                 close(file_descript);
280         }
281
282         return rc;
283 }
284
285 static int parse_options(char * options, int * filesys_flags)
286 {
287         char * data;
288         char * percent_char = NULL;
289         char * value = NULL;
290         char * next_keyword = NULL;
291         int rc = 0;
292
293         if (!options)
294                 return 1;
295         else
296                 data = options;
297
298         if(verboseflag)
299                 printf("parsing options: %s\n", options);
300
301         /* BB fixme check for separator override BB */
302
303 /* while ((data = strsep(&options, ",")) != NULL) { */
304         while(data != NULL) {
305                 /*  check if ends with trailing comma */
306                 if(*data == 0)
307                         break;
308
309                 /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
310                 /* data  = next keyword */
311                 /* value = next value ie stuff after equal sign */
312
313                 next_keyword = strchr(data,','); /* BB handle sep= */
314         
315                 /* temporarily null terminate end of keyword=value pair */
316                 if(next_keyword)
317                         *next_keyword = 0;
318
319                 /* temporarily null terminate keyword to make keyword and value distinct */
320                 if ((value = strchr(data, '=')) != NULL) {
321                         *value = '\0';
322                         value++;
323                 }
324
325                 if (strncmp(data, "users",5) == 0) {
326                         if(!value || !*value) {
327                                 strncpy(data,",,,,,",5);
328                         }
329                 } else if (strncmp(data, "user_xattr",10) == 0) {
330                    /* do nothing - need to skip so not parsed as user name */
331                 } else if (strncmp(data, "user", 4) == 0) {
332
333                         if (!value || !*value) {
334                                 if(data[4] == '\0') {
335                                         if(verboseflag)
336                                                 printf("\nskipping empty user mount parameter\n");
337                                         /* remove the parm since it would otherwise be confusing
338                                         to the kernel code which would think it was a real username */
339                                                 data[0] = ',';
340                                                 data[1] = ',';
341                                                 data[2] = ',';
342                                                 data[3] = ',';
343                                 } else {
344                                         printf("username specified with no parameter\n");
345                                         return 1;       /* needs_arg; */
346                                 }
347                         } else {
348                                 if (strnlen(value, 260) < 260) {
349                                         got_user=1;
350                                         percent_char = strchr(value,'%');
351                                         if(percent_char) {
352                                                 *percent_char = ',';
353                                                 if(mountpassword == NULL)
354                                                         mountpassword = calloc(65,1);
355                                                 if(mountpassword) {
356                                                         if(got_password)
357                                                                 printf("\nmount.cifs warning - password specified twice\n");
358                                                         got_password = 1;
359                                                         percent_char++;
360                                                         strncpy(mountpassword, percent_char,64);
361                                                 /*  remove password from username */
362                                                         while(*percent_char != 0) {
363                                                                 *percent_char = ',';
364                                                                 percent_char++;
365                                                         }
366                                                 }
367                                         }
368                                         /* this is only case in which the user
369                                         name buf is not malloc - so we have to
370                                         check for domain name embedded within
371                                         the user name here since the later
372                                         call to check_for_domain will not be
373                                         invoked */
374                                         domain_name = check_for_domain(&value);
375                                 } else {
376                                         printf("username too long\n");
377                                         return 1;
378                                 }
379                         }
380                 } else if (strncmp(data, "pass", 4) == 0) {
381                         if (!value || !*value) {
382                                 if(got_password) {
383                                         printf("\npassword specified twice, ignoring second\n");
384                                 } else
385                                         got_password = 1;
386                         } else if (strnlen(value, 17) < 17) {
387                                 if(got_password)
388                                         printf("\nmount.cifs warning - password specified twice\n");
389                                 got_password = 1;
390                         } else {
391                                 printf("password too long\n");
392                                 return 1;
393                         }
394                 } else if (strncmp(data, "ip", 2) == 0) {
395                         if (!value || !*value) {
396                                 printf("target ip address argument missing");
397                         } else if (strnlen(value, 35) < 35) {
398                                 if(verboseflag)
399                                         printf("ip address %s override specified\n",value);
400                                 got_ip = 1;
401                         } else {
402                                 printf("ip address too long\n");
403                                 return 1;
404                         }
405                 } else if ((strncmp(data, "unc", 3) == 0)
406                    || (strncmp(data, "target", 6) == 0)
407                    || (strncmp(data, "path", 4) == 0)) {
408                         if (!value || !*value) {
409                                 printf("invalid path to network resource\n");
410                                 return 1;  /* needs_arg; */
411                         } else if(strnlen(value,5) < 5) {
412                                 printf("UNC name too short");
413                         }
414
415                         if (strnlen(value, 300) < 300) {
416                                 got_unc = 1;
417                                 if (strncmp(value, "//", 2) == 0) {
418                                         if(got_unc)
419                                                 printf("unc name specified twice, ignoring second\n");
420                                         else
421                                                 got_unc = 1;
422                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
423                                         printf("UNC Path does not begin with // or \\\\ \n");
424                                         return 1;
425                                 } else {
426                                         if(got_unc)
427                                                 printf("unc name specified twice, ignoring second\n");
428                                         else
429                                                 got_unc = 1;
430                                 }
431                         } else {
432                                 printf("CIFS: UNC name too long\n");
433                                 return 1;
434                         }
435                 } else if ((strncmp(data, "domain", 3) == 0)
436                            || (strncmp(data, "workgroup", 5) == 0)) {
437                         if (!value || !*value) {
438                                 printf("CIFS: invalid domain name\n");
439                                 return 1;       /* needs_arg; */
440                         }
441                         if (strnlen(value, 65) < 65) {
442                                 got_domain = 1;
443                         } else {
444                                 printf("domain name too long\n");
445                                 return 1;
446                         }
447                 } else if (strncmp(data, "cred", 4) == 0) {
448                         if (value && *value) {
449                                 rc = open_cred_file(value);
450                                 if(rc) {
451                                         printf("error %d opening credential file %s\n",rc, value);
452                                         return 1;
453                                 }
454                         } else {
455                                 printf("invalid credential file name specified\n");
456                                 return 1;
457                         }
458                 } else if (strncmp(data, "uid", 3) == 0) {
459                         if (value && *value) {
460                                 got_uid = 1;
461                         }
462                 } else if (strncmp(data, "gid", 3) == 0) {
463                         if (value && *value) {
464                                 got_gid = 1;
465                         }
466        /* fmask and dmask synonyms for people used to smbfs syntax */
467                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
468                         if (!value || !*value) {
469                                 printf ("Option '%s' requires a numerical argument\n", data);
470                                 return 1;
471                         }
472
473                         if (value[0] != '0') {
474                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
475                         }
476
477                         if (strcmp (data, "fmask") == 0) {
478                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
479                                 data = "file_mode"; /* BB fix this */
480                         }
481                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
482                         if (!value || !*value) {
483                                 printf ("Option '%s' requires a numerical argument\n", data);
484                                 return 1;
485                         }
486
487                         if (value[0] != '0') {
488                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
489                         }
490
491                         if (strcmp (data, "dmask") == 0) {
492                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
493                                 data = "dir_mode";
494                         }
495                         /* the following eight mount options should be
496                         stripped out from what is passed into the kernel
497                         since these eight options are best passed as the
498                         mount flags rather than redundantly to the kernel 
499                         and could generate spurious warnings depending on the
500                         level of the corresponding cifs vfs kernel code */
501                 } else if (strncmp(data, "nosuid", 6) == 0) {
502                         *filesys_flags |= MS_NOSUID;
503                 } else if (strncmp(data, "suid", 4) == 0) {
504                         *filesys_flags &= ~MS_NOSUID;
505                 } else if (strncmp(data, "nodev", 5) == 0) {
506                         *filesys_flags |= MS_NODEV;
507                 } else if (strncmp(data, "dev", 3) == 0) {
508                         *filesys_flags &= ~MS_NODEV;
509                 } else if (strncmp(data, "noexec", 6) == 0) {
510                         *filesys_flags |= MS_NOEXEC;
511                 } else if (strncmp(data, "exec", 4) == 0) {
512                         *filesys_flags &= ~MS_NOEXEC;
513                 } else if (strncmp(data, "guest", 5) == 0) {
514                         got_password=1;
515                         /* remove the parm since it would otherwise be logged by kern */
516                         data[0] = ',';
517                         data[1] = ',';
518                         data[2] = ',';
519                         data[3] = ',';
520                         data[4] = ',';
521                 } else if (strncmp(data, "ro", 2) == 0) {
522                         *filesys_flags |= MS_RDONLY;
523                 } else if (strncmp(data, "rw", 2) == 0) {
524                         *filesys_flags &= ~MS_RDONLY;
525                 } else if (strncmp(data, "remount", 7) == 0) {
526                         *filesys_flags |= MS_REMOUNT;
527                 } /* else if (strnicmp(data, "port", 4) == 0) {
528                         if (value && *value) {
529                                 vol->port =
530                                         simple_strtoul(value, &value, 0);
531                         }
532                 } else if (strnicmp(data, "rsize", 5) == 0) {
533                         if (value && *value) {
534                                 vol->rsize =
535                                         simple_strtoul(value, &value, 0);
536                         }
537                 } else if (strnicmp(data, "wsize", 5) == 0) {
538                         if (value && *value) {
539                                 vol->wsize =
540                                         simple_strtoul(value, &value, 0);
541                         }
542                 } else if (strnicmp(data, "version", 3) == 0) {
543                 } else {
544                         printf("CIFS: Unknown mount option %s\n",data);
545                 } */ /* nothing to do on those four mount options above.
546                         Just pass to kernel and ignore them here */
547
548                         /* move to next option */
549                 data = next_keyword+1;
550
551                 /* put overwritten equals sign back */
552                 if(value) {
553                         value--;
554                         *value = '=';
555                 }
556         
557                 /* put previous overwritten comma back */
558                 if(next_keyword)
559                         *next_keyword = ','; /* BB handle sep= */
560                 else
561                         data = NULL;
562         }
563         return 0;
564 }
565
566 /* replace all (one or more) commas with double commas */
567 static void check_for_comma(char ** ppasswrd)
568 {
569         char *new_pass_buf;
570         char *pass;
571         int i,j;
572         int number_of_commas = 0;
573         int len = strlen(*ppasswrd);
574
575         if(ppasswrd == NULL)
576                 return;
577         else 
578                 (pass = *ppasswrd);
579
580         for(i=0;i<len;i++)  {
581                 if(pass[i] == ',')
582                         number_of_commas++;
583         }
584
585         if(number_of_commas == 0)
586                 return;
587         if(number_of_commas > 64) {
588                 /* would otherwise overflow the mount options buffer */
589                 printf("\nInvalid password. Password contains too many commas.\n");
590                 return;
591         }
592
593         new_pass_buf = malloc(len+number_of_commas+1);
594         if(new_pass_buf == NULL)
595                 return;
596
597         for(i=0,j=0;i<len;i++,j++) {
598                 new_pass_buf[j] = pass[i];
599                 if(pass[i] == ',') {
600                         j++;
601                         new_pass_buf[j] = pass[i];
602                 }
603         }
604         new_pass_buf[len+number_of_commas] = 0;
605
606         free(*ppasswrd);
607         *ppasswrd = new_pass_buf;
608         
609         return;
610 }
611
612 /* Usernames can not have backslash in them and we use
613    [BB check if usernames can have forward slash in them BB] 
614    backslash as domain\user separator character
615 */
616 static char * check_for_domain(char **ppuser)
617 {
618         char * original_string;
619         char * usernm;
620         char * domainnm;
621         int    original_len;
622         int    len;
623         int    i;
624
625         if(ppuser == NULL)
626                 return NULL;
627
628         original_string = *ppuser;
629
630         if (original_string == NULL)
631                 return NULL;
632         
633         original_len = strlen(original_string);
634
635         usernm = strchr(*ppuser,'/');
636         if (usernm == NULL) {
637                 usernm = strchr(*ppuser,'\\');
638                 if (usernm == NULL)
639                         return NULL;
640         }
641
642         if(got_domain) {
643                 printf("Domain name specified twice. Username probably malformed\n");
644                 return NULL;
645         }
646
647         usernm[0] = 0;
648         domainnm = *ppuser;
649         if (domainnm[0] != 0) {
650                 got_domain = 1;
651         } else {
652                 printf("null domain\n");
653         }
654         len = strlen(domainnm);
655         /* reset domainm to new buffer, and copy
656         domain name into it */
657         domainnm = malloc(len+1);
658         if(domainnm == NULL)
659                 return NULL;
660
661         strcpy(domainnm,*ppuser);
662
663 /*      move_string(*ppuser, usernm+1) */
664         len = strlen(usernm+1);
665
666         if(len >= original_len) {
667                 /* should not happen */
668                 return domainnm;
669         }
670
671         for(i=0;i<original_len;i++) {
672                 if(i<len)
673                         original_string[i] = usernm[i+1];
674                 else /* stuff with commas to remove last parm */
675                         original_string[i] = ',';
676         }
677
678         /* BB add check for more than one slash? 
679           strchr(*ppuser,'/');
680           strchr(*ppuser,'\\') 
681         */
682         
683         return domainnm;
684 }
685
686 /* Note that caller frees the returned buffer if necessary */
687 static char * parse_server(char ** punc_name)
688 {
689         char * unc_name = *punc_name;
690         int length = strnlen(unc_name,1024);
691         char * share;
692         char * ipaddress_string = NULL;
693         struct hostent * host_entry = NULL;
694         struct in_addr server_ipaddr;
695         int rc;
696
697         if(length > 1023) {
698                 printf("mount error: UNC name too long");
699                 return NULL;
700         }
701         if (strncasecmp("cifs://",unc_name,7) == 0)
702                 return parse_cifs_url(unc_name+7);
703         if (strncasecmp("smb://",unc_name,6) == 0) {
704                 return parse_cifs_url(unc_name+6);
705         }
706
707         if(length < 3) {
708                 /* BB add code to find DFS root here */
709                 printf("\nMounting the DFS root for domain not implemented yet");
710                 return NULL;
711         } else {
712                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
713                         /* check for nfs syntax ie server:share */
714                         share = strchr(unc_name,':');
715                         if(share) {
716                                 free_share_name = 1;
717                                 *punc_name = malloc(length+3);
718                                 *share = '/';
719                                 strncpy((*punc_name)+2,unc_name,length);
720                                 unc_name = *punc_name;
721                                 unc_name[length+2] = 0;
722                                 goto continue_unc_parsing;
723                         } else {
724                                 printf("mount error: improperly formatted UNC name.");
725                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
726                                 return NULL;
727                         }
728                 } else {
729 continue_unc_parsing:
730                         unc_name[0] = '/';
731                         unc_name[1] = '/';
732                         unc_name += 2;
733                         if ((share = strchr(unc_name, '/')) || 
734                                 (share = strchr(unc_name,'\\'))) {
735                                 *share = 0;  /* temporarily terminate the string */
736                                 share += 1;
737                                 if(got_ip == 0) {
738                                         host_entry = gethostbyname(unc_name);
739                                 }
740                                 *(share - 1) = '/'; /* put the slash back */
741                                 if(got_ip) {
742                                         if(verboseflag)
743                                                 printf("ip address specified explicitly\n");
744                                         return NULL;
745                                 }
746                                 if(host_entry == NULL) {
747                                         printf("mount error: could not find target server. TCP name %s not found ", unc_name);
748                                         printf(" rc = %d\n",rc);
749                                         return NULL;
750                                 } else {
751                                         /* BB should we pass an alternate version of the share name as Unicode */
752                                         /* BB what about ipv6? BB */
753                                         /* BB add retries with alternate servers in list */
754
755                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
756
757                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
758                                         if(ipaddress_string == NULL) {
759                                                 printf("mount error: could not get valid ip address for target server\n");
760                                                 return NULL;
761                                         }
762                                         return ipaddress_string; 
763                                 }
764                         } else {
765                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
766                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
767                                 return NULL;
768                         }
769                 }
770         }
771 }
772
773 static struct option longopts[] = {
774         { "all", 0, NULL, 'a' },
775         { "help",0, NULL, 'h' },
776         { "move",0, NULL, 'm' },
777         { "bind",0, NULL, 'b' },
778         { "read-only", 0, NULL, 'r' },
779         { "ro", 0, NULL, 'r' },
780         { "verbose", 0, NULL, 'v' },
781         { "version", 0, NULL, 'V' },
782         { "read-write", 0, NULL, 'w' },
783         { "rw", 0, NULL, 'w' },
784         { "options", 1, NULL, 'o' },
785         { "type", 1, NULL, 't' },
786         { "rsize",1, NULL, 'R' },
787         { "wsize",1, NULL, 'W' },
788         { "uid", 1, NULL, '1'},
789         { "gid", 1, NULL, '2'},
790         { "user",1,NULL,'u'},
791         { "username",1,NULL,'u'},
792         { "dom",1,NULL,'d'},
793         { "domain",1,NULL,'d'},
794         { "password",1,NULL,'p'},
795         { "pass",1,NULL,'p'},
796         { "credentials",1,NULL,'c'},
797         { "port",1,NULL,'P'},
798         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
799         { NULL, 0, NULL, 0 }
800 };
801
802 int main(int argc, char ** argv)
803 {
804         int c;
805         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
806         char * orgoptions = NULL;
807         char * share_name = NULL;
808         char * ipaddr = NULL;
809         char * uuid = NULL;
810         char * mountpoint;
811         char * options;
812         char * resolved_path;
813         char * temp;
814         int rc;
815         int rsize = 0;
816         int wsize = 0;
817         int nomtab = 0;
818         int uid = 0;
819         int gid = 0;
820         int optlen = 0;
821         int orgoptlen = 0;
822         int retry = 0; /* set when we have to retry mount with uppercase */
823         struct stat statbuf;
824         struct utsname sysinfo;
825         struct mntent mountent;
826         FILE * pmntfile;
827
828         /* setlocale(LC_ALL, "");
829         bindtextdomain(PACKAGE, LOCALEDIR);
830         textdomain(PACKAGE); */
831
832         if(argc && argv) {
833                 thisprogram = argv[0];
834         }
835         if(thisprogram == NULL)
836                 thisprogram = "mount.cifs";
837
838         uname(&sysinfo);
839         /* BB add workstation name and domain and pass down */
840
841 /* #ifdef _GNU_SOURCE
842         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
843 #endif */
844
845         share_name = argv[1];
846         mountpoint = argv[2];
847
848         /* add sharename in opts string as unc= parm */
849
850         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
851                          longopts, NULL)) != -1) {
852                 switch (c) {
853 /* No code to do the following  options yet */
854 /*      case 'l':
855                 list_with_volumelabel = 1;
856                 break;
857         case 'L':
858                 volumelabel = optarg;
859                 break; */
860 /*      case 'a':              
861                 ++mount_all;
862                 break; */
863
864                 case '?':
865                 case 'h':        /* help */
866                         mount_cifs_usage ();
867                         exit(1);
868                 case 'n':
869                     ++nomtab;
870                     break;
871                 case 'b':
872                         flags |= MS_BIND;
873                         break;
874                 case 'm':
875                         flags |= MS_MOVE;
876                         break;
877                 case 'o':
878                         orgoptions = strdup(optarg);
879                     break;
880                 case 'r':  /* mount readonly */
881                         flags |= MS_RDONLY;
882                         break;
883                 case 'U':
884                         uuid = optarg;
885                         break;
886                 case 'v':
887                         ++verboseflag;
888                         break;
889                 case 'V':          
890                         printf ("mount.cifs version: %s.%s%s\n",
891                         MOUNT_CIFS_VERSION_MAJOR,
892                         MOUNT_CIFS_VERSION_MINOR,
893                         MOUNT_CIFS_VENDOR_SUFFIX);
894                         if(mountpassword) {
895                                 memset(mountpassword,0,64);
896                         }
897                         exit (0);
898                 case 'w':
899                         flags &= ~MS_RDONLY;
900                         break;
901                 case 'R':
902                         rsize = atoi(optarg) ;
903                         break;
904                 case 'W':
905                         wsize = atoi(optarg);
906                         break;
907                 case '1':
908                         uid = atoi(optarg);
909                         break;
910                 case '2':
911                         gid = atoi(optarg);
912                         break;
913                 case 'u':
914                         got_user = 1;
915                         user_name = optarg;
916                         break;
917                 case 'd':
918                         domain_name = optarg; /* BB fix this - currently ignored */
919                         got_domain = 1;
920                         break;
921                 case 'p':
922                         if(mountpassword == NULL)
923                                 mountpassword = calloc(65,1);
924                         if(mountpassword) {
925                                 got_password = 1;
926                                 strncpy(mountpassword,optarg,64);
927                         }
928                         break;
929                 case 'S':
930                         get_password_from_file(0 /* stdin */,NULL);
931                         break;
932                 case 't':
933                         break;
934                 default:
935                         printf("unknown mount option %c\n",c);
936                         mount_cifs_usage();
937                         exit(1);
938                 }
939         }
940
941         if(argc < 3)
942                 mount_cifs_usage();
943
944         if (getenv("PASSWD")) {
945                 if(mountpassword == NULL)
946                         mountpassword = calloc(65,1);
947                 if(mountpassword) {
948                         strncpy(mountpassword,getenv("PASSWD"),64);
949                         got_password = 1;
950                 }
951         } else if (getenv("PASSWD_FD")) {
952                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
953         } else if (getenv("PASSWD_FILE")) {
954                 get_password_from_file(0, getenv("PASSWD_FILE"));
955         }
956
957         if (orgoptions && parse_options(orgoptions, &flags))
958                 return -1;
959         ipaddr = parse_server(&share_name);
960         if((ipaddr == NULL) && (got_ip == 0)) {
961                 printf("No ip address specified and hostname not found\n");
962                 return -1;
963         }
964         
965         /* BB save off path and pop after mount returns? */
966         resolved_path = malloc(PATH_MAX+1);
967         if(resolved_path) {
968                 /* Note that if we can not canonicalize the name, we get
969                 another chance to see if it is valid when we chdir to it */
970                 if (realpath(mountpoint, resolved_path)) {
971                         mountpoint = resolved_path; 
972                 }
973         }
974         if(chdir(mountpoint)) {
975                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
976                 return -1;
977         }
978
979         if(stat (".", &statbuf)) {
980                 printf("mount error: mount point %s does not exist\n",mountpoint);
981                 return -1;
982         }
983
984         if (S_ISDIR(statbuf.st_mode) == 0) {
985                 printf("mount error: mount point %s is not a directory\n",mountpoint);
986                 return -1;
987         }
988
989         if((getuid() != 0) && (geteuid() == 0)) {
990                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
991 #ifndef CIFS_ALLOW_USR_SUID
992                         /* Do not allow user mounts to control suid flag
993                         for mount unless explicitly built that way */
994                         flags |= MS_NOSUID | MS_NODEV;
995 #endif                                          
996                 } else {
997                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
998                         return -1;
999                 }
1000         }
1001
1002         if(got_user == 0) {
1003                 user_name = getusername();
1004                 got_user = 1;
1005         }
1006        
1007         if(got_password == 0) {
1008                 mountpassword = getpass("Password: "); /* BB obsolete */
1009                 got_password = 1;
1010         }
1011         /* FIXME launch daemon (handles dfs name resolution and credential change) 
1012            remember to clear parms and overwrite password field before launching */
1013 mount_retry:
1014         if(orgoptions) {
1015                 optlen = strlen(orgoptions);
1016                 orgoptlen = optlen;
1017         } else
1018                 optlen = 0;
1019         if(share_name)
1020                 optlen += strlen(share_name) + 4;
1021         if(user_name)
1022                 optlen += strlen(user_name) + 6;
1023         if(ipaddr)
1024                 optlen += strlen(ipaddr) + 4;
1025         if(mountpassword)
1026                 optlen += strlen(mountpassword) + 6;
1027         options = malloc(optlen + 10 + 64 /* space for commas in password */ + 8 /* space for domain=  , domain name itself was counted as part of the length username string above */);
1028
1029         if(options == NULL) {
1030                 printf("Could not allocate memory for mount options\n");
1031                 return -1;
1032         }
1033                 
1034
1035         options[0] = 0;
1036         strncat(options,"unc=",4);
1037         strcat(options,share_name);
1038         /* scan backwards and reverse direction of slash */
1039         temp = strrchr(options, '/');
1040         if(temp > options + 6)
1041                 *temp = '\\';
1042         if(ipaddr) {
1043                 strncat(options,",ip=",4);
1044                 strcat(options,ipaddr);
1045         }
1046
1047         if(user_name) {
1048                 /* check for syntax like user=domain\user */
1049                 if(got_domain == 0)
1050                         domain_name = check_for_domain(&user_name);
1051                 strncat(options,",user=",6);
1052                 strcat(options,user_name);
1053         }
1054         if(retry == 0) {
1055                 if(domain_name) { 
1056                         /* extra length accounted for in option string above */
1057                         strncat(options,",domain=",8);
1058                         strcat(options,domain_name);
1059                 }
1060         }
1061         if(mountpassword) {
1062                 /* Commas have to be doubled, or else they will
1063                 look like the parameter separator */
1064 /*              if(sep is not set)*/
1065                 if(retry == 0)
1066                         check_for_comma(&mountpassword);
1067                 strncat(options,",pass=",6);
1068                 strcat(options,mountpassword);
1069         }
1070
1071         strncat(options,",ver=",5);
1072         strcat(options,MOUNT_CIFS_VERSION_MAJOR);
1073
1074         if(orgoptions) {
1075                 strcat(options,",");
1076                 strcat(options,orgoptions);
1077         }
1078         if(verboseflag)
1079                 printf("\nmount.cifs kernel mount options %s \n",options);
1080         if(mount(share_name, mountpoint, "cifs", flags, options)) {
1081         /* remember to kill daemon on error */
1082                 char * tmp;
1083
1084                 switch (errno) {
1085                 case 0:
1086                         printf("mount failed but no error number set\n");
1087                         break;
1088                 case ENODEV:
1089                         printf("mount error: cifs filesystem not supported by the system\n");
1090                         break;
1091                 case ENXIO:
1092                         if(retry == 0) {
1093                                 retry = 1;
1094                                 tmp = share_name;
1095                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
1096                                         *tmp = toupper((unsigned char)*tmp);
1097                                         tmp++;
1098                                 }
1099                                 if(!*tmp) {
1100                                         printf("retrying with upper case share name\n");
1101                                         goto mount_retry;
1102                                 }
1103                         }
1104                 default:
1105                         
1106                         printf("mount error %d = %s\n",errno,strerror(errno));
1107                 }
1108                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
1109                 if(mountpassword) {
1110                         memset(mountpassword,0,64);
1111                 }
1112                 return -1;
1113         } else {
1114                 pmntfile = setmntent(MOUNTED, "a+");
1115                 if(pmntfile) {
1116                         mountent.mnt_fsname = share_name;
1117                         mountent.mnt_dir = mountpoint; 
1118                         mountent.mnt_type = "cifs"; 
1119                         mountent.mnt_opts = malloc(220);
1120                         if(mountent.mnt_opts) {
1121                                 char * mount_user = getusername();
1122                                 memset(mountent.mnt_opts,0,200);
1123                                 if(flags & MS_RDONLY)
1124                                         strcat(mountent.mnt_opts,"ro");
1125                                 else
1126                                         strcat(mountent.mnt_opts,"rw");
1127                                 if(flags & MS_MANDLOCK)
1128                                         strcat(mountent.mnt_opts,",mand");
1129                                 else
1130                                         strcat(mountent.mnt_opts,",nomand");
1131                                 if(flags & MS_NOEXEC)
1132                                         strcat(mountent.mnt_opts,",noexec");
1133                                 if(flags & MS_NOSUID)
1134                                         strcat(mountent.mnt_opts,",nosuid");
1135                                 if(flags & MS_NODEV)
1136                                         strcat(mountent.mnt_opts,",nodev");
1137                                 if(flags & MS_SYNCHRONOUS)
1138                                         strcat(mountent.mnt_opts,",synch");
1139                                 if(mount_user) {
1140                                         if(getuid() != 0) {
1141                                                 strcat(mountent.mnt_opts,",user=");
1142                                                 strcat(mountent.mnt_opts,mount_user);
1143                                         }
1144                                         free(mount_user);
1145                                 }
1146                         }
1147                         mountent.mnt_freq = 0;
1148                         mountent.mnt_passno = 0;
1149                         rc = addmntent(pmntfile,&mountent);
1150                         endmntent(pmntfile);
1151                         if(mountent.mnt_opts)
1152                                 free(mountent.mnt_opts);
1153                 } else {
1154                     printf("could not update mount table\n");
1155                 }
1156         }
1157         if(mountpassword) {
1158                 int len = strlen(mountpassword);
1159                 memset(mountpassword,0,len);
1160                 free(mountpassword);
1161         }
1162
1163         if(options) {
1164                 memset(options,0,optlen);
1165                 free(options);
1166         }
1167
1168         if(orgoptions) {
1169                 memset(orgoptions,0,orgoptlen);
1170                 free(orgoptions);
1171         }
1172         if(resolved_path) {
1173                 free(resolved_path);
1174         }
1175
1176         if(free_share_name) {
1177                 free(share_name);
1178                 }
1179         return 0;
1180 }
1181