Merge branch 'master' of git://git.samba.org/samba into teventfix
[ira/wip.git] / source4 / client / mount.cifs.c
1 #define _GNU_SOURCE
2
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <pwd.h>
6 #include <sys/types.h>
7 #include <sys/mount.h>
8 #include <sys/stat.h>
9 #include <sys/utsname.h>
10 #include <sys/socket.h>
11 #include <arpa/inet.h>
12 #include <getopt.h>
13 #include <errno.h>
14 #include <netdb.h>
15 #include <string.h>
16 #include <mntent.h>
17
18 #define MOUNT_CIFS_VERSION "1"
19
20 extern char *getusername(void);
21
22 char * thisprogram;
23 int verboseflag = 0;
24 static int got_password = 0;
25 static int got_user = 0;
26 static int got_domain = 0;
27 static int got_ip = 0;
28 static int got_unc = 0;
29 static int got_uid = 0;
30 static int got_gid = 0;
31 static char * user_name = NULL;
32 char * mountpassword = NULL;
33
34
35 void mount_cifs_usage()
36 {
37         printf("\nUsage:  %s remotetarget dir\n", thisprogram);
38         printf("\nMount the remotetarget, specified as either a UNC name or ");
39         printf(" CIFS URL, to the local directory, dir.\n");
40
41         exit(1);
42 }
43
44 /* caller frees username if necessary */
45 char * getusername() {
46         char *username = NULL;
47         struct passwd *password = getpwuid(getuid());
48
49         if (password) {
50                 username = password->pw_name;
51         }
52         return username;
53 }
54
55 char * parse_cifs_url(unc_name)
56 {
57         printf("\ncifs url %s\n",unc_name);
58 }
59
60 int parse_options(char * options)
61 {
62         char * data;
63         char * value = 0;
64
65         if (!options)
66                 return 1;
67
68         while ((data = strsep(&options, ",")) != NULL) {
69                 if (!*data)
70                         continue;
71                 if ((value = strchr(data, '=')) != NULL) {
72                         *value++ = '\0';
73                 }
74                 if (strncmp(data, "user", 4) == 0) {
75                         if (!value || !*value) {
76                                 printf("invalid or missing username\n");
77                                 return 1;       /* needs_arg; */
78                         }
79                         if (strnlen(value, 260) < 260) {
80                                 got_user=1;
81                                 /* BB add check for format user%pass */
82                                 /* if(strchr(username%passw) got_password = 1) */
83                         } else {
84                                 printf("username too long\n");
85                                 return 1;
86                         }
87         } else if (strncmp(data, "pass", 4) == 0) {
88                 if (!value || !*value) {
89                         if(got_password) {
90                                 printf("password specified twice, ignoring second\n");
91                         } else
92                                 got_password = 1;
93                 } else if (strnlen(value, 17) < 17) {
94                         got_password = 1;
95                 } else {
96                         printf("password too long\n");
97                         return 1;
98                 }
99         } else if (strncmp(data, "ip", 2) == 0) {
100                 if (!value || !*value) {
101                         printf("target ip address argument missing");
102                 } else if (strnlen(value, 35) < 35) {
103                         got_ip = 1;
104                 } else {
105                         printf("ip address too long\n");
106                         return 1;
107                 }
108         } else if ((strncmp(data, "unc", 3) == 0)
109                    || (strncmp(data, "target", 6) == 0)
110                    || (strncmp(data, "path", 4) == 0)) {
111                 if (!value || !*value) {
112                         printf("invalid path to network resource\n");
113                         return 1;  /* needs_arg; */
114                 } else if(strnlen(value,5) < 5) {
115                         printf("UNC name too short");
116                 }
117
118                 if (strnlen(value, 300) < 300) {
119                         got_unc = 1;
120                         if (strncmp(value, "//", 2) == 0) {
121                                 if(got_unc)
122                                         printf("unc name specified twice, ignoring second\n");
123                                 else
124                                         got_unc = 1;
125                         } else if (strncmp(value, "\\\\", 2) != 0) {                       
126                                 printf("UNC Path does not begin with // or \\\\ \n");
127                                 return 1;
128                         } else {
129                                 if(got_unc)
130                                         printf("unc name specified twice, ignoring second\n");
131                                 else
132                                         got_unc = 1;
133                         }
134                 } else {
135                         printf("CIFS: UNC name too long\n");
136                         return 1;
137                 }
138         } else if ((strncmp(data, "domain", 3) == 0)
139                    || (strncmp(data, "workgroup", 5) == 0)) {
140                 if (!value || !*value) {
141                         printf("CIFS: invalid domain name\n");
142                         return 1;       /* needs_arg; */
143                 }
144                 if (strnlen(value, 65) < 65) {
145                         got_domain = 1;
146                 } else {
147                         printf("domain name too long\n");
148                         return 1;
149                 }
150         } else if (strncmp(data, "uid", 3) == 0) {
151                 if (value && *value) {
152                         got_uid = 1;
153                 }
154         } else if (strncmp(data, "gid", 3) == 0) {
155                 if (value && *value) {
156                         got_gid = 1;
157                 }
158         } /* else if (strnicmp(data, "file_mode", 4) == 0) {
159                 if (value && *value) {
160                         vol->file_mode =
161                                 simple_strtoul(value, &value, 0);
162                 }
163         } else if (strnicmp(data, "dir_mode", 3) == 0) {
164                 if (value && *value) {
165                         vol->dir_mode =
166                                 simple_strtoul(value, &value, 0);
167                 }
168         } else if (strnicmp(data, "port", 4) == 0) {
169                 if (value && *value) {
170                         vol->port =
171                                 simple_strtoul(value, &value, 0);
172                 }
173         } else if (strnicmp(data, "rsize", 5) == 0) {
174                 if (value && *value) {
175                         vol->rsize =
176                                 simple_strtoul(value, &value, 0);
177                 }
178         } else if (strnicmp(data, "wsize", 5) == 0) {
179                 if (value && *value) {
180                         vol->wsize =
181                                 simple_strtoul(value, &value, 0);
182                 }
183         } else if (strnicmp(data, "version", 3) == 0) {
184                 
185         } else if (strnicmp(data, "rw", 2) == 0) {
186                 
187         } else
188                 printf("CIFS: Unknown mount option %s\n",data); */
189         }
190         return 0;
191 }
192
193 /* Note that caller frees the returned buffer if necessary */
194 char * parse_server(char * unc_name)
195 {
196         int length = strnlen(unc_name,1024);
197         char * share;
198         char * ipaddress_string = NULL;
199         struct hostent * host_entry;
200         struct in_addr server_ipaddr;
201         int rc,j;
202         char temp[64];
203
204         if(length > 1023) {
205                 printf("mount error: UNC name too long");
206                 return 0;
207         }
208         if (strncasecmp("cifs://",unc_name,7) == 0)
209                 return parse_cifs_url(unc_name+7);
210         if (strncasecmp("smb://",unc_name,6) == 0) {
211                 return parse_cifs_url(unc_name+6);
212         }
213
214         if(length < 3) {
215                 /* BB add code to find DFS root here */
216                 printf("\nMounting the DFS root for domain not implemented yet");
217                 return 0;
218         } else {
219                 /* BB add support for \\\\ not just // */
220                 if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
221                         printf("mount error: improperly formatted UNC name.");
222                         printf(" %s does not begin with \\\\ or //\n",unc_name);
223                         return 0;
224                 } else {
225                         unc_name[0] = '\\';
226                         unc_name[1] = '\\';
227                         unc_name += 2;
228                         if ((share = strchr(unc_name, '/')) || 
229                                 (share = strchr(unc_name,'\\'))) {
230                                 *share = 0;  /* temporarily terminate the string */
231                                 share += 1;
232                                 host_entry = gethostbyname(unc_name);
233                                 *(share - 1) = '\\'; /* put the slash back */
234 /*                              rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
235                                 if(host_entry == NULL) {
236                                         printf("mount error: could not find target server. TCP name %s not found ", unc_name);
237                                         printf(" rc = %d\n",rc);
238                                         return 0;
239                                 }
240                                 else {
241                                         /* BB should we pass an alternate version of the share name as Unicode */
242                                         /* BB what about ipv6? BB */
243                                         /* BB add retries with alternate servers in list */
244
245                                         memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
246
247                                         ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
248                                         if(ipaddress_string == NULL) {
249                                                 printf("mount error: could not get valid ip address for target server\n");
250                                                 return 0;
251                                         }
252                                         return ipaddress_string; 
253                                 }
254                         } else {
255                                 /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
256                                 printf("Mounting the DFS root for a particular server not implemented yet\n");
257                                 return 0;
258                         }
259                 }
260         }
261 }
262
263 static struct option longopts[] = {
264         { "all", 0, 0, 'a' },
265         { "help", 0, 0, 'h' },
266         { "read-only", 0, 0, 'r' },
267         { "ro", 0, 0, 'r' },
268         { "verbose", 0, 0, 'v' },
269         { "version", 0, 0, 'V' },
270         { "read-write", 0, 0, 'w' },
271         { "rw", 0, 0, 'w' },
272         { "options", 1, 0, 'o' },
273         { "types", 1, 0, 't' },
274         { "replace", 0, 0, 129 },
275         { "after", 0, 0, 130 },
276         { "before", 0, 0, 131 },
277         { "over", 0, 0, 132 },
278         { "move", 0, 0, 133 },
279         { "rsize",1, 0, 136 },
280         { "wsize",1, 0, 137 },
281         { "uid", 1, 0, 138},
282         { "gid", 1, 0, 139},
283         { "uuid",1,0,'U' },
284         { "user",1,0,140},
285         { "username",1,0,140},
286         { "dom",1,0,141},
287         { "domain",1,0,141},
288         { "password",1,0,142},
289         { NULL, 0, 0, 0 }
290 };
291
292 int main(int argc, char ** argv)
293 {
294         int c;
295         int flags = MS_MANDLOCK | MS_MGC_VAL;
296         char * orgoptions = NULL;
297         char * share_name = NULL;
298         char * domain_name = NULL;
299         char * ipaddr = NULL;
300         char * uuid = NULL;
301         char * mountpoint;
302         char * options;
303         int rc,i;
304         int rsize = 0;
305         int wsize = 0;
306         int nomtab = 0;
307         int uid = 0;
308         int gid = 0;
309         int optlen = 0;
310         struct stat statbuf;
311         struct utsname sysinfo;
312         struct mntent mountent;
313         FILE * pmntfile;
314
315         /* setlocale(LC_ALL, "");
316 #if defined(LOCALEDIR)
317         bindtextdomain(PACKAGE, LOCALEDIR);
318         textdomain(PACKAGE); */
319 #endif
320
321         if(argc && argv) {
322                 thisprogram = argv[0];
323         }
324         if(thisprogram == NULL)
325                 thisprogram = "mount.cifs";
326
327         uname(&sysinfo);
328         /* BB add workstation name and domain and pass down */
329 /*#ifdef _GNU_SOURCE
330         printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
331 #endif*/
332         if(argc < 3)
333                 mount_cifs_usage();
334         share_name = argv[1];
335         mountpoint = argv[2];
336         /* add sharename in opts string as unc= parm */
337
338         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
339                          longopts, NULL)) != -1) {
340                 switch (c) {
341 /*      case 'a':              
342                 ++mount_all;
343                 break;
344         case 'f':              
345                 ++fake;
346                 break;
347         case 'F':
348                 ++optfork;
349                 break; */
350                 case 'h':        /* help */
351                         mount_cifs_usage ();
352                         break;
353 /*      case 'i':
354                 external_allowed = 0;
355                 break;
356         case 'l':
357                 list_with_volumelabel = 1;
358                 break;
359         case 'L':
360                 volumelabel = optarg;
361                 break; */
362         case 'n':
363                 ++nomtab;
364                 break;
365         case 'o':
366                 if (orgoptions) {
367                         orgoptions = strcat(orgoptions, ",");
368                         orgoptions = strcat(orgoptions,optarg);
369                 } else
370                         orgoptions = strdup(optarg);
371                 break;
372
373 /*      case 'O':
374                 if (test_opts)
375                         test_opts = xstrconcat3(test_opts, ",", optarg);
376                 else
377                         test_opts = xstrdup(optarg);
378                 break;*/
379                 case 'r':  /* mount readonly */
380                         flags |= MS_RDONLY;;
381                         break;
382                 case 'U':
383                         uuid = optarg;
384                         break;
385                 case 'v':
386                         ++verboseflag;
387                         break;
388 /*      case 'V':          
389                 printf ("mount: %s\n", version);
390                 exit (0);*/
391                 case 'w':
392                         flags &= ~MS_RDONLY;;
393                         break;
394 /*      case 0:
395                 break;
396
397         case 128: 
398                 mounttype = MS_BIND;
399                 break;
400         case 129: 
401                 mounttype = MS_REPLACE;
402                 break;
403         case 130: 
404                 mounttype = MS_AFTER;
405                 break;
406         case 131: 
407                 mounttype = MS_BEFORE;
408                 break;
409         case 132: 
410                 mounttype = MS_OVER;
411                 break;
412         case 133: 
413                 mounttype = MS_MOVE;
414                 break;
415         case 135:
416                 mounttype = (MS_BIND | MS_REC);
417                 break; */
418                 case 136:
419                         rsize = atoi(optarg) ;
420                         break;
421                 case 137:
422                         wsize = atoi(optarg);
423                         break;
424                 case 138:
425                         uid = atoi(optarg);
426                         break;
427                 case 139:
428                         gid = atoi(optarg);
429                         break;
430                 case 140:
431                         got_user = 1;
432                         user_name = optarg;
433                         break;
434                 case 141:
435                         domain_name = optarg;
436                         break;
437                 case 142:
438                         got_password = 1;
439                          mountpassword = optarg;
440                         break;
441                 case '?':
442                 default:
443                         mount_cifs_usage ();
444                 }
445         }
446
447         /* canonicalize the path in argv[1]? */
448
449         if(stat (mountpoint, &statbuf)) {
450                 printf("mount error: mount point %s does not exist\n",mountpoint);
451                 return -1;
452         }
453         if (S_ISDIR(statbuf.st_mode) == 0) {
454                 printf("mount error: mount point %s is not a directory\n",mountpoint);
455                 return -1;
456         }
457
458         if(geteuid()) {
459                 printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n"); 
460                 return -1;
461         }
462
463         ipaddr = parse_server(share_name);
464 /*      if(share_name == NULL)
465                 return 1; */
466         if (parse_options(strdup(orgoptions)))
467                 return 1;
468
469         if(got_user == 0)
470                 user_name = getusername();
471        
472 /*      check username for user%password format */
473
474         if(got_password == 0) {
475                 if (getenv("PASSWD")) {
476                         mountpassword = malloc(33);
477                         if(mountpassword) {
478                                 strncpy(mountpassword,getenv("PASSWD"),32);
479                                 got_password = 1;
480                         }
481 /*              } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
482                         get_password_file();
483                         got_password = 1;*/ /* BB add missing function */
484                 } else {
485                         mountpassword = getpass("Password: "); /* BB obsolete */
486                         got_password = 1;
487                 }
488         }
489         /* FIXME launch daemon (handles dfs name resolution and credential change) 
490            remember to clear parms and overwrite password field before launching */
491         if(orgoptions) {
492                 optlen = strlen(orgoptions);
493         } else
494                 optlen = 0;
495         if(share_name)
496                 optlen += strlen(share_name) + 4;
497         if(user_name)
498                 optlen += strlen(user_name) + 6;
499         if(ipaddr)
500                 optlen += strlen(ipaddr) + 4;
501         if(mountpassword)
502                 optlen += strlen(mountpassword) + 6;
503         options = malloc(optlen + 10);
504
505     options[0] = 0;
506         strncat(options,"unc=",4);
507         strcat(options,share_name);
508         if(ipaddr) {
509                 strncat(options,",ip=",4);
510                 strcat(options,ipaddr);
511         } 
512         if(user_name) {
513                 strncat(options,",user=",6);
514                 strcat(options,user_name);
515         } 
516         if(mountpassword) {
517                 strncat(options,",pass=",6);
518                 strcat(options,mountpassword);
519         }
520         strncat(options,",ver=",5);
521         strcat(options,MOUNT_CIFS_VERSION);
522
523         if(orgoptions) {
524                 strcat(options,",");
525                 strcat(options,orgoptions);
526         }
527         /* printf("\noptions %s \n",options);*/
528         if(mount(share_name, mountpoint, "cifs", flags, options)) {
529         /* remember to kill daemon on error */
530                 switch (errno) {
531                 case 0:
532                         printf("mount failed but no error number set\n");
533                         return 0;
534                 case ENODEV:
535                         printf("mount error: cifs filesystem not supported by the system\n");
536                         break;
537                 default:
538                         printf("mount error %d = %s",errno,strerror(errno));
539                 }
540                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
541                 return -1;
542         } else {
543                 pmntfile = setmntent(MOUNTED, "a+");
544                 if(pmntfile) {
545                         mountent.mnt_fsname = share_name;
546                         mountent.mnt_dir = mountpoint; 
547                         mountent.mnt_type = "cifs"; 
548                         mountent.mnt_opts = "";
549                         mountent.mnt_freq = 0;
550                         mountent.mnt_passno = 0;
551                         rc = addmntent(pmntfile,&mountent);
552                         endmntent(pmntfile);
553                 } else {
554                     printf("could not update mount table\n");
555                 }
556         }
557         return 0;
558 }
559