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 0 : #define FD_TAR_BLOCK_SZ (512UL)
23 :
24 : struct __attribute__((packed)) fd_tar_meta {
25 0 : # define FD_TAR_NAME_SZ 100
26 : # define FD_TAR_SIZE_SZ 12
27 : /* 0x000 */ char name [ FD_TAR_NAME_SZ ];
28 : /* 0x064 */ char mode [ 8 ];
29 : /* 0x06c */ char uid [ 8 ];
30 : /* 0x074 */ char gid [ 8 ];
31 : /* 0x07c */ char size [ 12 ];
32 : /* 0x088 */ char mtime [ 12 ];
33 : /* 0x094 */ char chksum [ 8 ];
34 : /* 0x09c */ char typeflag;
35 : /* 0x09d */ char linkname[ 100 ];
36 : # define FD_TAR_MAGIC_SZ 5
37 : /* 0x101 */ char magic [ FD_TAR_MAGIC_SZ+1 ];
38 : /* 0x107 */ char version [ 2 ];
39 : /* 0x109 */ char uname [ 32 ];
40 : /* 0x129 */ char gname [ 32 ];
41 : /* 0x149 */ char devmajor[ 8 ];
42 : /* 0x151 */ char devminor[ 8 ];
43 : /* 0x159 */ char prefix [ 155 ];
44 : /* 0x1f4 */ char padding [ 12 ];
45 : };
46 :
47 : typedef struct fd_tar_meta fd_tar_meta_t;
48 :
49 : /* FD_TAR_MAGIC is the only value of fd_tar_meta::magic supported by
50 : fd_tar. */
51 :
52 0 : #define FD_TAR_MAGIC "ustar"
53 :
54 : /* Known file types */
55 :
56 0 : #define FD_TAR_TYPE_NULL ('\0') /* implies FD_TAR_TYPE_REGULAR */
57 0 : #define FD_TAR_TYPE_REGULAR ('0')
58 : #define FD_TAR_TYPE_HARD_LINK ('1')
59 : #define FD_TAR_TYPE_SYM_LINK ('2')
60 : #define FD_TAR_TYPE_CHAR_DEV ('3')
61 : #define FD_TAR_TYPE_BLOCK_DEV ('4')
62 : #define FD_TAR_TYPE_DIR ('5')
63 : #define FD_TAR_TYPE_FIFO ('6')
64 :
65 : FD_PROTOTYPES_BEGIN
66 :
67 : /* fd_tar_meta_is_reg returns 1 if the file type is 'regular', and 0
68 : otherwise. */
69 :
70 : FD_FN_PURE static inline int
71 0 : fd_tar_meta_is_reg( fd_tar_meta_t const * meta ) {
72 0 : return ( meta->typeflag == FD_TAR_TYPE_NULL )
73 0 : | ( meta->typeflag == FD_TAR_TYPE_REGULAR );
74 0 : }
75 :
76 : /* fd_tar_meta_get_size parses the size field of the TAR header.
77 : Returns ULONG_MAX if parsing failed. */
78 :
79 : FD_FN_PURE FD_FN_UNUSED static ulong
80 0 : fd_tar_meta_get_size( fd_tar_meta_t const * meta ) {
81 0 : char const * buf = meta->size;
82 0 : if( ((uchar)buf[0]) & 0x80U ) {
83 : /* OLDGNU tar files may use a binary size encoding */
84 0 : return fd_ulong_bswap( FD_LOAD( ulong, buf+4 ) );
85 0 : }
86 :
87 0 : ulong ret = 0UL;
88 0 : for( char const * p=buf; p<buf+12; p++ ) {
89 0 : if( *p == '\0' ) break;
90 0 : ret = (ret << 3) + (ulong)(*p - '0');
91 0 : }
92 :
93 0 : return ret;
94 0 : }
95 :
96 : /* fd_tar_set_octal is a helper function to write octal fields per TAR
97 : standard. Each field of width buf_sz contains buf_sz-1 zero-filled
98 : octal digits and a null terminator. Returns 1 on success, 0 if val
99 : is too large to be represented in the field. */
100 : static inline int
101 : fd_tar_set_octal( char * buf,
102 : ulong buf_sz,
103 0 : ulong val ) {
104 : /* Need at least 1 byte for null terminator */
105 0 : if( FD_UNLIKELY( buf_sz < 1 ) ) return 0;
106 :
107 : /* Check if val fits in buf_sz-1 octal digits */
108 0 : if( FD_UNLIKELY( val >> (3UL*(buf_sz-1UL)) ) ) return 0;
109 :
110 0 : memset( buf, '0', buf_sz-1UL );
111 0 : buf[ buf_sz-1UL ] = '\0';
112 :
113 0 : for( ulong i=buf_sz-1UL; i>0UL && val>0UL; i-- ) {
114 0 : buf[ i-1UL ] = '0' + (val&7UL); /* Extract low 3 bits as octal digit */
115 0 : val >>= 3; /* Divide by 8 */
116 0 : }
117 :
118 0 : return 1;
119 0 : }
120 :
121 : /* fd_tar_meta_set_size sets the size field. Returns 1 on success, 0
122 : if sz is too large to be represented in TAR header. */
123 :
124 : static inline int
125 : fd_tar_meta_set_size( fd_tar_meta_t * meta,
126 0 : ulong sz ) {
127 0 : return fd_tar_set_octal( meta->size, sizeof(meta->size), sz );
128 0 : }
129 :
130 : /* fd_tar_meta_set_mtime sets the modification time field. Returns 1
131 : on success, 0 if mtime cannot be represented in TAR header. */
132 :
133 : static inline int
134 : fd_tar_meta_set_mtime( fd_tar_meta_t * meta,
135 0 : ulong mtime ) {
136 0 : return fd_tar_set_octal( meta->mtime, sizeof(meta->mtime), mtime );
137 0 : }
138 :
139 : static inline int
140 : fd_tar_meta_init_file_default( fd_tar_meta_t * meta,
141 : char const * filename,
142 : ulong filesize,
143 0 : long now ) {
144 0 : int valid = 1;
145 0 : memset( meta, 0, sizeof(fd_tar_meta_t) );
146 0 : valid &= fd_cstr_printf_check( meta->name, sizeof(meta->name), NULL, "%s", filename );
147 0 : valid &= fd_cstr_printf_check( meta->mode, sizeof(meta->mode), NULL, "0000644" );
148 0 : valid &= fd_cstr_printf_check( meta->uid, sizeof(meta->uid), NULL, "0000000" );
149 0 : valid &= fd_cstr_printf_check( meta->gid, sizeof(meta->gid), NULL, "0000000" );
150 0 : valid &= fd_tar_meta_set_size( meta, filesize );
151 0 : valid &= fd_tar_meta_set_mtime( meta, (ulong)(now/1000000000L));
152 0 : valid &= fd_cstr_printf_check( meta->magic, sizeof(meta->magic), NULL, FD_TAR_MAGIC );
153 0 : valid &= fd_cstr_printf_check( meta->uname, sizeof(meta->uname), NULL, "root" );
154 0 : valid &= fd_cstr_printf_check( meta->gname, sizeof(meta->gname), NULL, "root" );
155 0 : valid &= fd_cstr_printf_check( meta->devmajor, sizeof(meta->devmajor), NULL, "0000000" );
156 0 : valid &= fd_cstr_printf_check( meta->devminor, sizeof(meta->devminor), NULL, "0000000" );
157 0 : meta->typeflag = FD_TAR_TYPE_REGULAR;
158 0 : meta->version[ 0 ] = '0'; meta->version[ 1 ] = '0';
159 : /* meta->linkname empty */
160 : /* meta->prefix empty. TODO: add support */
161 :
162 0 : ulong checksum = 0;
163 :
164 0 : for( ulong i=0UL; i<FD_TAR_BLOCK_SZ; i++ ) {
165 : /* Special handling for the checksum field itself
166 : 148UL==offsetof(meta->chksum)
167 : 156UL==offsetof(meta->chksum)+sizeof(meta->chksum)
168 : */
169 0 : checksum += (i>=148UL && i<156UL) ? 32UL : ((uchar *)meta)[ i ];
170 0 : }
171 :
172 0 : valid &= fd_tar_set_octal( meta->chksum, sizeof(meta->chksum), checksum );
173 :
174 0 : return valid;
175 0 : }
176 :
177 : FD_PROTOTYPES_END
178 :
179 : #endif /* HEADER_fd_src_util_archive_fd_tar_h */
|