summaryrefslogtreecommitdiff
path: root/gsystem-file-utils.c
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2013-09-06 18:14:02 -0400
committerColin Walters <walters@verbum.org>2013-09-06 18:22:19 -0400
commitd63409a3d44b61e40f30cde79ebae879925c716d (patch)
tree2070fc83db6b7b823691820c732f580c987901f2 /gsystem-file-utils.c
parent2d24676846e3058d7a03bc127591f9fdcc346fab (diff)
downloadlibgsystem-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.c111
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;
}