instow

:)
git clone https://git.sr.ht/~ashymad/instow
Log | Files | Refs | LICENSE

commit 26f2dd931f94f37e2c389a2680ca21a3542edfe6
parent e87b61d4c850001ddee1e2420e576c676887aa57
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Mon, 16 Mar 2026 16:27:41 +0100

Full bootstrap achieved

Diffstat:
Mproject.janet | 1+
Msrc/file.janet | 11++++++++++-
Msrc/libc.janet | 30++++++++++++++++++++++++++++++
Msrc/main.janet | 4+++-
Msrc/native/nftw.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/native/os.c | 192+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/native/os.h | 8++------
7 files changed, 254 insertions(+), 94 deletions(-)

diff --git a/project.janet b/project.janet @@ -3,6 +3,7 @@ (declare-binscript :main "instowl" + :hardcode-syspath true :is-janet true) (declare-native diff --git a/src/file.janet b/src/file.janet @@ -10,9 +10,18 @@ (defn mkdirp [path] (if (not (dir-exists? path)) (do (mkdirp (libc/dirname path)) (os/mkdir path)))) +(defn copy-file [src dst] + (def src_fd (libc/ctry (nftw/open src :r))) + (if (file-exists? dst) (os/rm dst)) + (def dst_fd (libc/ctry (nftw/open dst :wxc (nftw/fstat src_fd :int-permissions)))) + (libc/sendfile dst_fd src_fd) + (nftw/close src_fd) + (nftw/close dst_fd)) + (defn move-file [src dst] (mkdirp (libc/dirname dst)) - (os/rename src dst)) + (try (os/rename src dst) + ([a b] (copy-file src dst)))) (defn rmrf [path] (nftw/nftw path (fn [file stat ftype info] diff --git a/src/libc.janet b/src/libc.janet @@ -1,4 +1,5 @@ (import ./utils) +(import ./native/nftw) (defmacro bind [name & types] (with-syms [$fn $sig] @@ -18,6 +19,13 @@ (defmacro defbind/str [name] ~(def ,name (bind/str ,(string/join [name])))) +(defmacro ctry [call] + (with-syms [$ret] + ~(let [,$ret ,call] + (if (= ,$ret -1) + (error (string/join ["Function failed with:" (nftw/strerror)] " ")) + ,$ret)))) + (def c/glob :private (bind "glob" :int :string :int :ptr :ptr)) (def c/globfree :private (bind "globfree" :void :ptr)) (def c/glob_t :private (ffi/struct :size :ptr :size)) @@ -42,6 +50,28 @@ (c/ioctl fd (args 0) arg) (ffi/read (args 1) arg)))) +(def c/sendfile :private (bind "sendfile" :ssize :int :int :ptr :size)) + +(def c/lseek :private (bind "lseek" :int :int :int :int)) + +(defn lseek [fd &opt whence offset] + (def whence_ + (case whence + :set 0 + :cur 1 + :end 2 + nil 1)) + (def offset_ (if (nil? offset) 0 offset)) + (c/lseek fd offset_ whence_)) + +(defn sendfile [out_fd in_fd &opt offset count] + (def offset_ (if (nil? offset) (lseek in_fd) offset)) + (var count_ (if (nil? count) (- (ctry (nftw/fstat in_fd :size)) offset_) count)) + (def offset/ptr @"") + (buffer/push-uint64 offset/ptr :native offset_) + #(while (> (set count_ (- count_ (ctry (c/sendfile out_fd in_fd offset/ptr count_)))) 0))) + (ctry (c/sendfile out_fd in_fd offset/ptr count_))) + (defbind get_nprocs :int) (defbind/str dirname) (defbind/str basename) diff --git a/src/main.janet b/src/main.janet @@ -78,6 +78,8 @@ (def pkgdir (path/join stowdir pkg)) (def destdir (libc/mkdtemp "/tmp/instowl.XXXXXX")) + (def adopt (= (get args 1) "--adopt")) + (def env (os/environ)) (merge-into env {:err :pipe :out :pipe @@ -231,7 +233,7 @@ (file/close logfile)) :stow - (checkrun :done :stow "-v" "-d" stowdir "-t" target "--override=.*" pkg) + (checkrun :done :stow "-v" "-d" stowdir "-t" target ;(if adopt ["--adopt"] []) pkg) :error (do diff --git a/src/native/nftw.c b/src/native/nftw.c @@ -1,14 +1,18 @@ #define _GNU_SOURCE #define _FILE_OFFSET_BITS 64 +#include <errno.h> +#include <fcntl.h> #include <ftw.h> #include <janet.h> +#include <stdbool.h> +#include <unistd.h> #include "os.h" static JanetFunction *janet_callback = NULL; -static const char *flagname(int flag) { +static const char *FTW_(int flag) { switch (flag) { case FTW_F: return "f"; @@ -31,14 +35,6 @@ static const char *flagname(int flag) { int callback(const char *path, const struct stat *st, int flag, struct FTW *info) { - JanetTable *stattab = janet_table(0); - - if (st) { - for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL; - sg++) { - janet_table_put(stattab, janet_ckeywordv(sg->name), sg->fn(st)); - } - } JanetTable *ftwtab = janet_table(0); janet_table_put(ftwtab, janet_ckeywordv("base"), @@ -48,10 +44,8 @@ int callback(const char *path, const struct stat *st, int flag, return janet_unwrap_integer(janet_call( janet_callback, 4, - (const Janet[]){janet_cstringv(path), - st ? janet_wrap_table(stattab) : janet_wrap_nil(), - janet_ckeywordv(flagname(flag)), - janet_wrap_table(ftwtab)})); + (const Janet[]){janet_cstringv(path), stat2table(st, NULL), + janet_ckeywordv(FTW_(flag)), janet_wrap_table(ftwtab)})); } struct { @@ -86,8 +80,90 @@ static Janet c_nftw(int32_t argc, Janet *argv) { flags)); } +static Janet c_fileno(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + return janet_wrap_integer( + fileno(((JanetFile *)janet_unwrap_abstract(argv[0]))->file)); +} + +static Janet c_fstat(int32_t argc, Janet *argv) { + janet_arity(argc, 1, 2); + + struct stat st; + if (fstat(janet_unwrap_integer(argv[0]), &st) == -1) + return janet_wrap_nil(); + + const char *key = argc == 2 ? janet_unwrap_string(argv[1]) : NULL; + + return stat2table(&st, key); +} + +static int O_(char name) { + switch (name) { + case '+': + return O_APPEND; + case 'c': + return O_CREAT; + case 'x': + return O_EXCL; + case '-': + return O_TRUNC; + default: + return 0; + } +} + +static int parseO(const char *opts) { + int flags = 0; + bool read = false; + bool write = false; + for (int i = 0; opts[i]; i++) { + if (opts[i] == 'w') + write = true; + else if (opts[i] == 'r') + read = true; + else + flags |= O_(opts[i]); + } + + if (read && write) + flags |= O_RDWR; + else if (write) + flags |= O_WRONLY; + else if (read) + flags |= O_RDONLY; + + return flags; +} + +static Janet c_open(int argc, Janet *argv) { + janet_arity(argc, 1, 3); + + int flags = argc > 1 ? parseO(janet_unwrap_string(argv[1])) : 0; + jmode_t mode = argc > 2 ? os_getmode(argv, 2) : 644; + + return janet_wrap_integer(open(janet_unwrap_string(argv[0]), flags, mode)); +} + +static Janet c_close(int argc, Janet *argv) { + janet_fixarity(argc, 1); + + return janet_wrap_integer(close(janet_unwrap_integer(argv[0]))); +} + +static Janet c_strerror(int argc, Janet *argv) { + janet_fixarity(argc, 0); + + return janet_cstringv(strerror(errno)); +} + static JanetReg cfuns[] = { {"nftw", c_nftw, "(nftw path callback fd_limit & flags)\n\nruns nftw"}, + {"fileno", c_fileno, "(fileno file)\n\nreturns fd"}, + {"fstat", c_fstat, "(fstat fd &opt key)\n\nreturns stats from fd"}, + {"open", c_open, "(open path &opt flags mode)\n\nreturns fd"}, + {"close", c_close, "(close fd)\n\ncloses fd"}, + {"strerror", c_strerror, "(strerror)\n\nprint errno error string"}, {NULL, NULL, NULL}}; JANET_MODULE_ENTRY(JanetTable *env) { janet_cfuns(env, "nftw", cfuns); } diff --git a/src/native/os.c b/src/native/os.c @@ -1,128 +1,149 @@ #include "os.h" -static int32_t janet_perm_to_unix(mode_t m) { - return (int32_t) m; -} +static int32_t janet_perm_to_unix(mode_t m) { return (int32_t)m; } -static mode_t janet_perm_from_unix(int32_t x) { - return (mode_t) x; -} +static mode_t janet_perm_from_unix(int32_t x) { return (mode_t)x; } static const uint8_t *janet_decode_mode(mode_t m) { - const char *str = "other"; - if (S_ISREG(m)) str = "file"; - else if (S_ISDIR(m)) str = "directory"; - else if (S_ISFIFO(m)) str = "fifo"; - else if (S_ISBLK(m)) str = "block"; - else if (S_ISSOCK(m)) str = "socket"; - else if (S_ISLNK(m)) str = "link"; - else if (S_ISCHR(m)) str = "character"; - return janet_ckeyword(str); + const char *str = "other"; + if (S_ISREG(m)) + str = "file"; + else if (S_ISDIR(m)) + str = "directory"; + else if (S_ISFIFO(m)) + str = "fifo"; + else if (S_ISBLK(m)) + str = "block"; + else if (S_ISSOCK(m)) + str = "socket"; + else if (S_ISLNK(m)) + str = "link"; + else if (S_ISCHR(m)) + str = "character"; + return janet_ckeyword(str); } static int32_t janet_decode_permissions(jmode_t mode) { - return (int32_t)(mode & 0777); + return (int32_t)(mode & 0777); } static int32_t os_parse_permstring(const uint8_t *perm) { - int32_t m = 0; - if (perm[0] == 'r') m |= 0400; - if (perm[1] == 'w') m |= 0200; - if (perm[2] == 'x') m |= 0100; - if (perm[3] == 'r') m |= 0040; - if (perm[4] == 'w') m |= 0020; - if (perm[5] == 'x') m |= 0010; - if (perm[6] == 'r') m |= 0004; - if (perm[7] == 'w') m |= 0002; - if (perm[8] == 'x') m |= 0001; - return m; + int32_t m = 0; + if (perm[0] == 'r') + m |= 0400; + if (perm[1] == 'w') + m |= 0200; + if (perm[2] == 'x') + m |= 0100; + if (perm[3] == 'r') + m |= 0040; + if (perm[4] == 'w') + m |= 0020; + if (perm[5] == 'x') + m |= 0010; + if (perm[6] == 'r') + m |= 0004; + if (perm[7] == 'w') + m |= 0002; + if (perm[8] == 'x') + m |= 0001; + return m; } static Janet os_make_permstring(int32_t permissions) { - uint8_t bytes[9] = {0}; - bytes[0] = (permissions & 0400) ? 'r' : '-'; - bytes[1] = (permissions & 0200) ? 'w' : '-'; - bytes[2] = (permissions & 0100) ? 'x' : '-'; - bytes[3] = (permissions & 0040) ? 'r' : '-'; - bytes[4] = (permissions & 0020) ? 'w' : '-'; - bytes[5] = (permissions & 0010) ? 'x' : '-'; - bytes[6] = (permissions & 0004) ? 'r' : '-'; - bytes[7] = (permissions & 0002) ? 'w' : '-'; - bytes[8] = (permissions & 0001) ? 'x' : '-'; - return janet_stringv(bytes, sizeof(bytes)); + uint8_t bytes[9] = {0}; + bytes[0] = (permissions & 0400) ? 'r' : '-'; + bytes[1] = (permissions & 0200) ? 'w' : '-'; + bytes[2] = (permissions & 0100) ? 'x' : '-'; + bytes[3] = (permissions & 0040) ? 'r' : '-'; + bytes[4] = (permissions & 0020) ? 'w' : '-'; + bytes[5] = (permissions & 0010) ? 'x' : '-'; + bytes[6] = (permissions & 0004) ? 'r' : '-'; + bytes[7] = (permissions & 0002) ? 'w' : '-'; + bytes[8] = (permissions & 0001) ? 'x' : '-'; + return janet_stringv(bytes, sizeof(bytes)); } static int32_t os_get_unix_mode(const Janet *argv, int32_t n) { - int32_t unix_mode; - if (janet_checkint(argv[n])) { - /* Integer mode */ - int32_t x = janet_unwrap_integer(argv[n]); - if (x < 0 || x > 0777) { - janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v", n, argv[n]); - } - unix_mode = x; - } else { - /* Bytes mode */ - JanetByteView bytes = janet_getbytes(argv, n); - if (bytes.len != 9) { - janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v", n, argv[n]); - } - unix_mode = os_parse_permstring(bytes.bytes); + int32_t unix_mode; + if (janet_checkint(argv[n])) { + /* Integer mode */ + int32_t x = janet_unwrap_integer(argv[n]); + if (x < 0 || x > 0777) { + janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v", + n, argv[n]); } - return unix_mode; + unix_mode = x; + } else { + /* Bytes mode */ + JanetByteView bytes = janet_getbytes(argv, n); + if (bytes.len != 9) { + janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v", + n, argv[n]); + } + unix_mode = os_parse_permstring(bytes.bytes); + } + return unix_mode; } -static jmode_t os_getmode(const Janet *argv, int32_t n) { - return janet_perm_from_unix(os_get_unix_mode(argv, n)); +jmode_t os_getmode(const Janet *argv, int32_t n) { + return janet_perm_from_unix(os_get_unix_mode(argv, n)); } /* Getters */ static Janet os_stat_dev(const jstat_t *st) { - return janet_wrap_number(st->st_dev); + return janet_wrap_number(st->st_dev); } static Janet os_stat_inode(const jstat_t *st) { - return janet_wrap_number(st->st_ino); + return janet_wrap_number(st->st_ino); } static Janet os_stat_mode(const jstat_t *st) { - return janet_wrap_keyword(janet_decode_mode(st->st_mode)); + return janet_wrap_keyword(janet_decode_mode(st->st_mode)); } static Janet os_stat_int_permissions(const jstat_t *st) { - return janet_wrap_integer(janet_perm_to_unix(janet_decode_permissions(st->st_mode))); + return janet_wrap_integer( + janet_perm_to_unix(janet_decode_permissions(st->st_mode))); } static Janet os_stat_permissions(const jstat_t *st) { - return os_make_permstring(janet_perm_to_unix(janet_decode_permissions(st->st_mode))); + return os_make_permstring( + janet_perm_to_unix(janet_decode_permissions(st->st_mode))); } static Janet os_stat_uid(const jstat_t *st) { - return janet_wrap_number(st->st_uid); + return janet_wrap_number(st->st_uid); } static Janet os_stat_gid(const jstat_t *st) { - return janet_wrap_number(st->st_gid); + return janet_wrap_number(st->st_gid); } static Janet os_stat_nlink(const jstat_t *st) { - return janet_wrap_number(st->st_nlink); + return janet_wrap_number(st->st_nlink); } static Janet os_stat_rdev(const jstat_t *st) { - return janet_wrap_number(st->st_rdev); + return janet_wrap_number(st->st_rdev); } static Janet os_stat_size(const jstat_t *st) { - return janet_wrap_number(st->st_size); + return janet_wrap_number(st->st_size); } static Janet os_stat_accessed(const jstat_t *st) { - return janet_wrap_number((double) st->st_atime); + return janet_wrap_number((double)st->st_atime); } static Janet os_stat_modified(const jstat_t *st) { - return janet_wrap_number((double) st->st_mtime); + return janet_wrap_number((double)st->st_mtime); } static Janet os_stat_changed(const jstat_t *st) { - return janet_wrap_number((double) st->st_ctime); + return janet_wrap_number((double)st->st_ctime); } static Janet os_stat_blocks(const jstat_t *st) { - return janet_wrap_number(st->st_blocks); + return janet_wrap_number(st->st_blocks); } static Janet os_stat_blocksize(const jstat_t *st) { - return janet_wrap_number(st->st_blksize); + return janet_wrap_number(st->st_blksize); } +struct OsStatGetter { + const char *name; + Janet (*fn)(const jstat_t *st); +}; + const struct OsStatGetter os_stat_getters[] = { {"dev", os_stat_dev}, {"inode", os_stat_inode}, @@ -139,5 +160,30 @@ const struct OsStatGetter os_stat_getters[] = { {"accessed", os_stat_accessed}, {"modified", os_stat_modified}, {"changed", os_stat_changed}, - {NULL, NULL} -}; + {NULL, NULL}}; + +Janet stat2table(const jstat_t *st, const uint8_t *key) { + + if (NULL == st) + return janet_wrap_nil(); + + JanetTable *tab = janet_table(0); + + if (NULL == key) { + /* Put results in table */ + for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL; + sg++) { + janet_table_put(tab, janet_ckeywordv(sg->name), sg->fn(st)); + } + return janet_wrap_table(tab); + } else { + /* Get one result */ + for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL; + sg++) { + if (janet_cstrcmp(key, sg->name)) + continue; + return sg->fn(st); + } + janet_panicf("unexpected keyword %v", janet_wrap_keyword(key)); + } +} diff --git a/src/native/os.h b/src/native/os.h @@ -12,9 +12,5 @@ typedef struct stat jstat_t; typedef mode_t jmode_t; -struct OsStatGetter { - const char *name; - Janet(*fn)(const jstat_t *st); -}; - -extern const struct OsStatGetter os_stat_getters[]; +Janet stat2table(const jstat_t *st, const uint8_t *key); +jmode_t os_getmode(const Janet *argv, int32_t n);