LCOV - code coverage report
Current view: top level - util/archive - fd_tar.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 35 71 49.3 %
Date: 2026-08-28 06:53:58 Functions: 4 175 2.3 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_archive_fd_tar_h
       2             : #define HEADER_fd_src_util_archive_fd_tar_h
       3             : 
       4             : /* fd_tar implements the ustar and old-GNU versions of the TAR file
       5             :    format. This is not a general-purpose TAR implementation.  It is
       6             :    currently only intended for loading and writing Solana snapshots. */
       7             : 
       8             : #include "../bits/fd_bits.h"
       9             : #include "../cstr/fd_cstr.h"
      10             : 
      11             : /* File Format ********************************************************/
      12             : 
      13             : /* The high level format of a tar archive/ball is a set of 512 byte blocks.
      14             :    Each file will be described a tar header (fd_tar_meta_t) and will be
      15             :    followed by the raw bytes of the file. The last block that is used for
      16             :    the file will be padded to fit into a tar block. When the archive is
      17             :    completed, it will be trailed by two EOF blocks which are populated with
      18             :    zero bytes. */
      19             : 
      20             : /* fd_tar_meta_t is the ustar/OLDGNU version of the TAR header. */
      21             : 
      22        4617 : #define FD_TAR_BLOCK_SZ (512UL)
      23             : 
      24             : union __attribute__((packed)) fd_tar_meta {
      25             :   struct __attribute__((packed)) {
      26           0 : # define FD_TAR_NAME_SZ 100
      27             : # define FD_TAR_SIZE_SZ 12
      28             :   /* 0x000 */ char name    [ FD_TAR_NAME_SZ ];
      29             :   /* 0x064 */ char mode    [   8 ];
      30             :   /* 0x06c */ char uid     [   8 ];
      31             :   /* 0x074 */ char gid     [   8 ];
      32             :   /* 0x07c */ char size    [  12 ];
      33             :   /* 0x088 */ char mtime   [  12 ];
      34             :   /* 0x094 */ char chksum  [   8 ];
      35             :   /* 0x09c */ char typeflag;
      36             :   /* 0x09d */ char linkname[ 100 ];
      37             : # define FD_TAR_MAGIC_SZ 5
      38             :   /* 0x101 */ char magic   [ FD_TAR_MAGIC_SZ+1 ];
      39             :   /* 0x107 */ char version [   2 ];
      40             :   /* 0x109 */ char uname   [  32 ];
      41             :   /* 0x129 */ char gname   [  32 ];
      42             :   /* 0x149 */ char devmajor[   8 ];
      43             :   /* 0x151 */ char devminor[   8 ];
      44             :   /* 0x159 */ char prefix  [ 155 ];
      45             :   /* 0x1f4 */ char padding [  12 ];
      46             :   };
      47             :   uchar raw[  FD_TAR_BLOCK_SZ ];
      48             : };
      49             : 
      50             : typedef union fd_tar_meta fd_tar_meta_t;
      51             : 
      52             : /* FD_TAR_MAGIC is the only value of fd_tar_meta::magic supported by
      53             :    fd_tar. */
      54             : 
      55           0 : #define FD_TAR_MAGIC "ustar"
      56             : 
      57             : /* Known file types */
      58             : 
      59           0 : #define FD_TAR_TYPE_NULL      ('\0')  /* implies FD_TAR_TYPE_REGULAR */
      60           0 : #define FD_TAR_TYPE_REGULAR   ('0')
      61             : #define FD_TAR_TYPE_HARD_LINK ('1')
      62           0 : #define FD_TAR_TYPE_SYM_LINK  ('2')
      63             : #define FD_TAR_TYPE_CHAR_DEV  ('3')
      64             : #define FD_TAR_TYPE_BLOCK_DEV ('4')
      65           0 : #define FD_TAR_TYPE_DIR       ('5')
      66             : #define FD_TAR_TYPE_FIFO      ('6')
      67             : 
      68             : FD_PROTOTYPES_BEGIN
      69             : 
      70             : /* fd_tar_meta_is_reg returns 1 if the file type is 'regular', and 0
      71             :    otherwise. */
      72             : 
      73             : FD_FN_PURE static inline int
      74           0 : fd_tar_meta_is_reg( fd_tar_meta_t const * meta ) {
      75           0 :   return ( meta->typeflag == FD_TAR_TYPE_NULL    )
      76           0 :        | ( meta->typeflag == FD_TAR_TYPE_REGULAR );
      77           0 : }
      78             : 
      79             : /* fd_tar_meta_get_size parses the size field of the TAR header.
      80             :    Returns ULONG_MAX if parsing failed. */
      81             : 
      82             : FD_FN_PURE FD_FN_UNUSED static ulong
      83          33 : fd_tar_meta_get_size( fd_tar_meta_t const * meta ) {
      84          33 :   char const * buf = meta->size;
      85          33 :   if( ((uchar)buf[0]) & 0x80U ) {
      86             :     /* OLDGNU tar files may use a binary size encoding */
      87           3 :     return fd_ulong_bswap( FD_LOAD( ulong, buf+4 ) );
      88           3 :   }
      89             : 
      90          30 :   char const * p = buf;
      91             : 
      92             :   /* Skip leading spaces (valid per POSIX TAR) */
      93          72 :   while( p<buf+12 && *p==' ' ) p++;
      94             : 
      95          30 :   ulong ret = 0UL;
      96         162 :   for( ; p<buf+12; p++ ) {
      97         162 :     if( *p=='\0' || *p==' ' ) break;
      98         141 :     if( FD_UNLIKELY( *p<'0' || *p>'7' ) ) return ULONG_MAX;
      99         132 :     ret = (ret << 3) + (ulong)(*p - '0');
     100         132 :   }
     101             : 
     102          21 :   return ret;
     103          30 : }
     104             : 
     105             : /* fd_tar_set_octal is a helper function to write octal fields per TAR
     106             :    standard.  Each field of width buf_sz contains buf_sz-1 zero-filled
     107             :    octal digits and a null terminator.  Returns 1 on success, 0 if val
     108             :    is too large to be represented in the field. */
     109             : static inline int
     110             : fd_tar_set_octal( char * buf,
     111             :                   ulong  buf_sz,
     112          12 :                   ulong  val ) {
     113             :   /* Need at least 1 byte for null terminator */
     114          12 :   if( FD_UNLIKELY( buf_sz < 1 ) ) return 0;
     115             : 
     116             :   /* Check if val fits in buf_sz-1 octal digits */
     117          12 :   if( FD_UNLIKELY( val >> (3UL*(buf_sz-1UL)) ) ) return 0;
     118             : 
     119          12 :   memset( buf, '0', buf_sz-1UL );
     120          12 :   buf[ buf_sz-1UL ] = '\0';
     121             : 
     122          75 :   for( ulong i=buf_sz-1UL; i>0UL && val>0UL; i-- ) {
     123          63 :     buf[ i-1UL ] = '0' + (val&7UL);  /* Extract low 3 bits as octal digit */
     124          63 :     val >>= 3;                       /* Divide by 8 */
     125          63 :   }
     126             : 
     127          12 :   return 1;
     128          12 : }
     129             : 
     130             : /* fd_tar_meta_set_size sets the size field.  Returns 1 on success, 0
     131             :    if sz is too large to be represented in TAR header. */
     132             : 
     133             : static inline int
     134             : fd_tar_meta_set_size( fd_tar_meta_t * meta,
     135           3 :                       ulong           sz ) {
     136           3 :   return fd_tar_set_octal( meta->size, sizeof(meta->size), sz );
     137           3 : }
     138             : 
     139             : /* fd_tar_meta_set_mtime sets the modification time field.  Returns 1
     140             :    on success, 0 if mtime cannot be represented in TAR header. */
     141             : 
     142             : static inline int
     143             : fd_tar_meta_set_mtime( fd_tar_meta_t * meta,
     144           0 :                        ulong           mtime ) {
     145           0 :   return fd_tar_set_octal( meta->mtime, sizeof(meta->mtime), mtime );
     146           0 : }
     147             : 
     148             : /* fd_tar_meta_set_chksum derives the TAR header checksum.  Assumes
     149             :    that the chksum field itself is filled with spaces. */
     150             : 
     151             : static inline void
     152           9 : fd_tar_meta_set_chksum( fd_tar_meta_t * meta ) {
     153           9 :   ulong check = 0UL;
     154        4617 :   for( ulong i=0UL; i<FD_TAR_BLOCK_SZ; i++ ) check += meta->raw[ i ];
     155           9 :   fd_tar_set_octal( meta->chksum, sizeof(meta->chksum), check );
     156           9 : }
     157             : 
     158             : static inline int
     159             : fd_tar_meta_init_file_default( fd_tar_meta_t * meta,
     160             :                                char const *    filename,
     161             :                                ulong           filesize,
     162           0 :                                long            now ) {
     163           0 :   int valid = 1;
     164           0 :   memset( meta, 0, sizeof(fd_tar_meta_t) );
     165           0 :   valid &= fd_cstr_printf_check( meta->name, sizeof(meta->name), NULL, "%s", filename );
     166           0 :   valid &= fd_cstr_printf_check( meta->mode, sizeof(meta->mode), NULL, "0000644" );
     167           0 :   valid &= fd_cstr_printf_check( meta->uid,  sizeof(meta->uid),  NULL, "0000000" );
     168           0 :   valid &= fd_cstr_printf_check( meta->gid,  sizeof(meta->gid),  NULL, "0000000" );
     169           0 :   valid &= fd_tar_meta_set_size( meta, filesize );
     170           0 :   valid &= fd_tar_meta_set_mtime( meta, (ulong)(now/1000000000L));
     171           0 :   valid &= fd_cstr_printf_check( meta->magic, sizeof(meta->magic), NULL, FD_TAR_MAGIC );
     172           0 :   valid &= fd_cstr_printf_check( meta->uname, sizeof(meta->uname), NULL, "root" );
     173           0 :   valid &= fd_cstr_printf_check( meta->gname, sizeof(meta->gname), NULL, "root" );
     174           0 :   valid &= fd_cstr_printf_check( meta->devmajor, sizeof(meta->devmajor), NULL, "0000000" );
     175           0 :   valid &= fd_cstr_printf_check( meta->devminor, sizeof(meta->devminor), NULL, "0000000" );
     176           0 :   meta->typeflag = FD_TAR_TYPE_REGULAR;
     177           0 :   meta->version[ 0 ] = '0'; meta->version[ 1 ] = '0';
     178             :   /* meta->linkname empty */
     179             :   /* meta->prefix empty. TODO: add support */
     180             : 
     181           0 :   ulong checksum = 0;
     182             : 
     183           0 :   for( ulong i=0UL; i<FD_TAR_BLOCK_SZ; i++ ) {
     184             :     /* Special handling for the checksum field itself
     185             :         148UL==offsetof(meta->chksum)
     186             :         156UL==offsetof(meta->chksum)+sizeof(meta->chksum)
     187             :     */
     188           0 :     checksum += (i>=148UL && i<156UL) ? 32UL : meta->raw[ i ];
     189           0 :   }
     190             : 
     191           0 :   valid &= fd_tar_set_octal( meta->chksum, sizeof(meta->chksum), checksum );
     192             : 
     193           0 :   return valid;
     194           0 : }
     195             : 
     196             : FD_PROTOTYPES_END
     197             : 
     198             : #endif /* HEADER_fd_src_util_archive_fd_tar_h */

Generated by: LCOV version 1.14