]>
git.saurik.com Git - apt.git/blob - apt-inst/contrib/extracttar.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extracttar.cc,v 1.7 2003/02/10 00:36:12 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"
23 #include <apt-pkg/extracttar.h>
25 #include <apt-pkg/error.h>
26 #include <apt-pkg/strutl.h>
27 #include <apt-pkg/configuration.h>
38 // The on disk header for a tar file.
39 struct ExtractTar::TarHeader
57 // ExtractTar::ExtractTar - Constructor /*{{{*/
58 // ---------------------------------------------------------------------
60 ExtractTar::ExtractTar(FileFd
&Fd
,unsigned long Max
) : File(Fd
),
69 // ExtractTar::ExtractTar - Destructor /*{{{*/
70 // ---------------------------------------------------------------------
72 ExtractTar::~ExtractTar()
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. */
82 bool ExtractTar::Done(bool Force
)
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)
93 // Make sure we clean it up!
95 if (ExecWait(GZPid
,_config
->Find("dir::bin::gzip","/bin/gzip").c_str(),
106 // ExtractTar::StartGzip - Startup gzip /*{{{*/
107 // ---------------------------------------------------------------------
108 /* This creates a gzip sub process that has its input as the file itself.
109 If this tar file is embedded into something like an ar file then
110 gzip will efficiently ignore the extra bits. */
111 bool ExtractTar::StartGzip()
114 if (pipe(Pipes
) != 0)
115 return _error
->Errno("pipe",_("Failed to create pipes"));
117 // Fork off the process
120 // Spawn the subprocess
124 dup2(Pipes
[1],STDOUT_FILENO
);
125 dup2(File
.Fd(),STDIN_FILENO
);
126 int Fd
= open("/dev/null",O_RDWR
);
129 dup2(Fd
,STDERR_FILENO
);
131 SetCloseExec(STDOUT_FILENO
,false);
132 SetCloseExec(STDIN_FILENO
,false);
133 SetCloseExec(STDERR_FILENO
,false);
136 Args
[0] = _config
->Find("dir::bin::gzip","/bin/gzip").c_str();
139 execv(Args
[0],(char **)Args
);
140 cerr
<< _("Failed to exec gzip ") << Args
[0] << endl
;
150 // ExtractTar::Go - Perform extraction /*{{{*/
151 // ---------------------------------------------------------------------
152 /* This reads each 512 byte block from the archive and extracts the header
153 information into the Item structure. Then it resolves the UID/GID and
154 invokes the correct processing function. */
155 bool ExtractTar::Go(pkgDirStream
&Stream
)
157 if (StartGzip() == false)
160 // Loop over all blocks
165 bool BadRecord
= false;
166 unsigned char Block
[512];
167 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
170 if (InFd
.Eof() == true)
174 TarHeader
*Tar
= (TarHeader
*)Block
;
175 unsigned long CheckSum
;
176 if (StrToNum(Tar
->Checksum
,CheckSum
,sizeof(Tar
->Checksum
),8) == false)
177 return _error
->Error(_("Corrupted archive"));
179 /* Compute the checksum field. The actual checksum is blanked out
180 with spaces so it is not included in the computation */
181 unsigned long NewSum
= 0;
182 memset(Tar
->Checksum
,' ',sizeof(Tar
->Checksum
));
183 for (int I
= 0; I
!= sizeof(Block
); I
++)
186 /* Check for a block of nulls - in this case we kill gzip, GNU tar
188 if (NewSum
== ' '*sizeof(Tar
->Checksum
))
191 if (NewSum
!= CheckSum
)
192 return _error
->Error(_("Tar Checksum failed, archive corrupted"));
194 // Decode all of the fields
195 pkgDirStream::Item Itm
;
196 if (StrToNum(Tar
->Mode
,Itm
.Mode
,sizeof(Tar
->Mode
),8) == false ||
197 StrToNum(Tar
->UserID
,Itm
.UID
,sizeof(Tar
->UserID
),8) == false ||
198 StrToNum(Tar
->GroupID
,Itm
.GID
,sizeof(Tar
->GroupID
),8) == false ||
199 StrToNum(Tar
->Size
,Itm
.Size
,sizeof(Tar
->Size
),8) == false ||
200 StrToNum(Tar
->MTime
,Itm
.MTime
,sizeof(Tar
->MTime
),8) == false ||
201 StrToNum(Tar
->Major
,Itm
.Major
,sizeof(Tar
->Major
),8) == false ||
202 StrToNum(Tar
->Minor
,Itm
.Minor
,sizeof(Tar
->Minor
),8) == false)
203 return _error
->Error(_("Corrupted archive"));
206 if (LastLongName
.empty() == false)
207 Itm
.Name
= (char *)LastLongName
.c_str();
210 Tar
->Name
[sizeof(Tar
->Name
)] = 0;
211 Itm
.Name
= Tar
->Name
;
213 if (Itm
.Name
[0] == '.' && Itm
.Name
[1] == '/' && Itm
.Name
[2] != 0)
216 // Grab the link target
217 Tar
->Name
[sizeof(Tar
->LinkName
)] = 0;
218 Itm
.LinkTarget
= Tar
->LinkName
;
220 if (LastLongLink
.empty() == false)
221 Itm
.LinkTarget
= (char *)LastLongLink
.c_str();
223 // Convert the type over
224 switch (Tar
->LinkFlag
)
228 Itm
.Type
= pkgDirStream::Item::File
;
232 Itm
.Type
= pkgDirStream::Item::HardLink
;
236 Itm
.Type
= pkgDirStream::Item::SymbolicLink
;
239 case CharacterDevice
:
240 Itm
.Type
= pkgDirStream::Item::CharDevice
;
244 Itm
.Type
= pkgDirStream::Item::BlockDevice
;
248 Itm
.Type
= pkgDirStream::Item::Directory
;
252 Itm
.Type
= pkgDirStream::Item::FIFO
;
257 unsigned long Length
= Itm
.Size
;
258 unsigned char Block
[512];
261 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
263 if (Length
<= sizeof(Block
))
265 LastLongLink
.append(Block
,Block
+sizeof(Block
));
268 LastLongLink
.append(Block
,Block
+sizeof(Block
));
269 Length
-= sizeof(Block
);
276 unsigned long Length
= Itm
.Size
;
277 unsigned char Block
[512];
280 if (InFd
.Read(Block
,sizeof(Block
),true) == false)
282 if (Length
< sizeof(Block
))
284 LastLongName
.append(Block
,Block
+sizeof(Block
));
287 LastLongName
.append(Block
,Block
+sizeof(Block
));
288 Length
-= sizeof(Block
);
295 _error
->Warning(_("Unkown TAR header type %u, member %s"),(unsigned)Tar
->LinkFlag
,Tar
->Name
);
300 if (BadRecord
== false)
301 if (Stream
.DoItem(Itm
,Fd
) == false)
304 // Copy the file over the FD
305 unsigned long Size
= Itm
.Size
;
308 unsigned char Junk
[32*1024];
309 unsigned long Read
= MIN(Size
,sizeof(Junk
));
310 if (InFd
.Read(Junk
,((Read
+511)/512)*512) == false)
313 if (BadRecord
== false)
317 if (write(Fd
,Junk
,Read
) != (signed)Read
)
318 return Stream
.Fail(Itm
,Fd
);
322 /* An Fd of -2 means to send to a special processing
325 if (Stream
.Process(Itm
,Junk
,Read
,Itm
.Size
- Size
) == false)
326 return Stream
.Fail(Itm
,Fd
);
334 if (Itm
.Size
!= 0 && BadRecord
== false)
335 if (Stream
.FinishedFile(Itm
,Fd
) == false)
338 LastLongName
.erase();
339 LastLongLink
.erase();