Welcome to little lamb

Code » limb » commit 20559b7

Add fd_mkdirpat() & related functions

author Olivier Brunel
2023-05-11 17:22:38 UTC
committer Olivier Brunel
2023-07-05 07:37:02 UTC
parent d6e0a08918a2e757f34f1127dd562cd473146f7d

Add fd_mkdirpat() & related functions

To easily create a full path of directories (or, a directory and its
parents) and (optionally) get a file descriptor on said directory.

src/doc/djbunix.h.0.md +14 -0
src/doc/djbunix.h/fd_mkdirpat.3.md +55 -0
src/liblimb/djbunix.h/fd_mkdirp.c +10 -0
src/liblimb/djbunix.h/fd_mkdirpat.c +42 -0
src/liblimb/djbunix.h/mkdirp.c +10 -0
src/liblimb/djbunix.h/mkdirpat.c +12 -0
src/liblimb/include/limb/djbunix.h +5 -0

diff --git a/src/doc/djbunix.h.0.md b/src/doc/djbunix.h.0.md
index a33da9b..d6eab10 100644
--- a/src/doc/djbunix.h.0.md
+++ b/src/doc/djbunix.h.0.md
@@ -22,6 +22,20 @@ operations, file descriptors, environment, child processes, and so on.
 
 The following functions/macros are defined :
 
+: [fd_mkdirpat](3)
+:: Create a path of directories and returns the file descriptor.
+
+: [fd_mkdirp](3)
+:: Same as [fd_mkdirpat](3) but relative path are based on current working
+:: directory.
+
+: [mkdirpat](3)
+:: Create a path of directories.
+
+: [mkdirp](3)
+:: Same as [mkdirpat](3) but relative path are based on current working
+:: directory.
+
 : [open_parsed_name](3)
 :: Return the specified file descriptor, or that of the given file name.
 
