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