aboutsummaryrefslogtreecommitdiff
path: root/lib/lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lib.c')
-rw-r--r--lib/lib.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 7a3ada4a..1efb642f 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -116,6 +116,55 @@ off_t lskip(int fd, off_t offset)
return offset;
}
+// flags: 1=make last dir (with mode lastmode, otherwise skips last component)
+// 2=make path (already exists is ok)
+// 4=verbose
+// returns 0 = path ok, 1 = error
+int mkpathat(int atfd, char *dir, mode_t lastmode, int flags)
+{
+ struct stat buf;
+ char *s;
+
+ // mkdir -p one/two/three is not an error if the path already exists,
+ // but is if "three" is a file. The others we dereference and catch
+ // not-a-directory along the way, but the last one we must explicitly
+ // test for. Might as well do it up front.
+
+ if (!fstatat(atfd, dir, &buf, 0) && !S_ISDIR(buf.st_mode)) {
+ errno = EEXIST;
+ return 1;
+ }
+
+ // Skip leading / of absolute paths
+ while (*dir == '/') dir++;
+
+ for (s=dir; ;s++) {
+ char save = 0;
+ mode_t mode = (0777&~toys.old_umask)|0300;
+
+ // Skip leading / of absolute paths.
+ if (*s == '/' && (flags&2)) {
+ save = *s;
+ *s = 0;
+ } else if (*s) continue;
+
+ // Use the mode from the -m option only for the last directory.
+ if (!save) {
+ if (flags&1) mode = lastmode;
+ else break;
+ }
+
+ if (mkdirat(atfd, dir, mode)) {
+ if (!(flags&2) || errno != EEXIST) return 1;
+ } else if (flags&4)
+ fprintf(stderr, "%s: created directory '%s'\n", toys.which->name, dir);
+
+ if (!(*s = save)) break;
+ }
+
+ return 0;
+}
+
// Split a path into linked list of components, tracking head and tail of list.
// Filters out // entries with no contents.
struct string_list **splitpath(char *path, struct string_list **list)