r5688: ignore user_xattr mount parm so as not to confuse it with a user name
[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,directio");
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_xattr",10) == 0) {
299                    /* do nothing - need to skip so not parsed as user name */
300                 } else if (strncmp(data, "user", 4) == 0) {
301                         if (!value || !*value) {
302                                 if(data[4] == '\0') {
303                                         if(verboseflag)
304                                                 printf("\nskipping empty user mount parameter\n");
305                                         /* remove the parm since it would otherwise be confusing
306                                         to the kernel code which would think it was a real username */
307                                                 data[0] = ',';
308                                                 data[1] = ',';
309                                                 data[2] = ',';
310                                                 data[3] = ',';
311                                         /* BB remove it from mount line so as not to confuse kernel code */
312                                 } else {
313                                         printf("username specified with no parameter\n");
314                                         return 1;       /* needs_arg; */
315                                 }
316                         } else {
317                                 if (strnlen(value, 260) < 260) {
318                                         got_user=1;
319                                         percent_char = strchr(value,'%');
320                                         if(percent_char) {
321                                                 *percent_char = ',';
322                                                 if(mountpassword == NULL)
323                                                         mountpassword = calloc(65,1);
324                                                 if(mountpassword) {
325                                                         if(got_password)
326                                                                 printf("\nmount.cifs warning - password specified twice\n");
327                                                         got_password = 1;
328                                                         percent_char++;
329                                                         strncpy(mountpassword, percent_char,64);
330                                                 /*  remove password from username */
331                                                         while(*percent_char != 0) {
332                                                                 *percent_char = ',';
333                                                                 percent_char++;
334                                                         }
335                                                 }
336                                         }
337                                 } else {
338                                         printf("username too long\n");
339                                         return 1;
340                                 }
341                         }
342                 } else if (strncmp(data, "pass", 4) == 0) {
343                         if (!value || !*value) {
344                                 if(got_password) {
345                                         printf("\npassword specified twice, ignoring second\n");
346                                 } else
347                                         got_password = 1;
348                         } else if (strnlen(value, 17) < 17) {
349                                 if(got_password)
350                                         printf("\nmount.cifs warning - password specified twice\n");
351                                 got_password = 1;
352                         } else {
353                                 printf("password too long\n");
354                                 return 1;
355                         }
356                 } else if (strncmp(data, "ip", 2) == 0) {
357                         if (!value || !*value) {
358                                 printf("target ip address argument missing");
359                         } else if (strnlen(value, 35) < 35) {
360                                 if(verboseflag)
361                                         printf("ip address %s override specified\n",value);
362                                 got_ip = 1;
363                         } else {
364                                 printf("ip address too long\n");
365                                 return 1;
366                         }
367                 } else if ((strncmp(data, "unc", 3) == 0)
368                    || (strncmp(data, "target", 6) == 0)
369                    || (strncmp(data, "path", 4) == 0)) {
370                         if (!value || !*value) {
371                                 printf("invalid path to network resource\n");
372                                 return 1;  /* needs_arg; */
373                         } else if(strnlen(value,5) < 5) {
374                                 printf("UNC name too short");
375                         }
376
377                         if (strnlen(value, 300) < 300) {
378                                 got_unc = 1;
379                                 if (strncmp(value, "//", 2) == 0) {
380                                         if(got_unc)
381                                                 printf("unc name specified twice, ignoring second\n");
382                                         else
383                                                 got_unc = 1;
384                                 } else if (strncmp(value, "\\\\", 2) != 0) {                       
385                                         printf("UNC Path does not begin with // or \\\\ \n");
386                                         return 1;
387                                 } else {
388                                         if(got_unc)
389                                                 printf("unc name specified twice, ignoring second\n");
390                                         else
391                                                 got_unc = 1;
392                                 }
393                         } else {
394                                 printf("CIFS: UNC name too long\n");
395                                 return 1;
396                         }
397                 } else if ((strncmp(data, "domain", 3) == 0)
398                            || (strncmp(data, "workgroup", 5) == 0)) {
399                         if (!value || !*value) {
400                                 printf("CIFS: invalid domain name\n");
401                                 return 1;       /* needs_arg; */
402                         }
403                         if (strnlen(value, 65) < 65) {
404                                 got_domain = 1;
405                         } else {
406                                 printf("domain name too long\n");
407                                 return 1;
408                         }
409                 } else if (strncmp(data, "cred", 4) == 0) {
410                         if (value && *value) {
411                                 rc = open_cred_file(value);
412                                 if(rc) {
413                                         printf("error %d opening credential file %s\n",rc, value);
414                                         return 1;
415                                 }
416                         } else {
417                                 printf("invalid credential file name specified\n");
418                                 return 1;
419                         }
420                 } else if (strncmp(data, "uid", 3) == 0) {
421                         if (value && *value) {
422                                 got_uid = 1;
423                         }
424                 } else if (strncmp(data, "gid", 3) == 0) {
425                         if (value && *value) {
426                                 got_gid = 1;
427                         }
428        /* fmask and dmask synonyms for people used to smbfs syntax */
429                 } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
430                         if (!value || !*value) {
431                                 printf ("Option '%s' requires a numerical argument\n", data);
432                                 return 1;
433                         }
434
435                         if (value[0] != '0') {
436                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
437                         }
438
439                         if (strcmp (data, "fmask") == 0) {
440                                 printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
441                                 data = "file_mode"; /* BB fix this */
442                         }
443                 } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
444                         if (!value || !*value) {
445                                 printf ("Option '%s' requires a numerical argument\n", data);
446                                 return 1;
447                         }
448
449                         if (value[0] != '0') {
450                                 printf ("WARNING: '%s' not expressed in octal.\n", data);
451                         }
452
453                         if (strcmp (data, "dmask") == 0) {
454                                 printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
455                                 data = "dir_mode";
456                         }
457                         /* the following eight mount options should be
458                         stripped out from what is passed into the kernel
459                         since these eight options are best passed as the
460                         mount flags rather than redundantly to the kernel 
461                         and could generate spurious warnings depending on the
462                         level of the corresponding cifs vfs kernel code */
463                 } else if (strncmp(data, "nosuid", 6) == 0) {
464                         *filesys_flags |= MS_NOSUID;
465                 } else if (strncmp(data, "suid", 4) == 0) {
466                         *filesys_flags &= ~MS_NOSUID;
467                 } else if (strncmp(data, "nodev", 5) == 0) {
468                         *filesys_flags |= MS_NODEV;
469                 } else if (strncmp(data, "dev", 3) == 0) {
470                         *filesys_flags &= ~MS_NODEV;
471                 } else if (strncmp(data, "noexec", 6) == 0) {
472                         *filesys_flags |= MS_NOEXEC;
473                 } else if (strncmp(data, "exec", 4) == 0) {
474                         *filesys_flags &= ~MS_NOEXEC;
475                 } else if (strncmp(data, "guest", 5) == 0) {
476                         got_password=1;
477                         /* remove the parm since it would otherwise be logged by kern */
478                         data[0] = ',';
479                         data[1] = ',';
480                         data[2] = ',';
481                         data[3] = ',';
482                         data[4] = ',';
483                 } else if (strncmp(data, "ro", 2) == 0) {
484                         *filesys_flags |= MS_RDONLY;
485                 } else if (strncmp(data, "rw", 2) == 0) {
486                         *filesys_flags &= ~MS_RDONLY;
487                 } /* else if (strnicmp(data, "port", 4) == 0) {
488                         if (value && *value) {
489                                 vol->port =
490                                         simple_strtoul(value, &value, 0);
491                         }
492                 } else if (strnicmp(data, "rsize", 5) == 0) {
493                         if (value && *value) {
494                                 vol->rsize =
495                                         simple_strtoul(value, &value, 0);
496                         }
497                 } else if (strnicmp(data, "wsize", 5) == 0) {
498                         if (value && *value) {
499                                 vol->wsize =
500                                         simple_strtoul(value, &value, 0);
501                         }
502                 } else if (strnicmp(data, "version", 3) == 0) {
503                 } else {
504                         printf("CIFS: Unknown mount option %s\n",data);
505                 } */ /* nothing to do on those four mount options above.
506                         Just pass to kernel and ignore them here */
507
508                         /* move to next option */
509                 data = next_keyword+1;
510
511                 /* put overwritten equals sign back */
512                 if(value) {
513                         value--;
514                         *value = '=';
515                 }
516         
517                 /* put previous overwritten comma back */
518                 if(next_keyword)
519                         *next_keyword = ',';
520                 else
521                         data = NULL;
522         }
523         return 0;
524 }
525
526 /* Note that caller frees the returned buffer if necessary */
527 char * parse_server(char ** punc_name)
528 {
529         char * unc_name = *punc_name;
530         int length = strnlen(unc_name,1024);
531         char * share;
532         char * ipaddress_string = NULL;
533         struct hostent * host_entry;
534         struct in_addr server_ipaddr;
535         int rc;
536
537         if(length > 1023) {
538                 printf("mount error: UNC name too long");
539                 return NULL;
540         }
541         if (strncasecmp("cifs://",unc_name,7) == 0)
542                 return parse_cifs_url(unc_name+7);
543         if (strncasecmp("smb://",unc_name,6) == 0) {
544                 return parse_cifs_url(unc_name+6);
545         }
546
547         if(length < 3) {
548                 /* BB add code to find DFS root here */
549                 printf("\nMounting the DFS root for domain not implemented yet");
550                 return NULL;
551         } else {
552                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
553                         /* check for nfs syntax ie server:share */
554                         share = strchr(unc_name,':');
555                         if(share) {
556                                 free_share_name = 1;
557                                 *punc_name = malloc(length+3);
558                                 *share = '/';
559                                 strncpy((*punc_name)+2,unc_name,length);
560                                 unc_name = *punc_name;
561                                 unc_name[length+2] = 0;
562                                 goto continue_unc_parsing;
563                         } else {
564                                 printf("mount error: improperly formatted UNC name.");
565                                 printf(" %s does not begin with \\\\ or //\n",unc_name);
566                                 return NULL;
567                         }
568                 } else {
569 continue_unc_parsing:
570                         unc_name[0] = '/';
571                         unc_name[1] = '/';
572                         unc_name += 2;
573                         if ((share = strchr(unc_name, '/')) || 
574                                 (share = strchr(unc_name,'\\'))) {
575                                 *share = 0;  /* temporarily terminate the string */
576                                 share += 1;
577                                 if(got_ip == 0) {
578                                         host_entry = gethostbyname(unc_name);
579                                 }
580                                 *(share - 1) = '/'; /* put the slash back */
581                                 if(got_ip) {
582                                         if(verboseflag)
583                                                 printf("ip address specified explicitly\n");
584                                         return NULL;
585                                 }
586                                 if(host_entry == NULL) {
587                                         printf("mount error: could not find target server. TCP name %s not found ", unc_name);
588                                         printf(" rc = %d\n",rc);
589                                         return NULL;
590                                 } else {
591                                         /* BB should we pass an alternate version of the share name as Unicode */
592                                         /* BB what about ipv6? BB */
593                                         /* BB add retries with alternate servers in list */
594
595                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
596
597                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
598                                         if(ipaddress_string == NULL) {
599                                                 printf("mount error: could not get valid ip address for target server\n");
600                                                 return NULL;
601                                         }
602                                         return ipaddress_string; 
603                                 }
604                         } else {
605                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
606                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
607                                 return NULL;
608                         }
609                 }
610         }
611 }
612
613 static struct option longopts[] = {
614         { "all", 0, NULL, 'a' },
615         { "help",0, NULL, 'h' },
616         { "move",0, NULL, 'm' },
617         { "bind",0, NULL, 'b' },
618         { "read-only", 0, NULL, 'r' },
619         { "ro", 0, NULL, 'r' },
620         { "verbose", 0, NULL, 'v' },
621         { "version", 0, NULL, 'V' },
622         { "read-write", 0, NULL, 'w' },
623         { "rw", 0, NULL, 'w' },
624         { "options", 1, NULL, 'o' },
625         { "type", 1, NULL, 't' },
626         { "rsize",1, NULL, 'R' },
627         { "wsize",1, NULL, 'W' },
628         { "uid", 1, NULL, '1'},
629         { "gid", 1, NULL, '2'},
630         { "user",1,NULL,'u'},
631         { "username",1,NULL,'u'},
632         { "dom",1,NULL,'d'},
633         { "domain",1,NULL,'d'},
634         { "password",1,NULL,'p'},
635         { "pass",1,NULL,'p'},
636         { "credentials",1,NULL,'c'},
637         { "port",1,NULL,'P'},
638         /* { "uuid",1,NULL,'U'}, */ /* BB unimplemented */
639         { NULL, 0, NULL, 0 }
640 };
641
642 int main(int argc, char ** argv)
643 {
644         int c;
645         int flags = MS_MANDLOCK; /* no need to set legacy MS_MGC_VAL */
646         char * orgoptions = NULL;
647         char * share_name = NULL;
648         char * domain_name = NULL;
649         char * ipaddr = NULL;
650         char * uuid = NULL;
651         char * mountpoint;
652         char * options;
653         char * resolved_path;
654         char * temp;
655         int rc;
656         int rsize = 0;
657         int wsize = 0;
658         int nomtab = 0;
659         int uid = 0;
660         int gid = 0;
661         int optlen = 0;
662         int orgoptlen = 0;
663         int retry = 0; /* set when we have to retry mount with uppercase */
664         struct stat statbuf;
665         struct utsname sysinfo;
666         struct mntent mountent;
667         FILE * pmntfile;
668
669         /* setlocale(LC_ALL, "");
670         bindtextdomain(PACKAGE, LOCALEDIR);
671         textdomain(PACKAGE); */
672
673         if(argc && argv) {
674                 thisprogram = argv[0];
675         }
676         if(thisprogram == NULL)
677                 thisprogram = "mount.cifs";
678
679         uname(&sysinfo);
680         /* BB add workstation name and domain and pass down */
681
682 /* #ifdef _GNU_SOURCE
683         printf(" node: %s machine: %s sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
684 #endif */
685
686         share_name = argv[1];
687         mountpoint = argv[2];
688
689         /* add sharename in opts string as unc= parm */
690
691         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsSU:vVwt:",
692                          longopts, NULL)) != -1) {
693                 switch (c) {
694 /* No code to do the following  options yet */
695 /*      case 'l':
696                 list_with_volumelabel = 1;
697                 break;
698         case 'L':
699                 volumelabel = optarg;
700                 break; */
701 /*      case 'a':              
702                 ++mount_all;
703                 break; */
704
705                 case '?':
706                 case 'h':        /* help */
707                         mount_cifs_usage ();
708                         exit(1);
709                 case 'n':
710                     ++nomtab;
711                     break;
712                 case 'b':
713                         flags |= MS_BIND;
714                         break;
715                 case 'm':
716                         flags |= MS_MOVE;
717                         break;
718                 case 'o':
719                         orgoptions = strdup(optarg);
720                     break;
721                 case 'r':  /* mount readonly */
722                         flags |= MS_RDONLY;
723                         break;
724                 case 'U':
725                         uuid = optarg;
726                         break;
727                 case 'v':
728                         ++verboseflag;
729                         break;
730                 case 'V':          
731                         printf ("mount.cifs version: %s.%s%s\n",
732                         MOUNT_CIFS_VERSION_MAJOR,
733                         MOUNT_CIFS_VERSION_MINOR,
734                         MOUNT_CIFS_VENDOR_SUFFIX);
735                         if(mountpassword) {
736                                 memset(mountpassword,0,64);
737                         }
738                         exit (0);
739                 case 'w':
740                         flags &= ~MS_RDONLY;
741                         break;
742                 case 'R':
743                         rsize = atoi(optarg) ;
744                         break;
745                 case 'W':
746                         wsize = atoi(optarg);
747                         break;
748                 case '1':
749                         uid = atoi(optarg);
750                         break;
751                 case '2':
752                         gid = atoi(optarg);
753                         break;
754                 case 'u':
755                         got_user = 1;
756                         user_name = optarg;
757                         break;
758                 case 'd':
759                         domain_name = optarg;
760                         break;
761                 case 'p':
762                         if(mountpassword == NULL)
763                                 mountpassword = calloc(65,1);
764                         if(mountpassword) {
765                                 got_password = 1;
766                                 strncpy(mountpassword,optarg,64);
767                         }
768                         break;
769                 case 'S':
770                         get_password_from_file(0 /* stdin */,NULL);
771                         break;
772                 case 't':
773                         break;
774                 default:
775                         printf("unknown mount option %c\n",c);
776                         mount_cifs_usage();
777                         exit(1);
778                 }
779         }
780
781         if(argc < 3)
782                 mount_cifs_usage();
783
784         if (getenv("PASSWD")) {
785                 if(mountpassword == NULL)
786                         mountpassword = calloc(65,1);
787                 if(mountpassword) {
788                         strncpy(mountpassword,getenv("PASSWD"),64);
789                         got_password = 1;
790                 }
791         } else if (getenv("PASSWD_FD")) {
792                 get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
793         } else if (getenv("PASSWD_FILE")) {
794                 get_password_from_file(0, getenv("PASSWD_FILE"));
795         }
796
797         if (orgoptions && parse_options(orgoptions, &flags))
798                 return -1;
799         
800         ipaddr = parse_server(&share_name);
801         if((ipaddr == NULL) && (got_ip == 0)) {
802                 printf("No ip address specified and hostname not found\n");
803                 return -1;
804         }
805         
806
807         /* BB save off path and pop after mount returns? */
808         resolved_path = malloc(PATH_MAX+1);
809         if(resolved_path) {
810                 /* Note that if we can not canonicalize the name, we get
811                 another chance to see if it is valid when we chdir to it */
812                 if (realpath(mountpoint, resolved_path)) {
813                         mountpoint = resolved_path; 
814                 }
815         }
816         if(chdir(mountpoint)) {
817                 printf("mount error: can not change directory into mount target %s\n",mountpoint);
818                 return -1;
819         }
820
821         if(stat (".", &statbuf)) {
822                 printf("mount error: mount point %s does not exist\n",mountpoint);
823                 return -1;
824         }
825
826         if (S_ISDIR(statbuf.st_mode) == 0) {
827                 printf("mount error: mount point %s is not a directory\n",mountpoint);
828                 return -1;
829         }
830
831         if((getuid() != 0) && (geteuid() == 0)) {
832                 if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
833 #ifndef CIFS_ALLOW_USR_SUID
834                         /* Do not allow user mounts to control suid flag
835                         for mount unless explicitly built that way */
836                         flags |= MS_NOSUID | MS_NODEV;
837 #endif                                          
838                 } else {
839                         printf("mount error: permission denied or not superuser and mount.cifs not installed SUID\n"); 
840                         return -1;
841                 }
842         }
843
844         if(got_user == 0)
845                 user_name = getusername();
846        
847         if(got_password == 0) {
848                 mountpassword = getpass("Password: "); /* BB obsolete */
849                 got_password = 1;
850         }
851         /* FIXME launch daemon (handles dfs name resolution and credential change) 
852            remember to clear parms and overwrite password field before launching */
853 mount_retry:
854         if(orgoptions) {
855                 optlen = strlen(orgoptions);
856                 orgoptlen = optlen;
857         } else
858                 optlen = 0;
859         if(share_name)
860                 optlen += strlen(share_name) + 4;
861         if(user_name)
862                 optlen += strlen(user_name) + 6;
863         if(ipaddr)
864                 optlen += strlen(ipaddr) + 4;
865         if(mountpassword)
866                 optlen += strlen(mountpassword) + 6;
867         options = malloc(optlen + 10);
868
869         if(options == NULL) {
870                 printf("Could not allocate memory for mount options\n");
871                 return -1;
872         }
873                 
874
875         options[0] = 0;
876         strncat(options,"unc=",4);
877         strcat(options,share_name);
878         /* scan backwards and reverse direction of slash */
879         temp = strrchr(options, '/');
880         if(temp > options + 6)
881                 *temp = '\\';
882         if(ipaddr) {
883                 strncat(options,",ip=",4);
884                 strcat(options,ipaddr);
885         } 
886         if(user_name) {
887                 strncat(options,",user=",6);
888                 strcat(options,user_name);
889         } 
890         if(mountpassword) {
891                 strncat(options,",pass=",6);
892                 strcat(options,mountpassword);
893         }
894         strncat(options,",ver=",5);
895         strcat(options,MOUNT_CIFS_VERSION_MAJOR);
896
897         if(orgoptions) {
898                 strcat(options,",");
899                 strcat(options,orgoptions);
900         }
901         if(verboseflag)
902                 printf("\nmount.cifs kernel mount options %s \n",options);
903         if(mount(share_name, mountpoint, "cifs", flags, options)) {
904         /* remember to kill daemon on error */
905                 char * tmp;
906
907                 switch (errno) {
908                 case 0:
909                         printf("mount failed but no error number set\n");
910                         break;
911                 case ENODEV:
912                         printf("mount error: cifs filesystem not supported by the system\n");
913                         break;
914                 case ENXIO:
915                         if(retry == 0) {
916                                 retry = 1;
917                                 tmp = share_name;
918                                 while (*tmp && !(((unsigned char)tmp[0]) & 0x80)) {
919                                         *tmp = toupper((unsigned char)*tmp);
920                                         tmp++;
921                                 }
922                                 if(!*tmp) {
923                                         printf("retrying with upper case share name\n");
924                                         goto mount_retry;
925                                 }
926                         }
927                 default:
928                         
929                         printf("mount error %d = %s\n",errno,strerror(errno));
930                 }
931                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
932                 if(mountpassword) {
933                         memset(mountpassword,0,64);
934                 }
935                 return -1;
936         } else {
937                 pmntfile = setmntent(MOUNTED, "a+");
938                 if(pmntfile) {
939                         mountent.mnt_fsname = share_name;
940                         mountent.mnt_dir = mountpoint; 
941                         mountent.mnt_type = "cifs"; 
942                         mountent.mnt_opts = malloc(220);
943                         if(mountent.mnt_opts) {
944                                 char * mount_user = getusername();
945                                 memset(mountent.mnt_opts,0,200);
946                                 if(flags & MS_RDONLY)
947                                         strcat(mountent.mnt_opts,"ro");
948                                 else
949                                         strcat(mountent.mnt_opts,"rw");
950                                 if(flags & MS_MANDLOCK)
951                                         strcat(mountent.mnt_opts,",mand");
952                                 else
953                                         strcat(mountent.mnt_opts,",nomand");
954                                 if(flags & MS_NOEXEC)
955                                         strcat(mountent.mnt_opts,",noexec");
956                                 if(flags & MS_NOSUID)
957                                         strcat(mountent.mnt_opts,",nosuid");
958                                 if(flags & MS_NODEV)
959                                         strcat(mountent.mnt_opts,",nodev");
960                                 if(flags & MS_SYNCHRONOUS)
961                                         strcat(mountent.mnt_opts,",synch");
962                                 if(mount_user) {
963                                         if(getuid() != 0) {
964                                                 strcat(mountent.mnt_opts,",user=");
965                                                 strcat(mountent.mnt_opts,mount_user);
966                                         }
967                                         free(mount_user);
968                                 }
969                         }
970                         mountent.mnt_freq = 0;
971                         mountent.mnt_passno = 0;
972                         rc = addmntent(pmntfile,&mountent);
973                         endmntent(pmntfile);
974                         if(mountent.mnt_opts)
975                                 free(mountent.mnt_opts);
976                 } else {
977                     printf("could not update mount table\n");
978                 }
979         }
980         if(mountpassword) {
981                 memset(mountpassword,0,64);
982                 free(mountpassword);
983         }
984
985         if(options) {
986                 memset(options,0,optlen);
987                 free(options);
988         }
989
990         if(orgoptions) {
991                 memset(orgoptions,0,orgoptlen);
992                 free(orgoptions);
993         }
994         if(resolved_path) {
995                 free(resolved_path);
996         }
997
998         if(free_share_name) {
999                 free(share_name);
1000                 }
1001         return 0;
1002 }
1003