diff --git a/Makefile b/Makefile index 1701a83c3..d0d2ab986 100644 --- a/Makefile +++ b/Makefile @@ -86,7 +86,7 @@ o/$(MODE): \ o/$(MODE)/examples \ o/$(MODE)/third_party -ifneq ($(USE_SYSTEM_TOOLCHAIN),) +ifeq ($(USE_SYSTEM_TOOLCHAIN),) .STRICT = 1 endif @@ -100,6 +100,7 @@ endif r:build/portcosmo.h \ /proc/stat \ rw:/dev/null \ + rw:/dev/full \ w:o/stack.log \ /etc/hosts \ ~/.runit.psk \ @@ -148,13 +149,13 @@ include libc/log/log.mk # │ include third_party/getopt/getopt.mk # │ include third_party/bzip2/bzip2.mk # │ include dsp/core/core.mk # │ +include third_party/musl/musl.mk # │ include libc/x/x.mk # │ include third_party/stb/stb.mk # │ include dsp/scale/scale.mk # │ include dsp/mpeg/mpeg.mk # │ include dsp/dsp.mk # │ include third_party/zlib/gz/gz.mk # │ -include third_party/musl/musl.mk # │ include third_party/intel/intel.mk # │ include third_party/aarch64/aarch64.mk # │ include libc/libc.mk #─┘ @@ -436,7 +437,7 @@ aarch64: $(MAKE) m=aarch64 clean: - rm -rf o + $(RM) -r o # UNSPECIFIED PREREQUISITES TUTORIAL # diff --git a/ape/ape.mk b/ape/ape.mk index 60c52d8b2..5d575aa38 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -129,6 +129,7 @@ o/$(MODE)/ape/ape-no-modify-self.o: \ ape/ape.internal.h \ libc/dce.h \ libc/elf/def.h \ + libc/thread/tls.h \ libc/intrin/asancodes.h \ libc/macho.internal.h \ libc/macros.internal.h \ @@ -158,6 +159,7 @@ o/$(MODE)/ape/ape-copy-self.o: \ ape/ape.internal.h \ libc/dce.h \ libc/elf/def.h \ + libc/thread/tls.h \ libc/intrin/asancodes.h \ libc/macho.internal.h \ libc/macros.internal.h \ diff --git a/build/bootstrap/rm.com b/build/bootstrap/rm.com index be89f8c0f..c1a311f3f 100755 Binary files a/build/bootstrap/rm.com and b/build/bootstrap/rm.com differ diff --git a/libc/calls/remove.c b/libc/calls/remove.c index 256adff3a..029b77bb4 100644 --- a/libc/calls/remove.c +++ b/libc/calls/remove.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/sysv/consts/at.h" /** * Deletes "file" or empty directory associtaed with name. @@ -26,5 +27,9 @@ * @see unlink() and rmdir() which this abstracts */ int remove(const char *name) { - return unlink(name) != -1 || (errno == EISDIR && rmdir(name) != -1) ? 0 : -1; + int e = errno; + if (!unlinkat(AT_FDCWD, name, 0)) return 0; + if (errno != EISDIR) return -1; + errno = e; + return unlinkat(AT_FDCWD, name, AT_REMOVEDIR); } diff --git a/libc/nexgen32e/nexgen32e.mk b/libc/nexgen32e/nexgen32e.mk index c7f9cd69d..b37bfb6f0 100644 --- a/libc/nexgen32e/nexgen32e.mk +++ b/libc/nexgen32e/nexgen32e.mk @@ -85,9 +85,9 @@ o/$(MODE)/libc/nexgen32e/gclongjmp.o: libc/nexgen32e/gclongjmp.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< o/$(MODE)/libc/nexgen32e/checkstackalign.o: libc/nexgen32e/checkstackalign.S @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/nexgen32e/blink_xnu_aarch64.o: libc/nexgen32e/blink_xnu_aarch64.S +o/$(MODE)/libc/nexgen32e/blink_xnu_aarch64.o: libc/nexgen32e/blink_xnu_aarch64.S ape/blink-xnu-aarch64.gz @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< -o/$(MODE)/libc/nexgen32e/blink_linux_aarch64.o: libc/nexgen32e/blink_linux_aarch64.S +o/$(MODE)/libc/nexgen32e/blink_linux_aarch64.o: libc/nexgen32e/blink_linux_aarch64.S ape/blink-linux-aarch64.gz @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $< LIBC_NEXGEN32E_LIBS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x))) diff --git a/libc/x/rmrf.c b/libc/x/rmrf.c index b59560e53..505998e9b 100644 --- a/libc/x/rmrf.c +++ b/libc/x/rmrf.c @@ -16,66 +16,38 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/calls/struct/dirent.h" -#include "libc/calls/struct/stat.h" #include "libc/errno.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/dt.h" -#include "libc/sysv/consts/s.h" +#include "libc/sysv/errfuns.h" #include "libc/x/x.h" +#include "third_party/musl/ftw.h" -static int rmrfdir(const char *dirpath) { +static int rmrf_callback(const char *fpath, // + const struct stat *st, // + int typeflag, // + struct FTW *ftwbuf) { // int rc; - DIR *d; - char *path; - struct dirent *e; - if (!(d = opendir(dirpath))) return -1; - while ((e = readdir(d))) { - if (!strcmp(e->d_name, ".")) continue; - if (!strcmp(e->d_name, "..")) continue; - _npassert(!strchr(e->d_name, '/')); - path = xjoinpaths(dirpath, e->d_name); - if (e->d_type == DT_DIR) { - rc = rmrfdir(path); - } else { - rc = unlink(path); - } - free(path); - if (rc == -1) { - closedir(d); - return -1; + if (typeflag == FTW_DNR) { + if (!(rc = chmod(fpath, 0700))) { + return nftw(fpath, rmrf_callback, 128 - ftwbuf->level, + FTW_PHYS | FTW_DEPTH); } + } else if (typeflag == FTW_DP) { + rc = rmdir(fpath); + } else { + rc = unlink(fpath); + } + if (rc == -1 && errno == ENOENT) { + rc = 0; } - rc = closedir(d); - rc |= rmdir(dirpath); return rc; } /** * Recursively removes file or directory. - * * @return 0 on success, or -1 w/ errno */ int rmrf(const char *path) { - int e; - struct stat st; - e = errno; - if (stat(path, &st) == -1) { - if (errno == ENOENT) { - errno = e; - return 0; - } else { - return -1; - } - } - if (!S_ISDIR(st.st_mode)) { - return unlink(path); - } else { - return rmrfdir(path); - } + if (path[0] == '/' && !path[1]) return enotsup(); + return nftw(path, rmrf_callback, 128, FTW_PHYS | FTW_DEPTH); } diff --git a/libc/x/x.mk b/libc/x/x.mk index 0d23edf8a..e1e814269 100644 --- a/libc/x/x.mk +++ b/libc/x/x.mk @@ -35,6 +35,7 @@ LIBC_X_A_DIRECTDEPS = \ LIBC_STR \ LIBC_SYSV \ THIRD_PARTY_GDTOA \ + THIRD_PARTY_MUSL \ THIRD_PARTY_ZLIB LIBC_X_A_DEPS := \ diff --git a/test/libc/runtime/grow_test.c b/test/libc/runtime/grow_test.c index e50565d58..a3a2338bc 100644 --- a/test/libc/runtime/grow_test.c +++ b/test/libc/runtime/grow_test.c @@ -27,6 +27,8 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" +STATIC_YOINK("realloc"); + TEST(grow, testNull_hasAllocatingBehavior) { void *p = NULL; size_t capacity = 0; diff --git a/test/libc/thread/pthread_setname_np_test.c b/test/libc/thread/pthread_setname_np_test.c index d4744351a..08903c1a4 100644 --- a/test/libc/thread/pthread_setname_np_test.c +++ b/test/libc/thread/pthread_setname_np_test.c @@ -101,6 +101,7 @@ TEST(pthread_setname_np, GetNameOfOtherThread) { while (!atomic_load(&sync1)) pthread_yield(); errno_t e = pthread_getname_np(id, me, sizeof(me)); if (IsLinux() && e == ENOENT) return; // bah old kernel + if (IsLinux() && e == EACCES) return; // meh landlock ASSERT_EQ(0, e); EXPECT_STREQ("justine", me); ASSERT_EQ(0, pthread_setname_np(id, "tunney")); diff --git a/third_party/musl/ftw.c b/third_party/musl/ftw.c index f3f174117..d92f3f8fb 100644 --- a/third_party/musl/ftw.c +++ b/third_party/musl/ftw.c @@ -31,19 +31,23 @@ asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ Copyright 2005-2014 Rich Felker, et. al.\""); asm(".include \"libc/disclaimer.inc\""); - -/* clang-format off */ +// clang-format off /** * Walks file tree. * + * @return 0 on success, -1 on error, or non-zero `fn` result * @see examples/walk.c for example * @see nftw() */ -int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int fd_limit) +int ftw(const char *dirpath, + int fn(const char *fpath, + const struct stat *st, + int typeflag), + int fd_limit) { /* The following cast assumes that calling a function with one * argument more than it needs behaves as expected. This is * actually undefined, but works on all real-world machines. */ - return nftw(path, (int (*)())fn, fd_limit, FTW_PHYS); + return nftw(dirpath, (int (*)())fn, fd_limit, FTW_PHYS); } diff --git a/third_party/musl/ftw.h b/third_party/musl/ftw.h index 1346a4cd6..91eaf0267 100644 --- a/third_party/musl/ftw.h +++ b/third_party/musl/ftw.h @@ -4,21 +4,79 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define FTW_F 1 /* file */ -#define FTW_D 2 /* directory */ -#define FTW_DNR 3 /* directory that cannot be read */ -#define FTW_NS 4 /* not a symbolic link and stat failed */ -#define FTW_SL 5 /* symbolic link */ -#define FTW_DP 6 /* directory and FTW_DEPTH was specified */ -#define FTW_SLN 7 /* symbolic link pointing to nonexistent file */ +/** + * Type for file. + */ +#define FTW_F 1 -#define FTW_PHYS 1 +/** + * Type for directory. + */ +#define FTW_D 2 + +/** + * Type for directory that cannot be read. + */ +#define FTW_DNR 3 + +/** + * Type for stat() failed and not a symbolic link. + */ +#define FTW_NS 4 + +/** + * Type for symbolic link when `FTW_PHYS` is in flags. + */ +#define FTW_SL 5 + +/** + * Directory and `FTW_DEPTH` in flags. + */ +#define FTW_DP 6 + +/** + * Type for broken symbolic link when `FTW_PHYS` is not in flags. + */ +#define FTW_SLN 7 + +/** + * Flag to prevent following symbolic links (recommended). + * @see nftw() flags + */ +#define FTW_PHYS 1 + +/** + * Flag to prevent crossing mount points. + * @see nftw() flags + */ #define FTW_MOUNT 2 + +/** + * Unsupported. + * @see nftw() flags + */ #define FTW_CHDIR 4 + +/** + * Flag for post-order traversal. + * + * 1. Will use `FTW_DP` instead of `FTW_D` as type. + * 2. Directory callback happens *after* rather than before. + * + * @see nftw() flags + */ #define FTW_DEPTH 8 struct FTW { + + /** + * Byte offset of basename component in `fpath` passed to callback. + */ int base; + + /** + * Depth relative to `dirpath` whose level is zero. + */ int level; }; diff --git a/third_party/musl/nftw.c b/third_party/musl/nftw.c index 48145e705..992478279 100644 --- a/third_party/musl/nftw.c +++ b/third_party/musl/nftw.c @@ -35,17 +35,13 @@ #include "libc/thread/thread.h" #include "third_party/musl/ftw.h" +#define PATH_MAXIMUS 4096 + asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ Copyright 2005-2014 Rich Felker, et. al.\""); asm(".include \"libc/disclaimer.inc\""); - -/* no reason to impose windows limit - small enough to fit in stack frame - should be changed to use realloc */ -#define PATH_MAX2 2048 - -/* clang-format off */ +// clang-format off struct history { @@ -56,7 +52,11 @@ struct history int base; }; -static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags, struct history *h) +static int do_nftw(char *path, + int fn(const char *, const struct stat *, int, struct FTW *), + int fd_limit, + int flags, + struct history *h) { size_t l = strlen(path), j = l && path[l-1]=='/' ? l-1 : l; struct stat st; @@ -128,7 +128,7 @@ static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, && (!de->d_name[1] || (de->d_name[1]=='.' && !de->d_name[2]))) continue; - if (strlen(de->d_name) >= PATH_MAX2-l) { + if (strlen(de->d_name) >= PATH_MAXIMUS-l) { errno = ENAMETOOLONG; closedir(d); return -1; @@ -157,25 +157,33 @@ static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, /** * Walks file tree. * + * @return 0 on success, -1 on error, or non-zero `fn` result * @see examples/walk.c for example */ -int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags) +int nftw(const char *dirpath, + int fn(const char *fpath, + const struct stat *st, + int typeflag, + struct FTW *ftwbuf), + int fd_limit, + int flags) { int r, cs; size_t l; - char pathbuf[PATH_MAX2+1]; + char pathbuf[PATH_MAXIMUS+1]; if (fd_limit <= 0) return 0; - l = strlen(path); - if (l > PATH_MAX2) { + l = strlen(dirpath); + if (l > PATH_MAXIMUS) { errno = ENAMETOOLONG; return -1; } - memcpy(pathbuf, path, l+1); + memcpy(pathbuf, dirpath, l+1); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); r = do_nftw(pathbuf, fn, fd_limit, flags, NULL); pthread_setcancelstate(cs, 0); + return r; } diff --git a/tool/build/rm.c b/tool/build/rm.c index d374e277f..b938142ac 100644 --- a/tool/build/rm.c +++ b/tool/build/rm.c @@ -17,13 +17,17 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/s.h" #include "third_party/getopt/getopt.internal.h" +#include "third_party/musl/ftw.h" #define USAGE \ " FILE...\n\ @@ -34,11 +38,15 @@ SYNOPSIS\n\ \n\ FLAGS\n\ \n\ - -h help\n\ - -f force\n\ + -h help\n\ + -f force\n\ + -f or -R recursive\n\ + -d remove empty dirs\n\ \n" static bool force; +static bool recursive; +static bool doemptydirs; static const char *prog; static wontreturn void PrintUsage(int rc, int fd) { @@ -48,11 +56,18 @@ static wontreturn void PrintUsage(int rc, int fd) { static void GetOpts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "hf")) != -1) { + while ((opt = getopt(argc, argv, "rRdhf")) != -1) { switch (opt) { case 'f': force = true; break; + case 'd': + doemptydirs = true; + break; + case 'r': + case 'R': + recursive = true; + break; case 'h': PrintUsage(0, 1); default: @@ -61,12 +76,56 @@ static void GetOpts(int argc, char *argv[]) { } } -static void Remove(const char *path) { - if (!force && access(path, W_OK) == -1) { - perror(path); +static int OnFile(const char *fpath, const struct stat *st, int typeflag, + struct FTW *ftwbuf) { + int rc; + if (!force && typeflag == FTW_DNR) { + rc = -1; + } else if (!force && !S_ISLNK(st->st_mode) && access(fpath, W_OK)) { + rc = -1; + } else if (typeflag == FTW_DNR) { + if (!(rc = chmod(fpath, 0700))) { + rc = nftw(fpath, OnFile, 128 - ftwbuf->level, FTW_PHYS | FTW_DEPTH); + } + } else if (typeflag == FTW_DP) { + rc = rmdir(fpath); + } else { + rc = unlink(fpath); + } + if (rc == -1) { + if (force && errno == ENOENT) return 0; + perror(fpath); exit(1); } - if (unlink(path) == -1) { + return 0; +} + +static void Remove(const char *path) { + int rc; + struct stat st; + if (path[0] == '/' && !path[1]) { + tinyprint(2, prog, ": add a dot if you want\n", NULL); + exit(1); + } + if (recursive) { + rc = nftw(path, OnFile, 128, FTW_PHYS | FTW_DEPTH); + } else { + if (lstat(path, &st)) { + if (force && errno == ENOENT) return; + perror(path); + exit(1); + } + if (!force && !S_ISLNK(st.st_mode) && access(path, W_OK)) { + perror(path); + exit(1); + } + if (S_ISDIR(st.st_mode) && doemptydirs) { + rc = rmdir(path); + } else { + rc = unlink(path); + } + } + if (rc == -1) { if (force && errno == ENOENT) return; perror(path); exit(1); @@ -75,17 +134,13 @@ static void Remove(const char *path) { int main(int argc, char *argv[]) { int i; - prog = argv[0]; if (!prog) prog = "rm"; - - if (argc < 2) { + GetOpts(argc, argv); + if (optind == argc) { tinyprint(2, prog, ": missing operand\n", NULL); exit(1); } - - GetOpts(argc, argv); - for (i = optind; i < argc; ++i) { Remove(argv[i]); } diff --git a/tool/scripts/cosmoc++ b/tool/scripts/cosmoc++ index 711349a0f..b3d8e49c2 100755 --- a/tool/scripts/cosmoc++ +++ b/tool/scripts/cosmoc++ @@ -71,7 +71,7 @@ ORIGINAL="$0 $*" PLATFORM="-D__COSMOPOLITAN__" PREDEF="-include libc/integral/normalize.inc" CCFLAGS="-fdata-sections -ffunction-sections -fno-pie -mno-tls-direct-seg-refs -mno-red-zone -fportcosmo" -CXXFLAGS="-fno-exceptions -fuse-cxa-atexit -fno-threadsafe-statics" +CXXFLAGS="-fno-rtti -fno-exceptions -fuse-cxa-atexit -fno-threadsafe-statics" CPPFLAGS="-nostdinc -iquote $COSMO -isystem $COSMOS/include -isystem $COSMO/libc/isystem" LDFLAGS="-static -no-pie -nostdlib -fuse-ld=bfd -Wl,-melf_x86_64" APEFLAGS="-L$COSMOS/lib -Wl,--gc-sections -Wl,-T,$COSMO/o/$MODE/ape/public/ape.lds $COSMO/o/$MODE/ape/ape-no-modify-self.o $COSMO/o/$MODE/libc/crt/crt.o"