1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extracttar.cc,v 1.8.2.1 2004/01/16 18:58:50 mdz Exp $
4 /* ######################################################################
6 Extract a Tar - Tar Extractor
8 Some performance measurements showed that zlib performed quite poorly
9 in comparison 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 /*{{{*/
21 #include <apt-pkg/dirstream.h>
22 #include <apt-pkg/extracttar.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/configuration.h>
26 #include <apt-pkg/fileutl.h>
41 // The on disk header for a tar file.
42 struct ExtractTar::TarHeader
60 // ExtractTar::ExtractTar - Constructor /*{{{*/
61 // ---------------------------------------------------------------------
63 ExtractTar::ExtractTar(FileFd
&Fd
,unsigned long long Max
,string DecompressionProgram
)
64 : File(Fd
), MaxInSize(Max
), DecompressProg(DecompressionProgram
)
70 // ExtractTar::ExtractTar - Destructor /*{{{*/
71 // ---------------------------------------------------------------------
73 ExtractTar::~ExtractTar()
79 // ExtractTar::Done - Reap the gzip sub process /*{{{*/
80 bool ExtractTar::Done(bool)
84 bool ExtractTar::Done()
89 // ExtractTar::StartGzip - Startup gzip /*{{{*/
90 // ---------------------------------------------------------------------
91 /* This creates a gzip sub process that has its input as the file itself.
92 If this tar file is embedded into something like an ar file then
93 gzip will efficiently ignore the extra bits. */
94 bool ExtractTar::StartGzip()
96 if (DecompressProg
.empty())
98 InFd
.OpenDescriptor(File
.Fd(), FileFd::ReadOnly
, FileFd::None
, false);
102 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
103 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
104 for (; compressor
!= compressors
.end(); ++compressor
) {
105 if (compressor
->Name
== DecompressProg
) {
106 return InFd
.OpenDescriptor(File
.Fd(), FileFd::ReadOnly
, *compressor
, false);
110 return _error
->Error(_("Cannot find a configured compressor for '%s'"),
111 DecompressProg
.c_str());
115 // ExtractTar::Go - Perform extraction /*{{{*/
116 // ---------------------------------------------------------------------
117 /* This reads each 512 byte block from the archive and extracts the header
118 information into the Item structure. Then it resolves the UID/GID and
119 invokes the correct processing function. */
120 bool ExtractTar::Go(pkgDirStream
&Stream
)
122 if (StartGzip() == false)
125 // Loop over all blocks
126 string LastLongLink
, ItemLink
;
127 string LastLongName
, ItemName
;
130 bool BadRecord
= false;
131 unsigned char Block
[512];
132 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
135 if (InFd
.Eof() == true)
139 TarHeader
*Tar
= (TarHeader
*)Block
;
140 unsigned long CheckSum
;
141 if (StrToNum(Tar
->Checksum
,CheckSum
,sizeof(Tar
->Checksum
),8) == false)
142 return _error
->Error(_("Corrupted archive"));
144 /* Compute the checksum field. The actual checksum is blanked out
145 with spaces so it is not included in the computation */
146 unsigned long NewSum
= 0;
147 memset(Tar
->Checksum
,' ',sizeof(Tar
->Checksum
));
148 for (int I
= 0; I
!= sizeof(Block
); I
++)
151 /* Check for a block of nulls - in this case we kill gzip, GNU tar
153 if (NewSum
== ' '*sizeof(Tar
->Checksum
))
156 if (NewSum
!= CheckSum
)
157 return _error
->Error(_("Tar checksum failed, archive corrupted"));
159 // Decode all of the fields
160 pkgDirStream::Item Itm
;
161 if (StrToNum(Tar
->Mode
,Itm
.Mode
,sizeof(Tar
->Mode
),8) == false ||
162 (Base256ToNum(Tar
->UserID
,Itm
.UID
,8) == false &&
163 StrToNum(Tar
->UserID
,Itm
.UID
,sizeof(Tar
->UserID
),8) == false) ||
164 (Base256ToNum(Tar
->GroupID
,Itm
.GID
,8) == false &&
165 StrToNum(Tar
->GroupID
,Itm
.GID
,sizeof(Tar
->GroupID
),8) == false) ||
166 (Base256ToNum(Tar
->Size
,Itm
.Size
,12) == false &&
167 StrToNum(Tar
->Size
,Itm
.Size
,sizeof(Tar
->Size
),8) == false) ||
168 (Base256ToNum(Tar
->MTime
,Itm
.MTime
,12) == false &&
169 StrToNum(Tar
->MTime
,Itm
.MTime
,sizeof(Tar
->MTime
),8) == false) ||
170 StrToNum(Tar
->Major
,Itm
.Major
,sizeof(Tar
->Major
),8) == false ||
171 StrToNum(Tar
->Minor
,Itm
.Minor
,sizeof(Tar
->Minor
),8) == false)
172 return _error
->Error(_("Corrupted archive"));
174 // Grab the filename and link target: use last long name if one was
175 // set, otherwise use the header value as-is, but remember that it may
176 // fill the entire 100-byte block and needs to be zero-terminated.
177 // See Debian Bug #689582.
178 if (LastLongName
.empty() == false)
179 Itm
.Name
= (char *)LastLongName
.c_str();
181 Itm
.Name
= (char *)ItemName
.assign(Tar
->Name
, sizeof(Tar
->Name
)).c_str();
182 if (Itm
.Name
[0] == '.' && Itm
.Name
[1] == '/' && Itm
.Name
[2] != 0)
185 if (LastLongLink
.empty() == false)
186 Itm
.LinkTarget
= (char *)LastLongLink
.c_str();
188 Itm
.LinkTarget
= (char *)ItemLink
.assign(Tar
->LinkName
, sizeof(Tar
->LinkName
)).c_str();
190 // Convert the type over
191 switch (Tar
->LinkFlag
)
195 Itm
.Type
= pkgDirStream::Item::File
;
199 Itm
.Type
= pkgDirStream::Item::HardLink
;
203 Itm
.Type
= pkgDirStream::Item::SymbolicLink
;
206 case CharacterDevice
:
207 Itm
.Type
= pkgDirStream::Item::CharDevice
;
211 Itm
.Type
= pkgDirStream::Item::BlockDevice
;
215 Itm
.Type
= pkgDirStream::Item::Directory
;
219 Itm
.Type
= pkgDirStream::Item::FIFO
;
224 unsigned long long Length
= Itm
.Size
;
225 unsigned char Block
[512];
228 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
230 if (Length
<= sizeof(Block
))
232 LastLongLink
.append(Block
,Block
+sizeof(Block
));
235 LastLongLink
.append(Block
,Block
+sizeof(Block
));
236 Length
-= sizeof(Block
);
243 unsigned long long Length
= Itm
.Size
;
244 unsigned char Block
[512];
247 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
249 if (Length
< sizeof(Block
))
251 LastLongName
.append(Block
,Block
+sizeof(Block
));
254 LastLongName
.append(Block
,Block
+sizeof(Block
));
255 Length
-= sizeof(Block
);
262 _error
->Warning(_("Unknown TAR header type %u, member %s"),(unsigned)Tar
->LinkFlag
,Tar
->Name
);
267 if (BadRecord
== false)
268 if (Stream
.DoItem(Itm
,Fd
) == false)
271 // Copy the file over the FD
272 unsigned long long Size
= Itm
.Size
;
275 unsigned char Junk
[32*1024];
276 unsigned long Read
= min(Size
, (unsigned long long)sizeof(Junk
));
277 if (InFd
.Read(Junk
,((Read
+511)/512)*512) == false)
280 if (BadRecord
== false)
284 if (write(Fd
,Junk
,Read
) != (signed)Read
)
285 return Stream
.Fail(Itm
,Fd
);
289 /* An Fd of -2 means to send to a special processing
292 if (Stream
.Process(Itm
,Junk
,Read
,Itm
.Size
- Size
) == false)
293 return Stream
.Fail(Itm
,Fd
);
301 if (BadRecord
== false)
302 if (Stream
.FinishedFile(Itm
,Fd
) == false)
305 LastLongName
.erase();
306 LastLongLink
.erase();