Newer
Older
vvd / src / main.c
@dd86k dd86k on 27 Jul 2020 7 KB Minor tweaks
#ifdef _WIN32 // Windows
#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 [OPTIONS]]\n"
	"\nOPERATION\n"
	"  info       Get vdisk image information\n"
	"  new        Create new empty vdisk\n"
	"  map        Show allocation map\n"
	"  compact    Compact vdisk image\n"
	"  help       Show help page and exit\n"
	"  version    Show version page and exit\n"
	"  license    Show license page and exit\n"
	"\nOPTIONS\n"
	"  --raw           Open as RAW\n"
	"  --create-raw    Create as RAW\n"
	"  --create-dyn    Create vdisk as dynamic\n"
	"  --create-fixed  Create vdisk as fixed\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-2020 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 (c) 2019-2020 dd86k <dd@dax.moe>\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)
#define vstr(quote) L##quote
#define PSTR "%ls"
int scmp(const wchar_t *s, const wchar_t *t) {
	return wcscmp(s, t) == 0;
}
#else
#define MAIN int main(int argc, char **argv)
#define vstr(quote) ##quote
#define PSTR "%s"
int scmp(const char *s, const char *t) {
	return strcmp(s, t) == 0;
}
#endif

// Main entry point. This only performs intepreting the command-line options
// for the core functions.
MAIN {
	if (argc <= 1)
		help();

	int mflags = 0;	// main: Command-line flags
	int oflags = 0;	// vdisk_open: file flags
	int cflags = 0;	// vdisk_create: file flags
	VDISK vdin;	// vdisk IN
	VDISK vdout;	// vdisk OUT
	uint64_t vsize;	// virtual disk size, used in 'new' and 'resize'
	const _vchar *file_input = NULL;
	const _vchar *file_output = NULL;

	// Additional arguments are processed first, since they're simpler
	for (size_t argi = 2; argi < argc; ++argi) {
		const _vchar *arg = argv[argi];
		//
		// Open flags
		//
		if (scmp(arg, vstr("--raw"))) {
			oflags |= VDISK_RAW;
			continue;
		}
		//
		// Create flags
		//
		if (scmp(arg, vstr("--create-raw"))) {
			cflags |= VDISK_RAW;
			continue;
		}
		if (scmp(arg, vstr("--create-dynamic"))) {
			cflags |= VDISK_CREATE_DYN;
			continue;
		}
		if (scmp(arg, vstr("--create-fixed"))) {
			cflags |= VDISK_CREATE_FIXED;
			continue;
		}
		//
		// Default arguments
		//
		if (file_input == NULL) {
			file_input = arg;
			continue;
		}
		if (file_output == NULL) {
			file_output = arg;
			continue;
		}
		//
		// Unknown argument
		//
		fprintf(stderr, "main: '" PSTR "' unknown option\n", arg);
		return EXIT_FAILURE;
	}

	const _vchar *action = argv[1];

	// Action time
	if (scmp(action, vstr("info"))) {
		if (file_input == NULL) {
			fputs("main: missing vdisk", stderr);
			return EXIT_FAILURE;
		}
		if (vdisk_open(file_input, &vdin, oflags)) {
			vdisk_perror(__func__);
			return vdisk_errno;
		}
		return vvd_info(&vdin);
	}
	if (scmp(action, vstr("map"))) {
		if (file_input == NULL) {
			fputs("main: missing vdisk", stderr);
			return EXIT_FAILURE;
		}
		if (vdisk_open(file_input, &vdin, oflags)) {
			vdisk_perror(__func__);
			return vdisk_errno;
		}
		return vvd_map(&vdin);
	}
	if (scmp(action, vstr("compact"))) {
		fputs("main: not implemented", stderr);
		return EXIT_FAILURE;
	}
	if (scmp(action, vstr("resize"))) {
		fputs("main: not implemented", stderr);
		return EXIT_FAILURE;
	}
	if (scmp(action, vstr("defrag"))) {
		fputs("main: not implemented", stderr);
		return EXIT_FAILURE;
	}
	if (scmp(action, vstr("new"))) {
		fputs("main: not implemented", stderr);
		/*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 VVD_ECLIARG;
		}
		*/
		return EXIT_FAILURE;
	}
	if (scmp(action, vstr("version")) || scmp(action, vstr("--version")))
		version();
	if (scmp(action, vstr("help")) || scmp(action, vstr("--help")))
		help();
	if (scmp(action, vstr("license")) || scmp(action, vstr("--license")))
		license();
#ifdef INCLUDE_TESTS
	if (scmp(action, vstr("test")))
		test();
#endif

	fprintf(stderr, "main: '" PSTR "' unknown operation, see 'vvd help'\n", action);
	return EXIT_FAILURE;
}