Newer
Older
vvd / src / main.c
@dd86k dd86k on 9 Nov 2019 7 KB INIT
#ifdef _WIN32
#include <Windows.h>
#include <WinBase.h>
#include <tchar.h>
#include <wchar.h>
#else // POSIX

#endif

#define VERSION "0.0.0"
#define INCLUDE_TESTS 1

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "utils.h"
#include "vvd.h"
#include "platform.h"

#ifdef INCLUDE_TESTS
#include <assert.h>
#include "fs/gpt.h"
#include "fs/mbr.h"

//
// test
//

void test() {
	fputs(
	"* Defines\n"
#ifdef __LITTLE_ENDIAN__
	"__LITTLE_ENDIAN__\n"
#endif
#ifdef __BIG_ENDIAN__
	"__BIG_ENDIAN__\n"
#endif
#ifdef __NO_INLINE__
	"__NO_INLINE__\n"
#endif
	, stdout);
#ifdef _ILP32
	printf("_ILP32		%u\n", _ILP32);
#endif
#ifdef __SIZE_WIDTH__
	printf("__SIZE_WIDTH__		%u\n", __SIZE_WIDTH__);
#endif
	printf(
		"sizeof	VDISK		%u\n"
		"sizeof	wchar_t		%u\n",
		(int)sizeof(VDISK),
		(int)sizeof(wchar_t)
	);
	puts("Running tests...");
	assert(sizeof(MBR) == 512);
	assert(sizeof(MBR_PARTITION_ENTRY) == 16);
	assert(sizeof(CHS_ENTRY) == 3);
	assert(sizeof(GPT) == 512);
	assert(sizeof(GPT_ENTRY) == 512);
	assert(sizeof(LBA64) == 8);
	// VDI
	assert(sizeof(VDI_HDR) == 8);
	assert(sizeof(VDIDISKGEOMETRY) == 16);
	assert(sizeof(VDIHEADER0) == 348);
	assert(sizeof(VDIHEADER1) == 400);
	// VMDK
	assert(sizeof(VMDK_HDR) == 512);
	assert(sizeof(VMDK_MARKER) == 512);
	// VHD
	assert(sizeof(VHD_HDR) == 512);
	assert(sizeof(VHD_PARENT_LOCATOR) == 24);
	assert(sizeof(VHD_DYN_HDR) == 1024);
	// VHDX
	assert(sizeof(VHDX_HDR) == 520);
	assert(sizeof(VHDX_HEADER1) == 76);
	assert(sizeof(VHDX_REGION_HDR) == 16);
	assert(sizeof(VHDX_REGION_ENTRY) == 32);
	assert(sizeof(VHDX_LOG_HDR) == 64);
	assert(sizeof(VHDX_LOG_ZERO) == 32);
	assert(sizeof(VDHX_LOG_DESC) == 32);
	assert(sizeof(VHDX_LOG_DATA) == 4100);
	// utils
	assert(bswap16(0xAABB) == 0xBBAA);
	assert(bswap32(0xAABBCCDD) == 0xDDCCBBAA);
	assert(bswap64(0xAABBCCDD11223344) == 0x44332211DDCCBBAA);
#ifdef _WIN32
	assert(extcmp(L"test.bin", L"bin"));
#else
	assert(extcmp("test.bin", "bin"));
#endif
	puts("OK");
	exit(EXIT_SUCCESS);
}
#endif // INCLUDE_TESTS

//
// CLI part
//

void help() {
	puts(
	"Manage virtual disks\n"
	"  Usage: vvd OPERATION FILE [...]\n"
	"         vvd PAGE\n"
	"\nOPERATIONS\n"
	"  -I    Info    - Get information\n"
	"  -N    New     - Create new empty vdisk\n"
	"  -M    Map     - Get allocation map if available\n"
	"  -C    Compact - Save disk space\n"
	"\nOPTIONS\n"
	"   r    Open as RAW\n"
	"\nPAGE\n"
	"  --help       Print this help screen and quit\n"
	"  --version    Print version screen and quit\n"
	"  --license    Print license screen and quit\n"
	);
	exit(EXIT_SUCCESS);
}

void version(void) {
	printf(
	"vvd-" PLATFORM " " VERSION 
#ifdef TIMESTAMP
	" (" TIMESTAMP ")"
#endif
	"\n"
#ifdef __VERSION__
	"Compiler: " __VERSION__ "\n"
#endif
	"MIT License: Copyright (c) 2019 dd86k\n"
	"Project page: <https://github.com/dd86k/vvd>\n"
	"VDISK   OPERATIONS\n"
	"VDI     I M N C\n"
	"VMDK    I\n"
	"VHD     I M\n"
	"VHDX    \n"
	"QED     \n"
	"QCOW    \n"
	"VHD     \n"
	"PHDD    \n"
	"RAW     I\n"
	);
	exit(EXIT_SUCCESS);
}

