Line data Source code
1 : #include "fd_ar.h" 2 : 3 : #if FD_HAS_HOSTED 4 : 5 : #include <stdio.h> 6 : #include <stdlib.h> 7 : #include <errno.h> 8 : 9 : /* An fd_ar_hdr_t is the raw header of a file entry in the ar file. It 10 : is not clear how strict ar is about ASCII encoding in these fields 11 : (is leading whitespace okay, is trailing whitespace okay, is garbage 12 : allowed in trailing field, are explicit + allowed, are signed 13 : quantities allowed, etc). We currently handle the encoding: 14 : 15 : field -> ^ [zero or more chars of whitespace] 16 : width | [optional '+' or '-'] 17 : chars | [one or more chars of radix digits] 18 : v [zero or more non-radix-digit chars (including '\0')] 19 : 20 : (That is, stuff strtol can convert and error trap.) 21 : 22 : Leading zeros are allowed as 'radix digits' and do not trigger radix 23 : detection; the spec dictates whether a string is interpreted as decimal or 24 : octal. 25 : 26 : We convert fields to a long. Though the fields here clearly can be 27 : stored more compactly, overflow / underflow is not possible into a 28 : long and it is also readily apparent that the ar format cannot handle 29 : routine things like very large files. It is conceivable in the 30 : future we might want to add support for variations on this file 31 : format and would like to avoid requiring user facing changes to use a 32 : different API. 33 : 34 : We also debatably allow negative values for all fields but filesz and 35 : kick the interpretation of that to the user (we might change this 36 : behavior in the future to give stricter guarantees). The filesz 37 : field val is required to be non-negative but we still store it in a 38 : long type for fseek friendliness (fseek with negative values for 39 : filesz could generate unexpected results, including infinite loop ar 40 : file iterations by using a filesz = -hdr_sz). */ 41 : 42 1140 : #define FD_AR_HDR_IDENT_SZ (FD_AR_META_IDENT_SZ-1UL) 43 : #define FD_AR_HDR_MTIME_SZ (12UL) 44 : #define FD_AR_HDR_UID_SZ ( 6UL) 45 : #define FD_AR_HDR_GID_SZ ( 6UL) 46 : #define FD_AR_HDR_MODE_SZ ( 8UL) 47 : #define FD_AR_HDR_FILESZ_SZ (10UL) 48 : #define FD_AR_HDR_MAGIC ((ushort)0x0a60) 49 : 50 : struct fd_ar_hdr { 51 : char ident [ FD_AR_HDR_IDENT_SZ ]; /* File identifier, WARNING: may not be '\0' terminated */ 52 : char mtime_dec [ FD_AR_HDR_MTIME_SZ ]; /* File modification timestamp (ASCII decimal), WARNING! may not be '\0' terminated */ 53 : char uid_dec [ FD_AR_HDR_UID_SZ ]; /* Owner ID (ASCII decimal), WARNING: may not be '\0' terminated */ 54 : char gid_dec [ FD_AR_HDR_GID_SZ ]; /* Group ID (ASCII decimal), WARNING: may not be '\0' terminated */ 55 : char mode_oct [ FD_AR_HDR_MODE_SZ ]; /* File mode (ASCII octal), WARNING: may not be '\0' terminated */ 56 : char filesz_dec[ FD_AR_HDR_FILESZ_SZ ]; /* File size (ASCII decimal), WARNING: may not be '\0' terminated */ 57 : ushort magic; /* ==FD_AR_HDR_MAGIC */ 58 : }; 59 : 60 : typedef struct fd_ar_hdr fd_ar_hdr_t; 61 : 62 : static int 63 : fd_ar_ascii_to_long( char const * field, 64 : ulong width, /* Should be less than 32 */ 65 : int base, /* Should be a supported strtol base */ 66 2850 : long * _val ) { 67 : 68 : /* Turn field into a proper cstr */ 69 : 70 2850 : if( FD_UNLIKELY( width>=32UL ) ) return EINVAL; 71 2850 : char cstr[ 32UL ]; 72 2850 : memcpy( cstr, field, width ); 73 2850 : cstr[ width ] = '\0'; 74 : 75 : /* Do the conversion. Note: strtol will set errno to EINVAL is an 76 : unsupported base (and maybe whitespace/empty string depending on 77 : implementation) and ERANGE if overflow / underflow. FIXME: This is 78 : probably simple enough to ween off stdlib (it'd probably be 79 : cleaner, faster and have less strtol weirdness to deal with). */ 80 : 81 2850 : char * endptr; 82 2850 : errno = 0; 83 2850 : long val = strtol( cstr, &endptr, base ); 84 2850 : if( FD_UNLIKELY( errno ) ) return errno; 85 2850 : if( FD_UNLIKELY( cstr==endptr ) ) return EINVAL; /* Consistent handling of whitespace / empty string */ 86 : 87 2850 : *_val = val; 88 2850 : return 0; 89 2850 : } 90 : 91 : int 92 18 : fd_ar_read_init( void * _stream ) { 93 18 : FILE * stream = (FILE *)_stream; 94 : 95 : /* Check input args */ 96 : 97 18 : if( FD_UNLIKELY( !stream ) ) return EINVAL; 98 : 99 : /* Check archive header magic */ 100 : 101 18 : char magic[ 8 ]; 102 18 : if( FD_UNLIKELY( fread( magic, 8UL, 1UL, stream )!=1UL ) ) return FD_LIKELY( feof( stream ) ) ? ENOENT : EIO; 103 18 : if( FD_UNLIKELY( memcmp( magic, "!<arch>\n", 8UL ) ) ) return EPROTO; /* Could do this single asm with ulong compare */ 104 : 105 : /* Everything ok */ 106 : 107 15 : return 0; 108 18 : } 109 : 110 : int 111 : fd_ar_read_next( void * _stream, 112 585 : fd_ar_meta_t * opt_meta ) { 113 585 : FILE * stream = (FILE *)_stream; 114 : 115 : /* Check input args */ 116 : 117 585 : if( FD_UNLIKELY( !stream ) ) return EINVAL; 118 : 119 : /* Read file header. Note: Headers are two-byte aligned */ 120 : 121 585 : long pos = ftell( stream ); 122 585 : if( FD_UNLIKELY( pos<0L ) ) return EIO; 123 585 : if( (pos&1L) && FD_UNLIKELY( fseek( stream, 1L, SEEK_CUR )<0L ) ) return FD_LIKELY( feof( stream ) ) ? ENOENT : EIO; 124 : 125 585 : fd_ar_hdr_t hdr[1]; 126 585 : if( FD_UNLIKELY( fread( hdr, sizeof(fd_ar_hdr_t), 1UL, stream )!=1UL ) ) return FD_LIKELY( feof( stream ) ) ? ENOENT : EIO; 127 573 : if( FD_UNLIKELY( hdr->magic!=FD_AR_HDR_MAGIC ) ) return EPROTO; 128 : 129 : /* Parse the file header */ 130 : 131 570 : fd_ar_meta_t meta[1]; 132 570 : if( FD_UNLIKELY( fd_ar_ascii_to_long( hdr->mtime_dec, FD_AR_HDR_MTIME_SZ, 10, &meta->mtime ) ) ) return EPROTO; 133 570 : if( FD_UNLIKELY( fd_ar_ascii_to_long( hdr->uid_dec, FD_AR_HDR_UID_SZ, 10, &meta->uid ) ) ) return EPROTO; 134 570 : if( FD_UNLIKELY( fd_ar_ascii_to_long( hdr->gid_dec, FD_AR_HDR_GID_SZ, 10, &meta->gid ) ) ) return EPROTO; 135 570 : if( FD_UNLIKELY( fd_ar_ascii_to_long( hdr->mode_oct, FD_AR_HDR_MODE_SZ, 8, &meta->mode ) ) ) return EPROTO; 136 570 : if( FD_UNLIKELY( fd_ar_ascii_to_long( hdr->filesz_dec, FD_AR_HDR_FILESZ_SZ, 10, &meta->filesz ) ) ) return EPROTO; 137 570 : if( FD_UNLIKELY( meta->filesz<0L ) ) return EPROTO; 138 570 : memcpy( meta->ident, hdr->ident, FD_AR_HDR_IDENT_SZ ); 139 570 : meta->ident[ FD_AR_HDR_IDENT_SZ ] = '\0'; 140 : 141 : /* Everything ok */ 142 : 143 570 : if( opt_meta ) *opt_meta = *meta; 144 570 : return 0; 145 570 : } 146 : 147 : #else /* Not supported on this target */ 148 : 149 : int fd_ar_read_init( void * stream ) { (void)stream; FD_COMPILER_MFENCE(); return 1; } 150 : int fd_ar_read_next( void * stream, fd_ar_meta_t * meta ) { (void)stream; (void)meta; FD_COMPILER_MFENCE(); return 1; } 151 : 152 : #endif