Skip to content

Commit

Permalink
Add a rewrite of fakefsify in c
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Jun 7, 2020
1 parent b3f5b9c commit 393eb21
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 54 deletions.
26 changes: 2 additions & 24 deletions fs/fake-migrate.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "kernel/fs.h"
#include "debug.h"
#include "kernel/errno.h"
#include "fs/sqlutil.h"

// The value of the user_version pragma is used to decide what needs migrating.

Expand Down Expand Up @@ -33,31 +34,8 @@ static struct migration {
};

int fakefs_migrate(struct mount *mount) {
sqlite3 *db = mount->db;
int err;
#define CHECK_ERR() \
if (err != SQLITE_OK && err != SQLITE_ROW && err != SQLITE_DONE) \
die("sqlite error while migrating: %s\n", sqlite3_errmsg(mount->db));
#define EXEC(sql) \
err = sqlite3_exec(mount->db, sql, NULL, NULL, NULL); \
CHECK_ERR();
#define PREPARE(sql) ({ \
sqlite3_stmt *stmt; \
err = sqlite3_prepare_v2(mount->db, sql, -1, &stmt, NULL); \
CHECK_ERR(); \
stmt; \
})
#define STEP(stmt) ({ \
err = sqlite3_step(stmt); \
CHECK_ERR(); \
err == SQLITE_ROW; \
})
#define RESET(stmt) \
err = sqlite3_reset(stmt); \
CHECK_ERR()
#define FINALIZE(stmt) \
err = sqlite3_finalize(stmt); \
CHECK_ERR()

sqlite3_stmt *user_version = PREPARE("pragma user_version");
STEP(user_version);
int version = sqlite3_column_int(user_version, 0);
Expand Down
26 changes: 2 additions & 24 deletions fs/fake-rebuild.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sqlite3.h>
#include "fs/sqlutil.h"
#include "kernel/fs.h"
#include "kernel/errno.h"
#include "util/list.h"
Expand Down Expand Up @@ -29,30 +29,8 @@ struct entry {
};

int fakefs_rebuild(struct mount *mount) {
sqlite3 *db = mount->db;
int err;
#define CHECK_ERR() \
if (err != SQLITE_OK && err != SQLITE_ROW && err != SQLITE_DONE) \
die("sqlite error while rebuilding: %s\n", sqlite3_errmsg(mount->db))
#define EXEC(sql) \
err = sqlite3_exec(mount->db, sql, NULL, NULL, NULL); \
CHECK_ERR();
#define PREPARE(sql) ({ \
sqlite3_stmt *stmt; \
sqlite3_prepare_v2(mount->db, sql, -1, &stmt, NULL); \
CHECK_ERR(); \
stmt; \
})
#define STEP(stmt) ({ \
err = sqlite3_step(stmt); \
CHECK_ERR(); \
err == SQLITE_ROW; \
})
#define RESET(stmt) \
err = sqlite3_reset(stmt); \
CHECK_ERR()
#define FINALIZE(stmt) \
err = sqlite3_finalize(stmt); \
CHECK_ERR()