void license() {
	puts(
	"Copyright 2019 dd86k\n"
	"\n"
	"Permission is hereby granted, free of charge, to any person obtaining a copy of\n"
	"this software and associated documentation files (the \"Software\"), to deal in\n"
	"the Software without restriction, including without limitation the rights to\n"
	"use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n"
	"of the Software, and to permit persons to whom the Software is furnished to do\n"
	"so, subject to the following conditions:\n"
	"\n"
	"The above copyright notice and this permission notice shall be included in all\n"
	"copies or substantial portions of the Software.\n"
	"\n"
	"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
	"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
	"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
	"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
	"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
	"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
	"SOFTWARE."
	);
	exit(EXIT_SUCCESS);
}

/**
 * Extension vdisk matcher, returns VDISK_FORMAT if matches an extension.
 * Otherwise 0.
 */
int vdextauto(_vchar *path) {
	if (extcmp(path, EXT_VDI))	return VDISK_FORMAT_VDI;
	if (extcmp(path, EXT_VMDK))	return VDISK_FORMAT_VMDK;
	if (extcmp(path, EXT_VHD))	return VDISK_FORMAT_VHD;
	return 0;
}

#ifdef _WIN32
#define MAIN int wmain(int argc, wchar_t **argv)
#else
#define MAIN int main(int argc, char **argv)
#endif

//
// main
//

MAIN {
	if (argc <= 1)
		help();

	char op_mode;
	int oflags = 0;	// open file flags
	int cflags = 0;	// create file flags

	if (argv[1][1] == '-') { // long arguments
		_vchar *h = argv[1] + 2;
#ifdef _WIN32
		if (wcscmp(h, L"help") == 0)
			help();
		if (wcscmp(h, L"version") == 0)
			version();
		if (wcscmp(h, L"license") == 0)
			license();
#ifdef INCLUDE_TESTS
		if (wcscmp(h, L"test") == 0)
			test();
#endif // INCLUDE_TESTS
		fprintf(stderr, "main: \"%ls\" option unknown, aborting\n", h);
#else
		if (strcmp(h, "help") == 0)
			help();
		if (strcmp(h, "version") == 0)
			version();
		if (strcmp(h, "license") == 0)
			license();
#ifdef INCLUDE_TESTS
		if (strcmp(h, "test") == 0)
			test();
#endif // INCLUDE_TESTS
		fprintf(stderr, "main: \"%s\" option unknown, aborting\n", h);
#endif
		return ECLIARG;
	} else if (argv[1][0] == '-') { // short arguments
		_vchar *h = argv[1];
		while (*++h) switch (*h) {
		case MODE_INFO:	// -I
		case MODE_MAP:	// -M
		case MODE_NEW:	// -N
		case MODE_COMPACT:	// -C
			op_mode = *h;
			break;
		case 'r':
			oflags |= VDISK_OPEN_RAW;
			break;
		case 'c':
			cflags |= VDISK_OPEN_RAW;
			break;
		default:
			fprintf(stderr, "main: unknown parameter '%c', aborting\n", *h);
			return ECLIARG;
		}
	}

	VDISK vdin;	// vdisk IN
	VDISK vdout;	// vdisk OUT
	uint64_t vsize;	// virtual disk size, used in -N and -R

	size_t fargi;	// File IN argument index
	switch (op_mode) {
	/*case MODE_RESIZE:
		if (argc < 4)
			goto L_MISSING_ARGS;
		fargi = 3;
		break;*/
	case MODE_NEW:
		if (argc < 4) // Needs vvd -N TYPE SIZE
			goto L_MISSING_ARGS;

		fargi = 2;
		oflags |= VDISK_CREATE;

		vdin.format = vdextauto(argv[2]);
		if (vdisk_default(&vdin)) {
			vdisk_perror(__func__);
			return vdisk_errno;
		}

		if (sbinf(argv[3], &vsize)) {
			fputs("main: Invalid binary size, must be higher than 0\n", stderr);
			return ECLIARG;
		}

		if (argc > 4) {
#ifdef _WIN32
			if (wcscmp(argv[4], L"fixed") == 0)
				oflags |= VDISK_CREATE_FIXED;
			else if (wcscmp(argv[4], L"dynamic") == 0)
				oflags |= VDISK_CREATE_DYN;
#else
			if (strcmp(argv[4], "fixed") == 0)
				oflags |= VDISK_CREATE_FIXED;
			else if (strcmp(argv[4], "dynamic") == 0)
				oflags |= VDISK_CREATE_DYN;
#endif
		} // ELSE DEFAULT: dynamic (vdisk_open)
		break;
	case MODE_COMPACT:
	case MODE_INFO:
	case MODE_MAP:
		if (argc < 3) {
L_MISSING_ARGS:
			fputs("main: Missing parameters, aborting\n", stderr);
			return ECLIARG;
		}
		fargi = 2;
		break;
	default:
		fputs("main: Invalid operation mode, aborting\n", stderr);
		return ECLIARG;
	}

	if (vdisk_open(argv[fargi], &vdin, oflags)) {
		vdisk_perror(__func__);
		return vdisk_errno;
	}

	switch (op_mode) {
	case MODE_INFO:	return vvd_info(&vdin);
	case MODE_MAP:	return vvd_map(&vdin);
	case MODE_NEW:	return vvd_new(&vdin, vsize);
	case MODE_COMPACT:	return vvd_compact(&vdin);
	default: assert(0);
	}

	return EXIT_SUCCESS;
}