summaryrefslogtreecommitdiff
path: root/src/init/tar.c
blob: d25b8a5a7b6b7ee807b7aae5f46dabc503c6f91d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <init/stdlib.h>
#include <shared/flags.h>
#include <shared/syscalls.h>
#include <stdint.h>

extern int tty_fd;

// TODO struct tar
static int tar_open(const char *path, int len, void *base, size_t base_len);
static int tar_size(void *sector);
static void *tar_find(const char *path, size_t path_len, void *base, size_t base_len);
static int oct_parse(char *str, size_t len);

void tar_driver(handle_t back, void *base) {
	static char buf[64];
	int len;
	void *id; // IDs usually are integers, but here i'm using them as pointers
	          // to the metadata sectors
	for (;;) {
		len = 64;
		switch (_syscall_fs_wait(back, buf, &len, (int*)&id)) {
			case VFSOP_OPEN:
				_syscall_fs_respond(NULL, tar_open(buf, len, base, ~0));
				break;

			case VFSOP_READ:
				_syscall_fs_respond(id + 512, tar_size(id));
				break;

			default:
				_syscall_fs_respond(NULL, -1); // unsupported
				break;
		}
	}

	// iterate over all sectors, printing filenames
	while (0 == memcmp(base + 257, "ustar", 5)) {
		int size = oct_parse(base + 124, 12);

		_syscall_write(tty_fd, base, 100);
		_syscall_write(tty_fd, " ", 1);

		base += 512;                 // skip metadata sector
		base += (size + 511) & ~511; // skip file (size rounded up to 512)
		// TODO might pagefault if the last sector was at a page boundary
	}
	_syscall_write(tty_fd, "done.", 5);
}

static int tar_open(const char *path, int len, void *base, size_t base_len) {
	void *ptr;

	if (len <= 1) return -1;
	path += 1; // skip the leading slash
	len  -= 1;

	ptr = tar_find(path, len, base, ~0);
	if (!ptr) return -1;
	return (int)ptr;
}

static int tar_size(void *sector) {
	return oct_parse(sector + 124, 12);
}

static void *tar_find(const char *path, size_t path_len, void *base, size_t base_len) {
	int size;
	if (path_len > 100) return NULL; // illegal path

	for (size_t off = 0; off < base_len;) {
		if (0 != memcmp(base + off + 257, "ustar", 5))
			break; // not a metadata sector
		if (0 == memcmp(base + off, path, path_len))
			return base + off; // file found, quit

		size = tar_size(base + off);
		off += 512;                 // skip this metadata sector
		off += (size + 511) & ~511; // skip the data sectors
	}
	return NULL;
}

static int oct_parse(char *str, size_t len) {
	int res = 0;
	for (size_t i = 0; i < len; i++) {
		res *= 8;
		res += str[i] - '0'; // no format checking
	}
	return res;
}