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 */
|