EXEC("begin");
EXEC("create table paths_old (path blob primary key, inode integer)");
Expand Down
2 changes: 1 addition & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int main(int argc, char *const argv[]) {
strcpy(envp, getenv("TERM") - strlen("TERM") - 1);
int err = xX_main_Xx(argc, argv, envp);
if (err < 0) {
fprintf(stderr, "%s\n", strerror(-err));
fprintf(stderr, "xX_main_Xx: %s\n", strerror(-err));
return err;
}
do_mount(&procfs, "proc", "/proc", "", 0);
Expand Down
10 changes: 7 additions & 3 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
project('ish', 'c',
default_options: ['default_library=static', 'c_std=gnu11', 'warning_level=2'])
cc = meson.get_compiler('c')

if meson.get_compiler('c').get_id() == 'clang'
if cc.get_id() == 'clang'
add_project_arguments('-Wimplicit-fallthrough', '-Wtautological-constant-in-range-compare', language: 'c')
endif

if get_option('b_sanitize').split(',').contains('undefined')
add_project_arguments('-fno-sanitize=alignment', language: 'c')
endif

log_on = get_option('log').split()
log_off = get_option('nolog').split()
foreach channel : log_on + log_off
Expand All @@ -29,7 +34,6 @@ add_project_arguments('-Wno-switch', language: 'c')

includes = [include_directories('.')]

cc = meson.get_compiler('c')
threads = dependency('threads')
librt = cc.find_library('rt', required: false)
libm = cc.find_library('m', required: false)
Expand Down Expand Up @@ -78,8 +82,8 @@ src = [
'fs/path.c',
'fs/real.c',
'fs/fake.c',
'fs/fake-rebuild.c',
'fs/fake-migrate.c',
'fs/fake-rebuild.c',

'fs/proc.c',
'fs/proc/entry.c',
Expand Down
168 changes: 168 additions & 0 deletions tools/fakefsify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <archive.h>
#include <archive_entry.h>

#define ISH_INTERNAL
#include "fs/fake.h"
#include "fs/sqlutil.h"
#include "misc.h"

static const char *schema = Q(
create table meta (id integer unique default 0, db_inode integer);
insert into meta (db_inode) values (0);
create table stats (inode integer primary key, stat blob);
create table paths (path blob primary key, inode integer references stats(inode));
create index inode_to_path on paths (inode, path);
// no index is needed on stats, because the rows are ordered by the primary key
pragma user_version=3;
);

void archive_err(const char *message, struct archive *archive) {
fprintf(stderr, "%s: %s\n", message, archive_error_string(archive));
abort();
}
void posix_err(const char *message) {
perror(message);
abort();
}
void sqlite_err(sqlite3 *db) {
fprintf(stderr, "sqlite error: %s\n", sqlite3_errmsg(db));
abort();
}
#undef HANDLE_ERR
#define HANDLE_ERR(db) sqlite_err(db)

// This isn't linked with ish which is why there's so much copy/pasted code

// I hate this code
static bool path_normalize(const char *path, char *out) {
#define ends_path(c) (c == '\0' || c == '/')
asm("pause");
// normalized format:
// ( '/' path-component ) *
while (path[0] != '\0') {
while (path[0] == '/')
path++;
if (path[0] == '\0')
break; // if the path ends with a slash
// path points to the start of a path component
if (path[0] == '.' && path[1] == '.' && ends_path(path[2]))
return false; // no dotdot allowed!
if (path[0] == '.' && ends_path(path[1])) {
path++;
} else {
*out++ = '/';
while (path[0] != '/' && path[0] != '\0')
*out++ = *path++;
}
}
*out = '\0';
return true;
}

extern const char *fix_path(const char *path);

int main(int argc, const char *argv[]) {
if (argc != 3) {
fprintf(stderr, "wrong number of arguments\n");
fprintf(stderr, "usage: %s <rootfs archive> <destination dir>\n", argv[0]);
return 1;
}
const char *archive_path = argv[1];
const char *fs = argv[2];

int err = mkdir(fs, 0777);
if (err < 0)
posix_err("mkdir");

// make the data root dir
char path_tmp[PATH_MAX];
snprintf(path_tmp, sizeof(path_tmp), "%s/data", fs);
err = mkdir(path_tmp, 0777);
int root_fd = open(path_tmp, O_RDONLY);
if (root_fd < 0)
posix_err("open root");

// open the database
snprintf(path_tmp, sizeof(path_tmp), "%s/meta.db", fs);
sqlite3 *db;
err = sqlite3_open_v2(path_tmp, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
CHECK_ERR();
EXEC("pragma journal_mode=wal")
EXEC("begin");
EXEC(schema);

// open the archive
struct archive *archive = archive_read_new();
if (archive == NULL)
archive_err("archive_read_new", archive);
archive_read_support_filter_gzip(archive);
archive_read_support_format_tar(archive);
if (archive_read_open_filename(archive, archive_path, 65536) != ARCHIVE_OK)
archive_err("archive_read_open_filename", archive);

sqlite3_stmt *insert_stat = PREPARE("insert into stats (stat) values (?)");
sqlite3_stmt *insert_path = PREPARE("insert or replace into paths values (?, last_insert_rowid())");

// do actual shit
struct archive_entry *entry;
while ((err = archive_read_next_header(archive, &entry)) == ARCHIVE_OK) {
char entry_path[PATH_MAX];
if (!path_normalize(archive_entry_pathname(entry), entry_path)) {
// Avoid pwnage
fprintf(stderr, "warning: skipped possible path traversal %s\n", archive_entry_pathname(entry));
continue;
}

int fd = -1;
if (archive_entry_filetype(entry) != AE_IFDIR) {
fd = openat(root_fd, fix_path(entry_path), O_CREAT | O_WRONLY, 0666);
if (fd < 0)
posix_err("open entry");
}
switch (archive_entry_filetype(entry)) {
case AE_IFDIR:
if (strcmp(entry_path, "") == 0)
break; // root has already been created
err = mkdirat(root_fd, fix_path(entry_path), 0777);
if (err < 0)
posix_err("mkdir entry");
break;
case AE_IFREG:
if (archive_read_data_into_fd(archive, fd) != ARCHIVE_OK)
archive_err("save file", archive);
break;
case AE_IFLNK:
err = write(fd, archive_entry_symlink(entry), strlen(archive_entry_symlink(entry)));
if (err < 0)
posix_err("save link");
}
if (fd != -1)
close(fd);

struct ish_stat stat = {
.mode = archive_entry_mode(entry),
.uid = archive_entry_uid(entry),
.gid = archive_entry_gid(entry),
.rdev = archive_entry_rdev(entry),
};
sqlite3_bind_blob(insert_stat, 1, &stat, sizeof(stat), SQLITE_TRANSIENT);
STEP_RESET(insert_stat);
sqlite3_bind_blob(insert_path, 1, entry_path, strlen(entry_path), SQLITE_TRANSIENT);
STEP_RESET(insert_path);
}
if (err != ARCHIVE_EOF)
archive_err("archive_read_next_header", archive);

FINALIZE(insert_stat);
FINALIZE(insert_path);
EXEC("commit");
sqlite3_close(db);

if (archive_read_free(archive) != ARCHIVE_OK)
archive_err("archive_read_free", archive);
}
1 change: 0 additions & 1 deletion tools/fakefsify.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
create index inode_to_path on paths (inode, path);
pragma user_version=3;
"""
# no index is needed on stats, because the rows are ordered by the primary key


def extract_member(archive, db, member):
Expand Down
7 changes: 6 additions & 1 deletion tools/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ if host_machine.system() == 'linux' and host_machine.cpu() == 'x86_64'
include_directories: includes)
endif

unicorn = meson.get_compiler('c').find_library('unicorn', required: false)
unicorn = cc.find_library('unicorn', required: false)
if unicorn.found()
executable('unicornomatic', ['unicornomatic.c', 'undefined-flags.c'], dependencies: [ish, unicorn])
configure_file(input: 'ptraceomatic-gdb.gdb', output: 'unicornomatic-gdb.gdb', copy: true)
endif

libarchive = cc.find_library('libarchive', required: false)
if libarchive.found()
executable('fakefsify', 'fakefsify.c', dependencies: [ish, libarchive])
endif

0 comments on commit 393eb21

Please sign in to comment.