summaryrefslogtreecommitdiff
path: root/bfs/file.ha
blob: b8f91cbaf5a8b60e7d22caeb2e07e7edbe7e315b (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use io;

// BFS file's implementation of stream
const file_impl: io::vtable = io::vtable {
	writer = &file_write,
	reader = &file_read,
	...
};

// BFS file
export type file = struct {
	// File implements stream trait
	stream_trait: io::stream,

	// Filesystem that underlies the file
	fs: *bfs,

	// Inode ID associated with file
	inid: u64,
	
	// Inode associated with file (owned)
	in: *inode,

	// Seeking offset in BFS file
	off: io::off,

	// Block IDs of the successive pages leading to
	// the resolution of the file content at specified index
	page_addrs: [16]u64,

	// Indices of entry in each of the successive pages
	page_idxs: [16]u64
};

fn file_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
	return io::EOF;
};

fn file_write(s: *io::stream, buf: const []u8) (size | io::error) = {
	return 0z;
};

fn file_seek(s: *io::stream, off: io::off, w: io::whence) (io::off | io::error) = {
	let file = s: *file;

	switch (w) {
	case io::whence::SET =>
		file.off = off;
	case io::whence::CUR =>
		file.off += off;
	case io::whence::END =>
		file.off = file.in.length: io::off + off;
	};

	return file.off;
};

// Builds the indices to reach an address
export fn file_mkindices(self: *file, off: u64) size = {
	const endshift = self.fs.bootsector.blocksize;
	const pageshift = endshift - 3;

	const endmask = masklsb(pageshift);
	const pagemask = endmask >> pageshift;

        self.page_idxs[0] = off & endmask;
	off >>= endshift;

	let different = 0z;

	for (let i = 1z; i < len(self.page_idxs); i += 1) {
		const idx = off & pagemask;
		off >> pageshift;

		if (self.page_idxs[i] != idx)
			different = i;
	};

	return different;
};

// Translate a file offset into a device offset
export fn file_translate(self: *file, off: u64) (u64 | error | io::error) = {
	// Calculate the indices
	const different = file_mkindices(self, off);

	// Calculate the level implied by given address
	let level = 0u8;
	for (let i = 0u8; i < 16; i += 1)
		if (self.page_idxs[i] != 0)
			level = i;

	// If it is higher than map level of inode, miss
	if (level > self.in.map_levels)
		return filehole;

	// Set the level address to be the base
	self.page_addrs[self.in.map_levels] = self.in.block;

	// DEBUG:
	const different = self.in.map_levels;

	// Resolve every successive page from
	// the highest changed index in the address
	const sz = exp2(self.fs.bootsector.blocksize);
	let next = self.page_addrs[different];
	
	for (let i = different; i > 0; i -= 1) {
		self.page_addrs[i] = next;

		const loc = next * sz + self.page_idxs[i]*8;
		pread(self.fs.device, &next: *[8]u8, loc: io::off)?;

		if (next == 0)
			return filehole;
	};

	if (next == 0)
		return filehole;

	// Set the block id of final page
	self.page_addrs[0] = next;

	// Finally, return the offset in terms of physical
	// address
	return next * sz + self.page_idxs[0];
};

export fn file_map(self: *file, off: u64) (void | error | io::error) = {
	file_mkindices(self, off);
	let level = 0u8;
	for (let i = 0u8; i < 16; i += 1)
		if (self.page_idxs[i] != 0)
			level = i;

	// Add as many paging levels as necessary
	for (let i = level; i < self.in.map_levels; i += 1)
		file_levelup(self)!;
};

// Increment the level of pages used to address
// file contents by one
export fn file_levelup(self: *file) (void | io::error) = {
	// Allocate new base block
	const base = allocblock(self.fs)?;

	// Write the address of previous base
	// as first entry of new base block
	const sz = exp2(self.fs.bootsector.blocksize);
	pwrite(self.fs.device, &self.page_addrs[self.in.map_levels]: *[8]u8, (base * sz): io::off)!;
	
	// Update inode
	self.in.block = base;
	self.in.map_levels += 1;
	setinode(self.fs, self.inid, self.in)!;
};

// Decrement the amount of pages used to address
// file contents by one
export fn file_leveldown(self: *file) (void | io::error) = {
	return;	
};

// Return 1 << n
fn exp2(n: uint) uint = {
	return 1u << n;
};

// Create a mask with the n least significant bits on
fn masklsb(n: u64) u64 = {
	return ~(-1i64 << n: i64): u64;
};