]> git.saurik.com Git - apt.git/blame - apt-inst/contrib/extracttar.cc
Corrected array handling
[apt.git] / apt-inst / contrib / extracttar.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: extracttar.cc,v 1.2 2001/02/20 07:03:17 jgg Exp $
4/* ######################################################################
5
6 Extract a Tar - Tar Extractor
7
8 Some performance measurements showed that zlib performed quite poorly
9 in comparision to a forked gzip process. This tar extractor makes use
10 of the fact that dup'd file descriptors have the same seek pointer
11 and that gzip will not read past the end of a compressed stream,
12 even if there is more data. We use the dup property to track extraction
13 progress and the gzip feature to just feed gzip a fd in the middle
14 of an AR file.
15
16 ##################################################################### */
17 /*}}}*/
18// Include Files /*{{{*/
19#ifdef __GNUG__
20#pragma implementation "apt-pkg/extracttar.h"
21#endif
22#include <apt-pkg/extracttar.h>
23
24#include <apt-pkg/error.h>
25#include <apt-pkg/strutl.h>
26#include <apt-pkg/configuration.h>
27#include <system.h>
28
29#include <stdlib.h>
30#include <unistd.h>
31#include <signal.h>
32#include <fcntl.h>
33 /*}}}*/
34
35// The on disk header for a tar file.
36struct ExtractTar::TarHeader
37{
38 char Name[100];
39 char Mode[8];
40 char UserID[8];
41 char GroupID[8];
42 char Size[12];
43 char MTime[12];
44 char Checksum[8];
45 char LinkFlag;
46 char LinkName[100];
47 char MagicNumber[8];
48 char UserName[32];
49 char GroupName[32];
50 char Major[8];
51 char Minor[8];
52};
53
54// ExtractTar::ExtractTar - Constructor /*{{{*/
55// ---------------------------------------------------------------------
56/* */
57ExtractTar::ExtractTar(FileFd &Fd,unsigned long Max) : File(Fd),
58 MaxInSize(Max)
59
60{
61 GZPid = -1;
62 InFd = -1;
63 Eof = false;
64}
65 /*}}}*/
66// ExtractTar::ExtractTar - Destructor /*{{{*/
67// ---------------------------------------------------------------------
68/* */
69ExtractTar::~ExtractTar()
70{
71 Done(false);
72}
73 /*}}}*/
74// ExtractTar::Done - Reap the gzip sub process /*{{{*/
75// ---------------------------------------------------------------------
76/* If the force flag is given then error messages are suppressed - this
77 means we hit the end of the tar file but there was still gzip data. */
78bool ExtractTar::Done(bool Force)
79{
80 InFd.Close();
81 if (GZPid <= 0)
82 return true;
83
84 /* If there is a pending error then we are cleaning up gzip and are
85 not interested in it's failures */
86 if (_error->PendingError() == true)
87 Force = true;
88
89 // Make sure we clean it up!
90 kill(GZPid,SIGINT);
91 if (ExecWait(GZPid,_config->Find("dir::bin::gzip","/bin/gzip").c_str(),
92 Force) == false)
93 {
94 GZPid = -1;
95 return Force;
96 }
97
98 GZPid = -1;
99 return true;
100}
101 /*}}}*/
102// ExtractTar::StartGzip - Startup gzip /*{{{*/
103// ---------------------------------------------------------------------
104/* This creates a gzip sub process that has its input as the file itself.
105 If this tar file is embedded into something like an ar file then
106 gzip will efficiently ignore the extra bits. */
107bool ExtractTar::StartGzip()
108{
109 int Pipes[2];
110 if (pipe(Pipes) != 0)
111 return _error->Errno("pipe","Failed to create pipes");
112
113 // Fork off the process
114 GZPid = ExecFork();
115
116 // Spawn the subprocess
117 if (GZPid == 0)
118 {
119 // Setup the FDs
120 dup2(Pipes[1],STDOUT_FILENO);
121 dup2(File.Fd(),STDIN_FILENO);
122 int Fd = open("/dev/null",O_RDWR);
123 if (Fd == -1)
124 _exit(101);
125 dup2(Fd,STDERR_FILENO);
126 close(Fd);
127 SetCloseExec(STDOUT_FILENO,false);
128 SetCloseExec(STDIN_FILENO,false);
129 SetCloseExec(STDERR_FILENO,false);
130
131 const char *Args[3];
132 Args[0] = _config->Find("dir::bin::gzip","/bin/gzip").c_str();
133 Args[1] = "-d";
134 Args[2] = 0;
135 execv(Args[0],(char **)Args);
136 cerr << "Failed to exec gzip " << Args[0] << endl;
137 _exit(100);
138 }
139
140 // Fix up our FDs
141 InFd.Fd(Pipes[0]);
142 close(Pipes[1]);
143 return true;
144}
145 /*}}}*/
146// ExtractTar::Go - Perform extraction /*{{{*/
147// ---------------------------------------------------------------------
148/* This reads each 512 byte block from the archive and extracts the header
149 information into the Item structure. Then it resolves the UID/GID and
150 invokes the correct processing function. */
151bool ExtractTar::Go(pkgDirStream &Stream)
152{
153 if (StartGzip() == false)
154 return false;
155
156 // Loop over all blocks
157 string LastLongLink;
158 string LastLongName;
159 while (1)
160 {
161 bool BadRecord = false;
162 unsigned char Block[512];
163 if (InFd.Read(Block,sizeof(Block),true) == false)
164 return false;
165
166 if (InFd.Eof() == true)
167 break;
168
169 // Get the checksum
170 TarHeader *Tar = (TarHeader *)Block;
171 unsigned long CheckSum;
172 if (StrToNum(Tar->Checksum,CheckSum,sizeof(Tar->Checksum),8) == false)
173 return _error->Error("Corrupted archive");
174
175 /* Compute the checksum field. The actual checksum is blanked out
176 with spaces so it is not included in the computation */
177 unsigned long NewSum = 0;
178 memset(Tar->Checksum,' ',sizeof(Tar->Checksum));
179 for (int I = 0; I != sizeof(Block); I++)
180 NewSum += Block[I];
181
182 /* Check for a block of nulls - in this case we kill gzip, GNU tar
183 does this.. */
184 if (NewSum == ' '*sizeof(Tar->Checksum))
185 return Done(true);
186
187 if (NewSum != CheckSum)
188 return _error->Error("Tar Checksum failed, archive corrupted");
189
190 // Decode all of the fields
191 pkgDirStream::Item Itm;
192 unsigned long UID;
193 unsigned long GID;
194 if (StrToNum(Tar->Mode,Itm.Mode,sizeof(Tar->Mode),8) == false ||
195 StrToNum(Tar->UserID,UID,sizeof(Tar->UserID),8) == false ||
196 StrToNum(Tar->GroupID,GID,sizeof(Tar->GroupID),8) == false ||
197 StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false ||
198 StrToNum(Tar->MTime,Itm.MTime,sizeof(Tar->MTime),8) == false ||
199 StrToNum(Tar->Major,Itm.Major,sizeof(Tar->Major),8) == false ||
200 StrToNum(Tar->Minor,Itm.Minor,sizeof(Tar->Minor),8) == false)
201 return _error->Error("Corrupted archive");
202
203 // Grab the filename
204 if (LastLongName.empty() == false)
205 Itm.Name = (char *)LastLongName.c_str();
206 else
207 {
208 Tar->Name[sizeof(Tar->Name)] = 0;
209 Itm.Name = Tar->Name;
210 }
211 if (Itm.Name[0] == '.' && Itm.Name[1] == '/' && Itm.Name[2] != 0)
212 Itm.Name += 2;
213
214 // Grab the link target
215 Tar->Name[sizeof(Tar->LinkName)] = 0;
216 Itm.LinkTarget = Tar->LinkName;
217
218 if (LastLongLink.empty() == false)
219 Itm.LinkTarget = (char *)LastLongLink.c_str();
220
221 // Convert the type over
222 switch (Tar->LinkFlag)
223 {
224 case NormalFile0:
225 case NormalFile:
226 Itm.Type = pkgDirStream::Item::File;
227 break;
228
229 case HardLink:
230 Itm.Type = pkgDirStream::Item::HardLink;
231 break;
232
233 case SymbolicLink:
234 Itm.Type = pkgDirStream::Item::SymbolicLink;
235 break;
236
237 case CharacterDevice:
238 Itm.Type = pkgDirStream::Item::CharDevice;
239 break;
240
241 case BlockDevice:
242 Itm.Type = pkgDirStream::Item::BlockDevice;
243 break;
244
245 case Directory:
246 Itm.Type = pkgDirStream::Item::Directory;
247 break;
248
249 case FIFO:
250 Itm.Type = pkgDirStream::Item::FIFO;
251 break;
252
253 case GNU_LongLink:
254 {
255 unsigned long Length = Itm.Size;
256 unsigned char Block[512];
257 while (Length > 0)
258 {
259 if (InFd.Read(Block,sizeof(Block),true) == false)
260 return false;
261 if (Length <= sizeof(Block))
262 {
263 LastLongLink.append(Block,Block+sizeof(Block));
264 break;
265 }
266 LastLongLink.append(Block,Block+sizeof(Block));
267 Length -= sizeof(Block);
268 }
269 continue;
270 }
271
272 case GNU_LongName:
273 {
274 unsigned long Length = Itm.Size;
275 unsigned char Block[512];
276 while (Length > 0)
277 {
278 if (InFd.Read(Block,sizeof(Block),true) == false)
279 return false;
280 if (Length < sizeof(Block))
281 {
282 LastLongName.append(Block,Block+sizeof(Block));
283 break;
284 }
285 LastLongName.append(Block,Block+sizeof(Block));
286 Length -= sizeof(Block);
287 }
288 continue;
289 }
290
291 default:
292 BadRecord = true;
293 _error->Warning("Unkown TAR header type %u, member %s",(unsigned)Tar->LinkFlag,Tar->Name);
294 break;
295 }
296
297 int Fd = -1;
298 if (BadRecord == false)
299 if (Stream.DoItem(Itm,Fd) == false)
300 return false;
301
302 // Copy the file over the FD
303 unsigned long Size = Itm.Size;
304 while (Size != 0)
305 {
306 unsigned char Junk[32*1024];
307 unsigned long Read = MIN(Size,sizeof(Junk));
308 if (InFd.Read(Junk,((Read+511)/512)*512) == false)
309 return false;
310
311 if (BadRecord == false)
312 {
313 if (Fd > 0)
314 {
315 if (write(Fd,Junk,Read) != (signed)Read)
316 return Stream.Fail(Itm,Fd);
317 }
318 else
319 {
320 /* An Fd of -2 means to send to a special processing
321 function */
322 if (Fd == -2)
323 if (Stream.Process(Itm,Junk,Read,Itm.Size - Size) == false)
324 return Stream.Fail(Itm,Fd);
325 }
326 }
327
328 Size -= Read;
329 }
330
331 // And finish up
332 if (Itm.Size != 0 && BadRecord == false)
333 if (Stream.FinishedFile(Itm,Fd) == false)
334 return false;
335
336 LastLongName.erase();
337 LastLongLink.erase();
338 }
339
340 return Done(false);
341}
342 /*}}}*/