1 |
/* |
2 |
* This file is in the public domain. Use it as you see fit. |
3 |
*/ |
4 |
|
5 |
/* |
6 |
* "untar" is an extremely simple tar extractor: |
7 |
* * A single C source file, so it should be easy to compile |
8 |
* and run on any system with a C compiler. |
9 |
* * Extremely portable standard C. The only non-ANSI function |
10 |
* used is mkdir(). |
11 |
* * Reads basic ustar tar archives. |
12 |
* * Does not require libarchive or any other special library. |
13 |
* |
14 |
* To compile: cc -o untar untar.c |
15 |
* |
16 |
* Usage: untar <archive> |
17 |
* |
18 |
* In particular, this program should be sufficient to extract the |
19 |
* distribution for libarchive, allowing people to bootstrap |
20 |
* libarchive on systems that do not already have a tar program. |
21 |
* |
22 |
* To unpack libarchive-x.y.z.tar.gz: |
23 |
* * gunzip libarchive-x.y.z.tar.gz |
24 |
* * untar libarchive-x.y.z.tar |
25 |
* |
26 |
* Written by Tim Kientzle, March 2009. |
27 |
* |
28 |
* Released into the public domain. |
29 |
*/ |
30 |
|
31 |
/* These are all highly standard and portable headers. */ |
32 |
#include <stdio.h> |
33 |
#include <stdlib.h> |
34 |
#include <string.h> |
35 |
|
36 |
/* This is for mkdir(); this may need to be changed for some platforms. */ |
37 |
#include <sys/stat.h> /* For mkdir() */ |
38 |
|
39 |
/* Parse an octal number, ignoring leading and trailing nonsense. */ |
40 |
static int |
41 |
parseoct(const char *p, size_t n) |
42 |
{ |
43 |
int i = 0; |
44 |
|
45 |
while ((*p < '0' || *p > '7') && n > 0) { |
46 |
++p; |
47 |
--n; |
48 |
} |
49 |
while (*p >= '0' && *p <= '7' && n > 0) { |
50 |
i *= 8; |
51 |
i += *p - '0'; |
52 |
++p; |
53 |
--n; |
54 |
} |
55 |
return (i); |
56 |
} |
57 |
|
58 |
/* Returns true if this is 512 zero bytes. */ |
59 |
static int |
60 |
is_end_of_archive(const char *p) |
61 |
{ |
62 |
int n; |
63 |
for (n = 511; n >= 0; --n) |
64 |
if (p[n] != '\0') |
65 |
return (0); |
66 |
return (1); |
67 |
} |
68 |
|
69 |
/* Create a directory, including parent directories as necessary. */ |
70 |
static void |
71 |
create_dir(char *pathname, int mode) |
72 |
{ |
73 |
char *p; |
74 |
int r; |
75 |
|
76 |
/* Strip trailing '/' */ |
77 |
if (pathname[strlen(pathname) - 1] == '/') |
78 |
pathname[strlen(pathname) - 1] = '\0'; |
79 |
|
80 |
/* Try creating the directory. */ |
81 |
r = mkdir(pathname, mode); |
82 |
|
83 |
if (r != 0) { |
84 |
/* On failure, try creating parent directory. */ |
85 |
p = strrchr(pathname, '/'); |
86 |
if (p != NULL) { |
87 |
*p = '\0'; |
88 |
create_dir(pathname, 0755); |
89 |
*p = '/'; |
90 |
r = mkdir(pathname, mode); |
91 |
} |
92 |
} |
93 |
if (r != 0) |
94 |
fprintf(stderr, "Could not create directory %s\n", pathname); |
95 |
} |
96 |
|
97 |
/* Create a file, including parent directory as necessary. */ |
98 |
static FILE * |
99 |
create_file(char *pathname, int mode) |
100 |
{ |
101 |
FILE *f; |
102 |
f = fopen(pathname, "wb+"); |
103 |
if (f == NULL) { |
104 |
/* Try creating parent dir and then creating file. */ |
105 |
char *p = strrchr(pathname, '/'); |
106 |
if (p != NULL) { |
107 |
*p = '\0'; |
108 |
create_dir(pathname, 0755); |
109 |
*p = '/'; |
110 |
f = fopen(pathname, "wb+"); |
111 |
} |
112 |
} |
113 |
return (f); |
114 |
} |
115 |
|
116 |
/* Verify the tar checksum. */ |
117 |
static int |
118 |
verify_checksum(const char *p) |
119 |
{ |
120 |
int n, u = 0; |
121 |
for (n = 0; n < 512; ++n) { |
122 |
if (n < 148 || n > 155) |
123 |
/* Standard tar checksum adds unsigned bytes. */ |
124 |
u += ((unsigned char *)p)[n]; |
125 |
else |
126 |
u += 0x20; |
127 |
|
128 |
} |
129 |
return (u == parseoct(p + 148, 8)); |
130 |
} |
131 |
|
132 |
/* Extract a tar archive. */ |
133 |
static void |
134 |
untar(FILE *a, const char *path) |
135 |
{ |
136 |
char buff[512]; |
137 |
FILE *f = NULL; |
138 |
size_t bytes_read; |
139 |
int filesize; |
140 |
|
141 |
printf("Extracting from %s\n", path); |
142 |
for (;;) { |
143 |
bytes_read = fread(buff, 1, 512, a); |
144 |
if (bytes_read < 512) { |
145 |
fprintf(stderr, |
146 |
"Short read on %s: expected 512, got %d\n", |
147 |
path, (int)bytes_read); |
148 |
return; |
149 |
} |
150 |
if (is_end_of_archive(buff)) { |
151 |
printf("End of %s\n", path); |
152 |
return; |
153 |
} |
154 |
if (!verify_checksum(buff)) { |
155 |
fprintf(stderr, "Checksum failure\n"); |
156 |
return; |
157 |
} |
158 |
filesize = parseoct(buff + 124, 12); |
159 |
switch (buff[156]) { |
160 |
case '1': |
161 |
printf(" Ignoring hardlink %s\n", buff); |
162 |
break; |
163 |
case '2': |
164 |
printf(" Ignoring symlink %s\n", buff); |
165 |
break; |
166 |
case '3': |
167 |
printf(" Ignoring character device %s\n", buff); |
168 |
break; |
169 |
case '4': |
170 |
printf(" Ignoring block device %s\n", buff); |
171 |
break; |
172 |
case '5': |
173 |
printf(" Extracting dir %s\n", buff); |
174 |
create_dir(buff, parseoct(buff + 100, 8)); |
175 |
filesize = 0; |
176 |
break; |
177 |
case '6': |
178 |
printf(" Ignoring FIFO %s\n", buff); |
179 |
break; |
180 |
default: |
181 |
printf(" Extracting file %s\n", buff); |
182 |
f = create_file(buff, parseoct(buff + 100, 8)); |
183 |
break; |
184 |
} |
185 |
while (filesize > 0) { |
186 |
bytes_read = fread(buff, 1, 512, a); |
187 |
if (bytes_read < 512) { |
188 |
fprintf(stderr, |
189 |
"Short read on %s: Expected 512, got %d\n", |
190 |
path, (int)bytes_read); |
191 |
return; |
192 |
} |
193 |
if (filesize < 512) |
194 |
bytes_read = filesize; |
195 |
if (f != NULL) { |
196 |
if (fwrite(buff, 1, bytes_read, f) |
197 |
!= bytes_read) |
198 |
{ |
199 |
fprintf(stderr, "Failed write\n"); |
200 |
fclose(f); |
201 |
f = NULL; |
202 |
} |
203 |
} |
204 |
filesize -= bytes_read; |
205 |
} |
206 |
if (f != NULL) { |
207 |
fclose(f); |
208 |
f = NULL; |
209 |
} |
210 |
} |
211 |
} |
212 |
|
213 |
int |
214 |
main(int argc, char **argv) |
215 |
{ |
216 |
FILE *a; |
217 |
|
218 |
++argv; /* Skip program name */ |
219 |
for ( ;*argv != NULL; ++argv) { |
220 |
a = fopen(*argv, "rb"); |
221 |
if (a == NULL) |
222 |
fprintf(stderr, "Unable to open %s\n", *argv); |
223 |
else { |
224 |
untar(a, *argv); |
225 |
fclose(a); |
226 |
} |
227 |
} |
228 |
return (0); |
229 |
} |