]>
git.saurik.com Git - apt.git/blob - apt-inst/contrib/extracttar.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extracttar.cc,v 1.2 2001/02/20 07:03:17 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>
35 // The on disk header for a tar file.
36 struct ExtractTar::TarHeader
54 // ExtractTar::ExtractTar - Constructor /*{{{*/
55 // ---------------------------------------------------------------------
57 ExtractTar::ExtractTar(FileFd
&Fd
,unsigned long Max
) : File(Fd
),
66 // ExtractTar::ExtractTar - Destructor /*{{{*/
67 // ---------------------------------------------------------------------
69 ExtractTar::~ExtractTar()
74 // ExtractTar::Done - Reap the gzip sub process /*{{{*/
75 // ---------------------------------------------------------------------
76 /* If the force flag is given then error messages are suppressed - this
77 means we hit the end of the tar file but there was still gzip data. */
78 bool ExtractTar::Done(bool Force
)
84 /* If there is a pending error then we are cleaning up gzip and are
85 not interested in it's failures */
86 if (_error
->PendingError() == true)
89 // Make sure we clean it up!
91 if (ExecWait(GZPid
,_config
->Find("dir::bin::gzip","/bin/gzip").c_str(),
102 // ExtractTar::StartGzip - Startup gzip /*{{{*/
103 // ---------------------------------------------------------------------
104 /* This creates a gzip sub process that has its input as the file itself.
105 If this tar file is embedded into something like an ar file then
106 gzip will efficiently ignore the extra bits. */
107 bool ExtractTar::StartGzip()
110 if (pipe(Pipes
) != 0)
111 return _error
->Errno("pipe","Failed to create pipes");
113 // Fork off the process
116 // Spawn the subprocess
120 dup2(Pipes
[1],STDOUT_FILENO
);
121 dup2(File
.Fd(),STDIN_FILENO
);
122 int Fd
= open("/dev/null",O_RDWR
);
125 dup2(Fd
,STDERR_FILENO
);
127 SetCloseExec(STDOUT_FILENO
,false);
128 SetCloseExec(STDIN_FILENO
,false);
129 SetCloseExec(STDERR_FILENO
,false);
132 Args
[0] = _config
->Find("dir::bin::gzip","/bin/gzip").c_str();
135 execv(Args
[0],(char **)Args
);
136 cerr
<< "Failed to exec gzip " << Args
[0] << endl
;
146 // ExtractTar::Go - Perform extraction /*{{{*/
147 // ---------------------------------------------------------------------
148 /* This reads each 512 byte block from the archive and extracts the header
149 information into the Item structure. Then it resolves the UID/GID and
150 invokes the correct processing function. */
151 bool ExtractTar::Go(pkgDirStream
&Stream
)
153 if (StartGzip() == false)
156 // Loop over all blocks
161 bool BadRecord
= false;
162 unsigned char Block
[512];
163 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
166 if (InFd
.Eof() == true)
170 TarHeader
*Tar
= (TarHeader
*)Block
;
171 unsigned long CheckSum
;
172 if (StrToNum(Tar
->Checksum
,CheckSum
,sizeof(Tar
->Checksum
),8) == false)
173 return _error
->Error("Corrupted archive");
175 /* Compute the checksum field. The actual checksum is blanked out
176 with spaces so it is not included in the computation */
177 unsigned long NewSum
= 0;
178 memset(Tar
->Checksum
,' ',sizeof(Tar
->Checksum
));
179 for (int I
= 0; I
!= sizeof(Block
); I
++)
182 /* Check for a block of nulls - in this case we kill gzip, GNU tar
184 if (NewSum
== ' '*sizeof(Tar
->Checksum
))
187 if (NewSum
!= CheckSum
)
188 return _error
->Error("Tar Checksum failed, archive corrupted");
190 // Decode all of the fields
191 pkgDirStream::Item Itm
;
194 if (StrToNum(Tar
->Mode
,Itm
.Mode
,sizeof(Tar
->Mode
),8) == false ||
195 StrToNum(Tar
->UserID
,UID
,sizeof(Tar
->UserID
),8) == false ||
196 StrToNum(Tar
->GroupID
,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();