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