LCOV - code coverage report
Current view: top level - util/archive - fd_ar.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 43 43 100.0 %
Date: 2024-11-13 11:58:15 Functions: 3 3 100.0 %

          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

Generated by: LCOV version 1.14