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