libostree: Move file creation APIs out of core, into checkout.c

Since this was the only user, let's not have generic code to go from
OSTree representation -> filesystem here.  It should live in checkout.
This commit is contained in:
Colin Walters 2013-09-04 08:17:42 -04:00
parent 79c922a00b
commit 36815f52b5
4 changed files with 216 additions and 235 deletions

View File

@ -40,8 +40,6 @@ ostree_checksum_file
ostree_checksum_file_async ostree_checksum_file_async
ostree_checksum_file_async_finish ostree_checksum_file_async_finish
ostree_create_directory_metadata ostree_create_directory_metadata
ostree_create_file_from_input
ostree_create_temp_file_from_input
ostree_create_temp_dir ostree_create_temp_dir
ostree_validate_structureof_objtype ostree_validate_structureof_objtype
ostree_validate_structureof_csum_v ostree_validate_structureof_csum_v

View File

@ -1511,214 +1511,6 @@ zlib_file_header_parse (GVariant *metadata,
return ret; return ret;
} }
/**
* ostree_create_file_from_input:
* @dest_file: Destination; must not exist
* @finfo: File information
* @xattrs: (allow-none): Optional extended attributes
* @input: (allow-none): Optional file content, must be %NULL for symbolic links
* @cancellable: Cancellable
* @error: Error
*
* Create a directory, regular file, or symbolic link, based on
* @finfo. Append extended attributes from @xattrs if provided. For
* %G_FILE_TYPE_REGULAR, set content based on @input.
*/
gboolean
ostree_create_file_from_input (GFile *dest_file,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
const char *dest_path;
guint32 uid, gid, mode;
gs_unref_object GOutputStream *out = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
if (finfo != NULL)
{
mode = g_file_info_get_attribute_uint32 (finfo, "unix::mode");
}
else
{
mode = S_IFREG | 0664;
}
dest_path = gs_file_get_path_cached (dest_file);
if (S_ISDIR (mode))
{
if (mkdir (gs_file_get_path_cached (dest_file), mode) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else if (S_ISREG (mode))
{
if (finfo != NULL)
{
uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid");
gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid");
if (!gs_file_create_with_uidgid (dest_file, mode, uid, gid, &out,
cancellable, error))
goto out;
}
else
{
if (!gs_file_create (dest_file, mode, &out,
cancellable, error))
goto out;
}
if (input)
{
if (g_output_stream_splice ((GOutputStream*)out, input, 0,
cancellable, error) < 0)
goto out;
}
if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
goto out;
/* Work around libguestfs/FUSE bug */
if (mode & (S_ISUID|S_ISGID))
{
if (chmod (dest_path, mode) == -1)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
}
else if (S_ISLNK (mode))
{
const char *target = g_file_info_get_attribute_byte_string (finfo, "standard::symlink-target");
if (symlink (target, dest_path) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid mode %u", mode);
goto out;
}
/* We only need to chown for directories and symlinks; we already
* did a chown for files above via fchown().
*/
if (finfo != NULL && !S_ISREG (mode))
{
uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid");
gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid");
if (lchown (dest_path, uid, gid) < 0)
{
ot_util_set_error_from_errno (error, errno);
g_prefix_error (error, "lchown(%u, %u) failed: ", uid, gid);
goto out;
}
}
if (xattrs != NULL)
{
if (!ostree_set_xattrs (dest_file, xattrs, cancellable, error))
goto out;
}
ret = TRUE;
out:
if (!ret && !S_ISDIR(mode))
{
(void) unlink (dest_path);
}
return ret;
}
/**
* ostree_create_temp_file_from_input:
* @dir: Target directory
* @prefix: Optional prefix
* @suffix: Optional suffix
* @finfo: File information
* @xattrs: (allow-none): Optional extended attributes
* @input: (allow-none): Optional file content, must be %NULL for symbolic links
* @out_file: (out): Path for newly created directory, file, or symbolic link
* @cancellable: Cancellable
* @error: Error
*
* Like ostree_create_file_from_input(), but securely allocates a
* randomly-named target in @dir. This is a unified version of
* mkstemp()/mkdtemp() that also supports symbolic links.
*/
gboolean
ostree_create_temp_file_from_input (GFile *dir,
const char *prefix,
const char *suffix,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GFile **out_file,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GError *temp_error = NULL;
int i = 0;
gs_unref_object GFile *possible_file = NULL;
/* 128 attempts seems reasonable... */
for (i = 0; i < 128; i++)
{
gs_free char *possible_name = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
possible_name = gsystem_fileutil_gen_tmp_name (prefix, suffix);
g_clear_object (&possible_file);
possible_file = g_file_get_child (dir, possible_name);
if (!ostree_create_file_from_input (possible_file, finfo, xattrs, input,
cancellable, &temp_error))
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
{
g_clear_error (&temp_error);
continue;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
else
{
break;
}
}
if (i >= 128)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Exhausted 128 attempts to create a temporary file");
goto out;
}
ret = TRUE;
ot_transfer_out_value(out_file, &possible_file);
out:
return ret;
}
/** /**
* ostree_create_temp_dir: * ostree_create_temp_dir:
* @dir: Use this as temporary base * @dir: Use this as temporary base

View File

@ -228,23 +228,6 @@ gboolean ostree_checksum_file_async_finish (GFile *f,
GVariant *ostree_create_directory_metadata (GFileInfo *dir_info, GVariant *ostree_create_directory_metadata (GFileInfo *dir_info,
GVariant *xattrs); GVariant *xattrs);
gboolean ostree_create_file_from_input (GFile *dest_file,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GCancellable *cancellable,
GError **error);
gboolean ostree_create_temp_file_from_input (GFile *dir,
const char *prefix,
const char *suffix,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GFile **out_file,
GCancellable *cancellable,
GError **error);
gboolean ostree_create_temp_dir (GFile *dir, gboolean ostree_create_temp_dir (GFile *dir,
const char *prefix, const char *prefix,
const char *suffix, const char *suffix,

View File

@ -28,6 +28,214 @@
#include "ostree-repo-file.h" #include "ostree-repo-file.h"
#include "ostree-repo-private.h" #include "ostree-repo-private.h"
/*
* create_file_from_input:
* @dest_file: Destination; must not exist
* @finfo: File information
* @xattrs: (allow-none): Optional extended attributes
* @input: (allow-none): Optional file content, must be %NULL for symbolic links
* @cancellable: Cancellable
* @error: Error
*
* Create a directory, regular file, or symbolic link, based on
* @finfo. Append extended attributes from @xattrs if provided. For
* %G_FILE_TYPE_REGULAR, set content based on @input.
*/
static gboolean
create_file_from_input (GFile *dest_file,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
const char *dest_path;
guint32 uid, gid, mode;
gs_unref_object GOutputStream *out = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
if (finfo != NULL)
{
mode = g_file_info_get_attribute_uint32 (finfo, "unix::mode");
}
else
{
mode = S_IFREG | 0664;
}
dest_path = gs_file_get_path_cached (dest_file);
if (S_ISDIR (mode))
{
if (mkdir (gs_file_get_path_cached (dest_file), mode) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else if (S_ISREG (mode))
{
if (finfo != NULL)
{
uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid");
gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid");
if (!gs_file_create_with_uidgid (dest_file, mode, uid, gid, &out,
cancellable, error))
goto out;
}
else
{
if (!gs_file_create (dest_file, mode, &out,
cancellable, error))
goto out;
}
if (input)
{
if (g_output_stream_splice ((GOutputStream*)out, input, 0,
cancellable, error) < 0)
goto out;
}
if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
goto out;
/* Work around libguestfs/FUSE bug */
if (mode & (S_ISUID|S_ISGID))
{
if (chmod (dest_path, mode) == -1)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
}
else if (S_ISLNK (mode))
{
const char *target = g_file_info_get_attribute_byte_string (finfo, "standard::symlink-target");
if (symlink (target, dest_path) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid mode %u", mode);
goto out;
}
/* We only need to chown for directories and symlinks; we already
* did a chown for files above via fchown().
*/
if (finfo != NULL && !S_ISREG (mode))
{
uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid");
gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid");
if (lchown (dest_path, uid, gid) < 0)
{
ot_util_set_error_from_errno (error, errno);
g_prefix_error (error, "lchown(%u, %u) failed: ", uid, gid);
goto out;
}
}
if (xattrs != NULL)
{
if (!ostree_set_xattrs (dest_file, xattrs, cancellable, error))
goto out;
}
ret = TRUE;
out:
if (!ret && !S_ISDIR(mode))
{
(void) unlink (dest_path);
}
return ret;
}
/*
* create_temp_file_from_input:
* @dir: Target directory
* @prefix: Optional prefix
* @suffix: Optional suffix
* @finfo: File information
* @xattrs: (allow-none): Optional extended attributes
* @input: (allow-none): Optional file content, must be %NULL for symbolic links
* @out_file: (out): Path for newly created directory, file, or symbolic link
* @cancellable: Cancellable
* @error: Error
*
* Like create_file_from_input(), but securely allocates a
* randomly-named target in @dir. This is a unified version of
* mkstemp()/mkdtemp() that also supports symbolic links.
*/
static gboolean
create_temp_file_from_input (GFile *dir,
const char *prefix,
const char *suffix,
GFileInfo *finfo,
GVariant *xattrs,
GInputStream *input,
GFile **out_file,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GError *temp_error = NULL;
int i = 0;
gs_unref_object GFile *possible_file = NULL;
/* 128 attempts seems reasonable... */
for (i = 0; i < 128; i++)
{
gs_free char *possible_name = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
possible_name = gsystem_fileutil_gen_tmp_name (prefix, suffix);
g_clear_object (&possible_file);
possible_file = g_file_get_child (dir, possible_name);
if (!create_file_from_input (possible_file, finfo, xattrs, input,
cancellable, &temp_error))
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
{
g_clear_error (&temp_error);
continue;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
else
{
break;
}
}
if (i >= 128)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Exhausted 128 attempts to create a temporary file");
goto out;
}
ret = TRUE;
ot_transfer_out_value(out_file, &possible_file);
out:
return ret;
}
static gboolean static gboolean
checkout_file_from_input (GFile *file, checkout_file_from_input (GFile *file,
OstreeRepoCheckoutMode mode, OstreeRepoCheckoutMode mode,
@ -60,7 +268,7 @@ checkout_file_from_input (GFile *file,
{ {
if (g_file_info_get_file_type (temp_info) == G_FILE_TYPE_DIRECTORY) if (g_file_info_get_file_type (temp_info) == G_FILE_TYPE_DIRECTORY)
{ {
if (!ostree_create_file_from_input (file, temp_info, if (!create_file_from_input (file, temp_info,
xattrs, input, xattrs, input,
cancellable, &temp_error)) cancellable, &temp_error))
{ {
@ -78,7 +286,7 @@ checkout_file_from_input (GFile *file,
else else
{ {
dir = g_file_get_parent (file); dir = g_file_get_parent (file);
if (!ostree_create_temp_file_from_input (dir, NULL, "checkout", if (!create_temp_file_from_input (dir, NULL, "checkout",
temp_info, xattrs, input, &temp_file, temp_info, xattrs, input, &temp_file,
cancellable, error)) cancellable, error))
goto out; goto out;
@ -98,7 +306,7 @@ checkout_file_from_input (GFile *file,
} }
else else
{ {
if (!ostree_create_file_from_input (file, temp_info, if (!create_file_from_input (file, temp_info,
xattrs, input, cancellable, error)) xattrs, input, cancellable, error))
goto out; goto out;