]>
git.saurik.com Git - apt.git/blob - apt-inst/extract.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extract.cc,v 1.5 2002/11/11 06:55:50 doogie Exp $
4 /* ######################################################################
6 Archive Extraction Directory Stream
8 Extraction for each file is a bit of an involved process. Each object
9 undergoes an atomic backup, overwrite, erase sequence. First the
10 object is unpacked to '.dpkg.new' then the original is hardlinked to
11 '.dpkg.tmp' and finally the new object is renamed to overwrite the old
12 one. From an external perspective the file never ceased to exist.
13 After the archive has been sucessfully unpacked the .dpkg.tmp files
14 are erased. A failure causes all the .dpkg.tmp files to be restored.
16 Decisions about unpacking go like this:
17 - Store the original filename in the file listing
18 - Resolve any diversions that would effect this file, all checks
19 below apply to the diverted name, not the real one.
20 - Resolve any symlinked configuration files.
21 - If the existing file does not exist then .dpkg-tmp is checked for.
22 [Note, this is reduced to only check if a file was expected to be
24 - If the existing link/file is not a directory then it is replaced
26 - If the existing link/directory is being replaced by a directory then
27 absolutely nothing happens.
28 - If the existing link/directory is being replaced by a link then
29 absolutely nothing happens.
30 - If the existing link/directory is being replaced by a non-directory
31 then this will abort if the package is not the sole owner of the
32 directory. [Note, this is changed to not happen if the directory
33 non-empty - that is, it only includes files that are part of this
34 package - prevents removing user files accidentally.]
35 - If the non-directory exists in the listing database and it
36 does not belong to the current package then an overwrite condition
39 As we unpack we record the file list differences in the FL cache. If
40 we need to unroll the the FL cache knows which files have been unpacked
41 and can undo. When we need to erase then it knows which files have not
44 ##################################################################### */
46 // Include Files /*{{{*/
48 #pragma implementation "apt-pkg/extract.h"
50 #include <apt-pkg/extract.h>
51 #include <apt-pkg/error.h>
52 #include <apt-pkg/debversion.h>
63 static const char *TempExt
= "dpkg-tmp";
64 //static const char *NewExt = "dpkg-new";
66 // Extract::pkgExtract - Constructor /*{{{*/
67 // ---------------------------------------------------------------------
69 pkgExtract::pkgExtract(pkgFLCache
&FLCache
,pkgCache::VerIterator Ver
) :
70 FLCache(FLCache
), Ver(Ver
)
72 FLPkg
= FLCache
.GetPkg(Ver
.ParentPkg().Name(),true);
73 if (FLPkg
.end() == true)
78 // Extract::DoItem - Handle a single item from the stream /*{{{*/
79 // ---------------------------------------------------------------------
80 /* This performs the setup for the extraction.. */
81 bool pkgExtract::DoItem(Item
&Itm
,int &Fd
)
83 char Temp
[sizeof(FileName
)];
85 /* Strip any leading/trailing /s from the filename, then copy it to the
86 temp buffer and re-apply the leading / We use a class variable
87 to store the new filename for use by the three extraction funcs */
88 char *End
= FileName
+1;
89 const char *I
= Itm
.Name
;
90 for (; *I
!= 0 && *I
== '/'; I
++);
92 for (; *I
!= 0 && End
< FileName
+ sizeof(FileName
); I
++, End
++)
94 if (End
+ 20 >= FileName
+ sizeof(FileName
))
95 return _error
->Error("The path %s is too long",Itm
.Name
);
96 for (; End
> FileName
&& End
[-1] == '/'; End
--);
100 /* Lookup the file. Nde is the file [group] we are going to write to and
101 RealNde is the actual node we are manipulating. Due to diversions
102 they may be entirely different. */
103 pkgFLCache::NodeIterator Nde
= FLCache
.GetNode(Itm
.Name
,End
,0,false,false);
104 pkgFLCache::NodeIterator RealNde
= Nde
;
106 // See if the file is already in the file listing
107 unsigned long FileGroup
= RealNde
->File
;
108 for (; RealNde
.end() == false && FileGroup
== RealNde
->File
; RealNde
++)
109 if (RealNde
.RealPackage() == FLPkg
)
112 // Nope, create an entry
113 if (RealNde
.end() == true)
115 RealNde
= FLCache
.GetNode(Itm
.Name
,End
,FLPkg
.Offset(),true,false);
116 if (RealNde
.end() == true)
118 RealNde
->Flags
|= pkgFLCache::Node::NewFile
;
121 /* Check if this entry already was unpacked. The only time this should
122 ever happen is if someone has hacked tar to support capabilities, in
123 which case this needs to be modified anyhow.. */
124 if ((RealNde
->Flags
& pkgFLCache::Node::Unpacked
) ==
125 pkgFLCache::Node::Unpacked
)
126 return _error
->Error("Unpacking %s more than once",Itm
.Name
);
128 if (Nde
.end() == true)
131 /* Consider a diverted file - We are not permitted to divert directories,
132 but everything else is fair game (including conf files!) */
133 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
135 if (Itm
.Type
== Item::Directory
)
136 return _error
->Error("The directory %s is diverted",Itm
.Name
);
138 /* A package overwriting a diversion target is just the same as
139 overwriting a normally owned file and is checked for below in
140 the overwrites mechanism */
142 /* If this package is trying to overwrite the target of a diversion,
143 that is never, ever permitted */
144 pkgFLCache::DiverIterator Div
= Nde
.Diversion();
145 if (Div
.DivertTo() == Nde
)
146 return _error
->Error("The package is trying to write to the "
147 "diversion target %s/%s",Nde
.DirN(),Nde
.File());
149 // See if it is us and we are following it in the right direction
150 if (Div
->OwnerPkg
!= FLPkg
.Offset() && Div
.DivertFrom() == Nde
)
152 Nde
= Div
.DivertTo();
153 End
= FileName
+ snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
154 Nde
.DirN(),Nde
.File());
156 return _error
->Error("The diversion path is too long");
160 // Deal with symlinks and conf files
161 if ((RealNde
->Flags
& pkgFLCache::Node::NewConfFile
) ==
162 pkgFLCache::Node::NewConfFile
)
164 string Res
= flNoLink(Itm
.Name
);
165 if (Res
.length() > sizeof(FileName
))
166 return _error
->Error("The path %s is too long",Res
.c_str());
168 clog
<< "Followed conf file from " << FileName
<< " to " << Res
<< endl
;
169 Itm
.Name
= strcpy(FileName
,Res
.c_str());
172 /* Get information about the existing file, and attempt to restore
173 a backup if it does not exist */
174 struct stat LExisting
;
176 if (lstat(Itm
.Name
,&LExisting
) != 0)
180 return _error
->Errno("stat","Failed to stat %s",Itm
.Name
);
182 // See if we can recover the backup file
183 if (Nde
.end() == false)
185 snprintf(Temp
,sizeof(Temp
),"%s.%s",Itm
.Name
,TempExt
);
186 if (rename(Temp
,Itm
.Name
) != 0 && errno
!= ENOENT
)
187 return _error
->Errno("rename","Failed to rename %s to %s",
189 if (stat(Itm
.Name
,&LExisting
) != 0)
192 return _error
->Errno("stat","Failed to stat %s",Itm
.Name
);
201 /* If the file is a link we need to stat its destination, get the
202 existing file modes */
203 struct stat Existing
= LExisting
;
204 if (EValid
== true && S_ISLNK(Existing
.st_mode
))
206 if (stat(Itm
.Name
,&Existing
) != 0)
209 return _error
->Errno("stat","Failed to stat %s",Itm
.Name
);
210 Existing
= LExisting
;
214 // We pretend a non-existing file looks like it is a normal file
216 Existing
.st_mode
= S_IFREG
;
218 /* Okay, at this point 'Existing' is the stat information for the
219 real non-link file */
221 /* The only way this can be a no-op is if a directory is being
222 replaced by a directory or by a link */
223 if (S_ISDIR(Existing
.st_mode
) != 0 &&
224 (Itm
.Type
== Item::Directory
|| Itm
.Type
== Item::SymbolicLink
))
227 /* Non-Directory being replaced by non-directory. We check for over
229 if (Nde
.end() == false)
231 if (HandleOverwrites(Nde
) == false)
235 /* Directory being replaced by a non-directory - this needs to see if
236 the package is the owner and then see if the directory would be
237 empty after the package is removed [ie no user files will be
239 if (S_ISDIR(Existing
.st_mode
) != 0)
241 if (CheckDirReplace(Itm
.Name
) == false)
242 return _error
->Error("The directory %s is being replaced by a non-directory",Itm
.Name
);
246 clog
<< "Extract " << string(Itm
.Name
,End
) << endl
;
248 return _error->Error("Done");*/
253 // Extract::Finished - Sequence finished, erase the temp files /*{{{*/
254 // ---------------------------------------------------------------------
256 bool pkgExtract::Finished()
261 // Extract::Aborted - Sequence aborted, undo all our unpacking /*{{{*/
262 // ---------------------------------------------------------------------
263 /* This undoes everything that was done by all calls to the DoItem method
264 and restores the File Listing cache to its original form. It bases its
265 actions on the flags value for each node in the cache. */
266 bool pkgExtract::Aborted()
269 clog
<< "Aborted, backing out" << endl
;
271 pkgFLCache::NodeIterator Files
= FLPkg
.Files();
272 map_ptrloc
*Last
= &FLPkg
->Files
;
274 /* Loop over all files, restore those that have been unpacked from their
276 while (Files
.end() == false)
278 // Locate the hash bucket for the node and locate its group head
279 pkgFLCache::NodeIterator
Nde(FLCache
,FLCache
.HashNode(Files
));
280 for (; Nde
.end() == false && Files
->File
!= Nde
->File
; Nde
++);
281 if (Nde
.end() == true)
282 return _error
->Error("Failed to locate node in its hash bucket");
284 if (snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
285 Nde
.DirN(),Nde
.File()) <= 0)
286 return _error
->Error("The path is too long");
288 // Deal with diversions
289 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
291 pkgFLCache::DiverIterator Div
= Nde
.Diversion();
293 // See if it is us and we are following it in the right direction
294 if (Div
->OwnerPkg
!= FLPkg
.Offset() && Div
.DivertFrom() == Nde
)
296 Nde
= Div
.DivertTo();
297 if (snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
298 Nde
.DirN(),Nde
.File()) <= 0)
299 return _error
->Error("The diversion path is too long");
303 // Deal with overwrites+replaces
304 for (; Nde
.end() == false && Files
->File
== Nde
->File
; Nde
++)
306 if ((Nde
->Flags
& pkgFLCache::Node::Replaced
) ==
307 pkgFLCache::Node::Replaced
)
310 clog
<< "De-replaced " << FileName
<< " from " << Nde
.RealPackage()->Name
<< endl
;
311 Nde
->Flags
&= ~pkgFLCache::Node::Replaced
;
315 // Undo the change in the filesystem
317 clog
<< "Backing out " << FileName
;
320 if ((Files
->Flags
& pkgFLCache::Node::NewFile
) ==
321 pkgFLCache::Node::NewFile
)
324 clog
<< " [new node]" << endl
;
325 pkgFLCache::Node
*Tmp
= Files
;
327 *Last
= Tmp
->NextPkg
;
330 FLCache
.DropNode(Tmp
- FLCache
.NodeP
);
337 Last
= &Files
->NextPkg
;
345 // Extract::Fail - Extraction of a file Failed /*{{{*/
346 // ---------------------------------------------------------------------
348 bool pkgExtract::Fail(Item
&Itm
,int Fd
)
350 return pkgDirStream::Fail(Itm
,Fd
);
353 // Extract::FinishedFile - Finished a file /*{{{*/
354 // ---------------------------------------------------------------------
356 bool pkgExtract::FinishedFile(Item
&Itm
,int Fd
)
358 return pkgDirStream::FinishedFile(Itm
,Fd
);
361 // Extract::HandleOverwrites - See if a replaces covers this overwrite /*{{{*/
362 // ---------------------------------------------------------------------
363 /* Check if the file is in a package that is being replaced by this
364 package or if the file is being overwritten. Note that if the file
365 is really a directory but it has been erased from the filesystem
366 this will fail with an overwrite message. This is a limitation of the
367 dpkg file information format.
369 XX If a new package installs and another package replaces files in this
370 package what should we do? */
371 bool pkgExtract::HandleOverwrites(pkgFLCache::NodeIterator Nde
,
374 pkgFLCache::NodeIterator TmpNde
= Nde
;
375 unsigned long DiverOwner
= 0;
376 unsigned long FileGroup
= Nde
->File
;
377 const char *FirstOwner
= 0;
378 for (; Nde
.end() == false && FileGroup
== Nde
->File
; Nde
++)
380 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
382 /* Store the diversion owner if this is the forward direction
384 if (DiverCheck
== true)
385 DiverOwner
= Nde
.Diversion()->OwnerPkg
;
389 pkgFLCache::PkgIterator
FPkg(FLCache
,Nde
.RealPackage());
390 if (FPkg
.end() == true || FPkg
== FLPkg
)
393 /* This tests trips when we are checking a diversion to see
394 if something has already been diverted by this diversion */
395 if (FPkg
.Offset() == DiverOwner
)
397 FirstOwner
= FPkg
.Name();
399 // Now see if this package matches one in a replace depends
400 pkgCache::DepIterator Dep
= Ver
.DependsList();
402 for (; Dep
.end() == false; Dep
++)
404 if (Dep
->Type
!= pkgCache::Dep::Replaces
)
407 // Does the replaces apply to this package?
408 if (strcmp(Dep
.TargetPkg().Name(),FPkg
.Name()) != 0)
411 /* Check the version for match. I do not think CurrentVer can be
412 0 if we are here.. */
413 pkgCache::PkgIterator Pkg
= Dep
.TargetPkg();
414 if (Pkg
->CurrentVer
== 0)
416 _error
->Warning("Overwrite package match with no version for %s",Pkg
.Name());
421 if (debVS
.CheckDep(Pkg
.CurrentVer().VerStr(),Dep
->CompareOp
,Dep
.TargetVer()) == true)
424 clog
<< "Replaced file " << Nde
.DirN() << '/' << Nde
.File() << " from " << Pkg
.Name() << endl
;
425 Nde
->Flags
|= pkgFLCache::Node::Replaced
;
433 return _error
->Error("File %s/%s overwrites the one in the package %s",
434 Nde
.DirN(),Nde
.File(),FPkg
.Name());
437 /* If this is a diversion we might have to recurse to process
438 the other side of it */
439 if ((TmpNde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
441 pkgFLCache::DiverIterator Div
= TmpNde
.Diversion();
442 if (Div
.DivertTo() == TmpNde
)
443 return HandleOverwrites(Div
.DivertFrom(),true);
449 // Extract::CheckDirReplace - See if this directory can be erased /*{{{*/
450 // ---------------------------------------------------------------------
451 /* If this directory is owned by a single package and that package is
452 replacing it with something non-directoryish then dpkg allows this.
453 We increase the requirement to be that the directory is non-empty after
454 the package is removed */
455 bool pkgExtract::CheckDirReplace(string Dir
,unsigned int Depth
)
461 if (Dir
[Dir
.size() - 1] != '/')
464 DIR *D
= opendir(Dir
.c_str());
466 return _error
->Errno("opendir","Unable to read %s",Dir
.c_str());
469 for (struct dirent
*Dent
= readdir(D
); Dent
!= 0; Dent
= readdir(D
))
472 if (strcmp(Dent
->d_name
,".") == 0 ||
473 strcmp(Dent
->d_name
,"..") == 0)
477 File
= Dir
+ Dent
->d_name
;
478 pkgFLCache::NodeIterator Nde
= FLCache
.GetNode(File
.c_str(),
479 File
.c_str() + File
.length(),0,false,false);
481 // The file is not owned by this package
482 if (Nde
.end() != false || Nde
.RealPackage() != FLPkg
)
488 // See if it is a directory
490 if (lstat(File
.c_str(),&St
) != 0)
493 return _error
->Errno("lstat","Unable to stat %s",File
.c_str());
496 // Recurse down directories
497 if (S_ISDIR(St
.st_mode
) != 0)
499 if (CheckDirReplace(File
,Depth
+ 1) == false)