diff --git a/contrib/bindings/python/pathrs/_pathrs.py b/contrib/bindings/python/pathrs/_pathrs.py index 0e0acf74..18ea81db 100644 --- a/contrib/bindings/python/pathrs/_pathrs.py +++ b/contrib/bindings/python/pathrs/_pathrs.py @@ -344,6 +344,13 @@ def mkdir(self, path, mode): if err < 0: raise Error._fetch(err) or INTERNAL_ERROR + def mkdir_all(self, path, mode): + path = _cstr(path) + fd = libpathrs_so.pathrs_mkdir_all(self.fileno(), path, mode) + if fd < 0: + raise Error._fetch(fd) or INTERNAL_ERROR + return Handle(fd) + def mknod(self, path, mode, dev=0): path = _cstr(path) err = libpathrs_so.pathrs_mknod(self.fileno(), path, mode, dev) diff --git a/go-pathrs/libpathrs_linux.go b/go-pathrs/libpathrs_linux.go index d79e5a93..cbe4a70b 100644 --- a/go-pathrs/libpathrs_linux.go +++ b/go-pathrs/libpathrs_linux.go @@ -130,6 +130,14 @@ func pathrsMkdir(rootFd uintptr, path string, mode uint32) error { return fetchError(err) } +func pathrsMkdirAll(rootFd uintptr, path string, mode uint32) (uintptr, error) { + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + + fd := C.pathrs_mkdir_all(C.int(rootFd), cPath, C.uint(mode)) + return uintptr(fd), fetchError(fd) +} + func pathrsMknod(rootFd uintptr, path string, mode uint32, dev uint64) error { cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) diff --git a/go-pathrs/root_linux.go b/go-pathrs/root_linux.go index b2e11696..d7d04163 100644 --- a/go-pathrs/root_linux.go +++ b/go-pathrs/root_linux.go @@ -147,6 +147,30 @@ func (r *Root) Mkdir(path string, mode os.FileMode) error { return err } +// MkdirAll creates a directory (and any parent path components if they don't +// exist) within a Root's directory tree. The provided mode is used for any +// directories created by this function (the process's umask applies). +// +// This is effectively equivalent to os.MkdirAll. +func (r *Root) MkdirAll(path string, mode os.FileMode) (*Handle, error) { + unixMode, err := toUnixMode(mode) + if err != nil { + return nil, err + } + + return withFileFd(r.inner, func(rootFd uintptr) (*Handle, error) { + handleFd, err := pathrsMkdirAll(rootFd, path, unixMode) + if err != nil { + return nil, err + } + handleFile, err := mkFile(uintptr(handleFd)) + if err != nil { + return nil, err + } + return &Handle{inner: handleFile}, err + }) +} + // Mknod creates a new device inode of the given type within a Root's directory // tree. The provided mode is used for the new directory (the process's umask // applies). diff --git a/include/pathrs.h b/include/pathrs.h index c0e2f8bf..27112d92 100644 --- a/include/pathrs.h +++ b/include/pathrs.h @@ -274,6 +274,22 @@ int pathrs_creat(int root_fd, const char *path, int flags, unsigned int mode); */ int pathrs_mkdir(int root_fd, const char *path, unsigned int mode); +/** + * Create a new directory (and any of its path components if they don't exist) + * within the rootfs referenced by root_fd. + * + * # Return Value + * + * On success, this function returns an O_DIRECTORY file descriptor to the + * newly created directory. + * + * If an error occurs, this function will return a negative error code. To + * retrieve information about the error (such as a string describing the error, + * the system errno(7) value associated with the error, etc), use + * pathrs_errorinfo(). + */ +int pathrs_mkdir_all(int root_fd, const char *path, unsigned int mode); + /** * Create a inode within the rootfs referenced by root_fd. The type of inode to * be created is configured using the S_IFMT bits in mode (a-la mknod(2)). diff --git a/src/capi/core.rs b/src/capi/core.rs index af15872f..e833e49f 100644 --- a/src/capi/core.rs +++ b/src/capi/core.rs @@ -288,6 +288,27 @@ pub extern "C" fn pathrs_mkdir(root_fd: RawFd, path: *const c_char, mode: c_uint pathrs_mknod(root_fd, path, libc::S_IFDIR | mode, 0) } +/// Create a new directory (and any of its path components if they don't exist) +/// within the rootfs referenced by root_fd. +/// +/// # Return Value +/// +/// On success, this function returns an O_DIRECTORY file descriptor to the +/// newly created directory. +/// +/// If an error occurs, this function will return a negative error code. To +/// retrieve information about the error (such as a string describing the error, +/// the system errno(7) value associated with the error, etc), use +/// pathrs_errorinfo(). +#[no_mangle] +pub extern "C" fn pathrs_mkdir_all(root_fd: RawFd, path: *const c_char, mode: c_uint) -> RawFd { + ret::with_fd(root_fd, |root: &mut Root| { + let mode = mode & !libc::S_IFMT; + let perm = Permissions::from_mode(mode); + root.mkdir_all(utils::parse_path(path)?, &perm) + }) +} + /// Create a inode within the rootfs referenced by root_fd. The type of inode to /// be created is configured using the S_IFMT bits in mode (a-la mknod(2)). ///