r13280: Tidy up formatting.
[jelmer/samba4-debian.git] / source / 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 ipv4_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         bindtextdomain(PACKAGE, LOCALEDIR);
317         textdomain(PACKAGE); */
318
319         if(argc && argv) {
320                 thisprogram = argv[0];
321         }
322         if(thisprogram == NULL)
323                 thisprogram = "mount.cifs";
324
325         uname(&sysinfo);
326         /* BB add workstation name and domain and pass down */
327 /*#ifdef _GNU_SOURCE
328         printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
329 #endif*/
330         if(argc < 3)
331                 mount_cifs_usage();
332         share_name = argv[1];
333         mountpoint = argv[2];
334         /* add sharename in opts string as unc= parm */
335
336         while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
337                          longopts, NULL)) != -1) {
338                 switch (c) {
339 /*      case 'a':              
340                 ++mount_all;
341                 break;
342         case 'f':              
343                 ++fake;
344                 break;
345         case 'F':
346                 ++optfork;
347                 break; */
348                 case 'h':        /* help */
349                         mount_cifs_usage ();
350                         break;
351 /*      case 'i':
352                 external_allowed = 0;
353                 break;
354         case 'l':
355                 list_with_volumelabel = 1;
356                 break;
357         case 'L':
358                 volumelabel = optarg;
359                 break; */
360         case 'n':
361                 ++nomtab;
362                 break;
363         case 'o':
364                 if (orgoptions) {
365                         orgoptions = strcat(orgoptions, ",");
366                         orgoptions = strcat(orgoptions,optarg);
367                 } else
368                         orgoptions = strdup(optarg);
369                 break;
370
371 /*      case 'O':
372                 if (test_opts)
373                         test_opts = xstrconcat3(test_opts, ",", optarg);
374                 else
375                         test_opts = xstrdup(optarg);
376                 break;*/
377                 case 'r':  /* mount readonly */
378                         flags |= MS_RDONLY;;
379                         break;
380                 case 'U':
381                         uuid = optarg;
382                         break;
383                 case 'v':
384                         ++verboseflag;
385                         break;
386 /*      case 'V':          
387                 printf ("mount: %s\n", version);
388                 exit (0);*/
389                 case 'w':
390                         flags &= ~MS_RDONLY;;
391                         break;
392 /*      case 0:
393                 break;
394
395         case 128: 
396                 mounttype = MS_BIND;
397                 break;
398         case 129: 
399                 mounttype = MS_REPLACE;
400                 break;
401         case 130: 
402                 mounttype = MS_AFTER;
403                 break;
404         case 131: 
405                 mounttype = MS_BEFORE;
406                 break;
407         case 132: 
408                 mounttype = MS_OVER;
409                 break;
410         case 133: 
411                 mounttype = MS_MOVE;
412                 break;
413         case 135:
414                 mounttype = (MS_BIND | MS_REC);
415                 break; */
416                 case 136:
417                         rsize = atoi(optarg) ;
418                         break;
419                 case 137:
420                         wsize = atoi(optarg);
421                         break;
422                 case 138:
423                         uid = atoi(optarg);
424                         break;
425                 case 139:
426                         gid = atoi(optarg);
427                         break;
428                 case 140:
429                         got_user = 1;
430                         user_name = optarg;
431                         break;
432                 case 141:
433                         domain_name = optarg;
434                         break;
435                 case 142:
436                         got_password = 1;
437                          mountpassword = optarg;
438                         break;
439                 case '?':
440                 default:
441                         mount_cifs_usage ();
442                 }
443         }
444
445         /* canonicalize the path in argv[1]? */
446
447         if(stat (mountpoint, &statbuf)) {
448                 printf("mount error: mount point %s does not exist\n",mountpoint);
449                 return -1;
450         }
451         if (S_ISDIR(statbuf.st_mode) == 0) {
452                 printf("mount error: mount point %s is not a directory\n",mountpoint);
453                 return -1;
454         }
455
456         if(geteuid()) {
457                 printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n"); 
458                 return -1;
459         }
460
461         ipaddr = parse_server(share_name);
462 /*      if(share_name == NULL)
463                 return 1; */
464         if (parse_options(strdup(orgoptions)))
465                 return 1;
466
467         if(got_user == 0)
468                 user_name = getusername();
469        
470 /*      check username for user%password format */
471
472         if(got_password == 0) {
473                 if (getenv("PASSWD")) {
474                         mountpassword = malloc(33);
475                         if(mountpassword) {
476                                 strncpy(mountpassword,getenv("PASSWD"),32);
477                                 got_password = 1;
478                         }
479 /*              } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
480                         get_password_file();
481                         got_password = 1;*/ /* BB add missing function */
482                 } else {
483                         mountpassword = getpass("Password: "); /* BB obsolete */
484                         got_password = 1;
485                 }
486         }
487         /* FIXME launch daemon (handles dfs name resolution and credential change) 
488            remember to clear parms and overwrite password field before launching */
489         if(orgoptions) {
490                 optlen = strlen(orgoptions);
491         } else
492                 optlen = 0;
493         if(share_name)
494                 optlen += strlen(share_name) + 4;
495         if(user_name)
496                 optlen += strlen(user_name) + 6;
497         if(ipaddr)
498                 optlen += strlen(ipaddr) + 4;
499         if(mountpassword)
500                 optlen += strlen(mountpassword) + 6;
501         options = malloc(optlen + 10);
502
503     options[0] = 0;
504         strncat(options,"unc=",4);
505         strcat(options,share_name);
506         if(ipaddr) {
507                 strncat(options,",ip=",4);
508                 strcat(options,ipaddr);
509         } 
510         if(user_name) {
511                 strncat(options,",user=",6);
512                 strcat(options,user_name);
513         } 
514         if(mountpassword) {
515                 strncat(options,",pass=",6);
516                 strcat(options,mountpassword);
517         }
518         strncat(options,",ver=",5);
519         strcat(options,MOUNT_CIFS_VERSION);
520
521         if(orgoptions) {
522                 strcat(options,",");
523                 strcat(options,orgoptions);
524         }
525         /* printf("\noptions %s \n",options);*/
526         if(mount(share_name, mountpoint, "cifs", flags, options)) {
527         /* remember to kill daemon on error */
528                 switch (errno) {
529                 case 0:
530                         printf("mount failed but no error number set\n");
531                         return 0;
532                 case ENODEV:
533                         printf("mount error: cifs filesystem not supported by the system\n");
534                         break;
535                 default:
536                         printf("mount error %d = %s",errno,strerror(errno));
537                 }
538                 printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
539                 return -1;
540         } else {
541                 pmntfile = setmntent(MOUNTED, "a+");
542                 if(pmntfile) {
543                         mountent.mnt_fsname = share_name;
544                         mountent.mnt_dir = mountpoint; 
545                         mountent.mnt_type = "cifs"; 
546                         mountent.mnt_opts = "";
547                         mountent.mnt_freq = 0;
548                         mountent.mnt_passno = 0;
549                         rc = addmntent(pmntfile,&mountent);
550                         endmntent(pmntfile);
551                 } else {
552                     printf("could not update mount table\n");
553                 }
554         }
555         return 0;
556 }
557