Welcome to little lamb

Code » limb » commit 9318e9b

Add rm_rfat(), rmstarat() and their *{,_in}_tmp..

author Olivier Brunel
2023-02-28 09:09:30 UTC
committer Olivier Brunel
2023-02-28 09:25:58 UTC
parent 1271e84b32e9e8d9d3fd8a22ca52bc029878afa7

Add rm_rfat(), rmstarat() and their *{,_in}_tmp..

..siblings.

Add rm_rfat(), rm_rf_tmpat(), rm_rf_in_tmpat() to remove an entire
directory entry. These are similar to the rm_rf(3) and friends family of
functions, only with a fd for interpreting relative path relative to the
opened directory instead of cwd.

Also add rmstarat(), rmstar_tmpat(), rmstar_in_tmpat() to remove the
full content of a directory. These are similar to the rmstar(3) and
friends family of functions, again only with a fd for interpreting
relative path.

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);
+}