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