diff --git a/src/doc/djbunix.h/fd_mkdirpat.3.md b/src/doc/djbunix.h/fd_mkdirpat.3.md
new file mode 100644
index 0000000..b476523
--- /dev/null
+++ b/src/doc/djbunix.h/fd_mkdirpat.3.md
@@ -0,0 +1,55 @@
+% limb manual
+% fd_mkdirpat(3)
+
+# NAME
+
+fd\_mkdirpat, fd\_mkdirp, mkdirpat, mkdirp - create a path of directories
+
+# SYNOPSIS
+
+    #include <limb/djbunix.h>
+
+```pre hl
+int fd_mkdirpat(int <em>fd</em>, const char *<em>name</em>, mode_t <em>mode</em>)
+int fd_mkdirp(const char *<em>name</em>, mode_t <em>mode</em>)
+int mkdirpat(int <em>fd</em>, const char *<em>name</em>, mode_t <em>mode</em>)
+int mkdirp(const char *<em>name</em>, mode_t <em>mode</em>)
+```
+
+# DESCRIPTION
+
+The `fd_mkdirpat`() function creates the path of directories `name`, i.e. it
+attempts not only to create the last element of `name` but every single one.
+This can be thinking thought of as equivalent to `mkdir --parent`.
+The file permission bits of the new directory will be initialized from `mode`
+for newly created directories (already existing ones aren't changed).
+
+If `name` specifies a relative path, it is relative to the directory associated
+with the file descriptor `fd` which may be *AT_FDCWD* to use the current working
+directory.
+
+A file descriptior to the last directory of the path in `name` is returned.
+
+The `fd_mkdirp`() function is similar to `fd_mkdirpat`() except when `name`
+specifies a relative path, in which case it is relative to the current working
+directory.
+
+The `mkdirpat`() function is similar to `fd_mkdirpat`() except that, on success,
+it returns zero.
+
+The `mkdirp`() function is similar to `fd_mkdirp`() except that, on success, it
+returns zero.
+
+# RETURN VALUE
+
+The `fd_mkdirpat`() and `fd_mkdirp`() functions return a file descriptor to the
+full directory specified in `name` on success. Otherwise, they return -1 and set
+`errno` to indicate the error.
+
+The `mkdirpat`() and `mkdirp`() functions return 0 on success. Otherwise, they
+return -1 and set `errno` to indicate the error.
+
+# ERRORS
+
+These functions may fail and set errno for any of the errors specified for
+[mkdirat](3) and [openat](3), save for *EEXIST*.
diff --git a/src/liblimb/djbunix.h/fd_mkdirp.c b/src/liblimb/djbunix.h/fd_mkdirp.c
new file mode 100644
index 0000000..c9a8167
--- /dev/null
+++ b/src/liblimb/djbunix.h/fd_mkdirp.c
@@ -0,0 +1,10 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/djbunix.h>
+
+int
+fd_mkdirp(const char *name, mode_t mode)
+{
+    return fd_mkdirpat(AT_FDCWD, name, mode);
+}
diff --git a/src/liblimb/djbunix.h/fd_mkdirpat.c b/src/liblimb/djbunix.h/fd_mkdirpat.c
new file mode 100644
index 0000000..c14701a
--- /dev/null
+++ b/src/liblimb/djbunix.h/fd_mkdirpat.c
@@ -0,0 +1,42 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <limb/bytestr.h>
+#include <limb/djbunix.h>
+#include <limb/unix-transactional.h>
+
+int
+fd_mkdirpat(int bfd, const char *name, mode_t mode)
+{
+    size_t nlen = strlen(name);
+    int fd = bfd;
+
+    for (;;) {
+        char buf[512];
+        const char *n;
+        size_t o = byte_chr(name, nlen, '/');
+        if (o == nlen) {
+            n = name;
+        } else {
+            n = buf;
+            memcpy(buf, name, o);
+            buf[o] = 0;
+        }
+        if (mkdirat(fd, n, mode) < 0 && errno != EEXIST)
+            goto err;
+        int dfd = open2_at(fd, n, O_RDONLY | O_DIRECTORY);
+        if (dfd < 0) goto err;
+        if (fd != bfd) fd_close(fd);
+        fd = dfd;
+        if (o == nlen) return fd;
+        name += o + 1;
+        nlen -= o + 1;
+    }
+
+err:
+    if (fd != bfd) fd_close(fd);
+    return -1;
+}
diff --git a/src/liblimb/djbunix.h/mkdirp.c b/src/liblimb/djbunix.h/mkdirp.c
new file mode 100644
index 0000000..836510a
--- /dev/null
+++ b/src/liblimb/djbunix.h/mkdirp.c
@@ -0,0 +1,10 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/djbunix.h>
+
+int
+mkdirp(const char *name, mode_t mode)
+{
+    return mkdirpat(AT_FDCWD, name, mode);
+}
diff --git a/src/liblimb/djbunix.h/mkdirpat.c b/src/liblimb/djbunix.h/mkdirpat.c
new file mode 100644
index 0000000..87f919c
--- /dev/null
+++ b/src/liblimb/djbunix.h/mkdirpat.c
@@ -0,0 +1,12 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/djbunix.h>
+
+int mkdirpat(int bfd, const char *name, mode_t mode)
+{
+    int fd = fd_mkdirpat(bfd, name, mode);
+    if (fd < 0) return -1;
+    fd_close(fd);
+    return 0;
+}
diff --git a/src/liblimb/include/limb/djbunix.h b/src/liblimb/include/limb/djbunix.h
index 6a276cd..a97af3c 100644
--- a/src/liblimb/include/limb/djbunix.h
+++ b/src/liblimb/include/limb/djbunix.h
@@ -19,6 +19,11 @@ extern int open_parsed_name(const char *name, open_fn open);
 #define open_write_close(file,data,dlen)    open_write_closeat(AT_FDCWD, file, data, dlen)
 #define open_writev_close(file,v,n)         open_writev_closeat(AT_FDCWD, file, v, n)
 
+extern int fd_mkdirpat(int fd, const char *name, mode_t mode);
+extern int fd_mkdirp(const char *name, mode_t mode);
+extern int mkdirpat(int fd, const char *name, mode_t mode);
+extern int mkdirp(const char *name, mode_t mode);
+
 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);