2011-11-07 11:25:49 -05:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright ( C ) 2011 Colin Walters < walters @ verbum . org >
*
2011-11-10 13:17:04 -05:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
2011-11-07 11:25:49 -05:00
*
2011-11-10 13:17:04 -05:00
* This library is distributed in the hope that it will be useful ,
2011-11-07 11:25:49 -05:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2011-11-10 13:17:04 -05:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2011-11-07 11:25:49 -05:00
*
2011-11-10 13:17:04 -05:00
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
2011-11-07 11:25:49 -05:00
*
* Author : Colin Walters < walters @ verbum . org >
*/
# include "config.h"
# include "ostree-repo-file-enumerator.h"
static void ostree_repo_file_file_iface_init ( GFileIface * iface ) ;
static void
tree_replace_contents ( OstreeRepoFile * self ,
GVariant * new_files ,
GVariant * new_dirs ) ;
struct _OstreeRepoFile
{
GObject parent_instance ;
OstreeRepo * repo ;
char * commit ;
GError * commit_resolve_error ;
OstreeRepoFile * parent ;
int index ;
char * name ;
char * tree_contents_checksum ;
GVariant * tree_contents ;
char * tree_metadata_checksum ;
GVariant * tree_metadata ;
} ;
# define ostree_repo_file_get_type _ostree_repo_file_get_type
G_DEFINE_TYPE_WITH_CODE ( OstreeRepoFile , ostree_repo_file , G_TYPE_OBJECT ,
G_IMPLEMENT_INTERFACE ( G_TYPE_FILE ,
ostree_repo_file_file_iface_init ) )
static void
ostree_repo_file_finalize ( GObject * object )
{
OstreeRepoFile * self ;
self = OSTREE_REPO_FILE ( object ) ;
if ( self - > tree_contents )
g_variant_unref ( self - > tree_contents ) ;
if ( self - > tree_metadata )
g_variant_unref ( self - > tree_metadata ) ;
g_free ( self - > tree_contents_checksum ) ;
g_free ( self - > tree_metadata_checksum ) ;
g_free ( self - > commit ) ;
g_free ( self - > name ) ;
G_OBJECT_CLASS ( ostree_repo_file_parent_class ) - > finalize ( object ) ;
}
static void
ostree_repo_file_dispose ( GObject * object )
{
OstreeRepoFile * self ;
self = OSTREE_REPO_FILE ( object ) ;
g_clear_object ( & self - > repo ) ;
g_clear_object ( & self - > parent ) ;
if ( G_OBJECT_CLASS ( ostree_repo_file_parent_class ) - > dispose )
G_OBJECT_CLASS ( ostree_repo_file_parent_class ) - > dispose ( object ) ;
}
static void
ostree_repo_file_class_init ( OstreeRepoFileClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > finalize = ostree_repo_file_finalize ;
gobject_class - > dispose = ostree_repo_file_dispose ;
}
static void
ostree_repo_file_init ( OstreeRepoFile * self )
{
self - > index = - 1 ;
}
static gboolean
set_error_noent ( GFile * self , GError * * error )
{
char * path = g_file_get_path ( self ) ;
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_NOT_FOUND ,
" No such file or directory: %s " , path ) ;
g_free ( path ) ;
return FALSE ;
}
GFile *
_ostree_repo_file_new_root ( OstreeRepo * repo ,
const char * commit )
{
OstreeRepoFile * self ;
g_return_val_if_fail ( repo ! = NULL , NULL ) ;
g_return_val_if_fail ( commit ! = NULL , NULL ) ;
g_return_val_if_fail ( strlen ( commit ) = = 64 , NULL ) ;
self = g_object_new ( OSTREE_TYPE_REPO_FILE , NULL ) ;
self - > repo = g_object_ref ( repo ) ;
self - > commit = g_strdup ( commit ) ;
return G_FILE ( self ) ;
}
GFile *
_ostree_repo_file_new_child ( OstreeRepoFile * parent ,
const char * name )
{
OstreeRepoFile * self ;
self = g_object_new ( OSTREE_TYPE_REPO_FILE , NULL ) ;
self - > repo = g_object_ref ( parent - > repo ) ;
self - > parent = g_object_ref ( parent ) ;
self - > name = g_strdup ( name ) ;
return G_FILE ( self ) ;
}
OstreeRepoFile *
_ostree_repo_file_new_empty_tree ( OstreeRepo * repo )
{
OstreeRepoFile * self ;
self = g_object_new ( OSTREE_TYPE_REPO_FILE , NULL ) ;
self - > repo = g_object_ref ( repo ) ;
tree_replace_contents ( self , NULL , NULL ) ;
return self ;
}
static gboolean
do_resolve_commit ( OstreeRepoFile * self ,
GError * * error )
{
gboolean ret = FALSE ;
GVariant * commit = NULL ;
GVariant * root_contents = NULL ;
GVariant * root_metadata = NULL ;
const char * tree_contents_checksum ;
const char * tree_meta_checksum ;
g_assert ( self - > parent = = NULL ) ;
if ( ! ostree_repo_load_variant_checked ( self - > repo , OSTREE_SERIALIZED_COMMIT_VARIANT ,
self - > commit , & commit , error ) )
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 ( ! ostree_repo_load_variant_checked ( self - > repo , OSTREE_SERIALIZED_TREE_VARIANT ,
tree_contents_checksum , & root_contents ,
error ) )
goto out ;
if ( ! ostree_repo_load_variant_checked ( self - > repo , OSTREE_SERIALIZED_DIRMETA_VARIANT ,
tree_meta_checksum , & root_metadata ,
error ) )
goto out ;
self - > tree_metadata = root_metadata ;
root_metadata = NULL ;
self - > tree_contents = root_contents ;
root_contents = NULL ;
out :
if ( commit )
g_variant_unref ( commit ) ;
if ( root_metadata )
g_variant_unref ( root_metadata ) ;
if ( root_contents )
g_variant_unref ( root_contents ) ;
return ret ;
}
static gboolean
do_resolve_nonroot ( OstreeRepoFile * self ,
GError * * error )
{
gboolean ret = FALSE ;
GVariant * container = NULL ;
GVariant * tree_contents = NULL ;
GVariant * tree_metadata = NULL ;
gboolean is_dir ;
int i ;
i = _ostree_repo_file_tree_find_child ( self - > parent , self - > name , & is_dir , & container ) ;
if ( i < 0 )
{
set_error_noent ( ( GFile * ) self , error ) ;
goto out ;
}
if ( is_dir )
{
const char * name ;
const char * content_checksum ;
const char * metadata_checksum ;
GVariant * files_variant ;
files_variant = g_variant_get_child_value ( self - > parent - > tree_contents , 2 ) ;
self - > index = g_variant_n_children ( files_variant ) + i ;
g_variant_unref ( files_variant ) ;
g_variant_get_child ( container , i , " (&s&s&s) " ,
& name , & content_checksum , & metadata_checksum ) ;
if ( ! ostree_repo_load_variant_checked ( self - > repo , OSTREE_SERIALIZED_TREE_VARIANT ,
content_checksum , & tree_contents ,
error ) )
goto out ;
if ( ! ostree_repo_load_variant_checked ( self - > repo , OSTREE_SERIALIZED_DIRMETA_VARIANT ,
metadata_checksum , & tree_metadata ,
error ) )
goto out ;
self - > tree_contents = tree_contents ;
tree_contents = NULL ;
self - > tree_metadata = tree_metadata ;
tree_metadata = NULL ;
}
else
self - > index = i ;
ret = TRUE ;
out :
if ( container )
g_variant_unref ( container ) ;
if ( tree_metadata )
g_variant_unref ( tree_metadata ) ;
if ( tree_contents )
g_variant_unref ( tree_contents ) ;
return ret ;
}
gboolean
_ostree_repo_file_ensure_resolved ( OstreeRepoFile * self ,
GError * * error )
{
if ( self - > commit_resolve_error ! = NULL )
goto out ;
if ( self - > parent = = NULL )
{
if ( self - > tree_contents = = NULL )
( void ) do_resolve_commit ( self , & ( self - > commit_resolve_error ) ) ;
}
else if ( self - > index = = - 1 )
{
( void ) do_resolve_nonroot ( self , & ( self - > commit_resolve_error ) ) ;
}
out :
if ( self - > commit_resolve_error )
{
if ( error )
* error = g_error_copy ( self - > commit_resolve_error ) ;
return FALSE ;
}
else
return TRUE ;
}
gboolean
_ostree_repo_file_get_xattrs ( OstreeRepoFile * self ,
GVariant * * out_xattrs ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
GVariant * ret_xattrs = NULL ;
GVariant * metadata = NULL ;
GInputStream * input = NULL ;
GFile * local_file = NULL ;
if ( ! _ostree_repo_file_ensure_resolved ( self , error ) )
goto out ;
if ( self - > tree_metadata )
ret_xattrs = g_variant_get_child_value ( self - > tree_metadata , 4 ) ;
else if ( ostree_repo_is_archive ( self - > repo ) )
{
local_file = _ostree_repo_file_nontree_get_local ( self ) ;
if ( ! ostree_parse_packed_file ( local_file , & metadata , & input , cancellable , error ) )
goto out ;
ret_xattrs = g_variant_get_child_value ( metadata , 4 ) ;
}
else
{
local_file = _ostree_repo_file_nontree_get_local ( self ) ;
ret_xattrs = ostree_get_xattrs_for_path ( ot_gfile_get_path_cached ( local_file ) , error ) ;
}
ret = TRUE ;
* out_xattrs = ret_xattrs ;
ret_xattrs = NULL ;
out :
if ( ret_xattrs )
g_variant_unref ( ret_xattrs ) ;
if ( metadata )
g_variant_unref ( metadata ) ;
g_clear_object ( & input ) ;
g_clear_object ( & local_file ) ;
return ret ;
}
GVariant *
_ostree_repo_file_tree_get_contents ( OstreeRepoFile * self )
{
return self - > tree_contents ;
}
GVariant *
_ostree_repo_file_tree_get_metadata ( OstreeRepoFile * self )
{
return self - > tree_metadata ;
}
void
_ostree_repo_file_tree_set_metadata ( OstreeRepoFile * self ,
const char * checksum ,
GVariant * metadata )
{
if ( self - > tree_metadata )
g_variant_unref ( self - > tree_metadata ) ;
self - > tree_metadata = g_variant_ref ( metadata ) ;
g_free ( self - > tree_metadata_checksum ) ;
self - > tree_metadata_checksum = g_strdup ( checksum ) ;
}
void
_ostree_repo_file_make_empty_tree ( OstreeRepoFile * self )
{
tree_replace_contents ( self , NULL , NULL ) ;
}
void
_ostree_repo_file_tree_set_content_checksum ( OstreeRepoFile * self ,
const char * checksum )
{
g_assert ( self - > parent = = NULL ) ;
g_free ( self - > tree_contents_checksum ) ;
self - > tree_contents_checksum = g_strdup ( checksum ) ;
}
const char *
_ostree_repo_file_tree_get_content_checksum ( OstreeRepoFile * self )
{
g_assert ( self - > parent = = NULL ) ;
return self - > tree_contents_checksum ;
}
GFile *
_ostree_repo_file_nontree_get_local ( OstreeRepoFile * self )
{
const char * checksum ;
char * path ;
GFile * ret ;
g_assert ( ! ostree_repo_is_archive ( self - > repo ) ) ;
checksum = _ostree_repo_file_nontree_get_checksum ( self ) ;
path = ostree_repo_get_object_path ( self - > repo , checksum , OSTREE_OBJECT_TYPE_FILE ) ;
ret = ot_util_new_file_for_path ( path ) ;
g_free ( path ) ;
return ret ;
}
OstreeRepo *
_ostree_repo_file_get_repo ( OstreeRepoFile * self )
{
return self - > repo ;
}
OstreeRepoFile *
_ostree_repo_file_get_root ( OstreeRepoFile * self )
{
OstreeRepoFile * parent = self ;
while ( parent - > parent )
parent = parent - > parent ;
return parent ;
}
const char *
_ostree_repo_file_nontree_get_checksum ( OstreeRepoFile * self )
{
int n ;
gboolean is_dir ;
g_assert ( self - > parent ) ;
n = _ostree_repo_file_tree_find_child ( self - > parent , self - > name , & is_dir , NULL ) ;
g_assert ( n > = 0 & & ! is_dir ) ;
return _ostree_repo_file_tree_get_child_checksum ( self - > parent , n ) ;
}
const char *
_ostree_repo_file_tree_get_child_checksum ( OstreeRepoFile * self ,
int n )
{
GVariant * files_variant ;
const char * checksum ;
g_assert ( self - > tree_contents ) ;
files_variant = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
g_variant_get_child ( files_variant , n , " (@s&s) " , NULL , & checksum ) ;
g_variant_unref ( files_variant ) ;
return checksum ;
}
static gboolean
ostree_repo_file_is_native ( GFile * file )
{
return FALSE ;
}
static gboolean
ostree_repo_file_has_uri_scheme ( GFile * file ,
const char * uri_scheme )
{
return g_ascii_strcasecmp ( uri_scheme , " ostree " ) = = 0 ;
}
static char *
ostree_repo_file_get_uri_scheme ( GFile * file )
{
return g_strdup ( " ostree " ) ;
}
static char *
ostree_repo_file_get_basename ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
return g_strdup ( self - > name ) ;
}
static char *
ostree_repo_file_get_path ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
OstreeRepoFile * parent ;
GString * buf ;
GSList * parents ;
GSList * iter ;
buf = g_string_new ( " " ) ;
parents = NULL ;
for ( parent = self - > parent ; parent ; parent = parent - > parent )
parents = g_slist_prepend ( parents , parent ) ;
if ( parents - > next )
{
for ( iter = parents - > next ; iter ; iter = iter - > next )
{
parent = iter - > data ;
g_string_append_c ( buf , ' / ' ) ;
g_string_append ( buf , parent - > name ) ;
}
}
g_string_append_c ( buf , ' / ' ) ;
g_string_append ( buf , self - > name ) ;
g_slist_free ( parents ) ;
return g_string_free ( buf , FALSE ) ;
}
static char *
ostree_repo_file_get_uri ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
char * path ;
char * uri_path ;
char * ret ;
path = g_file_get_path ( file ) ;
uri_path = g_filename_to_uri ( path , NULL , NULL ) ;
g_free ( path ) ;
g_assert ( g_str_has_prefix ( uri_path , " file:// " ) ) ;
ret = g_strconcat ( " ostree:// " , self - > commit , uri_path + strlen ( " file:// " ) , NULL ) ;
g_free ( uri_path ) ;
return ret ;
}
static char *
ostree_repo_file_get_parse_name ( GFile * file )
{
return ostree_repo_file_get_uri ( file ) ;
}
static GFile *
ostree_repo_file_get_parent ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
return g_object_ref ( self - > parent ) ;
}
static GFile *
ostree_repo_file_dup ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
if ( self - > parent )
return _ostree_repo_file_new_child ( self - > parent , self - > name ) ;
else
return _ostree_repo_file_new_root ( self - > repo , self - > commit ) ;
}
static guint
ostree_repo_file_hash ( GFile * file )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
if ( self - > parent )
return g_file_hash ( self - > parent ) + g_str_hash ( self - > name ) ;
else
return g_str_hash ( self - > commit ) ;
}
static gboolean
ostree_repo_file_equal ( GFile * file1 ,
GFile * file2 )
{
OstreeRepoFile * self1 = OSTREE_REPO_FILE ( file1 ) ;
OstreeRepoFile * self2 = OSTREE_REPO_FILE ( file2 ) ;
if ( self1 - > parent & & self2 - > parent )
{
return g_str_equal ( self1 - > name , self2 - > name )
& & g_file_equal ( ( GFile * ) self1 - > parent , ( GFile * ) self2 - > parent ) ;
}
else if ( ! self1 - > parent & & ! self2 - > parent )
{
return g_str_equal ( self1 - > commit , self2 - > commit ) ;
}
else
return FALSE ;
}
static const char *
match_prefix ( const char * path ,
const char * prefix )
{
int prefix_len ;
prefix_len = strlen ( prefix ) ;
if ( strncmp ( path , prefix , prefix_len ) ! = 0 )
return NULL ;
/* Handle the case where prefix is the root, so that
* the IS_DIR_SEPRARATOR check below works */
if ( prefix_len > 0 & &
G_IS_DIR_SEPARATOR ( prefix [ prefix_len - 1 ] ) )
prefix_len - - ;
return path + prefix_len ;
}
static gboolean
ostree_repo_file_prefix_matches ( GFile * parent ,
GFile * descendant )
{
const char * remainder ;
char * parent_path ;
char * descendant_path ;
parent_path = g_file_get_path ( parent ) ;
descendant_path = g_file_get_path ( descendant ) ;
remainder = match_prefix ( descendant_path , parent_path ) ;
g_free ( parent_path ) ;
g_free ( descendant_path ) ;
if ( remainder ! = NULL & & G_IS_DIR_SEPARATOR ( * remainder ) )
return TRUE ;
return FALSE ;
}
static char *
ostree_repo_file_get_relative_path ( GFile * parent ,
GFile * descendant )
{
const char * remainder ;
char * parent_path ;
char * descendant_path ;
parent_path = g_file_get_path ( parent ) ;
descendant_path = g_file_get_path ( descendant ) ;
remainder = match_prefix ( descendant_path , parent_path ) ;
g_free ( parent_path ) ;
g_free ( descendant_path ) ;
if ( remainder ! = NULL & & G_IS_DIR_SEPARATOR ( * remainder ) )
return g_strdup ( remainder + 1 ) ;
return NULL ;
}
static GFile *
ostree_repo_file_resolve_relative_path ( GFile * file ,
const char * relative_path )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
OstreeRepoFile * parent ;
char * filename ;
const char * rest ;
GFile * ret ;
if ( g_path_is_absolute ( relative_path ) & & self - > parent )
{
g_assert ( * relative_path = = ' / ' ) ;
return ostree_repo_file_resolve_relative_path ( ( GFile * ) _ostree_repo_file_get_root ( self ) ,
relative_path + 1 ) ;
}
rest = strchr ( relative_path , ' / ' ) ;
if ( rest )
{
rest + = 1 ;
filename = g_strndup ( relative_path , rest - relative_path ) ;
}
else
filename = g_strdup ( relative_path ) ;
parent = ( OstreeRepoFile * ) _ostree_repo_file_new_child ( self , filename ) ;
g_free ( filename ) ;
if ( ! rest )
ret = ( GFile * ) parent ;
else
{
ret = ostree_repo_file_resolve_relative_path ( ( GFile * ) parent , rest ) ;
g_clear_object ( & parent ) ;
}
return ret ;
}
static GFileEnumerator *
ostree_repo_file_enumerate_children ( GFile * file ,
const char * attributes ,
GFileQueryInfoFlags flags ,
GCancellable * cancellable ,
GError * * error )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
return _ostree_repo_file_enumerator_new ( self ,
attributes , flags ,
cancellable , error ) ;
}
static GFile *
ostree_repo_file_get_child_for_display_name ( GFile * file ,
const char * display_name ,
GError * * error )
{
return g_file_get_child ( file , display_name ) ;
}
static GFile *
get_child_local_file ( OstreeRepo * repo ,
const char * checksum )
{
char * path ;
GFile * ret ;
path = ostree_repo_get_object_path ( repo , checksum , OSTREE_OBJECT_TYPE_FILE ) ;
ret = ot_util_new_file_for_path ( path ) ;
g_free ( path ) ;
return ret ;
}
static gboolean
query_child_info_file_nonarchive ( OstreeRepo * repo ,
const char * checksum ,
GFileAttributeMatcher * matcher ,
GFileInfo * info ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
GFileInfo * local_info = NULL ;
GFile * local_file = NULL ;
int i ;
const char * mapped_boolean [ ] = {
" standard::is-symlink "
} ;
const char * mapped_string [ ] = {
} ;
const char * mapped_byte_string [ ] = {
" standard::symlink-target "
} ;
const char * mapped_uint32 [ ] = {
" standard::type " ,
" unix::device " ,
" unix::mode " ,
" unix::nlink " ,
" unix::uid " ,
" unix::gid " ,
" unix::rdev "
} ;
const char * mapped_uint64 [ ] = {
" standard::size " ,
" standard::allocated-size " ,
" unix::inode "
} ;
if ( ! ( g_file_attribute_matcher_matches ( matcher , " unix::mode " )
| | g_file_attribute_matcher_matches ( matcher , " standard::type " ) ) )
{
ret = TRUE ;
goto out ;
}
local_file = get_child_local_file ( repo , checksum ) ;
local_info = g_file_query_info ( local_file ,
OSTREE_GIO_FAST_QUERYINFO ,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ,
cancellable ,
error ) ;
if ( ! local_info )
goto out ;
for ( i = 0 ; i < G_N_ELEMENTS ( mapped_boolean ) ; i + + )
g_file_info_set_attribute_boolean ( info , mapped_boolean [ i ] , g_file_info_get_attribute_boolean ( local_info , mapped_boolean [ i ] ) ) ;
for ( i = 0 ; i < G_N_ELEMENTS ( mapped_string ) ; i + + )
{
const char * string = g_file_info_get_attribute_string ( local_info , mapped_string [ i ] ) ;
if ( string )
g_file_info_set_attribute_string ( info , mapped_string [ i ] , string ) ;
}
for ( i = 0 ; i < G_N_ELEMENTS ( mapped_byte_string ) ; i + + )
{
const char * byte_string = g_file_info_get_attribute_byte_string ( local_info , mapped_byte_string [ i ] ) ;
if ( byte_string )
g_file_info_set_attribute_byte_string ( info , mapped_byte_string [ i ] , byte_string ) ;
}
for ( i = 0 ; i < G_N_ELEMENTS ( mapped_uint32 ) ; i + + )
g_file_info_set_attribute_uint32 ( info , mapped_uint32 [ i ] , g_file_info_get_attribute_uint32 ( local_info , mapped_uint32 [ i ] ) ) ;
for ( i = 0 ; i < G_N_ELEMENTS ( mapped_uint64 ) ; i + + )
g_file_info_set_attribute_uint64 ( info , mapped_uint64 [ i ] , g_file_info_get_attribute_uint64 ( local_info , mapped_uint64 [ i ] ) ) ;
ret = TRUE ;
out :
g_clear_object ( & local_info ) ;
g_clear_object ( & local_file ) ;
return ret ;
}
static gboolean
query_child_info_file_archive ( OstreeRepo * repo ,
const char * checksum ,
GFileAttributeMatcher * matcher ,
GFileInfo * info ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
GFile * local_file = NULL ;
GVariant * metadata = NULL ;
GInputStream * input = NULL ;
guint32 version , uid , gid , mode ;
guint64 content_len ;
guint32 file_type ;
gsize bytes_read ;
char * buf = NULL ;
local_file = get_child_local_file ( repo , checksum ) ;
if ( ! ostree_parse_packed_file ( local_file , & metadata , & input , cancellable , error ) )
goto out ;
g_variant_get ( metadata , " (uuuu@a(ayay)t) " ,
& version , & uid , & gid , & mode ,
NULL , & content_len ) ;
uid = GUINT32_FROM_BE ( uid ) ;
gid = GUINT32_FROM_BE ( gid ) ;
mode = GUINT32_FROM_BE ( mode ) ;
content_len = GUINT64_FROM_BE ( content_len ) ;
g_file_info_set_attribute_boolean ( info , " standard::is-symlink " ,
S_ISLNK ( mode ) ) ;
if ( S_ISLNK ( mode ) )
file_type = G_FILE_TYPE_SYMBOLIC_LINK ;
else if ( S_ISREG ( mode ) )
file_type = G_FILE_TYPE_REGULAR ;
2011-11-09 09:08:58 -05:00
else if ( S_ISBLK ( mode ) | | S_ISCHR ( mode ) | | S_ISFIFO ( mode ) )
2011-11-07 11:25:49 -05:00
file_type = G_FILE_TYPE_SPECIAL ;
else
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Corrupted packfile %s: Invalid mode " , checksum ) ;
goto out ;
}
g_file_info_set_attribute_uint32 ( info , " standard::type " , file_type ) ;
g_file_info_set_attribute_uint32 ( info , " unix::uid " , uid ) ;
g_file_info_set_attribute_uint32 ( info , " unix::gid " , gid ) ;
g_file_info_set_attribute_uint32 ( info , " unix::mode " , mode ) ;
if ( file_type = = G_FILE_TYPE_REGULAR )
{
g_file_info_set_attribute_uint64 ( info , " standard::size " , content_len ) ;
}
else if ( file_type = = G_FILE_TYPE_SYMBOLIC_LINK )
{
gsize len = MIN ( PATH_MAX , content_len ) + 1 ;
buf = g_malloc ( len ) ;
if ( ! g_input_stream_read_all ( input , buf , len , & bytes_read , cancellable , error ) )
goto out ;
buf [ bytes_read ] = ' \0 ' ;
g_file_info_set_attribute_byte_string ( info , " standard::symlink-target " , buf ) ;
}
else if ( file_type = = G_FILE_TYPE_SPECIAL )
{
guint32 device ;
if ( ! g_input_stream_read_all ( input , & device , 4 , & bytes_read , cancellable , error ) )
goto out ;
device = GUINT32_FROM_BE ( device ) ;
g_file_info_set_attribute_uint32 ( info , " unix::device " , device ) ;
}
ret = TRUE ;
out :
g_free ( buf ) ;
if ( metadata )
g_variant_unref ( metadata ) ;
g_clear_object ( & local_file ) ;
g_clear_object ( & input ) ;
return ret ;
}
static void
set_info_from_dirmeta ( GFileInfo * info ,
GVariant * metadata )
{
guint32 version , uid , gid , mode ;
g_file_info_set_attribute_uint32 ( info , " standard::type " , G_FILE_TYPE_DIRECTORY ) ;
/* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
g_variant_get ( metadata , " (uuuu@a(ayay)) " ,
& version , & uid , & gid , & mode ,
NULL ) ;
version = GUINT32_FROM_BE ( version ) ;
uid = GUINT32_FROM_BE ( uid ) ;
gid = GUINT32_FROM_BE ( gid ) ;
mode = GUINT32_FROM_BE ( mode ) ;
g_file_info_set_attribute_uint32 ( info , " unix::uid " , uid ) ;
g_file_info_set_attribute_uint32 ( info , " unix::gid " , gid ) ;
g_file_info_set_attribute_uint32 ( info , " unix::mode " , mode ) ;
}
static gboolean
query_child_info_dir ( OstreeRepo * repo ,
const char * metadata_checksum ,
GFileAttributeMatcher * matcher ,
GFileQueryInfoFlags flags ,
GFileInfo * info ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
GVariant * metadata = NULL ;
if ( ! g_file_attribute_matcher_matches ( matcher , " unix::mode " ) )
{
ret = TRUE ;
goto out ;
}
if ( ! ostree_repo_load_variant_checked ( repo , OSTREE_SERIALIZED_DIRMETA_VARIANT ,
metadata_checksum , & metadata , error ) )
goto out ;
set_info_from_dirmeta ( info , metadata ) ;
ret = TRUE ;
out :
if ( metadata )
g_variant_unref ( metadata ) ;
return ret ;
}
static gboolean
bsearch_in_file_variant ( GVariant * variant ,
const char * name ,
int * out_pos )
{
int i , n ;
int m ;
i = 0 ;
n = g_variant_n_children ( variant ) - 1 ;
m = 0 ;
while ( i < = n )
{
GVariant * child ;
const char * cur ;
int cmp ;
m = i + ( ( n - i ) / 2 ) ;
child = g_variant_get_child_value ( variant , m ) ;
g_variant_get_child ( child , 0 , " &s " , & cur , NULL ) ;
cmp = strcmp ( cur , name ) ;
if ( cmp < 0 )
i = m + 1 ;
else if ( cmp > 0 )
n = m - 1 ;
else
{
g_variant_unref ( child ) ;
* out_pos = m ;
return TRUE ;
}
g_variant_unref ( child ) ;
}
* out_pos = m ;
return FALSE ;
}
static GVariant *
remove_variant_child ( GVariant * variant ,
int n )
{
GVariantBuilder builder ;
GVariantIter * iter ;
int i ;
GVariant * child ;
g_variant_builder_init ( & builder , g_variant_get_type ( variant ) ) ;
iter = g_variant_iter_new ( variant ) ;
i = 0 ;
while ( ( child = g_variant_iter_next_value ( iter ) ) ! = NULL )
{
if ( i ! = n )
g_variant_builder_add_value ( & builder , child ) ;
g_variant_unref ( child ) ;
}
g_variant_iter_free ( iter ) ;
return g_variant_builder_end ( & builder ) ;
}
static GVariant *
insert_variant_child ( GVariant * variant ,
int n ,
GVariant * item )
{
GVariantBuilder builder ;
GVariantIter * iter ;
int i ;
GVariant * child ;
g_variant_builder_init ( & builder , g_variant_get_type ( variant ) ) ;
iter = g_variant_iter_new ( variant ) ;
i = 0 ;
while ( ( child = g_variant_iter_next_value ( iter ) ) ! = NULL )
{
if ( i = = n )
g_variant_builder_add_value ( & builder , item ) ;
g_variant_builder_add_value ( & builder , child ) ;
g_variant_unref ( child ) ;
}
g_variant_iter_free ( iter ) ;
return g_variant_builder_end ( & builder ) ;
}
static void
tree_replace_contents ( OstreeRepoFile * self ,
GVariant * new_files ,
GVariant * new_dirs )
{
guint version ;
GVariant * metadata ;
GVariant * tmp_files = NULL ;
GVariant * tmp_dirs = NULL ;
if ( ! ( new_files | | new_dirs ) & & self - > tree_contents )
return ;
else if ( ! self - > tree_contents )
{
version = GUINT32_TO_BE ( 0 ) ;
metadata = g_variant_new_array ( G_VARIANT_TYPE ( " {sv} " ) , NULL , 0 ) ;
tmp_dirs = g_variant_new_array ( G_VARIANT_TYPE ( " (ss) " ) , NULL , 0 ) ;
tmp_files = g_variant_new_array ( G_VARIANT_TYPE ( " (ss) " ) , NULL , 0 ) ;
}
else
{
g_variant_get_child ( self - > tree_contents , 0 , " u " , & version ) ;
metadata = g_variant_get_child_value ( self - > tree_contents , 1 ) ;
if ( ! new_files )
tmp_files = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
if ( ! new_dirs )
tmp_dirs = g_variant_get_child_value ( self - > tree_contents , 3 ) ;
}
if ( self - > tree_contents )
g_variant_unref ( self - > tree_contents ) ;
self - > tree_contents = g_variant_new ( " (u@a{sv}@a(ss)@a(sss)) " , version , metadata ,
new_files ? new_files : tmp_files ,
new_dirs ? new_dirs : tmp_dirs ) ;
g_variant_unref ( metadata ) ;
if ( tmp_files )
g_variant_unref ( tmp_files ) ;
if ( tmp_dirs )
g_variant_unref ( tmp_dirs ) ;
}
void
_ostree_repo_file_tree_remove_child ( OstreeRepoFile * self ,
const char * name )
{
int i ;
GVariant * files_variant ;
GVariant * new_files_variant = NULL ;
GVariant * dirs_variant ;
GVariant * new_dirs_variant = NULL ;
files_variant = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
dirs_variant = g_variant_get_child_value ( self - > tree_contents , 3 ) ;
if ( bsearch_in_file_variant ( files_variant , name , & i ) )
{
new_files_variant = remove_variant_child ( files_variant , i ) ;
}
else
{
if ( bsearch_in_file_variant ( dirs_variant , name , & i ) )
{
new_dirs_variant = remove_variant_child ( dirs_variant , i ) ;
}
}
tree_replace_contents ( self , new_files_variant , new_dirs_variant ) ;
g_variant_unref ( files_variant ) ;
g_variant_unref ( dirs_variant ) ;
}
void
_ostree_repo_file_tree_add_file ( OstreeRepoFile * self ,
const char * name ,
const char * checksum )
{
int n ;
GVariant * files_variant ;
GVariant * new_files_variant ;
files_variant = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
if ( ! bsearch_in_file_variant ( files_variant , name , & n ) )
{
new_files_variant = insert_variant_child ( files_variant , n ,
g_variant_new ( " (ss) " , name , checksum ) ) ;
g_variant_ref_sink ( new_files_variant ) ;
tree_replace_contents ( self , new_files_variant , NULL ) ;
g_variant_unref ( new_files_variant ) ;
}
g_variant_unref ( files_variant ) ;
}
void
_ostree_repo_file_tree_add_dir ( OstreeRepoFile * self ,
const char * name ,
const char * content_checksum ,
const char * metadata_checksum )
{
int n ;
GVariant * dirs_variant ;
GVariant * new_dirs_variant ;
dirs_variant = g_variant_get_child_value ( self - > tree_contents , 3 ) ;
if ( ! bsearch_in_file_variant ( dirs_variant , name , & n ) )
{
new_dirs_variant = insert_variant_child ( dirs_variant , n ,
g_variant_new ( " (sss) " , name , content_checksum ,
metadata_checksum ) ) ;
g_variant_ref_sink ( new_dirs_variant ) ;
tree_replace_contents ( self , NULL , new_dirs_variant ) ;
g_variant_unref ( new_dirs_variant ) ;
}
g_variant_unref ( dirs_variant ) ;
}
int
_ostree_repo_file_tree_find_child ( OstreeRepoFile * self ,
const char * name ,
gboolean * is_dir ,
GVariant * * out_container )
{
int i ;
GVariant * files_variant = NULL ;
GVariant * dirs_variant = NULL ;
GVariant * ret_container = NULL ;
files_variant = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
dirs_variant = g_variant_get_child_value ( self - > tree_contents , 3 ) ;
i = - 1 ;
if ( bsearch_in_file_variant ( files_variant , name , & i ) )
{
* is_dir = FALSE ;
ret_container = files_variant ;
files_variant = NULL ;
}
else
{
if ( bsearch_in_file_variant ( dirs_variant , name , & i ) )
{
* is_dir = TRUE ;
ret_container = dirs_variant ;
dirs_variant = NULL ;
}
else
{
i = - 1 ;
}
}
if ( ret_container & & out_container )
{
* out_container = ret_container ;
ret_container = NULL ;
}
if ( ret_container )
g_variant_unref ( ret_container ) ;
if ( files_variant )
g_variant_unref ( files_variant ) ;
if ( dirs_variant )
g_variant_unref ( dirs_variant ) ;
return i ;
}
gboolean
_ostree_repo_file_tree_query_child ( OstreeRepoFile * self ,
int n ,
const char * attributes ,
GFileQueryInfoFlags flags ,
GFileInfo * * out_info ,
GCancellable * cancellable ,
GError * * error )
{
const char * name = NULL ;
gboolean ret = FALSE ;
GFileInfo * ret_info = NULL ;
GVariant * files_variant = NULL ;
GVariant * dirs_variant = NULL ;
GVariant * tree_child_metadata = NULL ;
GFileAttributeMatcher * matcher = NULL ;
int c ;
if ( ! _ostree_repo_file_ensure_resolved ( self , error ) )
goto out ;
matcher = g_file_attribute_matcher_new ( attributes ) ;
ret_info = g_file_info_new ( ) ;
g_assert ( self - > tree_contents ) ;
files_variant = g_variant_get_child_value ( self - > tree_contents , 2 ) ;
dirs_variant = g_variant_get_child_value ( self - > tree_contents , 3 ) ;
c = g_variant_n_children ( files_variant ) ;
if ( n < c )
{
const char * checksum ;
g_variant_get_child ( files_variant , n , " (&s&s) " , & name , & checksum ) ;
if ( ostree_repo_is_archive ( self - > repo ) )
{
if ( ! query_child_info_file_archive ( self - > repo , checksum , matcher , ret_info ,
cancellable , error ) )
goto out ;
}
else
{
if ( ! query_child_info_file_nonarchive ( self - > repo , checksum , matcher , ret_info ,
cancellable , error ) )
goto out ;
}
}
else
{
const char * tree_checksum ;
const char * meta_checksum ;
n - = c ;
c = g_variant_n_children ( dirs_variant ) ;
if ( n < c )
{
g_variant_get_child ( dirs_variant , n , " (&s&s&s) " ,
& name , & tree_checksum , & meta_checksum ) ;
if ( ! query_child_info_dir ( self - > repo , meta_checksum ,
matcher , flags , ret_info ,
cancellable , error ) )
goto out ;
}
else
n - = c ;
}
if ( name )
{
g_file_info_set_attribute_byte_string ( ret_info , " standard::name " ,
name ) ;
g_file_info_set_attribute_string ( ret_info , " standard::display-name " ,
name ) ;
if ( * name = = ' . ' )
g_file_info_set_is_hidden ( ret_info , TRUE ) ;
}
else
{
g_clear_object ( & ret_info ) ;
}
ret = TRUE ;
* out_info = ret_info ;
ret_info = NULL ;
out :
g_clear_object ( & ret_info ) ;
if ( matcher )
g_file_attribute_matcher_unref ( matcher ) ;
if ( tree_child_metadata )
g_variant_unref ( tree_child_metadata ) ;
g_variant_unref ( files_variant ) ;
g_variant_unref ( dirs_variant ) ;
return ret ;
}
static GFileInfo *
ostree_repo_file_query_info ( GFile * file ,
const char * attributes ,
GFileQueryInfoFlags flags ,
GCancellable * cancellable ,
GError * * error )
{
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
gboolean ret = FALSE ;
GFileInfo * info = NULL ;
if ( ! _ostree_repo_file_ensure_resolved ( self , error ) )
goto out ;
if ( ! self - > parent )
{
info = g_file_info_new ( ) ;
set_info_from_dirmeta ( info , self - > tree_metadata ) ;
}
else
{
if ( ! _ostree_repo_file_tree_query_child ( self - > parent , self - > index ,
attributes , flags ,
& info , cancellable , error ) )
goto out ;
}
ret = TRUE ;
out :
if ( ! ret )
g_clear_object ( & info ) ;
return info ;
}
static GFileAttributeInfoList *
ostree_repo_file_query_settable_attributes ( GFile * file ,
GCancellable * cancellable ,
GError * * error )
{
return g_file_attribute_info_list_new ( ) ;
}
static GFileAttributeInfoList *
ostree_repo_file_query_writable_namespaces ( GFile * file ,
GCancellable * cancellable ,
GError * * error )
{
return g_file_attribute_info_list_new ( ) ;
}
static GFileInputStream *
ostree_repo_file_read ( GFile * file ,
GCancellable * cancellable ,
GError * * error )
{
gboolean ret = FALSE ;
GFile * local_file = NULL ;
GFileInputStream * ret_stream = NULL ;
OstreeRepoFile * self = OSTREE_REPO_FILE ( file ) ;
if ( self - > tree_contents )
{
g_set_error_literal ( error , G_IO_ERROR ,
G_IO_ERROR_IS_DIRECTORY ,
" Can't open directory " ) ;
goto out ;
}
if ( ostree_repo_is_archive ( self - > repo ) )
{
g_set_error_literal ( error , G_IO_ERROR ,
G_IO_ERROR_NOT_SUPPORTED ,
" Can't open archived file (yet) " ) ;
goto out ;
}
else
{
local_file = _ostree_repo_file_nontree_get_local ( self ) ;
ret_stream = g_file_read ( local_file , cancellable , error ) ;
if ( ! ret_stream )
goto out ;
}
ret = TRUE ;
out :
g_clear_object ( & local_file ) ;
if ( ! ret )
g_clear_object ( & ret_stream ) ;
return ret_stream ;
}
static void
ostree_repo_file_file_iface_init ( GFileIface * iface )
{
iface - > dup = ostree_repo_file_dup ;
iface - > hash = ostree_repo_file_hash ;
iface - > equal = ostree_repo_file_equal ;
iface - > is_native = ostree_repo_file_is_native ;
iface - > has_uri_scheme = ostree_repo_file_has_uri_scheme ;
iface - > get_uri_scheme = ostree_repo_file_get_uri_scheme ;
iface - > get_basename = ostree_repo_file_get_basename ;
iface - > get_path = ostree_repo_file_get_path ;
iface - > get_uri = ostree_repo_file_get_uri ;
iface - > get_parse_name = ostree_repo_file_get_parse_name ;
iface - > get_parent = ostree_repo_file_get_parent ;
iface - > prefix_matches = ostree_repo_file_prefix_matches ;
iface - > get_relative_path = ostree_repo_file_get_relative_path ;
iface - > resolve_relative_path = ostree_repo_file_resolve_relative_path ;
iface - > get_child_for_display_name = ostree_repo_file_get_child_for_display_name ;
iface - > set_display_name = NULL ;
iface - > enumerate_children = ostree_repo_file_enumerate_children ;
iface - > query_info = ostree_repo_file_query_info ;
iface - > query_filesystem_info = NULL ;
iface - > find_enclosing_mount = NULL ;
iface - > query_settable_attributes = ostree_repo_file_query_settable_attributes ;
iface - > query_writable_namespaces = ostree_repo_file_query_writable_namespaces ;
iface - > set_attribute = NULL ;
iface - > set_attributes_from_info = NULL ;
iface - > read_fn = ostree_repo_file_read ;
iface - > append_to = NULL ;
iface - > create = NULL ;
iface - > replace = NULL ;
iface - > open_readwrite = NULL ;
iface - > create_readwrite = NULL ;
iface - > replace_readwrite = NULL ;
iface - > delete_file = NULL ;
iface - > trash = NULL ;
iface - > make_directory = NULL ;
iface - > make_symbolic_link = NULL ;
iface - > copy = NULL ;
iface - > move = NULL ;
iface - > monitor_dir = NULL ;
iface - > monitor_file = NULL ;
iface - > supports_thread_contexts = TRUE ;
}