tarfs: new filesystem driver for read-only ustar mounting
This commit is contained in:
parent
5bf2cd5ea2
commit
f993a753cb
@ -84,6 +84,7 @@ static char * modules[] = {
|
||||
"E1000.KO", // 21
|
||||
"PCSPKR.KO", // 22
|
||||
"PORTIO.KO", // 23
|
||||
"TARFS.KO", // 24
|
||||
0
|
||||
};
|
||||
|
||||
|
485
modules/tarfs.c
Normal file
485
modules/tarfs.c
Normal file
@ -0,0 +1,485 @@
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* This file is part of ToaruOS and is released under the terms
|
||||
* of the NCSA / University of Illinois License - see LICENSE.md
|
||||
* Copyright (C) 2018 K. Lange
|
||||
*
|
||||
* tarfs - Allows read-only mounting of ustar archives
|
||||
*/
|
||||
#include <kernel/system.h>
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/fs.h>
|
||||
#include <kernel/logging.h>
|
||||
#include <kernel/module.h>
|
||||
#include <kernel/args.h>
|
||||
#include <kernel/printf.h>
|
||||
#include <kernel/tokenize.h>
|
||||
|
||||
#include <toaru/list.h>
|
||||
#include <toaru/hashmap.h>
|
||||
|
||||
#define TARFS_LOG_LEVEL WARNING
|
||||
|
||||
struct tarfs {
|
||||
fs_node_t * device;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
struct ustar {
|
||||
char filename[100];
|
||||
char mode[8];
|
||||
char ownerid[8];
|
||||
char groupid[8];
|
||||
|
||||
char size[12];
|
||||
char mtime[12];
|
||||
|
||||
char checksum[8];
|
||||
char type[1];
|
||||
char link[100];
|
||||
|
||||
char ustar[6];
|
||||
char version[2];
|
||||
|
||||
char owner[32];
|
||||
char group[32];
|
||||
|
||||
char dev_major[8];
|
||||
char dev_minor[8];
|
||||
|
||||
char prefix[155];
|
||||
};
|
||||
|
||||
static unsigned int interpret_uid(struct ustar * file) {
|
||||
return
|
||||
((file->ownerid[0] - '0') << 18) |
|
||||
((file->ownerid[1] - '0') << 15) |
|
||||
((file->ownerid[2] - '0') << 12) |
|
||||
((file->ownerid[3] - '0') << 9) |
|
||||
((file->ownerid[4] - '0') << 6) |
|
||||
((file->ownerid[5] - '0') << 3) |
|
||||
((file->ownerid[6] - '0') << 0);
|
||||
}
|
||||
|
||||
static unsigned int interpret_gid(struct ustar * file) {
|
||||
return
|
||||
((file->groupid[0] - '0') << 18) |
|
||||
((file->groupid[1] - '0') << 15) |
|
||||
((file->groupid[2] - '0') << 12) |
|
||||
((file->groupid[3] - '0') << 9) |
|
||||
((file->groupid[4] - '0') << 6) |
|
||||
((file->groupid[5] - '0') << 3) |
|
||||
((file->groupid[6] - '0') << 0);
|
||||
}
|
||||
|
||||
static unsigned int interpret_mode(struct ustar * file) {
|
||||
return
|
||||
((file->mode[0] - '0') << 18) |
|
||||
((file->mode[1] - '0') << 15) |
|
||||
((file->mode[2] - '0') << 12) |
|
||||
((file->mode[3] - '0') << 9) |
|
||||
((file->mode[4] - '0') << 6) |
|
||||
((file->mode[5] - '0') << 3) |
|
||||
((file->mode[6] - '0') << 0);
|
||||
}
|
||||
|
||||
static unsigned int interpret_size(struct ustar * file) {
|
||||
return
|
||||
((file->size[ 0] - '0') << 30) |
|
||||
((file->size[ 1] - '0') << 27) |
|
||||
((file->size[ 2] - '0') << 24) |
|
||||
((file->size[ 3] - '0') << 21) |
|
||||
((file->size[ 4] - '0') << 18) |
|
||||
((file->size[ 5] - '0') << 15) |
|
||||
((file->size[ 6] - '0') << 12) |
|
||||
((file->size[ 7] - '0') << 9) |
|
||||
((file->size[ 8] - '0') << 6) |
|
||||
((file->size[ 9] - '0') << 3) |
|
||||
((file->size[10] - '0') << 0);
|
||||
}
|
||||
|
||||
static unsigned int round_to_512(unsigned int i) {
|
||||
unsigned int t = i % 512;
|
||||
|
||||
if (!t) return i;
|
||||
return i + (512 - t);
|
||||
}
|
||||
|
||||
struct ustar * ustar_from_offset(struct tarfs * self, unsigned int offset);
|
||||
static fs_node_t * file_from_ustar(struct tarfs * self, struct ustar * file, unsigned int offset);
|
||||
|
||||
static char filename_workspace[256];
|
||||
|
||||
#ifndef strncat
|
||||
static char * strncat(char *dest, const char *src, size_t n) {
|
||||
char * end = dest;
|
||||
while (*end != '\0') {
|
||||
++end;
|
||||
}
|
||||
size_t i = 0;
|
||||
while (*src && i < n) {
|
||||
*end = *src;
|
||||
end++;
|
||||
src++;
|
||||
i++;
|
||||
}
|
||||
*end = '\0';
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int count_slashes(char * string) {
|
||||
int i = 0;
|
||||
char * s = strstr(string, "/");
|
||||
while (s) {
|
||||
if (*(s+1) == '\0') return i;
|
||||
i++;
|
||||
s = strstr(s+1,"/");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static struct dirent * readdir_tar_root(fs_node_t *node, uint32_t index) {
|
||||
if (index == 0) {
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = 0;
|
||||
strcpy(out->name, ".");
|
||||
return out;
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = 0;
|
||||
strcpy(out->name, "..");
|
||||
return out;
|
||||
}
|
||||
|
||||
index -= 2;
|
||||
|
||||
struct tarfs * self = node->device;
|
||||
/* Go through each file and pick the ones are at the root */
|
||||
/* Root files will have no /, so this is easy */
|
||||
unsigned int offset = 0;
|
||||
while (offset < self->length) {
|
||||
struct ustar * file = ustar_from_offset(self, offset);
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(filename_workspace, 0, 256);
|
||||
strncat(filename_workspace, file->prefix, 155);
|
||||
strncat(filename_workspace, file->filename, 100);
|
||||
|
||||
if (!count_slashes(filename_workspace)) {
|
||||
char * slash = strstr(filename_workspace,"/");
|
||||
if (slash) *slash = '\0'; /* remove trailing slash */
|
||||
if (strlen(filename_workspace)) {
|
||||
if (index == 0) {
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = offset;
|
||||
strcpy(out->name, filename_workspace);
|
||||
return out;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 512;
|
||||
offset += round_to_512(interpret_size(file));
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t read_tarfs(fs_node_t * node, uint64_t offset, uint32_t size, uint8_t * buffer) {
|
||||
struct tarfs * self = node->device;
|
||||
struct ustar * file = ustar_from_offset(self, node->inode);
|
||||
size_t file_size = interpret_size(file);
|
||||
|
||||
if (offset > file_size) return 0;
|
||||
if (offset + size > file_size) {
|
||||
size = file_size - offset;
|
||||
}
|
||||
|
||||
return read_fs(self->device, offset + node->inode + 512, size, buffer);
|
||||
}
|
||||
|
||||
static char my_filename[256];
|
||||
static struct dirent * readdir_tarfs(fs_node_t *node, uint32_t index) {
|
||||
if (index == 0) {
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = 0;
|
||||
strcpy(out->name, ".");
|
||||
return out;
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = 0;
|
||||
strcpy(out->name, "..");
|
||||
return out;
|
||||
}
|
||||
|
||||
index -= 2;
|
||||
|
||||
struct tarfs * self = node->device;
|
||||
|
||||
/* Go through each file and pick the ones are at the root */
|
||||
/* Root files will have no /, so this is easy */
|
||||
unsigned int offset = 0;
|
||||
|
||||
/* Read myself */
|
||||
struct ustar * directory = ustar_from_offset(self, node->inode);
|
||||
|
||||
/* Figure out my own filename, with forward slash */
|
||||
memset(my_filename, 0, 256);
|
||||
strncat(my_filename, directory->prefix, 155);
|
||||
strncat(my_filename, directory->filename, 100);
|
||||
|
||||
while (offset < self->length) {
|
||||
struct ustar * file = ustar_from_offset(self, offset);
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(filename_workspace, 0, 256);
|
||||
strncat(filename_workspace, file->prefix, 155);
|
||||
strncat(filename_workspace, file->filename, 100);
|
||||
|
||||
if (startswith(filename_workspace, my_filename)) {
|
||||
if (!count_slashes(filename_workspace + strlen(my_filename))) {
|
||||
if (strlen(filename_workspace + strlen(my_filename))) {
|
||||
if (index == 0) {
|
||||
char * slash = strstr(filename_workspace+strlen(my_filename),"/");
|
||||
if (slash) *slash = '\0'; /* remove trailing slash */
|
||||
struct dirent * out = malloc(sizeof(struct dirent));
|
||||
memset(out, 0x00, sizeof(struct dirent));
|
||||
out->ino = offset;
|
||||
strcpy(out->name, filename_workspace+strlen(my_filename));
|
||||
return out;
|
||||
} else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 512;
|
||||
offset += round_to_512(interpret_size(file));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static fs_node_t * finddir_tarfs(fs_node_t *node, char *name) {
|
||||
struct tarfs * self = node->device;
|
||||
|
||||
/* find my own filename */
|
||||
struct ustar * directory = ustar_from_offset(self, node->inode);
|
||||
|
||||
/* Figure out my own filename, with forward slash */
|
||||
memset(my_filename, 0, 256);
|
||||
strncat(my_filename, directory->prefix, 155);
|
||||
strncat(my_filename, directory->filename, 100);
|
||||
|
||||
/* Append name */
|
||||
strncat(my_filename, name, strlen(name));
|
||||
if (strlen(my_filename) > 255) {
|
||||
debug_print(CRITICAL, "what");
|
||||
}
|
||||
|
||||
unsigned int offset = 0;
|
||||
while (offset < self->length) {
|
||||
struct ustar * file = ustar_from_offset(self, offset);
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(filename_workspace, 0, 256);
|
||||
strncat(filename_workspace, file->prefix, 155);
|
||||
strncat(filename_workspace, file->filename, 100);
|
||||
|
||||
if (filename_workspace[strlen(filename_workspace)-1] == '/') {
|
||||
filename_workspace[strlen(filename_workspace)-1] = '\0';
|
||||
}
|
||||
if (!strcmp(filename_workspace, my_filename)) {
|
||||
return file_from_ustar(self, file, offset);
|
||||
}
|
||||
|
||||
offset += 512;
|
||||
offset += round_to_512(interpret_size(file));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int readlink_tarfs(fs_node_t * node, char * buf, size_t size) {
|
||||
struct tarfs * self = node->device;
|
||||
struct ustar * file = ustar_from_offset(self, node->inode);
|
||||
|
||||
if (size < strlen(file->link) + 1) {
|
||||
debug_print(INFO, "Requested read size was only %d, need %d.", size, strlen(file->link)+1);
|
||||
memcpy(buf, file->link, size-1);
|
||||
buf[size-1] = '\0';
|
||||
return size-1;
|
||||
} else {
|
||||
debug_print(INFO, "Reading link target is [%s]", file->link);
|
||||
memcpy(buf, file->link, strlen(file->link) + 1);
|
||||
return strlen(file->link);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static fs_node_t * file_from_ustar(struct tarfs * self, struct ustar * file, unsigned int offset) {
|
||||
fs_node_t * fs = malloc(sizeof(fs_node_t));
|
||||
memset(fs, 0, sizeof(fs_node_t));
|
||||
fs->device = self;
|
||||
fs->inode = offset;
|
||||
fs->impl = 0;
|
||||
memcpy(fs->name, filename_workspace, strlen(filename_workspace)+1);
|
||||
|
||||
fs->uid = interpret_uid(file);
|
||||
fs->gid = interpret_gid(file);
|
||||
fs->length = interpret_size(file);
|
||||
fs->mask = interpret_mode(file);
|
||||
fs->nlink = 0; /* Unsupported */
|
||||
fs->flags = FS_FILE;
|
||||
if (file->type[0] == '5') {
|
||||
fs->flags = FS_DIRECTORY;
|
||||
fs->readdir = readdir_tarfs;
|
||||
fs->finddir = finddir_tarfs;
|
||||
} else if (file->type[0] == '1') {
|
||||
debug_print(ERROR, "Hardlink detected");
|
||||
/* go through file and find target, reassign inode to point to that */
|
||||
} else if (file->type[0] == '2') {
|
||||
fs->flags = FS_SYMLINK;
|
||||
fs->readlink = readlink_tarfs;
|
||||
} else {
|
||||
fs->flags = FS_FILE;
|
||||
fs->read = read_tarfs;
|
||||
}
|
||||
#if 0
|
||||
/* TODO times are also available from the file */
|
||||
fs->atime = now();
|
||||
fs->mtime = now();
|
||||
fs->ctime = now();
|
||||
#endif
|
||||
return fs;
|
||||
}
|
||||
|
||||
static fs_node_t * finddir_tar_root(fs_node_t *node, char *name) {
|
||||
struct tarfs * self = node->device;
|
||||
|
||||
unsigned int offset = 0;
|
||||
while (offset < self->length) {
|
||||
struct ustar * file = ustar_from_offset(self, offset);
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(filename_workspace, 0, 256);
|
||||
strncat(filename_workspace, file->prefix, 155);
|
||||
strncat(filename_workspace, file->filename, 100);
|
||||
|
||||
if (count_slashes(filename_workspace)) {
|
||||
/* skip */
|
||||
} else {
|
||||
char * slash = strstr(filename_workspace,"/");
|
||||
if (slash) *slash = '\0';
|
||||
if (!strcmp(filename_workspace, name)) {
|
||||
return file_from_ustar(self, file, offset);
|
||||
}
|
||||
}
|
||||
|
||||
offset += 512;
|
||||
offset += round_to_512(interpret_size(file));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ustar _star = {0};
|
||||
|
||||
struct ustar * ustar_from_offset(struct tarfs * self, unsigned int offset) {
|
||||
read_fs(self->device, offset, sizeof(struct ustar), (unsigned char*)&_star);
|
||||
if (_star.ustar[0] != 'u' ||
|
||||
_star.ustar[1] != 's' ||
|
||||
_star.ustar[2] != 't' ||
|
||||
_star.ustar[3] != 'a' ||
|
||||
_star.ustar[4] != 'r') {
|
||||
return NULL;
|
||||
}
|
||||
return &_star;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static fs_node_t * node_from_directory(struct tarfs * self, struct tarfs_file * file) {
|
||||
struct ustar * s = ustar_from_offset(self, file->offset);
|
||||
fs_node_t * fnode = malloc(sizeof(fs_node_t));
|
||||
memset(fnode, 0, sizeof(fs_node_t));
|
||||
|
||||
fnode->flags = FS_DIRECTORY;
|
||||
|
||||
return fnode;
|
||||
}
|
||||
#endif
|
||||
|
||||
static fs_node_t * tar_mount(char * device, char * mount_path) {
|
||||
char * arg = strdup(device);
|
||||
char * argv[10];
|
||||
int argc = tokenize(arg, ",", argv);
|
||||
|
||||
if (argc > 1) {
|
||||
debug_print(WARNING, "tarfs driver takes no options");
|
||||
}
|
||||
|
||||
fs_node_t * dev = kopen(argv[0], 0);
|
||||
free(arg); /* Shouldn't need the filename or args anymore */
|
||||
|
||||
if (!dev) {
|
||||
debug_print(ERROR, "failed to open %s", device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a metadata struct for this mount */
|
||||
struct tarfs * self = malloc(sizeof(struct tarfs));
|
||||
|
||||
self->device = dev;
|
||||
self->length = dev->length;
|
||||
|
||||
fs_node_t * root = malloc(sizeof(fs_node_t));
|
||||
memset(root, 0, sizeof(fs_node_t));
|
||||
|
||||
root->uid = 0;
|
||||
root->gid = 0;
|
||||
root->length = 0;
|
||||
root->mask = 0555;
|
||||
root->readdir = readdir_tar_root;
|
||||
root->finddir = finddir_tar_root;
|
||||
root->flags = FS_DIRECTORY;
|
||||
root->device = self;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static int init(void) {
|
||||
vfs_register("tar", tar_mount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fini(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DEF(tarfs, init, fini);
|
||||
|
Loading…
Reference in New Issue
Block a user