]>
git.saurik.com Git - apt.git/blob - apt-inst/contrib/extracttar.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extracttar.cc,v 1.4 2001/09/30 04:06:59 jgg 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>
36 // The on disk header for a tar file.
37 struct ExtractTar::TarHeader
55 // ExtractTar::ExtractTar - Constructor /*{{{*/
56 // ---------------------------------------------------------------------
58 ExtractTar::ExtractTar(FileFd
&Fd
,unsigned long Max
) : File(Fd
),
67 // ExtractTar::ExtractTar - Destructor /*{{{*/
68 // ---------------------------------------------------------------------
70 ExtractTar::~ExtractTar()
76 // ExtractTar::Done - Reap the gzip sub process /*{{{*/
77 // ---------------------------------------------------------------------
78 /* If the force flag is given then error messages are suppressed - this
79 means we hit the end of the tar file but there was still gzip data. */
80 bool ExtractTar::Done(bool Force
)
86 /* If there is a pending error then we are cleaning up gzip and are
87 not interested in it's failures */
88 if (_error
->PendingError() == true)
91 // Make sure we clean it up!
93 if (ExecWait(GZPid
,_config
->Find("dir::bin::gzip","/bin/gzip").c_str(),
104 // ExtractTar::StartGzip - Startup gzip /*{{{*/
105 // ---------------------------------------------------------------------
106 /* This creates a gzip sub process that has its input as the file itself.
107 If this tar file is embedded into something like an ar file then
108 gzip will efficiently ignore the extra bits. */
109 bool ExtractTar::StartGzip()
112 if (pipe(Pipes
) != 0)
113 return _error
->Errno("pipe","Failed to create pipes");
115 // Fork off the process
118 // Spawn the subprocess
122 dup2(Pipes
[1],STDOUT_FILENO
);
123 dup2(File
.Fd(),STDIN_FILENO
);
124 int Fd
= open("/dev/null",O_RDWR
);
127 dup2(Fd
,STDERR_FILENO
);
129 SetCloseExec(STDOUT_FILENO
,false);
130 SetCloseExec(STDIN_FILENO
,false);
131 SetCloseExec(STDERR_FILENO
,false);
134 Args
[0] = _config
->Find("dir::bin::gzip","/bin/gzip").c_str();
137 execv(Args
[0],(char **)Args
);
138 cerr
<< "Failed to exec gzip " << Args
[0] << endl
;
148 // ExtractTar::Go - Perform extraction /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This reads each 512 byte block from the archive and extracts the header
151 information into the Item structure. Then it resolves the UID/GID and
152 invokes the correct processing function. */
153 bool ExtractTar::Go(pkgDirStream
&Stream
)
155 if (StartGzip() == false)
158 // Loop over all blocks
163 bool BadRecord
= false;
164 unsigned char Block
[512];
165 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
168 if (InFd
.Eof() == true)
172 TarHeader
*Tar
= (TarHeader
*)Block
;
173 unsigned long CheckSum
;
174 if (StrToNum(Tar
->Checksum
,CheckSum
,sizeof(Tar
->Checksum
),8) == false)
175 return _error
->Error("Corrupted archive");
177 /* Compute the checksum field. The actual checksum is blanked out
178 with spaces so it is not included in the computation */
179 unsigned long NewSum
= 0;
180 memset(Tar
->Checksum
,' ',sizeof(Tar
->Checksum
));
181 for (int I
= 0; I
!= sizeof(Block
); I
++)
184 /* Check for a block of nulls - in this case we kill gzip, GNU tar
186 if (NewSum
== ' '*sizeof(Tar
->Checksum
))
189 if (NewSum
!= CheckSum
)
190 return _error
->Error("Tar Checksum failed, archive corrupted");
192 // Decode all of the fields
193 pkgDirStream::Item Itm
;
194 if (StrToNum(Tar
->Mode
,Itm
.Mode
,sizeof(Tar
->Mode
),8) == false ||
195 StrToNum(Tar
->UserID
,Itm
.UID
,sizeof(Tar
->UserID
),8) == false ||
196 StrToNum(Tar
->GroupID
,Itm
.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");
204 if (LastLongName
.empty() == false)
205 Itm
.Name
= (char *)LastLongName
.c_str();
208 Tar
->Name
[sizeof(Tar
->Name
)] = 0;
209 Itm
.Name
= Tar
->Name
;
211 if (Itm
.Name
[0] == '.' && Itm
.Name
[1] == '/' && Itm
.Name
[2] != 0)
214 // Grab the link target
215 Tar
->Name
[sizeof(Tar
->LinkName
)] = 0;
216 Itm
.LinkTarget
= Tar
->LinkName
;
218 if (LastLongLink
.empty() == false)
219 Itm
.LinkTarget
= (char *)LastLongLink
.c_str();
221 // Convert the type over
222 switch (Tar
->LinkFlag
)
226 Itm
.Type
= pkgDirStream::Item::File
;
230 Itm
.Type
= pkgDirStream::Item::HardLink
;
234 Itm
.Type
= pkgDirStream::Item::SymbolicLink
;
237 case CharacterDevice
:
238 Itm
.Type
= pkgDirStream::Item::CharDevice
;
242 Itm
.Type
= pkgDirStream::Item::BlockDevice
;
246 Itm
.Type
= pkgDirStream::Item::Directory
;
250 Itm
.Type
= pkgDirStream::Item::FIFO
;
255 unsigned long Length
= Itm
.Size
;
256 unsigned char Block
[512];
259 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
261 if (Length
<= sizeof(Block
))
263 LastLongLink
.append(Block
,Block
+sizeof(Block
));
266 LastLongLink
.append(Block
,Block
+sizeof(Block
));
267 Length
-= sizeof(Block
);
274 unsigned long Length
= Itm
.Size
;
275 unsigned char Block
[512];
278 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
280 if (Length
< sizeof(Block
))
282 LastLongName
.append(Block
,Block
+sizeof(Block
));
285 LastLongName
.append(Block
,Block
+sizeof(Block
));
286 Length
-= sizeof(Block
);
293 _error
->Warning("Unkown TAR header type %u, member %s",(unsigned)Tar
->LinkFlag
,Tar
->Name
);
298 if (BadRecord
== false)
299 if (Stream
.DoItem(Itm
,Fd
) == false)
302 // Copy the file over the FD
303 unsigned long Size
= Itm
.Size
;
306 unsigned char Junk
[32*1024];
307 unsigned long Read
= MIN(Size
,sizeof(Junk
));
308 if (InFd
.Read(Junk
,((Read
+511)/512)*512) == false)
311 if (BadRecord
== false)
315 if (write(Fd
,Junk
,Read
) != (signed)Read
)
316 return Stream
.Fail(Itm
,Fd
);
320 /* An Fd of -2 means to send to a special processing
323 if (Stream
.Process(Itm
,Junk
,Read
,Itm
.Size
- Size
) == false)
324 return Stream
.Fail(Itm
,Fd
);
332 if (Itm
.Size
!= 0 && BadRecord
== false)
333 if (Stream
.FinishedFile(Itm
,Fd
) == false)
336 LastLongName
.erase();
337 LastLongLink
.erase();