mirror of
https://github.com/flatpak/flatpak.git
synced 2026-06-26 17:26:50 -04:00
chase: Add glnx_chase_and_mkdirat
We found that there is a common use case where we need to get a subdirectory (potentially multiple levels) which might not exist yet. Adding another flag for this to GlnxChaseFlags is what systemd has done, but creating a directory takes a mode, so the flag creates directories with a fixed mode. This approach instead takes the mode as argument.
This commit is contained in:
87
glnx-chase.c
87
glnx-chase.c
@@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glnx-backports.h>
|
||||
#include <glnx-dirfd.h>
|
||||
#include <glnx-errors.h>
|
||||
#include <glnx-fdio.h>
|
||||
#include <glnx-local-alloc.h>
|
||||
@@ -823,3 +824,89 @@ glnx_chase_and_statxat (int dirfd,
|
||||
|
||||
return g_steal_fd (&fd);
|
||||
}
|
||||
|
||||
static int
|
||||
chase_and_mkdir (int next_fd,
|
||||
int current_fd,
|
||||
const char *segment,
|
||||
G_GNUC_UNUSED int open_tree_flags,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
mode_t mode = GPOINTER_TO_INT (user_data);
|
||||
glnx_autofd int new_fd = -1;
|
||||
|
||||
/* if chase managed to get the next segment, we already got our answer */
|
||||
if (next_fd >= 0)
|
||||
return next_fd;
|
||||
|
||||
/* if the problem isn't that the file doesn't exist, we propagate the error */
|
||||
g_assert (*error != NULL);
|
||||
if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
return -1;
|
||||
g_clear_error (error);
|
||||
|
||||
/* create the directory with the specified mode */
|
||||
if (!glnx_ensure_dir (current_fd, segment, mode, error))
|
||||
return -1;
|
||||
|
||||
/* Get a fd to the created dir.
|
||||
* This is racy, meaning another process can modify the filesystem and we
|
||||
* might open a directory that we did not create and might have a different
|
||||
* mode. There isn't a kernel API which returns a fd or an inode which makes
|
||||
* this race unavoidable. This is fine though because the semantics of
|
||||
* `glnx_chase_and_mkdirat` accept arbitrary directories (even with different
|
||||
* modes) when chasing the path.
|
||||
*/
|
||||
new_fd = openat (current_fd, segment,
|
||||
O_CLOEXEC | O_PATH | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (new_fd < 0)
|
||||
return glnx_fd_throw_errno_prefix (error, "opening created dir failed");
|
||||
|
||||
return g_steal_fd (&new_fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* glnx_chase_and_mkdirat:
|
||||
* @dirfd: a directory file descriptor
|
||||
* @path: a path
|
||||
* @flags: restricted combination of GlnxChaseFlags flags
|
||||
* @mode: the mode for new directories
|
||||
* @error: a #GError
|
||||
*
|
||||
* Same as glnx_chase with `GLNX_CHASE_MUST_BE_DIRECTORY`, but when a path
|
||||
* segment does not exist, a directory is created for the segment with the mode
|
||||
* @mode.
|
||||
* This essentially implement a fd-relative equivalent of g_mkdir_with_parents()
|
||||
* or `mkdir -p`.
|
||||
* Note, that directories in the path which already exist can have arbitrary
|
||||
* modes.
|
||||
*
|
||||
* See glnx_chaseat for the meaning of @dirfd and @path.
|
||||
*
|
||||
* The @flags argument is the same as in glnx_chaseat, but setting
|
||||
* `GLNX_CHASE_MUST_BE_REGULAR`, `GLNX_CHASE_MUST_BE_SOCKET`, or
|
||||
* `GLNX_CHASE_MUST_BE_DIRECTORY` is an error.
|
||||
*
|
||||
* Returns: the chased file, or -1 with @error set on error
|
||||
*/
|
||||
int
|
||||
glnx_chase_and_mkdirat (int dirfd,
|
||||
const char *path,
|
||||
GlnxChaseFlags flags,
|
||||
mode_t mode,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail ((flags & ~(GLNX_CHASE_ALL_FLAGS)) == 0, -1);
|
||||
g_return_val_if_fail ((flags & (GLNX_CHASE_MUST_BE_REGULAR |
|
||||
GLNX_CHASE_MUST_BE_SOCKET |
|
||||
GLNX_CHASE_MUST_BE_DIRECTORY)) == 0, -1);
|
||||
g_return_val_if_fail ((mode & ~((mode_t) 07777)) == 0, -1);
|
||||
|
||||
/* This function always implies GLNX_CHASE_MUST_BE_DIRECTORY */
|
||||
flags |= GLNX_CHASE_MUST_BE_DIRECTORY;
|
||||
|
||||
return glnx_chaseat_full (dirfd, path, flags,
|
||||
chase_and_mkdir, GINT_TO_POINTER (mode),
|
||||
error);
|
||||
}
|
||||
|
||||
@@ -50,4 +50,10 @@ int glnx_chase_and_statxat (int dirfd,
|
||||
struct glnx_statx *statbuf,
|
||||
GError **error);
|
||||
|
||||
int glnx_chase_and_mkdirat (int dirfd,
|
||||
const char *path,
|
||||
GlnxChaseFlags flags,
|
||||
mode_t mode,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -50,6 +50,18 @@ path_get_ino (const char *path)
|
||||
return st.st_ino;
|
||||
}
|
||||
|
||||
static mode_t
|
||||
get_mode (int fd)
|
||||
{
|
||||
int r;
|
||||
struct stat st;
|
||||
|
||||
r = fstatat (fd, "", &st, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
||||
g_assert_cmpint (r, >=, 0);
|
||||
|
||||
return st.st_mode & 0777;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_abspath (int dfd,
|
||||
const char *path)
|
||||
@@ -586,6 +598,62 @@ test_chase_and_statxat_permissions (void)
|
||||
g_clear_fd (&chase_fd, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_chase_and_mkdir (void)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
glnx_autofd int dfd = -1;
|
||||
glnx_autofd int file_fd = -1;
|
||||
glnx_autofd int chase_fd = -1;
|
||||
ino_t expected_ino;
|
||||
|
||||
g_assert_true (glnx_shutil_mkdir_p_at_open (AT_FDCWD, "d1", 0755,
|
||||
&dfd,
|
||||
NULL, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
chase_fd = glnx_chase_and_mkdirat (AT_FDCWD, "d1/d2/d3",
|
||||
GLNX_CHASE_DEFAULT,
|
||||
0755,
|
||||
&error);
|
||||
g_assert_cmpint (chase_fd, >=, 0);
|
||||
g_assert_no_error (error);
|
||||
|
||||
file_fd = openat (AT_FDCWD, "d1/d2/d3", O_PATH | O_CLOEXEC | O_NOFOLLOW);
|
||||
g_assert_cmpint (file_fd, >=, 0);
|
||||
expected_ino = get_ino (file_fd);
|
||||
|
||||
g_assert_cmpint (get_ino (chase_fd), ==, expected_ino);
|
||||
|
||||
g_assert_cmpint (get_mode (dfd), ==, 0755);
|
||||
g_assert_cmpint (get_mode (chase_fd), ==, 0755);
|
||||
|
||||
g_clear_fd (&chase_fd, NULL);
|
||||
g_clear_fd (&file_fd, NULL);
|
||||
|
||||
chase_fd = glnx_chase_and_mkdirat (AT_FDCWD, "d1/d4/d5",
|
||||
GLNX_CHASE_DEFAULT,
|
||||
0700,
|
||||
&error);
|
||||
g_assert_cmpint (chase_fd, >=, 0);
|
||||
g_assert_no_error (error);
|
||||
|
||||
file_fd = openat (AT_FDCWD, "d1", O_PATH | O_CLOEXEC | O_NOFOLLOW);
|
||||
g_assert_cmpint (file_fd, >=, 0);
|
||||
g_assert_cmpint (get_mode (file_fd), ==, 0755);
|
||||
g_clear_fd (&file_fd, NULL);
|
||||
|
||||
file_fd = openat (AT_FDCWD, "d1/d4", O_PATH | O_CLOEXEC | O_NOFOLLOW);
|
||||
g_assert_cmpint (file_fd, >=, 0);
|
||||
g_assert_cmpint (get_mode (file_fd), ==, 0700);
|
||||
g_clear_fd (&file_fd, NULL);
|
||||
|
||||
file_fd = openat (AT_FDCWD, "d1/d4/d5", O_PATH | O_CLOEXEC | O_NOFOLLOW);
|
||||
g_assert_cmpint (file_fd, >=, 0);
|
||||
g_assert_cmpint (get_mode (file_fd), ==, 0700);
|
||||
g_clear_fd (&file_fd, NULL);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
_GLNX_TEST_SCOPED_TEMP_DIR;
|
||||
@@ -602,6 +670,7 @@ int main (int argc, char **argv)
|
||||
g_test_add_func ("/chase-and-statxat-basic", test_chase_and_statxat_basic);
|
||||
g_test_add_func ("/chase-and-statxat-symlink", test_chase_and_statxat_symlink);
|
||||
g_test_add_func ("/chase-and-statxat-permissions", test_chase_and_statxat_permissions);
|
||||
g_test_add_func ("/chase-and-mkdir", test_chase_and_mkdir);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user