]>
git.saurik.com Git - apt.git/blob - apt-inst/contrib/extracttar.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extracttar.cc,v 1.6 2002/11/11 06:55:50 doogie Exp $
4 /* ######################################################################
6 Extract a Tar - Tar Extractor
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
16 ##################################################################### */
18 // Include Files /*{{{*/
20 #pragma implementation "apt-pkg/extracttar.h"
22 #include <apt-pkg/extracttar.h>
24 #include <apt-pkg/error.h>
25 #include <apt-pkg/strutl.h>
26 #include <apt-pkg/configuration.h>
37 // The on disk header for a tar file.
38 struct ExtractTar::TarHeader
56 // ExtractTar::ExtractTar - Constructor /*{{{*/
57 // ---------------------------------------------------------------------
59 ExtractTar::ExtractTar(FileFd
&Fd
,unsigned long Max
) : File(Fd
),
68 // ExtractTar::ExtractTar - Destructor /*{{{*/
69 // ---------------------------------------------------------------------
71 ExtractTar::~ExtractTar()
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. */
81 bool ExtractTar::Done(bool Force
)
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)
92 // Make sure we clean it up!
94 if (ExecWait(GZPid
,_config
->Find("dir::bin::gzip","/bin/gzip").c_str(),
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. */
110 bool ExtractTar::StartGzip()
113 if (pipe(Pipes
) != 0)
114 return _error
->Errno("pipe","Failed to create pipes");
116 // Fork off the process
119 // Spawn the subprocess
123 dup2(Pipes
[1],STDOUT_FILENO
);
124 dup2(File
.Fd(),STDIN_FILENO
);
125 int Fd
= open("/dev/null",O_RDWR
);
128 dup2(Fd
,STDERR_FILENO
);
130 SetCloseExec(STDOUT_FILENO
,false);
131 SetCloseExec(STDIN_FILENO
,false);
132 SetCloseExec(STDERR_FILENO
,false);
135 Args
[0] = _config
->Find("dir::bin::gzip","/bin/gzip").c_str();
138 execv(Args
[0],(char **)Args
);
139 cerr
<< "Failed to exec gzip " << Args
[0] << endl
;
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. */
154 bool ExtractTar::Go(pkgDirStream
&Stream
)
156 if (StartGzip() == false)
159 // Loop over all blocks
164 bool BadRecord
= false;
165 unsigned char Block
[512];
166 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
169 if (InFd
.Eof() == true)
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");
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
++)
185 /* Check for a block of nulls - in this case we kill gzip, GNU tar
187 if (NewSum
== ' '*sizeof(Tar
->Checksum
))
190 if (NewSum
!= CheckSum
)
191 return _error
->Error("Tar Checksum failed, archive corrupted");
193 // Decode all of the fields
194 pkgDirStream::Item Itm
;
195 if (StrToNum(Tar
->Mode
,Itm
.Mode
,sizeof(Tar
->Mode
),8) == false ||
196 StrToNum(Tar
->UserID
,Itm
.UID
,sizeof(Tar
->UserID
),8) == false ||
197 StrToNum(Tar
->GroupID
,Itm
.GID
,sizeof(Tar
->GroupID
),8) == false ||
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");
205 if (LastLongName
.empty() == false)
206 Itm
.Name
= (char *)LastLongName
.c_str();
209 Tar
->Name
[sizeof(Tar
->Name
)] = 0;
210 Itm
.Name
= Tar
->Name
;
212 if (Itm
.Name
[0] == '.' && Itm
.Name
[1] == '/' && Itm
.Name
[2] != 0)
215 // Grab the link target
216 Tar
->Name
[sizeof(Tar
->LinkName
)] = 0;
217 Itm
.LinkTarget
= Tar
->LinkName
;
219 if (LastLongLink
.empty() == false)
220 Itm
.LinkTarget
= (char *)LastLongLink
.c_str();
222 // Convert the type over
223 switch (Tar
->LinkFlag
)
227 Itm
.Type
= pkgDirStream::Item::File
;
231 Itm
.Type
= pkgDirStream::Item::HardLink
;
235 Itm
.Type
= pkgDirStream::Item::SymbolicLink
;
238 case CharacterDevice
:
239 Itm
.Type
= pkgDirStream::Item::CharDevice
;
243 Itm
.Type
= pkgDirStream::Item::BlockDevice
;
247 Itm
.Type
= pkgDirStream::Item::Directory
;
251 Itm
.Type
= pkgDirStream::Item::FIFO
;
256 unsigned long Length
= Itm
.Size
;
257 unsigned char Block
[512];
260 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
262 if (Length
<= sizeof(Block
))
264 LastLongLink
.append(Block
,Block
+sizeof(Block
));
267 LastLongLink
.append(Block
,Block
+sizeof(Block
));
268 Length
-= sizeof(Block
);
275 unsigned long Length
= Itm
.Size
;
276 unsigned char Block
[512];
279 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
281 if (Length
< sizeof(Block
))
283 LastLongName
.append(Block
,Block
+sizeof(Block
));
286 LastLongName
.append(Block
,Block
+sizeof(Block
));
287 Length
-= sizeof(Block
);
294 _error
->Warning("Unkown TAR header type %u, member %s",(unsigned)Tar
->LinkFlag
,Tar
->Name
);
299 if (BadRecord
== false)
300 if (Stream
.DoItem(Itm
,Fd
) == false)
303 // Copy the file over the FD
304 unsigned long Size
= Itm
.Size
;
307 unsigned char Junk
[32*1024];
308 unsigned long Read
= MIN(Size
,sizeof(Junk
));
309 if (InFd
.Read(Junk
,((Read
+511)/512)*512) == false)
312 if (BadRecord
== false)
316 if (write(Fd
,Junk
,Read
) != (signed)Read
)
317 return Stream
.Fail(Itm
,Fd
);
321 /* An Fd of -2 means to send to a special processing
324 if (Stream
.Process(Itm
,Junk
,Read
,Itm
.Size
- Size
) == false)
325 return Stream
.Fail(Itm
,Fd
);
333 if (Itm
.Size
!= 0 && BadRecord
== false)
334 if (Stream
.FinishedFile(Itm
,Fd
) == false)
337 LastLongName
.erase();
338 LastLongLink
.erase();