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