diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-archive.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-archive.c')
| -rw-r--r-- | utils/mcpu-archive.c | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/utils/mcpu-archive.c b/utils/mcpu-archive.c new file mode 100644 index 0000000..e10dd5c --- /dev/null +++ b/utils/mcpu-archive.c @@ -0,0 +1,1025 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mcpu-archive.h" +#include "mcpu-asm.h" +#include "mcpu-melf.h" +#include "mcpu-object.h" + + +#define MCPU_AR_MAGIC "!<arch>\n" +#define MCPU_AR_MAGIC_SIZE 8U +#define MCPU_AR_HEADER_SIZE 60U + + +typedef struct mcpu_ar_raw_header +{ + char name[16]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; +} mcpu_ar_raw_header; + + +static int +add_size( size_t a, size_t b, size_t *result ) +{ + if( b > SIZE_MAX - a ) + return( -1 ); + *result = a + b; + return( 0 ); + +} /* End of add_size() */ + + +static int +mul_size( size_t a, size_t b, size_t *result ) +{ + if( a && b > SIZE_MAX / a ) + return( -1 ); + *result = a * b; + return( 0 ); + +} /* End of mul_size() */ + + +static char * +copy_string( const char *s, size_t n ) +{ + char *p = (char *)malloc( n + 1 ); + + if( !p ) + return( NULL ); + memcpy( p, s, n ); + p[n] = 0; + return( p ); + +} /* End of copy_string() */ + + +const char * +mcpu_archive_basename( const char *path ) +{ + const char *p; + + if( !path ) + return( NULL ); + p = strrchr( path, '/' ); + return( p ? p + 1 : path ); + +} /* End of mcpu_archive_basename() */ + + +static int +valid_member_name( const char *name ) +{ + const unsigned char *p = (const unsigned char *)name; + + if( !name || !*name || !strcmp( name, "." ) || !strcmp( name, ".." ) ) + return( 0 ); + + while( *p ) + { + if( *p == '/' || *p == '\n' || *p == '\r' || *p == 0x7f ) + return( 0 ); + ++p; + } + return( 1 ); + +} /* End of valid_member_name() */ + + +void +mcpu_archive_member_free( mcpu_archive_member *member ) +{ + size_t i; + + if( !member ) + return; + + free( member->name ); + free( member->data ); + for( i = 0; i < member->symbol_count; ++i ) + free( member->symbols[i] ); + free( member->symbols ); + memset( member, 0, sizeof( *member ) ); + +} /* End of mcpu_archive_member_free() */ + + +void +mcpu_archive_init( mcpu_archive *archive ) +{ + if( archive ) + memset( archive, 0, sizeof( *archive ) ); + +} /* End of mcpu_archive_init() */ + + +void +mcpu_archive_free( mcpu_archive *archive ) +{ + size_t i; + + if( !archive ) + return; + + for( i = 0; i < archive->member_count; ++i ) + mcpu_archive_member_free( &archive->members[i] ); + free( archive->members ); + memset( archive, 0, sizeof( *archive ) ); + +} /* End of mcpu_archive_free() */ + + +static int +member_add_symbol( mcpu_archive_member *member, const char *name, + char *err, size_t errsz ) +{ + char **q; + char *s; + + q = (char **)realloc( member->symbols, + (member->symbol_count + 1) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + s = strdup( name ); + if( !s ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + member->symbols = q; + member->symbols[member->symbol_count++] = s; + return( 0 ); + +} /* End of member_add_symbol() */ + + +static int +member_collect_symbols( const char *path, mcpu_archive_member *member, + char *err, size_t errsz ) +{ + cpu_asm_object object; + size_t i; + + memset( &object, 0, sizeof( object ) ); + if( cpu_object_read( path, &object, err, errsz ) ) + return( -1 ); + + for( i = 0; i < object.symbol_count; ++i ) + { + const cpu_asm_symbol *s = &object.symbols[i]; + + if( s->section == CPU_OBJ_SEC_UNDEF || s->binding == CPU_OBJ_BIND_LOCAL ) + continue; + if( member_add_symbol( member, s->name, err, errsz ) ) + { + cpu_asm_object_free( &object ); + return( -1 ); + } + } + + cpu_asm_object_free( &object ); + return( 0 ); + +} /* End of member_collect_symbols() */ + + + + + +static int +member_collect_symbols_data( const unsigned char *data, size_t size, + mcpu_archive_member *member, + char *err, size_t errsz ) +{ + cpu_asm_object object; + size_t i; + + memset( &object, 0, sizeof( object ) ); + if( cpu_object_read_data(data, size, &object, err, errsz) ) + return( -1 ); + + for( i = 0; i < object.symbol_count; ++i ) + { + const cpu_asm_symbol *s = &object.symbols[i]; + + if( s->section == CPU_OBJ_SEC_UNDEF || s->binding == CPU_OBJ_BIND_LOCAL ) + continue; + if( member_add_symbol(member, s->name, err, errsz) ) + { + cpu_asm_object_free( &object ); + return( -1 ); + } + } + + cpu_asm_object_free( &object ); + return( 0 ); + +} /* End of member_collect_symbols_data() */ + + +int +mcpu_archive_member_load( const char *path, mcpu_archive_member *member, + char *err, size_t errsz ) +{ + const char *name; + unsigned char *data = NULL; + size_t size = 0; + + if( !path || !member ) + return( -1 ); + memset( member, 0, sizeof( *member ) ); + + name = mcpu_archive_basename( path ); + if( !valid_member_name( name ) ) + { + snprintf( err, errsz, "invalid archive member name: %s", + name ? name : "<null>" ); + return( -1 ); + } + + if( cpu_melf_readall( path, &data, &size, err, errsz ) ) + return( -1 ); + + member->name = strdup( name ); + member->data = data; + member->size = size; + if( !member->name ) + { + snprintf( err, errsz, "out of memory" ); + mcpu_archive_member_free( member ); + return( -1 ); + } + + if( member_collect_symbols( path, member, err, errsz ) ) + { + mcpu_archive_member_free( member ); + return( -1 ); + } + return( 0 ); + +} /* End of mcpu_archive_member_load() */ + + +int +mcpu_archive_append( mcpu_archive *archive, mcpu_archive_member *member, + char *err, size_t errsz ) +{ + mcpu_archive_member *q; + + if( !archive || !member ) + return( -1 ); + + q = (mcpu_archive_member *)realloc( archive->members, + (archive->member_count + 1) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + archive->members = q; + archive->members[archive->member_count++] = *member; + memset( member, 0, sizeof( *member ) ); + return( 0 ); + +} /* End of mcpu_archive_append() */ + + +static int +parse_decimal_field( const char *p, size_t n, size_t *value ) +{ + size_t i = 0; + size_t v = 0; + int any = 0; + + while( i < n && p[i] == ' ' ) + ++i; + while( i < n && p[i] != ' ' ) + { + unsigned d; + + if( p[i] < '0' || p[i] > '9' ) + return( -1 ); + d = (unsigned)(p[i] - '0'); + if( v > (SIZE_MAX - d) / 10 ) + return( -1 ); + v = v * 10 + d; + any = 1; + ++i; + } + while( i < n ) + { + if( p[i] != ' ' ) + return( -1 ); + ++i; + } + + if( !any ) + return( -1 ); + *value = v; + return( 0 ); + +} /* End of parse_decimal_field() */ + + +static int +raw_name( const mcpu_ar_raw_header *h, char *name, size_t namesz ) +{ + size_t n = sizeof( h->name ); + + while( n && h->name[n - 1] == ' ' ) + --n; + if( n + 1 > namesz ) + return( -1 ); + memcpy( name, h->name, n ); + name[n] = 0; + return( 0 ); + +} /* End of raw_name() */ + + +static int +long_name_at( const unsigned char *tab, size_t tabsize, size_t off, + char **name, char *err, size_t errsz ) +{ + size_t end; + + if( off >= tabsize ) + { + snprintf( err, errsz, "bad GNU archive long-name offset" ); + return( -1 ); + } + + end = off; + while( end < tabsize && tab[end] != '\n' && tab[end] != 0 ) + ++end; + if( end == tabsize ) + { + snprintf( err, errsz, "unterminated GNU archive long name" ); + return( -1 ); + } + + if( end > off && tab[end - 1] == '/' ) + --end; + *name = copy_string( (const char *)tab + off, end - off ); + if( !*name ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + return( 0 ); + +} /* End of long_name_at() */ + + +static int +read_member_name( const mcpu_ar_raw_header *h, + const unsigned char *payload, size_t payload_size, + const unsigned char *longtab, size_t longtab_size, + char **name, size_t *name_prefix, + int *special, char *err, size_t errsz ) +{ + char raw[17]; + size_t n; + + *name = NULL; + *name_prefix = 0; + *special = 0; + + if( raw_name( h, raw, sizeof( raw ) ) ) + { + snprintf( err, errsz, "bad archive member name" ); + return( -1 ); + } + + if( !strcmp( raw, "/" ) || !strcmp( raw, "/SYM64/" ) ) + { + *special = 1; + return( 0 ); + } + if( !strcmp( raw, "//" ) ) + { + *special = 2; + return( 0 ); + } + + if( !strncmp( raw, "#1/", 3 ) ) + { + if( parse_decimal_field( raw + 3, strlen( raw + 3 ), &n ) || + n == 0 || n > payload_size ) + { + snprintf( err, errsz, "bad BSD archive extended name" ); + return( -1 ); + } + *name = copy_string( (const char *)payload, n ); + if( !*name ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + *name_prefix = n; + } + else if( raw[0] == '/' && raw[1] >= '0' && raw[1] <= '9' ) + { + size_t off; + + if( !longtab || parse_decimal_field( raw + 1, strlen( raw + 1 ), &off ) || + long_name_at( longtab, longtab_size, off, name, err, errsz ) ) + return( -1 ); + } + else + { + n = strlen( raw ); + if( n && raw[n - 1] == '/' ) + --n; + *name = copy_string( raw, n ); + if( !*name ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + } + + if( !valid_member_name( *name ) ) + { + snprintf( err, errsz, "invalid archive member name: %s", *name ); + free( *name ); + *name = NULL; + return( -1 ); + } + return( 0 ); + +} /* End of read_member_name() */ + + +int +mcpu_archive_read( const char *path, mcpu_archive *archive, + char *err, size_t errsz ) +{ + unsigned char *file = NULL; + size_t file_size = 0; + size_t off; + const unsigned char *longtab = NULL; + size_t longtab_size = 0; + int rc = -1; + + if( !path || !archive ) + return( -1 ); + mcpu_archive_init( archive ); + + if( cpu_melf_readall( path, &file, &file_size, err, errsz ) ) + return( -1 ); + if( file_size < MCPU_AR_MAGIC_SIZE || + memcmp( file, MCPU_AR_MAGIC, MCPU_AR_MAGIC_SIZE ) ) + { + snprintf( err, errsz, "not a UNIX archive" ); + goto out; + } + + off = MCPU_AR_MAGIC_SIZE; + while( off < file_size ) + { + const mcpu_ar_raw_header *h; + size_t stored_size; + size_t next; + const unsigned char *payload; + char *name = NULL; + size_t name_prefix = 0; + int special = 0; + mcpu_archive_member member; + + memset( &member, 0, sizeof( member ) ); + + if( file_size - off < MCPU_AR_HEADER_SIZE ) + { + snprintf( err, errsz, "truncated archive member header" ); + goto out; + } + h = (const mcpu_ar_raw_header *)(file + off); + if( h->fmag[0] != '`' || h->fmag[1] != '\n' || + parse_decimal_field( h->size, sizeof( h->size ), &stored_size ) ) + { + snprintf( err, errsz, "bad archive member header" ); + goto out; + } + + if( stored_size > file_size - off - MCPU_AR_HEADER_SIZE ) + { + snprintf( err, errsz, "truncated archive member" ); + goto out; + } + payload = file + off + MCPU_AR_HEADER_SIZE; + + if( read_member_name( h, payload, stored_size, longtab, longtab_size, + &name, &name_prefix, &special, err, errsz ) ) + goto out; + + if( special == 1 ) + archive->had_symbol_index = 1; + else if( special == 2 ) + { + longtab = payload; + longtab_size = stored_size; + } + else + { + if( name_prefix > stored_size ) + { + free( name ); + snprintf( err, errsz, "bad archive extended member size" ); + goto out; + } + + member.name = name; + member.size = stored_size - name_prefix; + member.data = (unsigned char *)malloc( member.size ? member.size : 1 ); + if( !member.data ) + { + snprintf( err, errsz, "out of memory" ); + mcpu_archive_member_free( &member ); + goto out; + } + if( member.size ) + memcpy( member.data, payload + name_prefix, member.size ); + + if( member_collect_symbols_data( member.data, member.size, &member, + err, errsz ) || + mcpu_archive_append( archive, &member, err, errsz ) ) + { + mcpu_archive_member_free( &member ); + goto out; + } + } + + if( add_size( off, MCPU_AR_HEADER_SIZE, &next ) || + add_size( next, stored_size, &next ) ) + { + snprintf( err, errsz, "archive offset overflow" ); + goto out; + } + if( stored_size & 1 ) + { + if( next >= file_size || file[next] != '\n' ) + { + snprintf( err, errsz, "bad archive member padding" ); + goto out; + } + ++next; + } + off = next; + } + + rc = 0; + +out: + free( file ); + if( rc ) + mcpu_archive_free( archive ); + return( rc ); + +} /* End of mcpu_archive_read() */ + + +static int +short_member_name( const char *name ) +{ + const unsigned char *p = (const unsigned char *)name; + size_t n = strlen( name ); + + if( n == 0 || n > 15 ) + return( 0 ); + while( *p ) + { + if( *p == ' ' || *p == '/' || *p == '\t' ) + return( 0 ); + ++p; + } + return( 1 ); + +} /* End of short_member_name() */ + + +static int +member_stored_size( const mcpu_archive_member *member, size_t *size ) +{ + size_t n = short_member_name( member->name ) ? 0 : strlen( member->name ); + + return( add_size( n, member->size, size ) ); + +} /* End of member_stored_size() */ + + +static int +format_field( char *dst, size_t n, const char *text ) +{ + size_t len = strlen( text ); + + if( len > n ) + return( -1 ); + memcpy( dst, text, len ); + memset( dst + len, ' ', n - len ); + return( 0 ); + +} /* End of format_field() */ + + +static int +format_size_field( char *dst, size_t n, size_t value ) +{ + char text[32]; + + snprintf( text, sizeof( text ), "%zu", value ); + return( format_field( dst, n, text ) ); + +} /* End of format_size_field() */ + + +static int +make_header( mcpu_ar_raw_header *h, const char *name, size_t stored_size, + int special ) +{ + char namebuf[32]; + + memset( h, ' ', sizeof( *h ) ); + + if( special ) + { + if( format_field( h->name, sizeof( h->name ), name ) ) + return( -1 ); + } + else if( short_member_name( name ) ) + { + snprintf( namebuf, sizeof( namebuf ), "%s/", name ); + if( format_field( h->name, sizeof( h->name ), namebuf ) ) + return( -1 ); + } + else + { + snprintf( namebuf, sizeof( namebuf ), "#1/%zu", strlen( name ) ); + if( format_field( h->name, sizeof( h->name ), namebuf ) ) + return( -1 ); + } + + if( format_field( h->date, sizeof( h->date ), "0" ) || + format_field( h->uid, sizeof( h->uid ), "0" ) || + format_field( h->gid, sizeof( h->gid ), "0" ) || + format_field( h->mode, sizeof( h->mode ), special ? "0" : "644" ) || + format_size_field( h->size, sizeof( h->size ), stored_size ) ) + return( -1 ); + + h->fmag[0] = '`'; + h->fmag[1] = '\n'; + return( 0 ); + +} /* End of make_header() */ + + +static int +write_bytes( FILE *fp, const void *data, size_t size ) +{ + return( size && fwrite( data, 1, size, fp ) != size ? -1 : 0 ); + +} /* End of write_bytes() */ + + +static void +put_be32( unsigned char *p, uint32_t v ) +{ + p[0] = (unsigned char)(v >> 24); + p[1] = (unsigned char)(v >> 16); + p[2] = (unsigned char)(v >> 8); + p[3] = (unsigned char)v; + +} /* End of put_be32() */ + + +static int +symbol_index_size( const mcpu_archive *archive, size_t *count, + size_t *payload_size, char *err, size_t errsz ) +{ + size_t i, j; + size_t c = 0; + size_t names = 0; + size_t offsets; + size_t total; + + for( i = 0; i < archive->member_count; ++i ) + { + for( j = 0; j < archive->members[i].symbol_count; ++j ) + { + size_t n = strlen( archive->members[i].symbols[j] ) + 1; + + if( add_size( names, n, &names ) || c == SIZE_MAX ) + { + snprintf( err, errsz, "archive symbol index is too large" ); + return( -1 ); + } + ++c; + } + } + + if( c > UINT32_MAX || mul_size( c, 4, &offsets ) || + add_size( 4, offsets, &total ) || add_size( total, names, &total ) ) + { + snprintf( err, errsz, "archive symbol index is too large" ); + return( -1 ); + } + + *count = c; + *payload_size = total; + return( 0 ); + +} /* End of symbol_index_size() */ + + +static int +compute_member_offsets( const mcpu_archive *archive, int write_index, + size_t index_size, uint32_t **offsets, + char *err, size_t errsz ) +{ + uint32_t *v; + size_t off = MCPU_AR_MAGIC_SIZE; + size_t i; + + v = (uint32_t *)calloc( archive->member_count ? archive->member_count : 1, + sizeof( *v ) ); + if( !v ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + if( write_index ) + { + if( add_size( off, MCPU_AR_HEADER_SIZE, &off ) || + add_size( off, index_size, &off ) ) + { + snprintf( err, errsz, "archive is too large" ); + free( v ); + return( -1 ); + } + if( index_size & 1 ) + { + if( off == SIZE_MAX ) + { + snprintf( err, errsz, "archive is too large" ); + free( v ); + return( -1 ); + } + ++off; + } + } + + for( i = 0; i < archive->member_count; ++i ) + { + size_t stored; + + if( off > UINT32_MAX || member_stored_size( &archive->members[i], &stored ) ) + { + snprintf( err, errsz, "archive is too large for a 32-bit symbol index" ); + free( v ); + return( -1 ); + } + v[i] = (uint32_t)off; + + if( add_size( off, MCPU_AR_HEADER_SIZE, &off ) || + add_size( off, stored, &off ) ) + { + snprintf( err, errsz, "archive is too large" ); + free( v ); + return( -1 ); + } + if( stored & 1 ) + { + if( off == SIZE_MAX ) + { + snprintf( err, errsz, "archive is too large" ); + free( v ); + return( -1 ); + } + ++off; + } + } + + *offsets = v; + return( 0 ); + +} /* End of compute_member_offsets() */ + + +static int +write_armap( FILE *fp, const mcpu_archive *archive, + const uint32_t *member_offsets, size_t count, + size_t payload_size, char *err, size_t errsz ) +{ + mcpu_ar_raw_header h; + unsigned char *payload; + size_t pos; + size_t i, j, k = 0; + + if( make_header( &h, "/", payload_size, 1 ) ) + { + snprintf( err, errsz, "archive symbol index header is too large" ); + return( -1 ); + } + + payload = (unsigned char *)malloc( payload_size ? payload_size : 1 ); + if( !payload ) + { + snprintf( err, errsz, "out of memory" ); + return( -1 ); + } + + put_be32( payload, (uint32_t)count ); + pos = 4; + for( i = 0; i < archive->member_count; ++i ) + for( j = 0; j < archive->members[i].symbol_count; ++j ) + { + put_be32( payload + pos, member_offsets[i] ); + pos += 4; + ++k; + } + + for( i = 0; i < archive->member_count; ++i ) + for( j = 0; j < archive->members[i].symbol_count; ++j ) + { + size_t n = strlen( archive->members[i].symbols[j] ) + 1; + memcpy( payload + pos, archive->members[i].symbols[j], n ); + pos += n; + } + + if( k != count || pos != payload_size || + write_bytes( fp, &h, sizeof( h ) ) || + write_bytes( fp, payload, payload_size ) || + (payload_size & 1 && fputc( '\n', fp ) == EOF) ) + { + snprintf( err, errsz, "cannot write archive symbol index: %s", + strerror( errno ? errno : EIO ) ); + free( payload ); + return( -1 ); + } + + free( payload ); + return( 0 ); + +} /* End of write_symbol_index() */ + + +static int +write_member( FILE *fp, const mcpu_archive_member *member, + char *err, size_t errsz ) +{ + mcpu_ar_raw_header h; + size_t stored; + int shortname = short_member_name( member->name ); + + if( member_stored_size( member, &stored ) || + make_header( &h, member->name, stored, 0 ) ) + { + snprintf( err, errsz, "archive member is too large: %s", member->name ); + return( -1 ); + } + + if( write_bytes( fp, &h, sizeof( h ) ) || + (!shortname && write_bytes( fp, member->name, strlen( member->name ) )) || + write_bytes( fp, member->data, member->size ) || + (stored & 1 && fputc( '\n', fp ) == EOF) ) + { + snprintf( err, errsz, "cannot write archive member %s: %s", + member->name, strerror( errno ? errno : EIO ) ); + return( -1 ); + } + return( 0 ); + +} /* End of write_member() */ + + +int +mcpu_archive_write( const char *path, const mcpu_archive *archive, + int write_symbol_index, char *err, size_t errsz ) +{ + char *tmp; + size_t tmpsz; + int fd = -1; + FILE *fp = NULL; + size_t symbol_count = 0; + size_t index_size = 0; + uint32_t *member_offsets = NULL; + size_t i; + int rc = -1; + + if( !path || !archive ) + return( -1 ); + + if( write_symbol_index && + symbol_index_size( archive, &symbol_count, &index_size, err, errsz ) ) + return( -1 ); + if( write_symbol_index && + compute_member_offsets( archive, 1, index_size, + &member_offsets, err, errsz ) ) + return( -1 ); + + if( add_size( strlen( path ), sizeof( ".tmp.XXXXXX" ), &tmpsz ) ) + { + snprintf( err, errsz, "archive pathname is too long" ); + goto out; + } + tmp = (char *)malloc( tmpsz ); + if( !tmp ) + { + snprintf( err, errsz, "out of memory" ); + goto out; + } + snprintf( tmp, tmpsz, "%s.tmp.XXXXXX", path ); + + fd = mkstemp( tmp ); + if( fd < 0 ) + { + snprintf( err, errsz, "cannot create archive temporary file: %s", + strerror( errno ) ); + free( tmp ); + goto out; + } + fp = fdopen( fd, "wb" ); + if( !fp ) + { + snprintf( err, errsz, "cannot open archive temporary stream: %s", + strerror( errno ) ); + close( fd ); + fd = -1; + unlink( tmp ); + free( tmp ); + goto out; + } + fd = -1; + + if( write_bytes( fp, MCPU_AR_MAGIC, MCPU_AR_MAGIC_SIZE ) || + (write_symbol_index && + write_armap( fp, archive, member_offsets, symbol_count, + index_size, err, errsz )) ) + goto write_fail; + + for( i = 0; i < archive->member_count; ++i ) + if( write_member( fp, &archive->members[i], err, errsz ) ) + goto write_fail; + + if( fflush( fp ) || fsync( fileno( fp ) ) || fclose( fp ) ) + { + fp = NULL; + snprintf( err, errsz, "cannot finalize archive: %s", strerror( errno ) ); + unlink( tmp ); + free( tmp ); + goto out; + } + fp = NULL; + + if( rename( tmp, path ) ) + { + snprintf( err, errsz, "cannot install archive %s: %s", + path, strerror( errno ) ); + unlink( tmp ); + free( tmp ); + goto out; + } + free( tmp ); + rc = 0; + goto out; + +write_fail: + if( !err[0] ) + snprintf( err, errsz, "cannot write archive: %s", strerror( errno ) ); + fclose( fp ); + fp = NULL; + unlink( tmp ); + free( tmp ); + +out: + if( fp ) + fclose( fp ); + if( fd >= 0 ) + close( fd ); + free( member_offsets ); + return( rc ); + +} /* End of mcpu_archive_write() */ |
