Add support for pulling from remote archives

This necessitated a large set of changes.

We now support an "archive" mode for repositories.  In this mode,
files are stored "packed" rather than hard linked.  This allows one to
e.g. store an OSTree repository with root-owned files as non-root.  It
is also used as the basis for serving repositories via HTTP.

While doing this I realized that GVariant is endianness-dependent; I
decided to just store all data in big endian.
This commit is contained in:
Colin Walters 2011-10-31 20:24:38 -04:00
parent fd4d25b8c1
commit a160a2a5fa
20 changed files with 1762 additions and 205 deletions

View File

@ -40,8 +40,8 @@ libostree_la_SOURCES = src/libostree/ostree.h \
src/libostree/ostree-checkout.c \
src/libostree/ostree-checkout.h \
$(NULL)
libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
libostree_la_LIBADD = libotutil.la $(GIO_UNIX_LIBS)
libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
bin_PROGRAMS += ostree
@ -53,10 +53,18 @@ ostree_SOURCES = src/main.c \
src/ot-builtin-init.c \
src/ot-builtin-link-file.c \
src/ot-builtin-log.c \
src/ot-builtin-pull.c \
src/ot-builtin-run-triggers.c \
src/ot-builtin-remote.c \
src/ot-builtin-rev-parse.c \
src/ot-builtin-show.c \
$(NULL)
ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
ostree_LDADD = libotutil.la libostree.la $(GIO_UNIX_LIBS)
ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
bin_PROGRAMS += ostree-http-backend
ostree_http_backend_SOURCES = src/ostree-http-backend.c
ostree_http_backend_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
ostree_http_backend_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)

View File

@ -32,6 +32,7 @@ LT_INIT
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0 >= 2.28])
PKG_CHECK_MODULES(OT_COREBIN_DEP, [libsoup-gnome-2.4 >= 2.34.0 gio-unix-2.0 >= 2.28])
AM_PATH_PYTHON

View File

@ -27,13 +27,27 @@
#include <sys/types.h>
#include <attr/xattr.h>
static char *
stat_to_string (struct stat *stbuf)
gboolean
ostree_validate_checksum_string (const char *sha256,
GError **error)
{
return g_strdup_printf ("%u:%u:%u",
(guint32)(stbuf->st_mode & ~S_IFMT),
(guint32)stbuf->st_uid,
(guint32)stbuf->st_gid);
if (strlen (sha256) != 64)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid rev '%s'", sha256);
return FALSE;
}
return TRUE;
}
void
ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
{
guint32 perms = (mode & ~S_IFMT);
g_checksum_update (checksum, (guint8*) &uid, 4);
g_checksum_update (checksum, (guint8*) &gid, 4);
g_checksum_update (checksum, (guint8*) &perms, 4);
}
static char *
@ -144,6 +158,7 @@ ostree_get_xattrs_for_path (const char *path,
}
ret = g_variant_builder_end (&builder);
g_variant_ref_sink (ret);
out:
if (!ret)
g_variant_builder_clear (&builder);
@ -154,9 +169,10 @@ ostree_get_xattrs_for_path (const char *path,
gboolean
ostree_stat_and_checksum_file (int dir_fd, const char *path,
GChecksum **out_checksum,
struct stat *out_stbuf,
GError **error)
OstreeObjectType objtype,
GChecksum **out_checksum,
struct stat *out_stbuf,
GError **error)
{
GChecksum *content_sha256 = NULL;
GChecksum *content_and_meta_sha256 = NULL;
@ -202,10 +218,12 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
}
}
stat_string = stat_to_string (&stbuf);
xattrs = ostree_get_xattrs_for_path (path, error);
if (!xattrs)
goto out;
if (objtype == OSTREE_OBJECT_TYPE_FILE)
{
xattrs = ostree_get_xattrs_for_path (path, error);
if (!xattrs)
goto out;
}
content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
@ -224,6 +242,8 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
else if (S_ISLNK(stbuf.st_mode))
{
symlink_target = g_malloc (PATH_MAX);
g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
if (bytes_read < 0)
@ -235,6 +255,7 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
}
else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
{
g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
}
@ -249,8 +270,12 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
content_and_meta_sha256 = g_checksum_copy (content_sha256);
g_checksum_update (content_and_meta_sha256, (guint8*)stat_string, strlen (stat_string));
g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
if (objtype == OSTREE_OBJECT_TYPE_FILE)
{
ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
stbuf.st_gid, stbuf.st_mode);
g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
}
*out_stbuf = stbuf;
*out_checksum = content_and_meta_sha256;
@ -267,6 +292,264 @@ ostree_stat_and_checksum_file (int dir_fd, const char *path,
g_variant_unref (xattrs);
if (content_sha256)
g_checksum_free (content_sha256);
return ret;
}
gboolean
ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
{
gboolean ret = FALSE;
int i, n;
n = g_variant_n_children (xattrs);
for (i = 0; i < n; i++)
{
const guint8* name;
GVariant *value;
const guint8* value_data;
gsize value_len;
gboolean loop_err;
g_variant_get_child (xattrs, i, "(^&ay@ay)",
&name, &value);
value_data = g_variant_get_fixed_array (value, &value_len, 1);
loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
g_variant_unref (value);
if (loop_err)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
ret = TRUE;
out:
return ret;
}
gboolean
ostree_parse_metadata_file (const char *path,
OstreeSerializedVariantType *out_type,
GVariant **out_variant,
GError **error)
{
GMappedFile *mfile = NULL;
gboolean ret = FALSE;
GVariant *ret_variant = NULL;
GVariant *container = NULL;
guint32 ret_type;
mfile = g_mapped_file_new (path, FALSE, error);
if (mfile == NULL)
{
goto out;
}
else
{
container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
g_mapped_file_get_contents (mfile),
g_mapped_file_get_length (mfile),
FALSE,
(GDestroyNotify) g_mapped_file_unref,
mfile);
g_variant_get (container, "(uv)",
&ret_type, &ret_variant);
if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted metadata object '%s'; invalid type %d", path, ret_type);
goto out;
}
mfile = NULL;
}
ret = TRUE;
*out_type = ret_type;
*out_variant = ret_variant;
ret_variant = NULL;
out:
if (ret_variant)
g_variant_unref (ret_variant);
if (container != NULL)
g_variant_unref (container);
if (mfile != NULL)
g_mapped_file_unref (mfile);
return ret;
}
char *
ostree_get_relative_object_path (const char *checksum,
OstreeObjectType type,
gboolean archive)
{
GString *path;
const char *type_string;
g_assert (strlen (checksum) == 64);
path = g_string_new ("objects/");
g_string_append_len (path, checksum, 2);
g_string_append_c (path, '/');
g_string_append (path, checksum + 2);
switch (type)
{
case OSTREE_OBJECT_TYPE_FILE:
if (archive)
type_string = ".packfile";
else
type_string = ".file";
break;
case OSTREE_OBJECT_TYPE_META:
type_string = ".meta";
break;
default:
g_assert_not_reached ();
}
g_string_append (path, type_string);
return g_string_free (path, FALSE);
}
gboolean
ostree_pack_object (GOutputStream *output,
GFile *file,
OstreeObjectType objtype,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
char *path = NULL;
GFileInfo *finfo = NULL;
GFileInputStream *instream = NULL;
gboolean pack_builder_initialized = FALSE;
GVariantBuilder pack_builder;
GVariant *pack_variant = NULL;
GVariant *xattrs = NULL;
gsize bytes_written;
path = g_file_get_path (file);
finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
if (!finfo)
goto out;
if (objtype == OSTREE_OBJECT_TYPE_META)
{
guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
goto out;
instream = g_file_read (file, NULL, error);
if (!instream)
goto out;
if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
goto out;
}
else
{
guint32 uid, gid, mode;
guint32 device = 0;
guint32 metadata_size_be;
const char *target = NULL;
guint64 object_size;
uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
pack_builder_initialized = TRUE;
g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
xattrs = ostree_get_xattrs_for_path (path, error);
if (!xattrs)
goto out;
g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
if (S_ISREG (mode))
{
object_size = (guint64)g_file_info_get_size (finfo);
}
else if (S_ISLNK (mode))
{
target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
object_size = strlen (target);
}
else if (S_ISBLK (mode) || S_ISCHR (mode))
{
device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
object_size = 4;
}
else
g_assert_not_reached ();
g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
pack_variant = g_variant_builder_end (&pack_builder);
pack_builder_initialized = FALSE;
metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
if (!g_output_stream_write_all (output, &metadata_size_be, 4,
&bytes_written, cancellable, error))
goto out;
g_assert (bytes_written == 4);
if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
&bytes_written, cancellable, error))
goto out;
if (S_ISREG (mode))
{
instream = g_file_read (file, NULL, error);
if (!instream)
goto out;
bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
if (bytes_written < 0)
goto out;
if (bytes_written != object_size)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"File size changed unexpectedly");
goto out;
}
}
else if (S_ISLNK (mode))
{
if (!g_output_stream_write_all (output, target, object_size,
&bytes_written, cancellable, error))
goto out;
}
else if (S_ISBLK (mode) || S_ISCHR (mode))
{
guint32 device_be = GUINT32_TO_BE (device);
g_assert (object_size == 4);
if (!g_output_stream_write_all (output, &device_be, object_size,
&bytes_written, cancellable, error))
goto out;
g_assert (bytes_written == 4);
}
else
g_assert_not_reached ();
}
ret = TRUE;
out:
g_free (path);
g_clear_object (&finfo);
g_clear_object (&instream);
if (xattrs)
g_variant_unref (xattrs);
if (pack_builder_initialized)
g_variant_builder_clear (&pack_builder);
if (pack_variant)
g_variant_unref (pack_variant);
return ret;
}

