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