d0804b8f9c3a5674568b571b9dfcd60327e7c483
[people/sha0/winvblock.git] / src / util / winvblk.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * Mount command
26  *
27  */
28
29 #include <windows.h>
30 #include <winioctl.h>
31 #include <stdio.h>
32 #include <malloc.h>
33
34 #include "winvblock.h"
35 #include "portable.h"
36 #include "mount.h"
37 #include "aoe.h"
38
39 /**
40  * Command routine
41  *
42  * @v boot_bus          A handle to the boot bus device
43  */
44 #define command_decl( x )       \
45                                 \
46 int STDCALL                     \
47 x (                             \
48   HANDLE boot_bus               \
49  )
50 /*
51  * Function pointer for a command routine.
52  * 'indent' mangles this, so it looks weird
53  */
54 typedef command_decl (
55    ( *command_routine )
56  );
57
58 winvblock__def_struct ( option )
59 {
60   char *name;
61   char *value;
62   int has_arg;
63 };
64
65 static option opt_h1 = {
66   "HELP", NULL, 0
67 };
68
69 static option opt_h2 = {
70   "?", NULL, 0
71 };
72
73 static option opt_cmd = {
74   "CMD", NULL, 1
75 };
76
77 static option opt_cyls = {
78   "C", NULL, 1
79 };
80
81 static option opt_heads = {
82   "H", NULL, 1
83 };
84
85 static option opt_spt = {
86   "S", NULL, 1
87 };
88
89 static option opt_disknum = {
90   "D", NULL, 1
91 };
92
93 static option opt_media = {
94   "M", NULL, 1
95 };
96
97 static option opt_uri = {
98   "U", NULL, 1
99 };
100
101 static option opt_mac = {
102   "MAC", NULL, 1
103 };
104
105 static option *options[] = {
106   &opt_h1,
107   &opt_h2,
108   &opt_cmd,
109   &opt_cyls,
110   &opt_heads,
111   &opt_spt,
112   &opt_disknum,
113   &opt_media,
114   &opt_uri,
115   &opt_mac,
116 };
117
118 static char present[] = "";
119 static char *invalid_opt = NULL;
120
121 static void
122 cmdline_options (
123   int argc,
124   char **argv
125  )
126 {
127   int cur = 1;
128
129   while ( cur < argc )
130     {
131       char *cur_arg = argv[cur];
132       int opt;
133
134       /*
135        * Check if the argument is an option.  Look for '-', '--', or '/'
136        */
137       switch ( *cur_arg )
138         {
139           case '-':
140             cur_arg++;
141             if ( *cur_arg == '-' )
142               cur_arg++;
143             break;
144           case '/':
145             cur_arg++;
146             break;
147           default:
148             invalid_opt = cur_arg;
149             return;
150         }
151       /*
152        * Convert possible option to upper-case
153        */
154       {
155         char *walker = cur_arg;
156         while ( *walker )
157           {
158             *walker = toupper ( *walker );
159             walker++;
160           }
161       }
162       /*
163        * Check if the argument is a _valid_ option
164        */
165       opt = 0;
166       while ( opt < sizeof ( options ) / sizeof ( option * ) )
167         {
168           if ( strcmp ( cur_arg, options[opt]->name ) == 0 )
169             {
170               /*
171                * We have a match.
172                * Check if the option was already specified
173                */
174               if ( options[opt]->value != NULL )
175                 {
176                   printf ( "%s specified more than once, making it invalid.\n",
177                            cur_arg );
178                   invalid_opt = cur_arg;
179                   return;
180                 }
181               /*
182                * The option's value is the next argument.  For boolean
183                * options, we ignore the value anyway, but need to know the
184                * option is present
185                */
186               if ( cur == argc - 1 )
187                 options[opt]->value = present;
188               else
189                 options[opt]->value = argv[cur + 1];
190               cur += options[opt]->has_arg;
191               break;
192             }
193           opt++;
194         }
195       /*
196        * Did we find no valid option match?
197        */
198       if ( opt == sizeof ( options ) / sizeof ( option * ) )
199         {
200           invalid_opt = cur_arg;
201           return;
202         }
203       cur++;
204     }
205 }
206
207 static
208 command_decl (
209   cmd_help
210  )
211 {
212   char help_text[] = "\n\
213 WinVBlock user-land utility for disk control. (C) 2006-2008 V.,\n\
214                                               (C) 2009-2010 Shao Miller\n\
215 Usage:\n\
216   winvblk -cmd <command> [-d <disk number>] [-m <media>] [-u <uri or path>]\n\
217     [-mac <client mac address>] [-c <cyls>] [-h <heads>] [-s <sects per track>]\n\
218   winvblk -?\n\
219 \n\
220 Parameters:\n\
221   <command> is one of:\n\
222     scan   - Shows the reachable AoE targets.\n\
223     show   - Shows the mounted AoE targets.\n\
224     mount  - Mounts an AoE target.  Requires -mac and -u\n\
225     umount - Unmounts an AoE disk.  Requires -d\n\
226     attach - Attaches <filepath> disk image file.  Requires -u and -m.\n\
227              -c, -h, -s are optional.\n\
228     detach - Detaches file-backed disk.  Requires -d\n\
229   <uri or path> is something like:\n\
230     aoe:eX.Y        - Where X is the \"major\" (shelf) and Y is\n\
231                       the \"minor\" (slot)\n\
232     c:\\my_disk.hdd - The path to a disk image file or .ISO\n\
233   <media> is one of 'c' for CD/DVD, 'f' for floppy, 'h' for hard disk drive\n\
234 \n";
235   printf ( help_text );
236   return 1;
237 }
238
239 static
240 command_decl (
241   cmd_scan
242  )
243 {
244   aoe__mount_targets_ptr targets;
245   DWORD bytes_returned;
246   winvblock__uint32 i;
247   winvblock__uint8 string[256];
248   int status = 2;
249
250   targets =
251     malloc ( sizeof ( aoe__mount_targets ) +
252              ( 32 * sizeof ( aoe__mount_target ) ) );
253   if ( targets == NULL )
254     {
255       printf ( "Out of memory\n" );
256       goto err_alloc;
257     }
258   if ( !DeviceIoControl
259        ( boot_bus, IOCTL_AOE_SCAN, NULL, 0, targets,
260          ( sizeof ( aoe__mount_targets ) +
261            ( 32 * sizeof ( aoe__mount_target ) ) ), &bytes_returned,
262          ( LPOVERLAPPED ) NULL ) )
263     {
264       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
265       status = 2;
266       goto err_ioctl;
267     }
268   if ( targets->Count == 0 )
269     {
270       printf ( "No AoE targets found.\n" );
271       goto err_no_targets;
272     }
273   printf ( "Client NIC          Target      Server MAC         Size\n" );
274   for ( i = 0; i < targets->Count && i < 10; i++ )
275     {
276       sprintf ( string, "e%lu.%lu      ", targets->Target[i].Major,
277                 targets->Target[i].Minor );
278       string[10] = 0;
279       printf ( " %02x:%02x:%02x:%02x:%02x:%02x  %s "
280                " %02x:%02x:%02x:%02x:%02x:%02x  %I64uM\n",
281                targets->Target[i].ClientMac[0],
282                targets->Target[i].ClientMac[1],
283                targets->Target[i].ClientMac[2],
284                targets->Target[i].ClientMac[3],
285                targets->Target[i].ClientMac[4],
286                targets->Target[i].ClientMac[5], string,
287                targets->Target[i].ServerMac[0],
288                targets->Target[i].ServerMac[1],
289                targets->Target[i].ServerMac[2],
290                targets->Target[i].ServerMac[3],
291                targets->Target[i].ServerMac[4],
292                targets->Target[i].ServerMac[5],
293                ( targets->Target[i].LBASize / 2048 ) );
294     }
295
296 err_no_targets:
297
298   status = 0;
299
300 err_ioctl:
301
302   free ( targets );
303 err_alloc:
304
305   return status;
306 }
307
308 static
309 command_decl (
310   cmd_show
311  )
312 {
313   aoe__mount_disks_ptr mounted_disks;
314   DWORD bytes_returned;
315   winvblock__uint32 i;
316   winvblock__uint8 string[256];
317   int status = 2;
318
319   mounted_disks =
320     malloc ( sizeof ( aoe__mount_disks ) +
321              ( 32 * sizeof ( aoe__mount_disk ) ) );
322   if ( mounted_disks == NULL )
323     {
324       printf ( "Out of memory\n" );
325       goto err_alloc;
326     }
327   if ( !DeviceIoControl
328        ( boot_bus, IOCTL_AOE_SHOW, NULL, 0, mounted_disks,
329          ( sizeof ( aoe__mount_disks ) + ( 32 * sizeof ( aoe__mount_disk ) ) ),
330          &bytes_returned, ( LPOVERLAPPED ) NULL ) )
331     {
332       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
333       goto err_ioctl;
334     }
335
336   status = 0;
337
338   if ( mounted_disks->Count == 0 )
339     {
340       printf ( "No AoE disks mounted.\n" );
341       goto err_no_disks;
342     }
343   printf ( "Disk  Client NIC         Server MAC         Target      Size\n" );
344   for ( i = 0; i < mounted_disks->Count && i < 10; i++ )
345     {
346       sprintf ( string, "e%lu.%lu      ", mounted_disks->Disk[i].Major,
347                 mounted_disks->Disk[i].Minor );
348       string[10] = 0;
349       printf
350         ( " %-4lu %02x:%02x:%02x:%02x:%02x:%02x  %02x:%02x:%02x:%02x:%02x:%02x  %s  %I64uM\n",
351           mounted_disks->Disk[i].Disk, mounted_disks->Disk[i].ClientMac[0],
352           mounted_disks->Disk[i].ClientMac[1],
353           mounted_disks->Disk[i].ClientMac[2],
354           mounted_disks->Disk[i].ClientMac[3],
355           mounted_disks->Disk[i].ClientMac[4],
356           mounted_disks->Disk[i].ClientMac[5],
357           mounted_disks->Disk[i].ServerMac[0],
358           mounted_disks->Disk[i].ServerMac[1],
359           mounted_disks->Disk[i].ServerMac[2],
360           mounted_disks->Disk[i].ServerMac[3],
361           mounted_disks->Disk[i].ServerMac[4],
362           mounted_disks->Disk[i].ServerMac[5], string,
363           ( mounted_disks->Disk[i].LBASize / 2048 ) );
364     }
365
366 err_no_disks:
367
368 err_ioctl:
369
370   free ( mounted_disks );
371 err_alloc:
372
373   return status;
374 }
375
376 static
377 command_decl (
378   cmd_mount
379  )
380 {
381   winvblock__uint8 mac_addr[6];
382   winvblock__uint32 ver_major,
383    ver_minor;
384   winvblock__uint8 in_buf[sizeof ( mount__filedisk ) + 1024];
385   DWORD bytes_returned;
386
387   if ( opt_mac.value == NULL || opt_uri.value == NULL )
388     {
389       printf ( "-mac and -u options required.  See -? for help.\n" );
390       return 1;
391     }
392   sscanf ( opt_mac.value, "%02x:%02x:%02x:%02x:%02x:%02x",
393            ( int * )&mac_addr[0], ( int * )&mac_addr[1], ( int * )&mac_addr[2],
394            ( int * )&mac_addr[3], ( int * )&mac_addr[4],
395            ( int * )&mac_addr[5] );
396   sscanf ( opt_uri.value, "aoe:e%lu.%lu", ( int * )&ver_major,
397            ( int * )&ver_minor );
398   printf ( "mounting e%lu.%lu from %02x:%02x:%02x:%02x:%02x:%02x\n",
399            ( int )ver_major, ( int )ver_minor, mac_addr[0], mac_addr[1],
400            mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5] );
401   memcpy ( &in_buf[0], mac_addr, 6 );
402   ( ( winvblock__uint16_ptr ) & in_buf[6] )[0] =
403     ( winvblock__uint16 ) ver_major;
404   ( ( winvblock__uint8_ptr ) & in_buf[8] )[0] = ( winvblock__uint8 ) ver_minor;
405   if ( !DeviceIoControl
406        ( boot_bus, IOCTL_AOE_MOUNT, in_buf, sizeof ( in_buf ), NULL, 0,
407          &bytes_returned, ( LPOVERLAPPED ) NULL ) )
408     {
409       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
410       return 2;
411     }
412   return 0;
413 }
414
415 static
416 command_decl (
417   cmd_umount
418  )
419 {
420   winvblock__uint32 disk_num;
421   winvblock__uint8 in_buf[sizeof ( mount__filedisk ) + 1024];
422   DWORD bytes_returned;
423
424   if ( opt_disknum.value == NULL )
425     {
426       printf ( "-d option required.  See -? for help.\n" );
427       return 1;
428     }
429   sscanf ( opt_disknum.value, "%d", ( int * )&disk_num );
430   printf ( "unmounting disk %d\n", ( int )disk_num );
431   memcpy ( &in_buf, &disk_num, 4 );
432   if ( !DeviceIoControl
433        ( boot_bus, IOCTL_AOE_UMOUNT, in_buf, sizeof ( in_buf ), NULL, 0,
434          &bytes_returned, ( LPOVERLAPPED ) NULL ) )
435     {
436       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
437       return 2;
438     }
439   return 0;
440 }
441
442 static
443 command_decl (
444   cmd_attach
445  )
446 {
447   mount__filedisk filedisk;
448   char obj_path_prefix[] = "\\??\\";
449   winvblock__uint8 in_buf[sizeof ( mount__filedisk ) + 1024];
450   DWORD bytes_returned;
451
452   if ( opt_uri.value == NULL || opt_media.value == NULL )
453     {
454       printf ( "-u and -m options required.  See -? for help.\n" );
455       return 1;
456     }
457   filedisk.type = opt_media.value[0];
458   if ( opt_cyls.value != NULL )
459     sscanf ( opt_cyls.value, "%d", ( int * )&filedisk.cylinders );
460   if ( opt_heads.value != NULL )
461     sscanf ( opt_heads.value, "%d", ( int * )&filedisk.heads );
462   if ( opt_spt.value != NULL )
463     sscanf ( opt_spt.value, "%d", ( int * )&filedisk.sectors );
464   memcpy ( &in_buf, &filedisk, sizeof ( mount__filedisk ) );
465   memcpy ( &in_buf[sizeof ( mount__filedisk )], obj_path_prefix,
466            sizeof ( obj_path_prefix ) );
467   memcpy ( &in_buf
468            [sizeof ( mount__filedisk ) + sizeof ( obj_path_prefix ) - 1],
469            opt_uri.value, strlen ( opt_uri.value ) + 1 );
470   if ( !DeviceIoControl
471        ( boot_bus, IOCTL_FILE_ATTACH, in_buf, sizeof ( in_buf ), NULL, 0,
472          &bytes_returned, ( LPOVERLAPPED ) NULL ) )
473     {
474       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
475       return 2;
476     }
477   return 0;
478 }
479
480 static
481 command_decl (
482   cmd_detach
483  )
484 {
485   winvblock__uint32 disk_num;
486   winvblock__uint8 in_buf[sizeof ( mount__filedisk ) + 1024];
487   DWORD bytes_returned;
488
489   if ( opt_disknum.value == NULL )
490     {
491       printf ( "-d option required.  See -? for help.\n" );
492       return 1;
493     }
494   sscanf ( opt_disknum.value, "%d", ( int * )&disk_num );
495   printf ( "Detaching file-backed disk %d\n", ( int )disk_num );
496   memcpy ( &in_buf, &disk_num, 4 );
497   if ( !DeviceIoControl
498        ( boot_bus, IOCTL_FILE_DETACH, in_buf, sizeof ( in_buf ), NULL, 0,
499          &bytes_returned, ( LPOVERLAPPED ) NULL ) )
500     {
501       printf ( "DeviceIoControl (%d)\n", ( int )GetLastError (  ) );
502       return 2;
503     }
504   return 0;
505 }
506
507 int
508 main (
509   int argc,
510   char **argv,
511   char **envp
512  )
513 {
514   command_routine cmd = cmd_help;
515   HANDLE boot_bus = NULL;
516   int status = 1;
517
518   cmdline_options ( argc, argv );
519   /*
520    * Check for invalid option
521    */
522   if ( invalid_opt != NULL )
523     {
524       printf ( "Use -? for help.  Invalid option: %s\n", invalid_opt );
525       goto err_bad_cmd;
526     }
527   /*
528    * Check for cry for help
529    */
530   if ( opt_h1.value || opt_h2.value )
531     goto do_cmd;
532   /*
533    * Check for no command
534    */
535   if ( opt_cmd.value == NULL )
536     goto do_cmd;
537   /*
538    * Check given command
539    */
540   if ( strcmp ( opt_cmd.value, "scan" ) == 0 )
541     {
542       cmd = cmd_scan;
543     }
544   if ( strcmp ( opt_cmd.value, "show" ) == 0 )
545     {
546       cmd = cmd_show;
547     }
548   if ( strcmp ( opt_cmd.value, "mount" ) == 0 )
549     {
550       cmd = cmd_mount;
551     }
552   if ( strcmp ( opt_cmd.value, "umount" ) == 0 )
553     {
554       cmd = cmd_umount;
555     }
556   if ( strcmp ( opt_cmd.value, "attach" ) == 0 )
557     {
558       cmd = cmd_attach;
559     }
560   if ( strcmp ( opt_cmd.value, "detach" ) == 0 )
561     {
562       cmd = cmd_detach;
563     }
564   /*
565    * Check for invalid command
566    */
567   if ( cmd == cmd_help )
568     goto do_cmd;
569
570   boot_bus =
571     CreateFile ( "\\\\.\\" winvblock__literal, GENERIC_READ | GENERIC_WRITE,
572                  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0,
573                  NULL );
574   if ( boot_bus == INVALID_HANDLE_VALUE )
575     {
576       printf ( "CreateFile (%d)\n", ( int )GetLastError (  ) );
577       goto err_handle;
578     }
579
580 do_cmd:
581   status = cmd ( boot_bus );
582
583   if ( boot_bus != NULL )
584     CloseHandle ( boot_bus );
585 err_handle:
586
587 err_bad_cmd:
588
589   return status;
590 }