1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <inttypes.h>
21 #include <time.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25
26 #define TAR_TYPE_FILE '0'
27 #define TAR_TYPE_DIRECTORY '5'
28
29 enum TarError {
30 TAR_OK =
0,
31 TAR_PATH_TOO_LONG,
32 TAR_FILE_TOO_LARGE,
33 TAR_CONTENT_TOO_LARGE,
34 TAR_UNFINISHED_FILE,
35 TAR_CONTENT_BROKEN,
36 TAR_ERROR
37 };
38
39 typedef enum TarError TarError;
40
41 typedef struct TarHeader {
42 char name[
100];
43 char mode[
8];
44 char uid[
8];
45 char gid[
8];
46 char size[
12];
47 char mtime[
12];
48 char chksum[
8];
49 char typeflag;
50 char linkname[
100];
51 char magic[
6];
52 char version[
2];
53 char uname[
32];
54 char gname[
32];
55 char devmajor[
8];
56 char devminor[
8];
57 char prefix[
155];
58 char padding[
12];
59 } TarHeader;
60
61 typedef struct TarOutputStream {
62 FILE *file;
63 uint64_t cur_filesize;
64 uint64_t cur_written;
65 TarError error;
66 } TarOutputStream;
67
68 typedef struct TarEntry {
69 char *path;
70 uint64_t size;
71 int type;
72 } TarEntry;
73
74 typedef struct TarInputStream {
75 FILE *file;
76 TarEntry cur_entry;
77 uint64_t cur_read;
78 TarError error;
79 } TarInputStream;
80
81 TarOutputStream* tar_open(
FILE *f) {
82 TarOutputStream *tar = malloc(
sizeof(TarOutputStream));
83 tar->file= f;
84 tar->cur_filesize =
0;
85 tar->cur_written =
0;
86 tar->error =
0;
87 return tar;
88 }
89
90
91 static int add_header(TarOutputStream *tar,
char *path,
uint32_t mode,
uint64_t size,
time_t mtime,
int type) {
92
93 char *prefix = path;
94 size_t prefixlen =
0;
95 char *name = path;
96 size_t namelen =
0;
97
98 int pathsep =
0;
99 char *p = path;
100 char c;
101 size_t i =
0;
102 while((c = *p) !=
'\0') {
103 if(c ==
'/') {
104 pathsep =
1;
105 }
else {
106 if(pathsep) {
107 name = p;
108 prefixlen = i;
109 }
110 pathsep =
0;
111 }
112
113 i++;
114 p++;
115 }
116 namelen = i - prefixlen;
117
118
119 if(prefixlen >
154) {
120 tar->error =
TAR_PATH_TOO_LONG;
121 return -
1;
122 }
123 if(namelen >
99) {
124 tar->error =
TAR_PATH_TOO_LONG;
125 return -
1;
126 }
127
128
129 if(size >=
077777777777 ) {
130 tar->error =
TAR_FILE_TOO_LARGE;
131 return -
1;
132 }
133
134
135 TarHeader h;
136 memset(&h,
0,
sizeof(TarHeader));
137
138
139 memcpy(h.name, name, namelen);
140
141 snprintf(h.mode,
8,
"%07o", mode);
142 h.mode[
7] =
' ';
143
144 memset(h.uid,
'0',
16);
145 h.uid[
7] =
' ';
146 h.gid[
7] =
' ';
147
148 snprintf(h.size,
12,
"%011lo", size);
149 h.size[
11] =
' ';
150
151 uint64_t t = (
uint64_t)mtime;
152 snprintf(h.mtime,
12,
"%011lo", mtime);
153 h.mtime[
11] =
' ';
154
155 memset(h.chksum,
' ',
8);
156
157 h.typeflag = type;
158
159
160 snprintf(h.magic,
6,
"ustar");
161
162 h.version[
0] =
'0';
163 h.version[
1] =
'0';
164
165
166 char *devbuf = (
char*)h.devmajor;
167 snprintf(devbuf,
16,
"%015o",
0);
168 h.devmajor[
7] =
' ';
169 h.devminor[
7] =
' ';
170
171 memcpy(h.prefix, prefix, prefixlen);
172
173
174 uint8_t *header = (
uint8_t*)&h;
175 uint32_t chksum =
0;
176 for(
int i=
0;i<
512;i++) {
177 chksum += header[i];
178 }
179 snprintf(h.chksum,
8,
"%07o", chksum);
180
181 fwrite(&h,
1,
512, tar->file);
182
183 return 0;
184 }
185
186
187 int tar_add_dir(TarOutputStream *tar,
char *path,
uint32_t mode,
time_t mtime) {
188 return add_header(tar, path, mode,
0, mtime,
TAR_TYPE_DIRECTORY);
189 }
190
191 int tar_begin_file(
192 TarOutputStream *tar,
193 char *path,
194 uint32_t mode,
195 uint64_t size,
196 time_t mtime)
197 {
198 if(add_header(tar, path, mode, size, mtime,
TAR_TYPE_FILE)) {
199 return -
1;
200 }
201
202 tar->cur_filesize = size;
203 tar->cur_written =
0;
204
205 return 0;
206 }
207
208 size_t tar_fwrite(
const void *ptr,
size_t s,
size_t n, TarOutputStream *stream) {
209 size_t w = fwrite(ptr, s, n, stream->file);
210 stream->cur_written += w;
211 if(stream->cur_written > stream->cur_filesize) {
212 stream->error =
TAR_CONTENT_TOO_LARGE;
213 return 0;
214 }
215 return w;
216 }
217
218 int tar_end_file(TarOutputStream *tar) {
219
220
221 size_t pad =
512 - tar->cur_written %
512;
222 char buf[
512];
223 memset(buf,
0,
512);
224 fwrite(buf,
1, pad, tar->file);
225
226 tar->cur_filesize =
0;
227 tar->cur_written =
0;
228 return 0;
229 }
230
231 int tar_close(TarOutputStream *tar) {
232 char buf[
1024];
233 memset(buf,
0,
1024);
234 fwrite(buf,
1,
1024, tar->file);
235 return 0;
236 }
237
238
239 #define IO_BUF_SIZE 0x4000
240 int main(
int argc,
char **argv) {
241 if(argc <=
1) {
242 fprintf(stderr,
"Usage: %s <file> ...\n", argv[
0]);
243 }
244
245 TarOutputStream *tar = tar_open(stdout);
246
247 for(
int i=
1;i<argc;i++) {
248 char *file = argv[i];
249
250 struct stat s;
251 if(stat(file, &s)) {
252 perror(
"stat");
253 continue;
254 }
255
256
257 uint32_t tmode = s.st_mode &
07777;
258
259
260 if(
S_ISDIR(s.st_mode)) {
261 fprintf(stderr,
"add: %s\n", file);
262
263
264 if(tar_add_dir(tar, file, tmode, s.st_mtime)) {
265 fprintf(stderr,
"tar_add_dir failed: %d\n", tar->error);
266 }
267 }
else if(
S_ISREG(s.st_mode)) {
268 fprintf(stderr,
"add: %s\n", file);
269
270 FILE *f = fopen(file,
"r");
271 if(!f) {
272 perror(
"open");
273 continue;
274 }
275
276
277 if(tar_begin_file(tar, file, tmode, s.st_size, s.st_mtime)) {
278 fprintf(stderr,
"tar_begin_file failed: %d\n", tar->error);
279 }
else {
280
281
282 char buf[
IO_BUF_SIZE];
283 size_t r;
284 while((r = fread(buf,
1,
IO_BUF_SIZE, f)) >
0) {
285 if(tar_fwrite(buf,
1, r, tar) ==
0) {
286 fprintf(stderr,
"tar_fwrite failed\n");
287 break;
288 }
289 }
290
291
292 tar_end_file(tar);
293 }
294
295 fclose(f);
296 }
297 }
298
299
300 tar_close(tar);
301
302 return 0;
303 }
304
305