Line data Source code
1 : #include "fd_tar.h"
2 : #include "../fd_util.h"
3 :
4 : #include <errno.h>
5 :
6 : fd_tar_reader_t *
7 : fd_tar_reader_new( void * mem,
8 : fd_tar_read_vtable_t const * cb_vt,
9 0 : void * cb_arg ) {
10 :
11 0 : if( FD_UNLIKELY( !mem ) ) {
12 0 : FD_LOG_WARNING(( "NULL mem" ));
13 0 : return NULL;
14 0 : }
15 0 : if( FD_UNLIKELY( !cb_vt || !cb_vt->file || !cb_vt->read ) ) {
16 0 : FD_LOG_WARNING(( "NULL callback" ));
17 0 : return NULL;
18 0 : }
19 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_tar_reader_align() ) ) ) {
20 0 : FD_LOG_WARNING(( "unaligned mem" ));
21 0 : return NULL;
22 0 : }
23 :
24 0 : fd_tar_reader_t * self = (fd_tar_reader_t *)mem;
25 0 : fd_memset( self, 0, sizeof(fd_tar_reader_t) );
26 0 : self->cb_vt = *cb_vt;
27 0 : self->cb_arg = cb_arg;
28 0 : return self;
29 0 : }
30 :
31 : void *
32 0 : fd_tar_reader_delete( fd_tar_reader_t * reader ) {
33 0 : if( FD_UNLIKELY( !reader ) ) return NULL;
34 0 : fd_memset( reader, 0, sizeof(fd_tar_reader_t) );
35 0 : return reader;
36 0 : }
37 :
38 : static int
39 0 : fd_tar_process_hdr( fd_tar_reader_t * reader ) {
40 :
41 0 : fd_tar_meta_t const * hdr = (fd_tar_meta_t const *)reader->buf;
42 :
43 : /* "ustar\x00" and "ustar \x00" (overlaps with version) are both
44 : valid values for magic. These are POSIX ustar and OLDGNU versions
45 : respectively. */
46 0 : if( FD_UNLIKELY( 0!=memcmp( hdr->magic, FD_TAR_MAGIC, 5UL ) ) ) {
47 :
48 : /* Detect EOF. A TAR EOF is marked by 1024 bytes of zeros.
49 : We abort after 512 bytes. */
50 0 : int not_zero=0;
51 0 : for( ulong i=0UL; i<sizeof(fd_tar_meta_t); i++ )
52 0 : not_zero |= reader->buf[ i ];
53 0 : if( !not_zero ) return -1;
54 :
55 : /* Not an EOF, so must be a protocol error */
56 0 : FD_LOG_WARNING(( "Invalid tar header magic at %#lx", reader->pos ));
57 0 : FD_LOG_HEXDUMP_WARNING(( "Tar header", hdr, sizeof(fd_tar_meta_t) ));
58 0 : return EPROTO;
59 0 : }
60 :
61 0 : ulong file_sz = fd_tar_meta_get_size( &reader->header );
62 0 : if( FD_UNLIKELY( file_sz==ULONG_MAX ) ) {
63 0 : FD_LOG_WARNING(( "Failed to parse file size in tar header" ));
64 0 : return EPROTO;
65 0 : }
66 0 : reader->file_sz = file_sz;
67 0 : reader->buf_ctr = (ushort)0U;
68 :
69 : /* Ensure name is terminated */
70 0 : reader->header.name[ FD_TAR_NAME_SZ-1 ] = '\0';
71 :
72 : /* Call back to recipient */
73 0 : int err = reader->cb_vt.file( reader->cb_arg, &reader->header, file_sz );
74 0 : return fd_int_if( err, EIO, 0 );
75 0 : }
76 :
77 : static int
78 : fd_tar_read_hdr( fd_tar_reader_t * reader,
79 : uchar const ** pcur,
80 0 : uchar const * end ) {
81 :
82 0 : uchar const * cur = *pcur;
83 :
84 : /* Skip padding */
85 0 : if( reader->buf_ctr==0UL ) {
86 0 : ulong pad_sz = fd_ulong_align_up( reader->pos, 512UL ) - reader->pos;
87 0 : pad_sz = fd_ulong_min( pad_sz, (ulong)( end-cur ) );
88 0 : cur += pad_sz;
89 0 : }
90 :
91 : /* Determine number of bytes to read */
92 0 : long chunk_sz = (long)sizeof(fd_tar_meta_t) - (long)reader->buf_ctr;
93 0 : FD_TEST( chunk_sz>=0L );
94 0 : if( end-cur < chunk_sz ) chunk_sz = end-cur;
95 :
96 : /* Copy to header */
97 0 : fd_memcpy( reader->buf + reader->buf_ctr, cur, (ulong)chunk_sz );
98 0 : cur += chunk_sz;
99 0 : reader->buf_ctr += (ulong)chunk_sz;
100 :
101 : /* Handle complete header */
102 0 : int ret = 0;
103 0 : if( reader->buf_ctr == sizeof(fd_tar_meta_t) )
104 0 : ret = fd_tar_process_hdr( reader );
105 :
106 0 : *pcur = cur;
107 0 : return ret;
108 0 : }
109 :
110 : static int
111 : fd_tar_read_data( fd_tar_reader_t * reader,
112 : uchar const ** pcur,
113 0 : uchar const * end ) {
114 :
115 0 : uchar const * cur = *pcur;
116 0 : FD_TEST( cur<=end );
117 :
118 : /* Determine number of bytes to read */
119 0 : ulong chunk_sz = reader->file_sz;
120 0 : ulong avail_sz = (ulong)( end-cur );
121 0 : if( avail_sz < chunk_sz ) chunk_sz = avail_sz;
122 :
123 : /* Call back to recipient */
124 0 : int err = reader->cb_vt.read( reader->cb_arg, cur, chunk_sz );
125 :
126 : /* Consume bytes */
127 0 : cur += chunk_sz;
128 0 : reader->file_sz -= chunk_sz;
129 :
130 0 : *pcur = cur;
131 0 : return fd_int_if( err, EIO, 0 );
132 0 : }
133 :
134 : int
135 : fd_tar_read( void * const reader_,
136 : uchar const * const data,
137 0 : ulong const data_sz ) {
138 :
139 0 : fd_tar_reader_t * reader = reader_;
140 0 : ulong const pos = reader->pos;
141 :
142 0 : uchar const * cur = data;
143 0 : uchar const * end = cur+data_sz;
144 :
145 0 : while( cur!=end ) {
146 0 : if( reader->file_sz ) {
147 0 : int err = fd_tar_read_data( reader, &cur, end );
148 0 : if( FD_UNLIKELY( !!err ) ) return err;
149 0 : reader->pos = pos + (ulong)( cur-data );
150 0 : }
151 0 : if( !reader->file_sz ) {
152 0 : int err = fd_tar_read_hdr( reader, &cur, end );
153 0 : if( FD_UNLIKELY( !!err ) ) return err;
154 0 : reader->pos = pos + (ulong)( cur-data );
155 0 : }
156 0 : }
157 :
158 0 : return 0;
159 0 : }
160 :
161 : FD_FN_PURE ulong
162 3 : fd_tar_meta_get_size( fd_tar_meta_t const * meta ) {
163 3 : char const * buf = meta->size;
164 3 : if( ((uchar)buf[0]) & 0x80U ) {
165 : /* OLDGNU tar files may use a binary size encoding */
166 3 : return fd_ulong_bswap( FD_LOAD( ulong, buf+4 ) );
167 3 : }
168 :
169 0 : ulong ret = 0UL;
170 0 : for( char const * p=buf; p<buf+12; p++ ) {
171 0 : if( *p == '\0' ) break;
172 0 : ret = (ret << 3) + (ulong)(*p - '0');
173 0 : }
174 :
175 0 : return ret;
176 3 : }
177 :
178 : int
179 : fd_tar_set_octal( char buf[ static 12 ],
180 0 : ulong val ) {
181 0 : buf[ 11 ] = '\0';
182 0 : for( int i=10; i>=0; i-- ) {
183 0 : buf[ i ] = (char)( '0' + (char)( val&7UL ) );
184 0 : val>>=3;
185 0 : }
186 0 : return val==0UL;
187 0 : }
|