]>
git.saurik.com Git - apt.git/blob - apt-inst/extract.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: extract.cc,v 1.6.2.1 2004/01/16 18:58:50 mdz 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 successfully 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 /*{{{*/
49 #include <apt-pkg/extract.h>
50 #include <apt-pkg/error.h>
51 #include <apt-pkg/debversion.h>
52 #include <apt-pkg/fileutl.h>
53 #include <apt-pkg/dirstream.h>
54 #include <apt-pkg/filelist.h>
55 #include <apt-pkg/mmap.h>
56 #include <apt-pkg/pkgcache.h>
57 #include <apt-pkg/cacheiterators.h>
71 static const char *TempExt
= "dpkg-tmp";
72 //static const char *NewExt = "dpkg-new";
74 // Extract::pkgExtract - Constructor /*{{{*/
75 // ---------------------------------------------------------------------
77 pkgExtract::pkgExtract(pkgFLCache
&FLCache
,pkgCache::VerIterator Ver
) :
78 FLCache(FLCache
), Ver(Ver
)
80 FLPkg
= FLCache
.GetPkg(Ver
.ParentPkg().Name(),true);
81 if (FLPkg
.end() == true)
86 // Extract::DoItem - Handle a single item from the stream /*{{{*/
87 // ---------------------------------------------------------------------
88 /* This performs the setup for the extraction.. */
89 bool pkgExtract::DoItem(Item
&Itm
, int &/*Fd*/)
91 /* Strip any leading/trailing /s from the filename, then copy it to the
92 temp buffer and re-apply the leading / We use a class variable
93 to store the new filename for use by the three extraction funcs */
94 char *End
= FileName
+1;
95 const char *I
= Itm
.Name
;
96 for (; *I
!= 0 && *I
== '/'; I
++);
98 for (; *I
!= 0 && End
< FileName
+ sizeof(FileName
); I
++, End
++)
100 if (End
+ 20 >= FileName
+ sizeof(FileName
))
101 return _error
->Error(_("The path %s is too long"),Itm
.Name
);
102 for (; End
> FileName
&& End
[-1] == '/'; End
--);
106 /* Lookup the file. Nde is the file [group] we are going to write to and
107 RealNde is the actual node we are manipulating. Due to diversions
108 they may be entirely different. */
109 pkgFLCache::NodeIterator Nde
= FLCache
.GetNode(Itm
.Name
,End
,0,false,false);
110 pkgFLCache::NodeIterator RealNde
= Nde
;
112 // See if the file is already in the file listing
113 unsigned long FileGroup
= RealNde
->File
;
114 for (; RealNde
.end() == false && FileGroup
== RealNde
->File
; RealNde
++)
115 if (RealNde
.RealPackage() == FLPkg
)
118 // Nope, create an entry
119 if (RealNde
.end() == true)
121 RealNde
= FLCache
.GetNode(Itm
.Name
,End
,FLPkg
.Offset(),true,false);
122 if (RealNde
.end() == true)
124 RealNde
->Flags
|= pkgFLCache::Node::NewFile
;
127 /* Check if this entry already was unpacked. The only time this should
128 ever happen is if someone has hacked tar to support capabilities, in
129 which case this needs to be modified anyhow.. */
130 if ((RealNde
->Flags
& pkgFLCache::Node::Unpacked
) ==
131 pkgFLCache::Node::Unpacked
)
132 return _error
->Error(_("Unpacking %s more than once"),Itm
.Name
);
134 if (Nde
.end() == true)
137 /* Consider a diverted file - We are not permitted to divert directories,
138 but everything else is fair game (including conf files!) */
139 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
141 if (Itm
.Type
== Item::Directory
)
142 return _error
->Error(_("The directory %s is diverted"),Itm
.Name
);
144 /* A package overwriting a diversion target is just the same as
145 overwriting a normally owned file and is checked for below in
146 the overwrites mechanism */
148 /* If this package is trying to overwrite the target of a diversion,
149 that is never, ever permitted */
150 pkgFLCache::DiverIterator Div
= Nde
.Diversion();
151 if (Div
.DivertTo() == Nde
)
152 return _error
->Error(_("The package is trying to write to the "
153 "diversion target %s/%s"),Nde
.DirN(),Nde
.File());
155 // See if it is us and we are following it in the right direction
156 if (Div
->OwnerPkg
!= FLPkg
.Offset() && Div
.DivertFrom() == Nde
)
158 Nde
= Div
.DivertTo();
159 End
= FileName
+ snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
160 Nde
.DirN(),Nde
.File());
162 return _error
->Error(_("The diversion path is too long"));
166 // Deal with symlinks and conf files
167 if ((RealNde
->Flags
& pkgFLCache::Node::NewConfFile
) ==
168 pkgFLCache::Node::NewConfFile
)
170 string Res
= flNoLink(Itm
.Name
);
171 if (Res
.length() > sizeof(FileName
))
172 return _error
->Error(_("The path %s is too long"),Res
.c_str());
174 clog
<< "Followed conf file from " << FileName
<< " to " << Res
<< endl
;
175 Itm
.Name
= strcpy(FileName
,Res
.c_str());
178 /* Get information about the existing file, and attempt to restore
179 a backup if it does not exist */
180 struct stat LExisting
;
182 if (lstat(Itm
.Name
,&LExisting
) != 0)
186 return _error
->Errno("stat",_("Failed to stat %s"),Itm
.Name
);
188 // See if we can recover the backup file
189 if (Nde
.end() == false)
191 char Temp
[sizeof(FileName
)];
192 snprintf(Temp
,sizeof(Temp
),"%s.%s",Itm
.Name
,TempExt
);
193 if (rename(Temp
,Itm
.Name
) != 0 && errno
!= ENOENT
)
194 return _error
->Errno("rename",_("Failed to rename %s to %s"),
196 if (stat(Itm
.Name
,&LExisting
) != 0)
199 return _error
->Errno("stat",_("Failed to stat %s"),Itm
.Name
);
208 /* If the file is a link we need to stat its destination, get the
209 existing file modes */
210 struct stat Existing
= LExisting
;
211 if (EValid
== true && S_ISLNK(Existing
.st_mode
))
213 if (stat(Itm
.Name
,&Existing
) != 0)
216 return _error
->Errno("stat",_("Failed to stat %s"),Itm
.Name
);
217 Existing
= LExisting
;
221 // We pretend a non-existing file looks like it is a normal file
223 Existing
.st_mode
= S_IFREG
;
225 /* Okay, at this point 'Existing' is the stat information for the
226 real non-link file */
228 /* The only way this can be a no-op is if a directory is being
229 replaced by a directory or by a link */
230 if (S_ISDIR(Existing
.st_mode
) != 0 &&
231 (Itm
.Type
== Item::Directory
|| Itm
.Type
== Item::SymbolicLink
))
234 /* Non-Directory being replaced by non-directory. We check for over
236 if (Nde
.end() == false)
238 if (HandleOverwrites(Nde
) == false)
242 /* Directory being replaced by a non-directory - this needs to see if
243 the package is the owner and then see if the directory would be
244 empty after the package is removed [ie no user files will be
246 if (S_ISDIR(Existing
.st_mode
) != 0)
248 if (CheckDirReplace(Itm
.Name
) == false)
249 return _error
->Error(_("The directory %s is being replaced by a non-directory"),Itm
.Name
);
253 clog
<< "Extract " << string(Itm
.Name
,End
) << endl
;
255 return _error->Error(_("Done"));*/
260 // Extract::Finished - Sequence finished, erase the temp files /*{{{*/
261 // ---------------------------------------------------------------------
263 APT_CONST
bool pkgExtract::Finished()
268 // Extract::Aborted - Sequence aborted, undo all our unpacking /*{{{*/
269 // ---------------------------------------------------------------------
270 /* This undoes everything that was done by all calls to the DoItem method
271 and restores the File Listing cache to its original form. It bases its
272 actions on the flags value for each node in the cache. */
273 bool pkgExtract::Aborted()
276 clog
<< "Aborted, backing out" << endl
;
278 pkgFLCache::NodeIterator Files
= FLPkg
.Files();
279 map_ptrloc
*Last
= &FLPkg
->Files
;
281 /* Loop over all files, restore those that have been unpacked from their
283 while (Files
.end() == false)
285 // Locate the hash bucket for the node and locate its group head
286 pkgFLCache::NodeIterator
Nde(FLCache
,FLCache
.HashNode(Files
));
287 for (; Nde
.end() == false && Files
->File
!= Nde
->File
; Nde
++);
288 if (Nde
.end() == true)
289 return _error
->Error(_("Failed to locate node in its hash bucket"));
291 if (snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
292 Nde
.DirN(),Nde
.File()) <= 0)
293 return _error
->Error(_("The path is too long"));
295 // Deal with diversions
296 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
298 pkgFLCache::DiverIterator Div
= Nde
.Diversion();
300 // See if it is us and we are following it in the right direction
301 if (Div
->OwnerPkg
!= FLPkg
.Offset() && Div
.DivertFrom() == Nde
)
303 Nde
= Div
.DivertTo();
304 if (snprintf(FileName
,sizeof(FileName
)-20,"%s/%s",
305 Nde
.DirN(),Nde
.File()) <= 0)
306 return _error
->Error(_("The diversion path is too long"));
310 // Deal with overwrites+replaces
311 for (; Nde
.end() == false && Files
->File
== Nde
->File
; Nde
++)
313 if ((Nde
->Flags
& pkgFLCache::Node::Replaced
) ==
314 pkgFLCache::Node::Replaced
)
317 clog
<< "De-replaced " << FileName
<< " from " << Nde
.RealPackage()->Name
<< endl
;
318 Nde
->Flags
&= ~pkgFLCache::Node::Replaced
;
322 // Undo the change in the filesystem
324 clog
<< "Backing out " << FileName
;
327 if ((Files
->Flags
& pkgFLCache::Node::NewFile
) ==
328 pkgFLCache::Node::NewFile
)
331 clog
<< " [new node]" << endl
;
332 pkgFLCache::Node
*Tmp
= Files
;
334 *Last
= Tmp
->NextPkg
;
337 FLCache
.DropNode(Tmp
- FLCache
.NodeP
);
344 Last
= &Files
->NextPkg
;
352 // Extract::Fail - Extraction of a file Failed /*{{{*/
353 // ---------------------------------------------------------------------
355 bool pkgExtract::Fail(Item
&Itm
,int Fd
)
357 return pkgDirStream::Fail(Itm
,Fd
);
360 // Extract::FinishedFile - Finished a file /*{{{*/
361 // ---------------------------------------------------------------------
363 bool pkgExtract::FinishedFile(Item
&Itm
,int Fd
)
365 return pkgDirStream::FinishedFile(Itm
,Fd
);
368 // Extract::HandleOverwrites - See if a replaces covers this overwrite /*{{{*/
369 // ---------------------------------------------------------------------
370 /* Check if the file is in a package that is being replaced by this
371 package or if the file is being overwritten. Note that if the file
372 is really a directory but it has been erased from the filesystem
373 this will fail with an overwrite message. This is a limitation of the
374 dpkg file information format.
376 XX If a new package installs and another package replaces files in this
377 package what should we do? */
378 bool pkgExtract::HandleOverwrites(pkgFLCache::NodeIterator Nde
,
381 pkgFLCache::NodeIterator TmpNde
= Nde
;
382 unsigned long DiverOwner
= 0;
383 unsigned long FileGroup
= Nde
->File
;
384 for (; Nde
.end() == false && FileGroup
== Nde
->File
; Nde
++)
386 if ((Nde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
388 /* Store the diversion owner if this is the forward direction
390 if (DiverCheck
== true)
391 DiverOwner
= Nde
.Diversion()->OwnerPkg
;
395 pkgFLCache::PkgIterator
FPkg(FLCache
,Nde
.RealPackage());
396 if (FPkg
.end() == true || FPkg
== FLPkg
)
399 /* This tests trips when we are checking a diversion to see
400 if something has already been diverted by this diversion */
401 if (FPkg
.Offset() == DiverOwner
)
404 // Now see if this package matches one in a replace depends
405 pkgCache::DepIterator Dep
= Ver
.DependsList();
407 for (; Dep
.end() == false; ++Dep
)
409 if (Dep
->Type
!= pkgCache::Dep::Replaces
)
412 // Does the replaces apply to this package?
413 if (strcmp(Dep
.TargetPkg().Name(),FPkg
.Name()) != 0)
416 /* Check the version for match. I do not think CurrentVer can be
417 0 if we are here.. */
418 pkgCache::PkgIterator Pkg
= Dep
.TargetPkg();
419 if (Pkg
->CurrentVer
== 0)
421 _error
->Warning(_("Overwrite package match with no version for %s"),Pkg
.Name());
426 if (debVS
.CheckDep(Pkg
.CurrentVer().VerStr(),Dep
->CompareOp
,Dep
.TargetVer()) == true)
429 clog
<< "Replaced file " << Nde
.DirN() << '/' << Nde
.File() << " from " << Pkg
.Name() << endl
;
430 Nde
->Flags
|= pkgFLCache::Node::Replaced
;
438 return _error
->Error(_("File %s/%s overwrites the one in the package %s"),
439 Nde
.DirN(),Nde
.File(),FPkg
.Name());
442 /* If this is a diversion we might have to recurse to process
443 the other side of it */
444 if ((TmpNde
->Flags
& pkgFLCache::Node::Diversion
) != 0)
446 pkgFLCache::DiverIterator Div
= TmpNde
.Diversion();
447 if (Div
.DivertTo() == TmpNde
)
448 return HandleOverwrites(Div
.DivertFrom(),true);
454 // Extract::CheckDirReplace - See if this directory can be erased /*{{{*/
455 // ---------------------------------------------------------------------
456 /* If this directory is owned by a single package and that package is
457 replacing it with something non-directoryish then dpkg allows this.
458 We increase the requirement to be that the directory is non-empty after
459 the package is removed */
460 bool pkgExtract::CheckDirReplace(string Dir
,unsigned int Depth
)
466 if (Dir
[Dir
.size() - 1] != '/')
469 DIR *D
= opendir(Dir
.c_str());
471 return _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
474 for (struct dirent
*Dent
= readdir(D
); Dent
!= 0; Dent
= readdir(D
))
477 if (strcmp(Dent
->d_name
,".") == 0 ||
478 strcmp(Dent
->d_name
,"..") == 0)
482 File
= Dir
+ Dent
->d_name
;
483 pkgFLCache::NodeIterator Nde
= FLCache
.GetNode(File
.c_str(),
484 File
.c_str() + File
.length(),0,false,false);
486 // The file is not owned by this package
487 if (Nde
.end() != false || Nde
.RealPackage() != FLPkg
)
493 // See if it is a directory
495 if (lstat(File
.c_str(),&St
) != 0)
498 return _error
->Errno("lstat",_("Unable to stat %s"),File
.c_str());
501 // Recurse down directories
502 if (S_ISDIR(St
.st_mode
) != 0)
504 if (CheckDirReplace(File
,Depth
+ 1) == false)