View File

@ -39,6 +39,7 @@ typedef enum {
OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
OSTREE_SERIALIZED_XATTR_VARIANT = 4
} OstreeSerializedVariantType;
#define OSTREE_SERIALIZED_VARIANT_LAST 4
#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
@ -83,13 +84,54 @@ typedef enum {
*/
#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
GVariant *ostree_get_xattrs_for_path (const char *path,
GError **error);
gboolean ostree_validate_checksum_string (const char *sha256,
GError **error);
char *ostree_get_relative_object_path (const char *checksum,
OstreeObjectType type,
gboolean archive);
GVariant *ostree_get_xattrs_for_path (const char *path,
GError **error);
gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
gboolean ostree_parse_metadata_file (const char *path,
OstreeSerializedVariantType *out_type,
GVariant **out_variant,
GError **error);
gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
GChecksum **out_checksum,
struct stat *out_stbuf,
GError **error);
OstreeObjectType type,
GChecksum **out_checksum,
struct stat *out_stbuf,
GError **error);
/* Packed files:
*
* guint32 metadata_length [metadata gvariant] [content]
*
* metadata variant:
* u - Version
* u - uid
* u - gid
* u - mode
* a(ayay) - xattrs
* t - content length
*
* And then following the end of the variant is the content. If
* symlink, then this is the target; if device, then device ID as
* network byte order uint32.
*/
#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
gboolean ostree_pack_object (GOutputStream *output,
GFile *path,
OstreeObjectType objtype,
GCancellable *cancellable,
GError **error);
void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
#endif /* _OSTREE_REPO */

View File

