diff options
author | Colin Walters <walters@verbum.org> | 2013-09-06 18:14:02 -0400 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2013-09-06 18:22:19 -0400 |
commit | d63409a3d44b61e40f30cde79ebae879925c716d (patch) | |
tree | 2070fc83db6b7b823691820c732f580c987901f2 /gsystem-file-utils.c | |
parent | 2d24676846e3058d7a03bc127591f9fdcc346fab (diff) | |
download | libgsystem-d63409a3d44b61e40f30cde79ebae879925c716d.tar.gz |
fileutil: Add initial directory-relative API
Modern UNIX come with a variety of filesystem API suffixed with "at",
like openat(), linkat(), etc. The reason for their existence
is multiple.
First, if you're doing a lot of file operations in a directory, it's
simply more efficient to avoid having the kernel traverse potentially
long pathnames constantly.
Second, this avoids a problem where if a user does e.g.:
rm -rf somedir &
mv somedir othername
mkdir somedir
touch somedir/otherfile
We won't end up deleting otherfile, because all of our operations are
relative to the original "somedir".
The second rationale is unlikely to matter for OSTree since we'll
assume the user isn't going to move a repository around while we're
committing to it, but anyways, it's just better to use fd-relative
pathnames.
Diffstat (limited to 'gsystem-file-utils.c')
-rw-r--r-- | gsystem-file-utils.c | 111 |
1 files changed, 89 insertions, 22 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c index 6291027..0486bf8 100644 --- a/gsystem-file-utils.c +++ b/gsystem-file-utils.c @@ -476,10 +476,36 @@ gsystem_fileutil_gen_tmp_name (const char *prefix, } /** - * gs_file_open_in_tmpdir: - * @tmpdir: Directory to place temporary file + * gs_file_open_dir_fd: + * @path: Directory name + * @out_fd: (out): File descriptor for directory + * @cancellable: Cancellable + * @error: Error + * + * On success, sets @out_fd to a file descriptor for the directory + * that can be used with UNIX functions such as openat(). + */ +gboolean +gs_file_open_dir_fd (GFile *path, + int *out_fd, + GCancellable *cancellable, + GError **error) +{ + /* Linux specific probably */ + *out_fd = open (gs_file_get_path_cached (path), O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC); + if (*out_fd == -1) + { + _set_error_from_errno (error); + return FALSE; + } + return TRUE; +} + +/** + * gs_file_open_in_tmpdir_at: + * @tmpdir_fd: Directory to place temporary file * @mode: Default mode (will be affected by umask) - * @out_file: (out) (transfer full): Newly created file path + * @out_name: (out) (transfer full): Newly created file name * @out_stream: (out) (transfer full) (allow-none): Newly created output stream * @cancellable: * @error: @@ -487,32 +513,22 @@ gsystem_fileutil_gen_tmp_name (const char *prefix, * Like g_file_open_tmp(), except the file will be created in the * provided @tmpdir, and allows specification of the Unix @mode, which * means private files may be created. Return values will be stored - * in @out_file, and optionally @out_stream. + * in @out_name, and optionally @out_stream. */ gboolean -gs_file_open_in_tmpdir (GFile *tmpdir, - int mode, - GFile **out_file, - GOutputStream **out_stream, - GCancellable *cancellable, - GError **error) +gs_file_open_in_tmpdir_at (int tmpdir_fd, + int mode, + char **out_name, + GOutputStream **out_stream, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; const int max_attempts = 128; guint i; - DIR *d = NULL; - int dfd = -1; char *tmp_name = NULL; int fd; - d = opendir (gs_file_get_path_cached (tmpdir)); - if (!d) - { - _set_error_from_errno (error); - goto out; - } - dfd = dirfd (d); - /* 128 attempts seems reasonable... */ for (i = 0; i < max_attempts; i++) { @@ -520,7 +536,7 @@ gs_file_open_in_tmpdir (GFile *tmpdir, tmp_name = gsystem_fileutil_gen_tmp_name (NULL, NULL); do - fd = openat (dfd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode); + fd = openat (tmpdir_fd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode); while (fd == -1 && errno == EINTR); if (fd < 0 && errno != EEXIST) { @@ -537,11 +553,62 @@ gs_file_open_in_tmpdir (GFile *tmpdir, } ret = TRUE; - *out_file = g_file_get_child (tmpdir, tmp_name); + gs_transfer_out_value (out_name, &tmp_name); if (out_stream) *out_stream = g_unix_output_stream_new (fd, TRUE); out: + g_free (tmp_name); + return ret; +} + +/** + * gs_file_open_in_tmpdir: + * @tmpdir: Directory to place temporary file + * @mode: Default mode (will be affected by umask) + * @out_file: (out) (transfer full): Newly created file path + * @out_stream: (out) (transfer full) (allow-none): Newly created output stream + * @cancellable: + * @error: + * + * Like g_file_open_tmp(), except the file will be created in the + * provided @tmpdir, and allows specification of the Unix @mode, which + * means private files may be created. Return values will be stored + * in @out_file, and optionally @out_stream. + */ +gboolean +gs_file_open_in_tmpdir (GFile *tmpdir, + int mode, + GFile **out_file, + GOutputStream **out_stream, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + DIR *d = NULL; + int dfd = -1; + char *tmp_name = NULL; + GOutputStream *ret_stream = NULL; + + d = opendir (gs_file_get_path_cached (tmpdir)); + if (!d) + { + _set_error_from_errno (error); + goto out; + } + dfd = dirfd (d); + + if (!gs_file_open_in_tmpdir_at (dfd, mode, &tmp_name, + out_stream ? &ret_stream : NULL, + cancellable, error)) + goto out; + + ret = TRUE; + *out_file = g_file_get_child (tmpdir, tmp_name); + gs_transfer_out_value (out_stream, &ret_stream); + out: if (d) (void) closedir (d); + g_clear_object (&ret_stream); + g_free (tmp_name); return ret; } |