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