@ -57,12 +57,14 @@ struct _OstreeRepoPrivate {
char *path;
GFile *repo_file;
GFile *local_heads_dir;
GFile *remote_heads_dir;
char *objects_path;
char *config_path;
gboolean inited;
GKeyFile *config;
gboolean archive;
};
static void
@ -74,6 +76,7 @@ ostree_repo_finalize (GObject *object)
g_free (priv->path);
g_clear_object (&priv->repo_file);
g_clear_object (&priv->local_heads_dir);
g_clear_object (&priv->remote_heads_dir);
g_free (priv->objects_path);
g_free (priv->config_path);
if (priv->config)
@ -140,6 +143,7 @@ ostree_repo_constructor (GType gtype,
priv->repo_file = ot_util_new_file_for_path (priv->path);
priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
priv->objects_path = g_build_filename (priv->path, "objects", NULL);
priv->config_path = g_build_filename (priv->path, "config", NULL);
@ -179,19 +183,6 @@ ostree_repo_new (const char *path)
return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
}
static gboolean
validate_checksum_string (const char *sha256,
GError **error)
{
if (strlen (sha256) != 64)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid rev '%s'", sha256);
return FALSE;
}
return TRUE;
}
static gboolean
parse_rev_file (OstreeRepo *self,
const char *path,
@ -252,7 +243,7 @@ parse_rev_file (OstreeRepo *self,
}
else
{
if (!validate_checksum_string (rev, error))
if (!ostree_validate_checksum_string (rev, error))
goto out;
}
@ -304,7 +295,7 @@ resolve_rev (OstreeRepo *self,
{
g_strchomp (ret_rev);
if (!validate_checksum_string (ret_rev, error))
if (!ostree_validate_checksum_string (ret_rev, error))
goto out;
}
}
@ -357,12 +348,88 @@ write_checksum_file (GFile *parentdir,
return ret;
}
/**
* ostree_repo_get_config:
* @self:
*
* Returns: (transfer none): The repository configuration; do not modify
*/
GKeyFile *
ostree_repo_get_config (OstreeRepo *self)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (priv->inited, NULL);
return priv->config;
}
/**
* ostree_repo_copy_config:
* @self:
*
* Returns: (transfer full): A newly-allocated copy of the repository config
*/
GKeyFile *
ostree_repo_copy_config (OstreeRepo *self)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
GKeyFile *copy;
char *data;
gsize len;
g_return_val_if_fail (priv->inited, NULL);
copy = g_key_file_new ();
data = g_key_file_to_data (priv->config, &len, NULL);
if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
g_assert_not_reached ();
g_free (data);
return copy;
}
/**
* ostree_repo_write_config:
* @self:
* @new_config: Overwrite the config file with this data. Do not change later!
* @error: a #GError
*
* Save @new_config in place of this repository's config file. Note
* that @new_config should not be modified after - this function
* simply adds a reference.
*/
gboolean
ostree_repo_write_config (OstreeRepo *self,
GKeyFile *new_config,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
char *data = NULL;
gsize len;
gboolean ret = FALSE;
g_return_val_if_fail (priv->inited, FALSE);
data = g_key_file_to_data (new_config, &len, error);
if (!g_file_set_contents (priv->config_path, data, len, error))
goto out;
g_key_file_unref (priv->config);
priv->config = g_key_file_ref (new_config);
ret = TRUE;
out:
g_free (data);
return ret;
}
gboolean
ostree_repo_check (OstreeRepo *self, GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
gboolean ret = FALSE;
char *version = NULL;;
GError *temp_error = NULL;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@ -383,9 +450,12 @@ ostree_repo_check (OstreeRepo *self, GError **error)
goto out;
}
version = g_key_file_get_value (priv->config, "core", "repo_version", error);
if (!version)
goto out;
version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
if (temp_error)
{
g_propagate_error (error, temp_error);
goto out;
}
if (strcmp (version, "0") != 0)
{
@ -394,6 +464,20 @@ ostree_repo_check (OstreeRepo *self, GError **error)
goto out;
}
priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
if (temp_error)
{
if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
priv->inited = TRUE;
ret = TRUE;
@ -402,6 +486,23 @@ ostree_repo_check (OstreeRepo *self, GError **error)
return ret;
}
const char *
ostree_repo_get_path (OstreeRepo *self)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
return priv->path;
}
gboolean
ostree_repo_is_archive (OstreeRepo *self)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (priv->inited, FALSE);
return priv->archive;
}
static gboolean
import_gvariant_object (OstreeRepo *self,
OstreeSerializedVariantType type,
@ -463,54 +564,13 @@ load_gvariant_object_unknown (OstreeRepo *self,
GVariant **out_variant,
GError **error)
{
GMappedFile *mfile = NULL;
gboolean ret = FALSE;
GVariant *ret_variant = NULL;
GVariant *container = NULL;
char *path = NULL;
guint32 ret_type;
path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
mfile = g_mapped_file_new (path, FALSE, error);
if (mfile == NULL)
goto out;
else
{
container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
g_mapped_file_get_contents (mfile),
g_mapped_file_get_length (mfile),
FALSE,
(GDestroyNotify) g_mapped_file_unref,
mfile);
if (!g_variant_is_of_type (container, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT)))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted metadata object '%s'", sha256);
goto out;
}
g_variant_get (container, "(uv)",
&ret_type, &ret_variant);
mfile = NULL;
}
ret = TRUE;
out:
if (!ret)
{
if (ret_variant)
g_variant_unref (ret_variant);
}
else
{
*out_type = ret_type;
*out_variant = ret_variant;
}
if (container != NULL)
g_variant_unref (container);
ret = ostree_parse_metadata_file (path, out_type, out_variant, error);
g_free (path);
if (mfile != NULL)
g_mapped_file_unref (mfile);
return ret;
}
@ -534,7 +594,6 @@ load_gvariant_object (OstreeRepo *self,
"Corrupted metadata object '%s'; found type %u, expected %u", sha256,
type, (guint32)expected_type);
goto out;
}
ret = TRUE;
@ -616,41 +675,26 @@ get_object_path (OstreeRepo *self,
OstreeObjectType type)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
char *checksum_prefix;
char *base_path;
char *ret;
const char *type_string;
char *relpath;
checksum_prefix = g_strndup (checksum, 2);
base_path = g_build_filename (priv->objects_path, checksum_prefix, checksum + 2, NULL);
switch (type)
{
case OSTREE_OBJECT_TYPE_FILE:
type_string = ".file";
break;
case OSTREE_OBJECT_TYPE_META:
type_string = ".meta";
break;
default:
g_assert_not_reached ();
}
ret = g_strconcat (base_path, type_string, NULL);
g_free (base_path);
g_free (checksum_prefix);
relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
ret = g_build_filename (priv->path, relpath, NULL);
g_free (relpath);
return ret;
}
static char *
prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
GChecksum *checksum,
const char *checksum,
OstreeObjectType type,
GError **error)
{
char *checksum_dir = NULL;
char *object_path = NULL;
object_path = get_object_path (self, g_checksum_get_string (checksum), type);
object_path = get_object_path (self, checksum, type);
checksum_dir = g_path_get_dirname (object_path);
if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
@ -662,21 +706,23 @@ prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
}
static gboolean
link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
gboolean ignore_exists, gboolean force,
GChecksum **out_checksum,
GError **error)
link_object_trusted (OstreeRepo *self,
const char *path,
const char *checksum,
OstreeObjectType objtype,
gboolean ignore_exists,
gboolean force,
gboolean *did_exist,
GError **error)
{
char *src_basename = NULL;
char *src_dirname = NULL;
char *dest_basename = NULL;
char *tmp_dest_basename = NULL;
char *dest_dirname = NULL;
GChecksum *id = NULL;
DIR *src_dir = NULL;
DIR *dest_dir = NULL;
gboolean ret = FALSE;
struct stat stbuf;
char *dest_path = NULL;
src_basename = g_path_get_basename (path);
@ -689,9 +735,7 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
goto out;
}
if (!ostree_stat_and_checksum_file (dirfd (src_dir), path, &id, &stbuf, error))
goto out;
dest_path = prepare_dir_for_checksum_get_object_path (self, id, type, error);
dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
if (!dest_path)
goto out;
@ -719,7 +763,11 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
ot_util_set_error_from_errno (error, errno);
goto out;
}
else
*did_exist = TRUE;
}
else
*did_exist = FALSE;
if (force)
{
@ -732,12 +780,8 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
(void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
}
*out_checksum = id;
id = NULL;
ret = TRUE;
out:
if (id != NULL)
g_checksum_free (id);
if (src_dir != NULL)
closedir (src_dir);
if (dest_dir != NULL)
@ -750,12 +794,108 @@ link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
return ret;
}
static gboolean
archive_file_trusted (OstreeRepo *self,
const char *path,
const char *checksum,
OstreeObjectType objtype,
gboolean ignore_exists,
gboolean force,
gboolean *did_exist,
GError **error)
{
GFile *infile = NULL;
GFile *outfile = NULL;
GFileOutputStream *out = NULL;
gboolean ret = FALSE;
char *dest_path = NULL;
char *dest_tmp_path = NULL;
infile = ot_util_new_file_for_path (path);
dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
if (!dest_path)
goto out;
dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
outfile = ot_util_new_file_for_path (dest_tmp_path);
out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
if (!out)
goto out;
if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
goto out;
if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
goto out;
if (rename (dest_tmp_path, dest_path) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
ret = TRUE;
out:
g_free (dest_path);
g_free (dest_tmp_path);
g_clear_object (&infile);
g_clear_object (&outfile);
g_clear_object (&out);
return ret;
}
gboolean
ostree_repo_store_object_trusted (OstreeRepo *self,
const char *path,
const char *checksum,
OstreeObjectType objtype,
gboolean ignore_exists,
gboolean force,
gboolean *did_exist,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
else
return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
}
static gboolean
link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
gboolean ignore_exists, gboolean force,
GChecksum **out_checksum,
GError **error)
{
gboolean ret = FALSE;
struct stat stbuf;
GChecksum *id = NULL;
gboolean did_exist;
if (!ostree_stat_and_checksum_file (-1, path, type, &id, &stbuf, error))
goto out;
if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
ignore_exists, force, &did_exist, error))
goto out;
*out_checksum = id;
id = NULL;
ret = TRUE;
out:
if (id != NULL)
g_checksum_free (id);
return ret;
}
gboolean
ostree_repo_link_file (OstreeRepo *self,
const char *path,
gboolean ignore_exists,
gboolean force,
GError **error)
const char *path,
gboolean ignore_exists,
gboolean force,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
GChecksum *checksum = NULL;
@ -770,6 +910,227 @@ ostree_repo_link_file (OstreeRepo *self,
return TRUE;
}
static gboolean
unpack_and_checksum_packfile (OstreeRepo *self,
const char *path,
gchar **out_filename,
GChecksum **out_checksum,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
gboolean ret = FALSE;
GFile *file = NULL;
char *temp_path = NULL;
GFile *temp_file = NULL;
GFileOutputStream *temp_out = NULL;
char *metadata_buf = NULL;
GVariant *metadata = NULL;
GVariant *xattrs = NULL;
GFileInputStream *in = NULL;
GChecksum *ret_checksum = NULL;
guint32 metadata_len;
guint32 version, uid, gid, mode;
guint64 content_len;
gsize bytes_read, bytes_written;
char buf[8192];
int temp_fd = -1;
file = ot_util_new_file_for_path (path);
in = g_file_read (file, NULL, error);
if (!in)
goto out;
if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
goto out;
if (bytes_read != 4)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted packfile; too short while reading metadata length");
goto out;
}
metadata_len = GUINT32_FROM_BE (metadata_len);
metadata_buf = g_malloc (metadata_len);
if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
goto out;
if (bytes_read != metadata_len)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted packfile; too short while reading metadata");
goto out;
}
metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
metadata_buf, metadata_len, FALSE, NULL, NULL);
g_variant_get (metadata, "(uuuu@a(ayay)t)",
&version, &uid, &gid, &mode,
&xattrs, &content_len);
uid = GUINT32_FROM_BE (uid);
gid = GUINT32_FROM_BE (gid);
mode = GUINT32_FROM_BE (mode);
content_len = GUINT64_FROM_BE (content_len);
temp_path = g_build_filename (priv->path, "tmp-packfile-XXXXXX");
temp_file = ot_util_new_file_for_path (temp_path);
ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
if (S_ISREG (mode))
{
temp_fd = g_mkstemp (temp_path);
if (temp_fd < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
close (temp_fd);
temp_fd = -1;
temp_out = g_file_replace (temp_file, NULL, FALSE, 0, NULL, error);
if (!temp_out)
goto out;
do
{
if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
goto out;
g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
if (!g_output_stream_write_all ((GOutputStream*)temp_out, buf, bytes_read, &bytes_written, NULL, error))
goto out;
}
while (bytes_read > 0);
if (!g_output_stream_close ((GOutputStream*)temp_out, NULL, error))
goto out;
}
else if (S_ISLNK (mode))
{
g_assert (sizeof (buf) > PATH_MAX);
if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
goto out;
buf[bytes_read] = '\0';
if (symlink (buf, temp_path) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else if (S_ISCHR (mode) || S_ISBLK (mode))
{
guint32 dev;
if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
goto out;
if (bytes_read != 4)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted packfile; too short while reading device id");
goto out;
}
dev = GUINT32_FROM_BE (dev);
if (mknod (temp_path, mode, dev) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted packfile; invalid mode %u", mode);
goto out;
}
if (!S_ISLNK (mode))
{
if (chmod (temp_path, mode) < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
}
if (!ostree_set_xattrs (temp_path, xattrs, error))
goto out;
ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
ret = TRUE;
*out_checksum = ret_checksum;
ret_checksum = NULL;
*out_filename = temp_path;
temp_path = NULL;
out:
if (ret_checksum)
g_checksum_free (ret_checksum);
g_free (metadata_buf);
if (temp_path)
(void) unlink (temp_path);
g_free (temp_path);
g_clear_object (&file);
g_clear_object (&in);
g_clear_object (&temp_file);
g_clear_object (&temp_out);
if (metadata)
g_variant_unref (metadata);
if (xattrs)
g_variant_unref (xattrs);
return ret;
}
gboolean
ostree_repo_store_packfile (OstreeRepo *self,
const char *expected_checksum,
const char *path,
OstreeObjectType objtype,
GError **error)
{
gboolean ret = FALSE;
char *tempfile = NULL;
GChecksum *checksum = NULL;
struct stat stbuf;
gboolean did_exist;
if (objtype == OSTREE_OBJECT_TYPE_META)
{
if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, error))
goto out;
}
else
{
if (!unpack_and_checksum_packfile (self, path, &tempfile, &checksum, error))
goto out;
}
if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted object %s (actual checksum is %s)",
expected_checksum, g_checksum_get_string (checksum));
goto out;
}
if (!ostree_repo_store_object_trusted (self, tempfile ? tempfile : path,
expected_checksum,
objtype,
TRUE, FALSE, &did_exist, error))
goto out;
ret = TRUE;
out:
if (tempfile)
(void) unlink (tempfile);
g_free (tempfile);
if (checksum)
g_checksum_free (checksum);
return ret;
}
typedef struct _ParsedTreeData ParsedTreeData;
typedef struct _ParsedDirectoryData ParsedDirectoryData;
@ -839,6 +1200,7 @@ parse_tree (OstreeRepo *self,
sha256, &tree_variant, error))
goto out;
/* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
g_variant_get (tree_variant, "(u@a{sv}@a(ss)@a(sss))",
&version, &meta_variant, &files_variant, &dirs_variant);
@ -922,6 +1284,7 @@ load_commit_and_trees (OstreeRepo *self,
commit_sha256, &ret_commit, error))
goto out;
/* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
@ -1366,6 +1729,18 @@ add_files_to_tree_and_import (OstreeRepo *self,
return ret;
}
gboolean
ostree_repo_write_ref (OstreeRepo *self,
gboolean is_local,
const char *name,
const char *rev,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir,
name, rev, error);
}
static gboolean
commit_parsed_tree (OstreeRepo *self,
const char *branch,
@ -1377,7 +1752,6 @@ commit_parsed_tree (OstreeRepo *self,
GChecksum **out_commit,
GError **error)
{
OstreeRepoPrivate *priv = GET_PRIVATE (self);
gboolean ret = FALSE;
GChecksum *root_checksum = NULL;
GChecksum *ret_commit = NULL;
@ -1403,7 +1777,7 @@ commit_parsed_tree (OstreeRepo *self,
commit, &ret_commit, error))
goto out;
if (!write_checksum_file (priv->local_heads_dir, branch, g_checksum_get_string (ret_commit), error))
if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
goto out;
ret = TRUE;
@ -1648,7 +2022,8 @@ iter_object_dir (OstreeRepo *self,
if (type != G_FILE_TYPE_DIRECTORY
&& (g_str_has_suffix (name, ".meta")
|| g_str_has_suffix (name, ".file")))
|| g_str_has_suffix (name, ".file")
|| g_str_has_suffix (name, ".packfile")))
{
char *dot;
char *path;
@ -1789,6 +2164,7 @@ checkout_one_directory (OstreeRepo *self,
dest_path = g_build_filename (destination, dirname, NULL);
/* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
g_variant_get (dir->meta_data, "(uuuu@a(ayay))",
&version, &uid, &gid, &mode,
&xattr_variant);

View File

@ -53,17 +53,50 @@ OstreeRepo* ostree_repo_new (const char *path);
gboolean ostree_repo_check (OstreeRepo *self, GError **error);
const char * ostree_repo_get_path (OstreeRepo *self);
gboolean ostree_repo_is_archive (OstreeRepo *self);
GKeyFile * ostree_repo_get_config (OstreeRepo *self);
GKeyFile * ostree_repo_copy_config (OstreeRepo *self);
gboolean ostree_repo_write_config (OstreeRepo *self,
GKeyFile *new_config,
GError **error);
gboolean ostree_repo_link_file (OstreeRepo *self,
const char *path,
gboolean ignore_exists,
gboolean force,
GError **error);
const char *path,
gboolean ignore_exists,
gboolean force,
GError **error);
gboolean ostree_repo_store_packfile (OstreeRepo *self,
const char *expected_checksum,
const char *path,
OstreeObjectType objtype,
GError **error);
gboolean ostree_repo_store_object_trusted (OstreeRepo *self,
const char *path,
const char *checksum,
OstreeObjectType objtype,
gboolean ignore_exists,
gboolean force,
gboolean *did_exist,
GError **error);
gboolean ostree_repo_resolve_rev (OstreeRepo *self,
const char *rev,
char **out_resolved,
GError **error);
gboolean ostree_repo_write_ref (OstreeRepo *self,
gboolean is_local,
const char *name,
const char *rev,
GError **error);
gboolean ostree_repo_load_variant (OstreeRepo *self,
const char *sha256,
OstreeSerializedVariantType *out_type,

View File

@ -33,6 +33,7 @@ static OstreeBuiltin builtins[] = {
{ "commit", ostree_builtin_commit, 0 },
{ "link-file", ostree_builtin_link_file, 0 },
{ "log", ostree_builtin_log, 0 },
{ "pull", ostree_builtin_pull, 0 },
{ "fsck", ostree_builtin_fsck, 0 },
{ "remote", ostree_builtin_remote, 0 },
{ "rev-parse", ostree_builtin_rev_parse, 0 },

45
src/ostree-http-backend.c Normal file
View File

@ -0,0 +1,45 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2011 Colin Walters <walters@verbum.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/
#include "config.h"
#include <libsoup/soup-gnome.h>
#include <ostree.h>
#include <string.h>
int
main (int argc,
char **argv)
{
const char *repo_root;
g_type_init ();
repo_root = g_getenv ("OSTREE_REPO_PREFIX");
if (!repo_root)
{
g_printerr ("OSTREE_REPO_PREFIX not set\n");
return 1;
}
return 0;
}

View File

@ -39,6 +39,82 @@ typedef struct {
guint n_objects;
} HtFsckData;
static gboolean
checksum_packed_file (HtFsckData *data,
const char *path,
GChecksum **out_checksum,
GError **error)
{
gboolean ret = FALSE;
GChecksum *ret_checksum = NULL;
GFile *file = NULL;
char *metadata_buf = NULL;
GVariant *metadata = NULL;
GVariant *xattrs = NULL;
GFileInputStream *in = NULL;
guint32 metadata_len;
guint32 version, uid, gid, mode;
guint64 content_len;
gsize bytes_read;
char buf[8192];
file = ot_util_new_file_for_path (path);
in = g_file_read (file, NULL, error);
if (!in)
goto out;
if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
goto out;
metadata_len = GUINT32_FROM_BE (metadata_len);
metadata_buf = g_malloc (metadata_len);
if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
goto out;
metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
metadata_buf, metadata_len, FALSE, NULL, NULL);
g_variant_get (metadata, "(uuuu@a(ayay)t)",
&version, &uid, &gid, &mode,
&xattrs, &content_len);
uid = GUINT32_FROM_BE (uid);
gid = GUINT32_FROM_BE (gid);
mode = GUINT32_FROM_BE (mode);
content_len = GUINT64_FROM_BE (content_len);
ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
do
{
if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
goto out;
g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
}
while (bytes_read > 0);
ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
ret = TRUE;
*out_checksum = ret_checksum;
ret_checksum = NULL;
out:
if (ret_checksum)
g_checksum_free (ret_checksum);
g_free (metadata_buf);
g_clear_object (&file);
g_clear_object (&in);
if (metadata)
g_variant_unref (metadata);
if (xattrs)
g_variant_unref (xattrs);
return ret;
}
static void
object_iter_callback (OstreeRepo *repo,
const char *path,
@ -53,25 +129,46 @@ object_iter_callback (OstreeRepo *repo,
char *checksum_prefix = NULL;
char *checksum_string = NULL;
char *filename_checksum = NULL;
gboolean packed = FALSE;
OstreeObjectType objtype;
char *dot;
dirname = g_path_get_dirname (path);
checksum_prefix = g_path_get_basename (dirname);
/* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
if (nlinks < 2 && !quiet)
g_printerr ("note: floating object: %s\n", path); */
if (!ostree_stat_and_checksum_file (-1, path, &checksum, &stbuf, &error))
goto out;
if (g_str_has_suffix (path, ".meta"))
objtype = OSTREE_OBJECT_TYPE_META;
else if (g_str_has_suffix (path, ".file"))
objtype = OSTREE_OBJECT_TYPE_FILE;
else if (g_str_has_suffix (path, ".packfile"))
{
objtype = OSTREE_OBJECT_TYPE_FILE;
packed = TRUE;
}
else
g_assert_not_reached ();
if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
{
if (!checksum_packed_file (data, path, &checksum, &error))
goto out;
}
else
{
if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
goto out;
}
filename_checksum = g_strdup (g_file_info_get_name (file_info));
dot = strrchr (filename_checksum, '.');
g_assert (dot != NULL);
*dot = '\0';
dirname = g_path_get_dirname (path);
checksum_prefix = g_path_get_basename (dirname);
checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
{
g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",

View File

@ -27,8 +27,11 @@
#include <glib/gi18n.h>
static char *repo_path;
static gboolean archive;
static GOptionEntry options[] = {
{ "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
{ "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
{ NULL }
};
@ -44,6 +47,7 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
GFile *repodir = NULL;
GFile *child = NULL;
GFile *grandchild = NULL;
GString *config_data = NULL;
context = g_option_context_new ("- Initialize a new empty repository");
g_option_context_add_main_entries (context, options, NULL);
@ -54,12 +58,15 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
if (repo_path == NULL)
repo_path = ".";
repodir = g_file_new_for_path (repo_path);
repodir = ot_util_new_file_for_path (repo_path);
child = g_file_get_child (repodir, "config");
config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
if (!g_file_replace_contents (child,
DEFAULT_CONFIG_CONTENTS,
strlen (DEFAULT_CONFIG_CONTENTS),
config_data->str,
config_data->len,
NULL, FALSE, 0, NULL,
NULL, error))
goto out;
@ -73,18 +80,20 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
child = g_file_get_child (repodir, "refs");
if (!g_file_make_directory (child, NULL, error))
goto out;
grandchild = g_file_get_child (child, "heads");
if (!g_file_make_directory (grandchild, NULL, error))
goto out;
g_clear_object (&child);
g_clear_object (&grandchild);
child = g_file_get_child (repodir, "tags");
if (!g_file_make_directory (child, NULL, error))
grandchild = g_file_get_child (child, "remotes");
if (!g_file_make_directory (grandchild, NULL, error))
goto out;
g_clear_object (&grandchild);
g_clear_object (&child);
child = g_file_get_child (repodir, "remotes");
child = g_file_get_child (repodir, "tags");
if (!g_file_make_directory (child, NULL, error))
goto out;
g_clear_object (&child);
@ -93,6 +102,8 @@ ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
out:
if (context)
g_option_context_free (context);
if (config_data)
g_string_free (config_data, TRUE);
g_clear_object (&repodir);
g_clear_object (&child);
g_clear_object (&grandchild);

365
src/ot-builtin-pull.c Normal file
View File

@ -0,0 +1,365 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2011 Colin Walters <walters@verbum.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/
#include "config.h"
#include "ot-builtins.h"
#include "ostree.h"
#include <glib/gi18n.h>
#include <libsoup/soup-gnome.h>
static char *repo_path;
static GOptionEntry options[] = {
{ "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
{ NULL }
};
static void
usage_error (GOptionContext *context, const char *message, GError **error)
{
gchar *help = g_option_context_get_help (context, TRUE, NULL);
g_printerr ("%s\n", help);
g_free (help);
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
message);
}
static gboolean
fetch_uri (OstreeRepo *repo,
SoupSession *soup,
SoupURI *uri,
char **temp_filename,
GError **error)
{
gboolean ret = FALSE;
SoupMessage *msg = NULL;
guint response;
char *template = NULL;
int fd;
SoupBuffer *buf = NULL;
GFile *tempf = NULL;
msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
response = soup_session_send_message (soup, msg);
if (response != 200)
{
char *uri_string = soup_uri_to_string (uri, FALSE);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to retrieve '%s': %d %s",
uri_string, response, msg->reason_phrase);
g_free (uri_string);
goto out;
}
template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
fd = g_mkstemp (template);
if (fd < 0)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
close (fd);
tempf = ot_util_new_file_for_path (template);
buf = soup_message_body_flatten (msg->response_body);
if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
goto out;
*temp_filename = template;
template = NULL;
ret = TRUE;
out:
g_free (template);
g_clear_object (&msg);
g_clear_object (&tempf);
return ret;
}
static gboolean
store_object (OstreeRepo *repo,
SoupSession *soup,
SoupURI *baseuri,
const char *object,
OstreeObjectType objtype,
gboolean *did_exist,
GError **error)
{
gboolean ret = FALSE;
char *filename = NULL;
char *objpath = NULL;
char *relpath = NULL;
SoupURI *obj_uri = NULL;
objpath = ostree_get_relative_object_path (object, objtype, TRUE);
obj_uri = soup_uri_copy (baseuri);
relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
soup_uri_set_path (obj_uri, relpath);
if (!fetch_uri (repo, soup, obj_uri, &filename, error))
goto out;
if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
goto out;
ret = TRUE;
out:
if (obj_uri)
soup_uri_free (obj_uri);
if (filename)
(void) unlink (filename);
g_free (filename);
g_free (objpath);
g_free (relpath);
return ret;
}
static gboolean
store_tree_recurse (OstreeRepo *repo,
SoupSession *soup,
SoupURI *base_uri,
const char *rev,
GError **error)
{
gboolean ret = FALSE;
GVariant *tree = NULL;
GVariant *files_variant = NULL;
GVariant *dirs_variant = NULL;
OstreeSerializedVariantType metatype;
gboolean did_exist;
int i, n;
if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
goto out;
if (!did_exist)
{
if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
goto out;
if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Tree metadata '%s' has wrong type %d, expected %d",
rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
goto out;
}
/* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
n = g_variant_n_children (files_variant);
for (i = 0; i < n; i++)
{
const char *filename;
const char *checksum;
g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
goto out;
}
for (i = 0; i < n; i++)
{
const char *dirname;
const char *tree_checksum;
const char *meta_checksum;
g_variant_get_child (dirs_variant, i, "(sss)",
&dirname, &tree_checksum, &meta_checksum);
if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
goto out;
if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
goto out;
}
}
ret = TRUE;
out:
if (tree)
g_variant_unref (tree);
if (files_variant)
g_variant_unref (files_variant);
if (dirs_variant)
g_variant_unref (dirs_variant);
return ret;
}
static gboolean
store_commit_recurse (OstreeRepo *repo,
SoupSession *soup,
SoupURI *base_uri,
const char *rev,
GError **error)
{
gboolean ret = FALSE;
GVariant *commit = NULL;
OstreeSerializedVariantType metatype;
const char *tree_contents_checksum;
const char *tree_meta_checksum;
gboolean did_exist;
if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
goto out;
if (!did_exist)
{
if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
goto out;
if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Commit '%s' has wrong type %d, expected %d",
rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
goto out;
}
/* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
goto out;
if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
goto out;
}
ret = TRUE;
out:
if (commit)
g_variant_unref (commit);
return ret;
}
gboolean
ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
{
GOptionContext *context;
gboolean ret = FALSE;
OstreeRepo *repo = NULL;
const char *remote;
const char *branch;
char *remote_branch_ref_path = NULL;
char *key = NULL;
char *baseurl = NULL;
char *refpath = NULL;
char *temppath = NULL;
GKeyFile *config = NULL;
SoupURI *base_uri = NULL;
SoupURI *target_uri = NULL;
SoupSession *soup = NULL;
char *rev = NULL;
context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
if (repo_path == NULL)
repo_path = ".";
repo = ostree_repo_new (repo_path);
if (!ostree_repo_check (repo, error))
goto out;
if (argc < 3)
{
usage_error (context, "REMOTE and BRANCH must be specified", error);
goto out;
}
remote = argv[1];
branch = argv[2];
config = ostree_repo_get_config (repo);
key = g_strdup_printf ("remote \"%s\"", remote);
baseurl = g_key_file_get_string (config, key, "url", error);
if (!baseurl)
goto out;
base_uri = soup_uri_new (baseurl);
if (!base_uri)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to parse url '%s'", baseurl);
goto out;
}
target_uri = soup_uri_copy (base_uri);
g_free (refpath);
refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
soup_uri_set_path (target_uri, refpath);
soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
NULL);
if (!fetch_uri (repo, soup, target_uri, &temppath, error))
goto out;
rev = ot_util_get_file_contents_utf8 (temppath, error);
if (!rev)
goto out;
g_strchomp (rev);
if (!ostree_validate_checksum_string (rev, error))
goto out;
if (!store_commit_recurse (repo, soup, base_uri, rev, error))
goto out;
if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
goto out;
ret = TRUE;
out:
if (context)
g_option_context_free (context);
if (temppath)
(void) unlink (temppath);
g_free (temppath);
g_free (key);
g_free (rev);
g_free (baseurl);
g_free (refpath);
g_free (remote_branch_ref_path);
g_clear_object (&soup);
if (base_uri)
soup_uri_free (base_uri);
if (target_uri)
soup_uri_free (target_uri);
g_clear_object (&repo);
g_clear_object (&soup);
return ret;
}

View File

@ -51,9 +51,6 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
OstreeRepo *repo = NULL;
OstreeCheckout *checkout = NULL;
const char *op;
gsize len;
char *config_path = NULL;
char *data = NULL;
GKeyFile *config = NULL;
context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
@ -77,10 +74,7 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
op = argv[1];
config = g_key_file_new ();
config_path = g_build_filename (repo_path, "config", NULL);
if (!g_key_file_load_from_file (config, config_path, 0, error))
goto out;
config = ostree_repo_copy_config (repo);
if (!strcmp (op, "add"))
{
@ -92,25 +86,23 @@ ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error
}
key = g_strdup_printf ("remote \"%s\"", argv[2]);
g_key_file_set_string (config, key, "url", argv[3]);
g_free (key);
}
else
{
usage_error (context, "Unknown operation", error);
goto out;
}
data = g_key_file_to_data (config, &len, error);
if (!g_file_set_contents (config_path, data, len, error))
goto out;
if (!ostree_repo_write_config (repo, config, error))
goto out;
ret = TRUE;
out:
if (context)
g_option_context_free (context);
if (config)
g_key_file_unref (config);
g_free (data);
g_free (config_path);
g_clear_object (&repo);
g_clear_object (&checkout);
return ret;

View File

@ -41,6 +41,7 @@ gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GErro
gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);

3
tests/.gitignore vendored
View File

@ -1 +1,4 @@
!Makefile
ostree-http-server
run-apache
tmpdir-lifecycle

View File

@ -20,7 +20,13 @@
TESTS = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
all:
all: tmpdir-lifecycle run-apache
tmpdir-lifecycle: tmpdir-lifecycle.c Makefile
gcc $(CFLAGS) `pkg-config --cflags --libs gio-unix-2.0` -o $@ $<
run-apache: run-apache.c Makefile
gcc $(CFLAGS) `pkg-config --cflags --libs gio-unix-2.0` -o $@ $<
check:
@for test in $(TESTS); do \

View File

@ -18,6 +18,9 @@
#
# Author: Colin Walters <walters@verbum.org>
cd `dirname $0`
SRCDIR=`pwd`
cd -
TMPDIR=${TMPDIR:-/tmp}
export TMPDIR
test_tmpdir=`mktemp -d "$TMPDIR/ostree-tests.XXXXXXXXXX"`
@ -91,4 +94,53 @@ setup_test_repository2 () {
ostree fsck -q $ot_repo
}
setup_fake_remote_repo1() {
oldpwd=`pwd`
mkdir ostree-srv
cd ostree-srv
mkdir gnomerepo
ostree init --archive --repo=gnomerepo
mkdir gnomerepo-files
cd gnomerepo-files
echo first > firstfile
mkdir baz
echo moo > baz/cow
echo alien > baz/saucer
find | grep -v '^\.$' | ostree commit --repo=${test_tmpdir}/ostree-srv/gnomerepo -b main -s "A remote commit" -m "Some Commit body" --from-stdin
mkdir baz/deeper
ostree commit --repo=${test_tmpdir}/ostree-srv/gnomerepo -b main -s "Add deeper" --add=baz/deeper
echo hi > baz/deeper/ohyeah
mkdir baz/another/
echo x > baz/another/y
find | grep -v '^\.$' | ostree commit --repo=${test_tmpdir}/ostree-srv/gnomerepo -b main -s "The rest" --from-stdin
cd ..
rm -rf gnomerepo-files
cd ${test_tmpdir}
mkdir ${test_tmpdir}/httpd
cd httpd
cp $(command -v ostree-http-backend) .
chmod a+x ostree-http-backend
cat >httpd.conf <<EOF
ServerRoot ${test_tmpdir}/httpd
PidFile pid
LogLevel crit
ErrorLog log
LockFile lock
ServerName localhost
LoadModule alias_module modules/mod_alias.so
LoadModule cgi_module modules/mod_cgi.so
LoadModule env_module modules/mod_env.so
StartServers 1
# SetEnv OSTREE_REPO_PREFIX ${test_tmpdir}/ostree-srv
Alias /ostree/ ${test_tmpdir}/ostree-srv/
# ScriptAlias /ostree/ ${test_tmpdir}/httpd/ostree-http-backend/
EOF
${SRCDIR}/tmpdir-lifecycle ${SRCDIR}/run-apache `pwd`/httpd.conf ${test_tmpdir}/httpd-address
cd ${oldpwd}
}
trap 'die' EXIT

167
tests/run-apache.c Normal file
View File

@ -0,0 +1,167 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2011 Colin Walters <walters@verbum.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/
#include <gio/gio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
/* Taken from gnome-user-share src/httpd.c under the GPLv2 */
static int
get_port (void)
{
int sock;
int saved_errno;
struct sockaddr_in addr;
int reuse;
socklen_t len;
sock = socket (PF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
return -1;
}
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
reuse = 1;
setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
{
saved_errno = errno;
close (sock);
errno = saved_errno;
return -1;
}
len = sizeof (addr);
if (getsockname (sock, (struct sockaddr *)&addr, &len) == -1)
{
saved_errno = errno;
close (sock);
errno = saved_errno;
return -1;
}
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
/* XXX This exposes a potential race condition, but without this,
* httpd will not start on the above listed platforms due to the fact
* that SO_REUSEADDR is also needed when Apache binds to the listening
* socket. At this time, Apache does not support that socket option.
*/
close (sock);
#endif
return ntohs (addr.sin_port);
}
static const char *known_httpd_modules_locations [] = {
"/usr/libexec/apache2",
"/usr/lib/apache2/modules",
"/usr/lib64/httpd/modules",
"/usr/lib/httpd/modules",
NULL
};
static gchar*
get_httpd_modules_path ()
{
int i;
for (i = 0; known_httpd_modules_locations[i]; i++)
{
if (g_file_test (known_httpd_modules_locations[i], G_FILE_TEST_IS_EXECUTABLE)
&& g_file_test (known_httpd_modules_locations[i], G_FILE_TEST_IS_DIR))
{
return g_strdup (known_httpd_modules_locations[i]);
}
}
return NULL;
}
int
main (int argc,
char **argv)
{
int port;
char *listen;
char *address_string;
GError *error = NULL;
GPtrArray *httpd_argv;
char *modules;
if (argc != 3)
{
fprintf (stderr, "usage: run-apache CONF PORTFILE");
return 1;
}
g_type_init ();
port = get_port ();
if (port == -1)
{
perror ("Failed to bind port");
return 1;
}
httpd_argv = g_ptr_array_new ();
g_ptr_array_add (httpd_argv, "httpd");
g_ptr_array_add (httpd_argv, "-f");
g_ptr_array_add (httpd_argv, argv[1]);
g_ptr_array_add (httpd_argv, "-C");
listen = g_strdup_printf ("Listen 127.0.0.1:%d", port);
g_ptr_array_add (httpd_argv, listen);
g_ptr_array_add (httpd_argv, NULL);
address_string = g_strdup_printf ("http://127.0.0.1:%d\n", port);
if (!g_file_set_contents (argv[2], address_string, -1, &error))
{
g_printerr ("%s\n", error->message);
return 1;
}
setenv ("LANG", "C", 1);
modules = get_httpd_modules_path ();
if (modules == NULL)
{
g_printerr ("Failed to find httpd modules\n");
return 1;
}
if (symlink (modules, "modules") < 0)
{
perror ("failed to make modules symlink");
return 1;
}
execvp ("httpd", (char**)httpd_argv->pdata);
perror ("Failed to run httpd");
return 1;
}

33
tests/t0012-pull.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Copyright (C) 2011 Colin Walters <walters@verbum.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Author: Colin Walters <walters@verbum.org>
set -e
. libtest.sh
echo '1..1'
setup_fake_remote_repo1
cd ${test_tmpdir}
mkdir repo
ostree init --repo=repo
ostree remote --repo=repo add origin $(cat httpd-address)/ostree/gnomerepo
ostree pull --repo=repo origin main
echo "ok pull"

42
tests/t0013-commit-archive.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
#
# Copyright (C) 2011 Colin Walters <walters@verbum.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Author: Colin Walters <walters@verbum.org>
set -e
. libtest.sh
echo '1..4'
mkdir files
cd files
echo first > firstfile
echo second > secondfile
ln -s foo bar
mkdir ../repo
repo="--repo=../repo"
ostree init --archive $repo
echo 'ok init'
ostree commit $repo -b test -s "Test Commit 1" -m "Commit body first" --add=firstfile
echo 'ok commit 1'
ostree commit $repo -b test -s "Test Commit 2" -m "Commit body first" --add=secondfile --add=bar
echo 'ok commit 2'
ostree fsck -q $repo
echo 'ok fsck'

View File

@ -1,4 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Kill a child process when the current directory is deleted
*
* Copyright (C) 2011 Colin Walters <walters@verbum.org>
*
@ -19,35 +21,16 @@
* Author: Colin Walters <walters@verbum.org>
*/
#include <libsoup/soup-gnome.h>
#include <sys/types.h>
#include <gio/gio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
static void
request_callback (SoupServer *server, SoupMessage *msg,
const char *path, GHashTable *query,
SoupClientContext *context, gpointer data)
{
if (msg->method == SOUP_METHOD_GET)
{
GFile *file;
char *content;
gsize len;
/* Strip leading / */
file = g_file_new_for_path (path + 1);
if (g_file_load_contents (file, NULL, &content, &len, NULL, NULL))
soup_message_set_response (msg, "application/octet-stream", SOUP_MEMORY_TAKE, content, len);
else
soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
}
else
{
soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
}
}
struct TmpdirLifecyleData {
GMainLoop *loop;
GPid pid;
gboolean exited;
};
static void
on_dir_changed (GFileMonitor *mon,
@ -56,49 +39,65 @@ on_dir_changed (GFileMonitor *mon,
GFileMonitorEvent event,
gpointer user_data)
{
GMainLoop *loop = user_data;
struct TmpdirLifecyleData *data = user_data;
if (event == G_FILE_MONITOR_EVENT_DELETED)
g_main_loop_quit (loop);
g_main_loop_quit (data->loop);
}
static void
on_child_exited (GPid pid,
int status,
gpointer user_data)
{
struct TmpdirLifecyleData *data = user_data;
data->exited = TRUE;
g_main_loop_quit (data->loop);
}
int
main (int argc,
char **argv)
{
SoupAddress *addr;
SoupServer *server;
GMainLoop *loop;
GFileMonitor *monitor;
GFile *curdir;
GError *error = NULL;
GPtrArray *new_argv;
int i;
GPid pid;
struct TmpdirLifecyleData data;
g_type_init ();
loop = g_main_loop_new (NULL, TRUE);
memset (&data, 0, sizeof (data));
data.loop = g_main_loop_new (NULL, TRUE);
curdir = g_file_new_for_path (".");
monitor = g_file_monitor_directory (curdir, 0, NULL, &error);
if (!monitor)
exit (1);
g_signal_connect (monitor, "changed",
G_CALLBACK (on_dir_changed), loop);
G_CALLBACK (on_dir_changed), &data);
addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
soup_address_resolve_sync (addr, NULL);
new_argv = g_ptr_array_new ();
for (i = 1; i < argc; i++)
g_ptr_array_add (new_argv, argv[i]);
g_ptr_array_add (new_argv, NULL);
server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
SOUP_SERVER_ASYNC_CONTEXT, g_main_loop_get_context (loop),
NULL);
soup_server_add_handler (server, NULL,
request_callback,
NULL, NULL);
if (!g_spawn_async (NULL, (char**)new_argv->pdata, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &pid, &error))
{
g_printerr ("%s\n", error->message);
return 1;
}
g_child_watch_add (pid, on_child_exited, &data);
soup_server_run_async (server);
g_main_loop_run (data.loop);
g_print ("http://127.0.0.1:%ld\n", (long)soup_server_get_port (server));
g_main_loop_run (loop);
if (!data.exited)
kill (data.pid, SIGTERM);
return 0;
}