author | Olivier Brunel
<jjk@jjacky.com> 2023-02-28 09:09:30 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2023-02-28 09:25:58 UTC |
parent | 1271e84b32e9e8d9d3fd8a22ca52bc029878afa7 |
doc/rm_rfat.3.md | +79 | -0 |
include/limb/djbunix.h | +7 | -0 |
meta/libs/limb | +6 | -0 |
src/rm_rf_in_tmpat.c | +28 | -0 |
src/rm_rf_tmpat.c | +15 | -0 |
src/rm_rfat.c | +8 | -0 |
src/rmstar_in_tmpat.c | +43 | -0 |
src/rmstar_tmpat.c | +15 | -0 |
src/rmstarat.c | +8 | -0 |
diff --git a/doc/rm_rfat.3.md b/doc/rm_rfat.3.md new file mode 100644 index 0000000..d42affb --- /dev/null +++ b/doc/rm_rfat.3.md @@ -0,0 +1,79 @@ +% limb manual +% rm_rfat(3) + +# NAME + +rm\_rfat, rm\_rf\_tmpat, rm\_rf\_in\_tmpat - remove an entire directory entry + +rmstarat, rmstar\_tmpat, rmstar\_in\_tmpat - remove the full content of a directory + +# SYNOPSIS + + #include <limb/djbunix.h> + +```pre hl +int rm_rfat(int <em>fd</em>, const char *<em>name</em>) +int rm_rf_tmpat(int <em>fd</em>, const char *<em>name</em>, stralloc *<em>sa</em>) +int rm_rf_in_tmpat(int <em>fd</em>, stralloc *<em>sa</em>, size_t <em>offset</em>) + +int rmstarat(int <em>fd</em>, const char *<em>name</em>) +int rmstar_tmpat(int <em>fd</em>, const char *<em>name</em>, stralloc *<em>sa</em>) +int rmstar_in_tmpat(int <em>fd</em>, stralloc *<em>sa</em>, size_t <em>offset</em>) +``` + +# DESCRIPTION + +The `rm_rfat`() function removes the entire directory entry named `name` from +the filesystem, whether a file, a directory (empty or not), etc. + +It is the equivalent to [rm_rf](3) function except when `name` specifies a +relative path, the directory to be opened is then determined relative to the +directory associated with the file descriptor `fd` instead of the current +working directory. + +The `rm_rf_tmpat`() function is similar except that it will use the given +stralloc `sa` as heap-allocated temporary space. + +It is the equivalent of the [rm_rf_tmp](3) function except when `name` specifies +a relative path, as with [rm_rfat](3). + +The `rm_rf_in_tmpat`() function is similar except that it expects `sa` to +contain a NUL-terminated string of the name at offset `offset`. + +It is the equivalent of the [rm_rf_in_tmp](3) function except when `name` +specifies a relative path, as with [rm_rfat](3). + + +The `rmstarat`() function removes the full content of directory `name` +recursively (leaving only the (empty) directory itself). + +It is the equivalent of the [rmstar](3) function except when `name` specifies a +relative path, as with [rm_rfat](3). + +The `rmstar_tmpat`() function is similar except that it will use the given +stralloc `sa` as heap-allocated temporary space. + +It is the equivalent of the [rmstar_tmp](3) function except when `name` +specifies a relative path, as with [rm_rfat](3). + +The `rmstar_in_tmpat`() function is similar except that it expects `sa` to +contain a NUL-terminated string of the name at offset `offset`. + + +# RETURN VALUE + +Upon successful completion, these functions return 0. Otherwise, they return -1 +and set `errno` to indicate the error. + +Note that these functions are *not* atomic and, in case of failure, might leave +the relevant directory partially removed. + +# ERRORS + +These functions may fail if : + +: *ENOMEM* +:: Out of memory + +They may also fail and set `errno` for any of the errors specified for the +functions [unlinkat](3) and [salsat](3). diff --git a/include/limb/djbunix.h b/include/limb/djbunix.h index 54423f2..5358aaf 100644 --- a/include/limb/djbunix.h +++ b/include/limb/djbunix.h @@ -3,6 +3,13 @@ #include <skalibs/stralloc.h> +extern int rm_rfat(int fd, const char *name); +extern int rm_rf_tmpat(int fd, const char *name, stralloc *sa); +extern int rm_rf_in_tmpat(int fd, stralloc *sa, size_t offset); +extern int rmstarat(int fd, const char *name); +extern int rmstar_tmpat(int fd, const char *name, stralloc *sa); +extern int rmstar_in_tmpat(int fd, stralloc *sa, size_t offset); + extern int salsat(int fd, const char *name, stralloc *sa, size_t *maxlen); extern int sareadlinkat(stralloc *sa, int fd, const char * restrict file); diff --git a/meta/libs/limb b/meta/libs/limb index afdaa12..76c5560 100644 --- a/meta/libs/limb +++ b/meta/libs/limb @@ -8,6 +8,12 @@ obj/open_exclat.o # direntry.h obj/opendirat.o # djbunix.h +obj/rm_rfat.o +obj/rm_rf_in_tmpat.o +obj/rm_rf_tmpat.o +obj/rmstarat.o +obj/rmstar_in_tmpat.o +obj/rmstar_tmpat.o obj/salsat.o obj/sareadlinkat.o # buffer.h diff --git a/src/rm_rf_in_tmpat.c b/src/rm_rf_in_tmpat.c new file mode 100644 index 0000000..6d290a7 --- /dev/null +++ b/src/rm_rf_in_tmpat.c @@ -0,0 +1,28 @@ +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include "limb/djbunix.h" + +int +rm_rf_in_tmpat(int fd, stralloc *sa, size_t offset) +{ + /* try basic unlinking */ + if (!unlinkat(fd, sa->s + offset, 0) || errno == ENOENT) + return 0; + + /* two possible errno-s (Linux & POSIX) for not unlinking a directory */ + if (errno != EISDIR && errno != EPERM) + return -1; + + /* might be that it's a dir, so let's empty it */ + if (rmstar_in_tmpat(fd, sa, offset) < 0) { + /* ENOTDIR means it's not a directory (couldn't be listed) so restore + * the previous errno */ + if (errno == ENOTDIR) + errno = EPERM; + return -1; + } + + /* remove the now empty directory */ + return unlinkat(fd, sa->s + offset, AT_REMOVEDIR); +} diff --git a/src/rm_rf_tmpat.c b/src/rm_rf_tmpat.c new file mode 100644 index 0000000..7fa682e --- /dev/null +++ b/src/rm_rf_tmpat.c @@ -0,0 +1,15 @@ +#include "limb/djbunix.h" + +int +rm_rf_tmpat(int fd, const char *name, stralloc *sa) +{ + size_t salen = sa->len; + int ret = 0; + + if (!stralloc_cats(sa, name) || !stralloc_0(sa) + || rm_rf_in_tmpat(fd, sa, salen) < 0) + ret = -1; + + sa->len = salen; + return ret; +} diff --git a/src/rm_rfat.c b/src/rm_rfat.c new file mode 100644 index 0000000..b91b800 --- /dev/null +++ b/src/rm_rfat.c @@ -0,0 +1,8 @@ +#include <skalibs/skamisc.h> +#include "limb/djbunix.h" + +int +rm_rfat(int fd, const char *name) +{ + return rm_rf_tmpat(fd, name, &satmp); +} diff --git a/src/rmstar_in_tmpat.c b/src/rmstar_in_tmpat.c new file mode 100644 index 0000000..d0b6302 --- /dev/null +++ b/src/rmstar_in_tmpat.c @@ -0,0 +1,43 @@ +#include "limb/djbunix.h" + +int +rmstar_in_tmpat(int fd, stralloc *sa, size_t offset) +{ + size_t salen = sa->len; + size_t maxlen; + int ret = 0; + + /* get all names from within */ + if (salstat(sa, fd, sa->s + offset, &maxlen) < 0) + return -1; + + size_t l = strlen(sa->s + offset); + size_t pathoff = sa->len; + + /* make sure we can store dir/longestfile */ + if (!stralloc_readyplus(sa, l + 1 + maxlen + 1)) { + ret = -1; + goto err; + } + + /* add the common (relative to fd) path */ + stralloc_catb(sa, sa->s + offset, l); + stralloc_catb(sa, "/", 1); + size_t fileoff = sa->len; + + /* and remove them all */ + for (size_t i = salen; i < pathoff; ) { + l = strlen(sa->s + i) + 1; + sa->len = fileoff; + stralloc_catb(sa, sa->s + i, l); + if (rm_rf_in_tmpat(fd, sa, pathoff) < 0) { + ret = -1; + break; + } + i += l; + } + +err: + sa->len = salen; + return ret; +} diff --git a/src/rmstar_tmpat.c b/src/rmstar_tmpat.c new file mode 100644 index 0000000..cf7e423 --- /dev/null +++ b/src/rmstar_tmpat.c @@ -0,0 +1,15 @@ +#include "limb/djbunix.h" + +int +rmstar_tmpat(int fd, const char *name, stralloc *sa) +{ + size_t salen = sa->len; + int ret = 0; + + if (!stralloc_cats(sa, name) || !stralloc_0(sa) + || rmstar_in_tmpat(fd, sa, salen) < 0) + ret = -1; + + sa->len = salen; + return ret; +} diff --git a/src/rmstarat.c b/src/rmstarat.c new file mode 100644 index 0000000..22ec1c8 --- /dev/null +++ b/src/rmstarat.c @@ -0,0 +1,8 @@ +#include <skalibs/skamisc.h> +#include "limb/djbunix.h" + +int +rmstarat(int fd, const char *name) +{ + return rmstar_tmpat(fd, name, &satmp); +}