Skip to content

Commit 4f4df49

Browse files
committed
tarfs: initial version of the TARFS driver
TARFS is a file system driver capable of reading buffers containing TAR files. TARFS is mainly used for designing ramdisks that can be included in the CD-ROM or provided by the bootloader when the operating system starts, so that a "live CD" environment can be prepared. The TARFS driver only supports read only file systems, therefore the driver does not have definitions for write functions. It is only possible to traverse a memory buffer that contains a TAR file, and read the files.
1 parent 59afb6c commit 4f4df49

File tree

8 files changed

+1076
-2
lines changed

8 files changed

+1076
-2
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@ build-kernel: check-profile
4949
tools/kcons conf/${PROFILE}
5050
make -C compile/${PROFILE}
5151

52+
dist/ramdisk.tar: check-profile
53+
tar -C ramdisk -cf dist/ramdisk.tar .
54+
5255
cdrom: check-profile dist/nativeos-${PROFILE}.iso
5356

54-
dist/nativeos-${PROFILE}.iso: compile/${PROFILE}/kernel
57+
dist/nativeos-${PROFILE}.iso: compile/${PROFILE}/kernel dist/ramdisk.tar
5558
rm -rf dist/${PROFILE}
5659
mkdir -p dist/${PROFILE}
5760
cp -R tools/cdrom/* dist/${PROFILE}
5861
cp compile/${PROFILE}/kernel dist/${PROFILE}/boot/nativeos.exe
62+
cp dist/ramdisk.tar dist/${PROFILE}/ramdisk.tar
5963
${GRUB_MKRESCUE} -d ${GRUB_ROOT}/../lib/grub/i386-pc -o dist/nativeos-${PROFILE}.iso dist/${PROFILE}
6064

6165

conf/files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
kernel/device/null.c standard
2+
kernel/fs/tarfs/tar.c standard
23
kernel/kern/fs_devfs.c standard
34
kernel/kern/fs_fsops.c standard
45
kernel/kern/fs_path.c standard

kernel/fs/tarfs/tar.c

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
#include <fs/tarfs/tar.h>
2+
#include <stddef.h>
3+
#include <sys/list.h>
4+
#include <sys/stdkern.h>
5+
#include <sys/vfs.h>
6+
7+
/**
8+
* An internal TAR file node, both as a header block reference for the TAR file
9+
* and as the VFS node that is exposed as the file system.
10+
*/
11+
struct tarfs_node {
12+
vfs_node_t *node;
13+
tar_header_block_t *block;
14+
};
15+
16+
/** The internals associated with a TARFS volume. */
17+
struct tarfs_payload {
18+
/** A pointer to the memory buffer of the TAR file. */
19+
unsigned char *buf;
20+
21+
/** A linked list with all the tarfs_node for all the files. */
22+
list_t *nodes;
23+
24+
/** Points back to the mounted VFS volume backing this payload. */
25+
vfs_volume_t *volume;
26+
27+
/** Points to the root node of the mounted TAR. */
28+
vfs_node_t *root;
29+
};
30+
31+
static unsigned int
32+
octal2int(char *octstring)
33+
{
34+
unsigned int acc = 0;
35+
while (*octstring) {
36+
acc = acc * 8 + (*octstring - '0');
37+
octstring++;
38+
}
39+
return acc;
40+
}
41+
42+
static int
43+
decode_block(struct tarfs_payload *tar, tar_header_block_t *block)
44+
{
45+
struct tarfs_node *tnode;
46+
47+
if ((tnode = malloc(sizeof(struct tarfs_node))) != 0) {
48+
tnode->block = block;
49+
tnode->node = 0;
50+
list_append(tar->nodes, tnode);
51+
return 0;
52+
} else {
53+
return -1;
54+
}
55+
}
56+
57+
static void
58+
split_basename(char *path, char **dirname, char **basename)
59+
{
60+
char *ptr = path, *slash = 0;
61+
62+
while (*ptr) {
63+
if (*ptr == '/') {
64+
slash = ptr;
65+
}
66+
ptr++;
67+
}
68+
69+
if (slash) {
70+
*slash = 0;
71+
*basename = slash + 1;
72+
*dirname = path;
73+
} else {
74+
*basename = path;
75+
*dirname = "";
76+
}
77+
}
78+
79+
static void reassemble_nodes(struct tarfs_payload *tarfile,
80+
struct tarfs_node *node);
81+
82+
static vfs_node_t *
83+
lookup_vnode(struct tarfs_payload *tarfile, const char *path)
84+
{
85+
listnode_t *listnode;
86+
struct tarfs_node *tarnode;
87+
char *kiwi;
88+
89+
list_foreach(tarfile->nodes, listnode)
90+
{
91+
unsigned int last_char;
92+
tarnode = (struct tarfs_node *) listnode->data;
93+
kiwi = tarnode->block->metadata.name;
94+
/* If file name is ./, remove trailing dot. */
95+
if (*kiwi == '.' && *(kiwi + 1) == '/') {
96+
kiwi++;
97+
}
98+
/* If starts with slash, remove it. */
99+
if (*kiwi == '/') {
100+
kiwi++;
101+
}
102+
/* Remove possible trailing slash. */
103+
last_char = strlen(kiwi) - 1;
104+
if (*kiwi && kiwi[last_char] == '/') {
105+
kiwi[last_char] = 0;
106+
}
107+
if (!strncmp(kiwi, path, 256)) {
108+
reassemble_nodes(tarfile, tarnode);
109+
return tarnode->node;
110+
}
111+
}
112+
113+
return NULL;
114+
}
115+
116+
static void
117+
reassemble_nodes(struct tarfs_payload *tarfile, struct tarfs_node *node)
118+
{
119+
char *orig_path, *path, *dirname, *basename;
120+
unsigned int last_char;
121+
vfs_node_t *vnode;
122+
123+
if (!node->node) {
124+
orig_path = strdup(node->block->metadata.name);
125+
path = orig_path;
126+
127+
/* Normalize paths removing absolute dir slashes and dots. */
128+
if (*path == '.' && *(path + 1) == '/') {
129+
path++;
130+
}
131+
if (*path == '/') {
132+
path++;
133+
}
134+
135+
/* Remove possible trailing slash. */
136+
last_char = strlen(path) - 1;
137+
if (*path && path[last_char] == '/') {
138+
path[last_char] = 0;
139+
}
140+
141+
/* Divide my path in dirname and basename. */
142+
143+
vnode = malloc(sizeof(vfs_node_t));
144+
vnode->vn_flags = 0;
145+
if (*path) {
146+
split_basename(path, &dirname, &basename);
147+
strcpy(vnode->vn_name, basename);
148+
vnode->vn_parent = lookup_vnode(tarfile, dirname);
149+
} else {
150+
strcpy(vnode->vn_name, "");
151+
vnode->vn_parent = NULL;
152+
if (!tarfile->root) {
153+
tarfile->root = vnode;
154+
}
155+
}
156+
vnode->vn_flags = 0;
157+
switch (node->block->metadata.type) {
158+
case '0':
159+
vnode->vn_flags |= VN_FREGFILE;
160+
break;
161+
case '5':
162+
vnode->vn_flags |= VN_FDIR;
163+
break;
164+
}
165+
vnode->vn_volume = tarfile->volume;
166+
vnode->vn_payload = node;
167+
node->node = vnode;
168+
169+
free(orig_path);
170+
}
171+
}
172+
173+
static void
174+
init_nodes(struct tarfs_payload *tar)
175+
{
176+
unsigned int bx = 0, file_length;
177+
tar_header_block_t *block = (tar_header_block_t *) tar->buf;
178+
listnode_t *lnode;
179+
180+
/* First we decode all the files in this TAR. */
181+
while (*block[bx].metadata.name) {
182+
decode_block(tar, &block[bx]);
183+
file_length = octal2int(block[bx].metadata.size);
184+
bx += (file_length / 512) + 1;
185+
if ((file_length % 512)) {
186+
bx++;
187+
}
188+
}
189+
190+
/* Then we reassemble the vfs_node_t data structures. */
191+
list_foreach(tar->nodes, lnode)
192+
{
193+
reassemble_nodes(tar, (struct tarfs_node *) lnode->data);
194+
}
195+
}
196+
197+
static int tarfs_mount(vfs_volume_t *volume);
198+
static int tarfs_open(vfs_node_t *node, unsigned int flags);
199+
static unsigned int tarfs_read(vfs_node_t *, unsigned, void *, unsigned);
200+
static unsigned int tarfs_write(vfs_node_t *, unsigned, void *, unsigned);
201+
static int tarfs_close(vfs_node_t *node);
202+
static vfs_node_t *tarfs_readdir(vfs_node_t *node, unsigned int index);
203+
static vfs_node_t *tarfs_finddir(vfs_node_t *node, char *name);
204+
205+
static vfs_ops_t tarfs_ops = {
206+
.vfs_close = &tarfs_close,
207+
.vfs_open = &tarfs_open,
208+
.vfs_read = &tarfs_read,
209+
.vfs_readdir = &tarfs_readdir,
210+
.vfs_finddir = &tarfs_finddir,
211+
};
212+
213+
static vfs_filesys_t tarfs_driver = {
214+
.fsd_ident = "tarfs",
215+
.fsd_name = "TAR File System",
216+
.fsd_mount = &tarfs_mount,
217+
.fsd_ops = &tarfs_ops,
218+
};
219+
220+
FS_DESCRIPTOR(tarfs, tarfs_driver);
221+
222+
static int
223+
tarfs_mount(vfs_volume_t *luna)
224+
{
225+
struct tarfs_payload *payload;
226+
227+
if ((payload = malloc(sizeof(struct tarfs_payload)))) {
228+
payload->buf = luna->vv_payload;
229+
payload->nodes = list_alloc();
230+
payload->volume = luna;
231+
init_nodes(payload);
232+
luna->vv_root = payload->root;
233+
luna->vv_payload = payload;
234+
return 0;
235+
} else {
236+
return -1;
237+
}
238+
}
239+
240+
static int
241+
tarfs_open(vfs_node_t *node, unsigned int flags)
242+
{
243+
/* TODO: Should check the flags, and mark the file as opened. */
244+
return 0;
245+
}
246+
247+
static unsigned int
248+
tarfs_read(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len)
249+
{
250+
unsigned char *data, *dst;
251+
struct tarfs_node *tar = (struct tarfs_node *) node->vn_payload;
252+
unsigned int read = 0, size = octal2int(tar->block->metadata.size);
253+
254+
data = (unsigned char *) &tar->block->metadata + 512;
255+
dst = (unsigned char *) buf;
256+
while (len > 0 && offt < size) {
257+
dst[read++] = data[offt++];
258+
len--;
259+
}
260+
return read;
261+
}
262+
263+
static unsigned int
264+
tarfs_write(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len)
265+
{
266+
return -1;
267+
}
268+
269+
static int
270+
tarfs_close(vfs_node_t *node)
271+
{
272+
return 0;
273+
}
274+
275+
static vfs_node_t *
276+
tarfs_readdir(vfs_node_t *klairm_cocayketa_voltereta,
277+
unsigned int clank_will_not_die)
278+
{
279+
listnode_t *esta_variable_es_del_mejor_mod_de_discord;
280+
struct tarfs_payload *dia_de_pago;
281+
struct tarfs_node *kiwi;
282+
283+
dia_de_pago = (struct tarfs_payload *)
284+
klairm_cocayketa_voltereta->vn_volume->vv_payload;
285+
286+
list_foreach(dia_de_pago->nodes,
287+
esta_variable_es_del_mejor_mod_de_discord)
288+
{
289+
kiwi = (struct tarfs_node *)
290+
esta_variable_es_del_mejor_mod_de_discord->data;
291+
if (kiwi->node->vn_parent == klairm_cocayketa_voltereta) {
292+
if (clank_will_not_die == 0) {
293+
return kiwi->node;
294+
} else {
295+
--clank_will_not_die;
296+
}
297+
}
298+
}
299+
300+
return NULL;
301+
}
302+
303+
static vfs_node_t *
304+
tarfs_finddir(vfs_node_t *node, char *name)
305+
{
306+
struct tarfs_payload *payload;
307+
struct tarfs_node *tarnode;
308+
listnode_t *listnode;
309+
310+
payload = node->vn_volume->vv_payload;
311+
list_foreach(payload->nodes, listnode)
312+
{
313+
tarnode = (struct tarfs_node *) listnode->data;
314+
if (tarnode->node->vn_parent == node
315+
&& !strcmp(name, tarnode->node->vn_name)) {
316+
return tarnode->node;
317+
}
318+
}
319+
320+
return NULL;
321+
}

kernel/fs/tarfs/tar.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
typedef struct tar_metadata {
4+
char name[100];
5+
char mode[8];
6+
char uid[8];
7+
char gid[8];
8+
char size[12];
9+
char mktime[12];
10+
char checksum[8];
11+
char type;
12+
char linkname[100];
13+
char magic[6];
14+
char version[2];
15+
char uname[32];
16+
char gname[32];
17+
char devmajor[8];
18+
char devminor[8];
19+
char prefix[155];
20+
} tar_metadata_t;
21+
22+
typedef struct tar_header_block {
23+
tar_metadata_t metadata;
24+
char reserved[12];
25+
} tar_header_block_t;

0 commit comments

Comments
 (0)