extract.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: extract.cc,v 1.6.2.1 2004/01/16 18:58:50 mdz Exp $
  4. /* ######################################################################
  5. Archive Extraction Directory Stream
  6. Extraction for each file is a bit of an involved process. Each object
  7. undergoes an atomic backup, overwrite, erase sequence. First the
  8. object is unpacked to '.dpkg.new' then the original is hardlinked to
  9. '.dpkg.tmp' and finally the new object is renamed to overwrite the old
  10. one. From an external perspective the file never ceased to exist.
  11. After the archive has been successfully unpacked the .dpkg.tmp files
  12. are erased. A failure causes all the .dpkg.tmp files to be restored.
  13. Decisions about unpacking go like this:
  14. - Store the original filename in the file listing
  15. - Resolve any diversions that would effect this file, all checks
  16. below apply to the diverted name, not the real one.
  17. - Resolve any symlinked configuration files.
  18. - If the existing file does not exist then .dpkg-tmp is checked for.
  19. [Note, this is reduced to only check if a file was expected to be
  20. there]
  21. - If the existing link/file is not a directory then it is replaced
  22. regardless
  23. - If the existing link/directory is being replaced by a directory then
  24. absolutely nothing happens.
  25. - If the existing link/directory is being replaced by a link then
  26. absolutely nothing happens.
  27. - If the existing link/directory is being replaced by a non-directory
  28. then this will abort if the package is not the sole owner of the
  29. directory. [Note, this is changed to not happen if the directory
  30. non-empty - that is, it only includes files that are part of this
  31. package - prevents removing user files accidentally.]
  32. - If the non-directory exists in the listing database and it
  33. does not belong to the current package then an overwrite condition
  34. is invoked.
  35. As we unpack we record the file list differences in the FL cache. If
  36. we need to unroll the the FL cache knows which files have been unpacked
  37. and can undo. When we need to erase then it knows which files have not
  38. been unpacked.
  39. ##################################################################### */
  40. /*}}}*/
  41. // Include Files /*{{{*/
  42. #include<config.h>
  43. #include <apt-pkg/extract.h>
  44. #include <apt-pkg/error.h>
  45. #include <apt-pkg/debversion.h>
  46. #include <apt-pkg/fileutl.h>
  47. #include <apt-pkg/dirstream.h>
  48. #include <apt-pkg/filelist.h>
  49. #include <apt-pkg/mmap.h>
  50. #include <apt-pkg/pkgcache.h>
  51. #include <apt-pkg/cacheiterators.h>
  52. #include <string.h>
  53. #include <string>
  54. #include <sys/stat.h>
  55. #include <stdio.h>
  56. #include <errno.h>
  57. #include <dirent.h>
  58. #include <iostream>
  59. #include <apti18n.h>
  60. /*}}}*/
  61. using namespace std;
  62. static const char *TempExt = "dpkg-tmp";
  63. //static const char *NewExt = "dpkg-new";
  64. // Extract::pkgExtract - Constructor /*{{{*/
  65. // ---------------------------------------------------------------------
  66. /* */
  67. pkgExtract::pkgExtract(pkgFLCache &FLCache,pkgCache::VerIterator Ver) :
  68. FLCache(FLCache), Ver(Ver)
  69. {
  70. FLPkg = FLCache.GetPkg(Ver.ParentPkg().Name(),true);
  71. if (FLPkg.end() == true)
  72. return;
  73. Debug = true;
  74. }
  75. /*}}}*/
  76. // Extract::DoItem - Handle a single item from the stream /*{{{*/
  77. // ---------------------------------------------------------------------
  78. /* This performs the setup for the extraction.. */
  79. bool pkgExtract::DoItem(Item &Itm, int &/*Fd*/)
  80. {
  81. /* Strip any leading/trailing /s from the filename, then copy it to the
  82. temp buffer and re-apply the leading / We use a class variable
  83. to store the new filename for use by the three extraction funcs */
  84. char *End = FileName+1;
  85. const char *I = Itm.Name;
  86. for (; *I != 0 && *I == '/'; I++);
  87. *FileName = '/';
  88. for (; *I != 0 && End < FileName + sizeof(FileName); I++, End++)
  89. *End = *I;
  90. if (End + 20 >= FileName + sizeof(FileName))
  91. return _error->Error(_("The path %s is too long"),Itm.Name);
  92. for (; End > FileName && End[-1] == '/'; End--);
  93. *End = 0;
  94. Itm.Name = FileName;
  95. /* Lookup the file. Nde is the file [group] we are going to write to and
  96. RealNde is the actual node we are manipulating. Due to diversions
  97. they may be entirely different. */
  98. pkgFLCache::NodeIterator Nde = FLCache.GetNode(Itm.Name,End,0,false,false);
  99. pkgFLCache::NodeIterator RealNde = Nde;
  100. // See if the file is already in the file listing
  101. unsigned long FileGroup = RealNde->File;
  102. for (; RealNde.end() == false && FileGroup == RealNde->File; RealNde++)
  103. if (RealNde.RealPackage() == FLPkg)
  104. break;
  105. // Nope, create an entry
  106. if (RealNde.end() == true)
  107. {
  108. RealNde = FLCache.GetNode(Itm.Name,End,FLPkg.Offset(),true,false);
  109. if (RealNde.end() == true)
  110. return false;
  111. RealNde->Flags |= pkgFLCache::Node::NewFile;
  112. }
  113. /* Check if this entry already was unpacked. The only time this should
  114. ever happen is if someone has hacked tar to support capabilities, in
  115. which case this needs to be modified anyhow.. */
  116. if ((RealNde->Flags & pkgFLCache::Node::Unpacked) ==
  117. pkgFLCache::Node::Unpacked)
  118. return _error->Error(_("Unpacking %s more than once"),Itm.Name);
  119. if (Nde.end() == true)
  120. Nde = RealNde;
  121. /* Consider a diverted file - We are not permitted to divert directories,
  122. but everything else is fair game (including conf files!) */
  123. if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
  124. {
  125. if (Itm.Type == Item::Directory)
  126. return _error->Error(_("The directory %s is diverted"),Itm.Name);
  127. /* A package overwriting a diversion target is just the same as
  128. overwriting a normally owned file and is checked for below in
  129. the overwrites mechanism */
  130. /* If this package is trying to overwrite the target of a diversion,
  131. that is never, ever permitted */
  132. pkgFLCache::DiverIterator Div = Nde.Diversion();
  133. if (Div.DivertTo() == Nde)
  134. return _error->Error(_("The package is trying to write to the "
  135. "diversion target %s/%s"),Nde.DirN(),Nde.File());
  136. // See if it is us and we are following it in the right direction
  137. if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
  138. {
  139. Nde = Div.DivertTo();
  140. End = FileName + snprintf(FileName,sizeof(FileName)-20,"%s/%s",
  141. Nde.DirN(),Nde.File());
  142. if (End <= FileName)
  143. return _error->Error(_("The diversion path is too long"));
  144. }
  145. }
  146. // Deal with symlinks and conf files
  147. if ((RealNde->Flags & pkgFLCache::Node::NewConfFile) ==
  148. pkgFLCache::Node::NewConfFile)
  149. {
  150. string Res = flNoLink(Itm.Name);
  151. if (Res.length() > sizeof(FileName))
  152. return _error->Error(_("The path %s is too long"),Res.c_str());
  153. if (Debug == true)
  154. clog << "Followed conf file from " << FileName << " to " << Res << endl;
  155. Itm.Name = strcpy(FileName,Res.c_str());
  156. }
  157. /* Get information about the existing file, and attempt to restore
  158. a backup if it does not exist */
  159. struct stat LExisting;
  160. bool EValid = false;
  161. if (lstat(Itm.Name,&LExisting) != 0)
  162. {
  163. // This is bad news.
  164. if (errno != ENOENT)
  165. return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
  166. // See if we can recover the backup file
  167. if (Nde.end() == false)
  168. {
  169. char Temp[sizeof(FileName)];
  170. snprintf(Temp,sizeof(Temp),"%s.%s",Itm.Name,TempExt);
  171. if (rename(Temp,Itm.Name) != 0 && errno != ENOENT)
  172. return _error->Errno("rename",_("Failed to rename %s to %s"),
  173. Temp,Itm.Name);
  174. if (stat(Itm.Name,&LExisting) != 0)
  175. {
  176. if (errno != ENOENT)
  177. return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
  178. }
  179. else
  180. EValid = true;
  181. }
  182. }
  183. else
  184. EValid = true;
  185. /* If the file is a link we need to stat its destination, get the
  186. existing file modes */
  187. struct stat Existing = LExisting;
  188. if (EValid == true && S_ISLNK(Existing.st_mode))
  189. {
  190. if (stat(Itm.Name,&Existing) != 0)
  191. {
  192. if (errno != ENOENT)
  193. return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
  194. Existing = LExisting;
  195. }
  196. }
  197. // We pretend a non-existing file looks like it is a normal file
  198. if (EValid == false)
  199. Existing.st_mode = S_IFREG;
  200. /* Okay, at this point 'Existing' is the stat information for the
  201. real non-link file */
  202. /* The only way this can be a no-op is if a directory is being
  203. replaced by a directory or by a link */
  204. if (S_ISDIR(Existing.st_mode) != 0 &&
  205. (Itm.Type == Item::Directory || Itm.Type == Item::SymbolicLink))
  206. return true;
  207. /* Non-Directory being replaced by non-directory. We check for over
  208. writes here. */
  209. if (Nde.end() == false)
  210. {
  211. if (HandleOverwrites(Nde) == false)
  212. return false;
  213. }
  214. /* Directory being replaced by a non-directory - this needs to see if
  215. the package is the owner and then see if the directory would be
  216. empty after the package is removed [ie no user files will be
  217. erased] */
  218. if (S_ISDIR(Existing.st_mode) != 0)
  219. {
  220. if (CheckDirReplace(Itm.Name) == false)
  221. return _error->Error(_("The directory %s is being replaced by a non-directory"),Itm.Name);
  222. }
  223. if (Debug == true)
  224. clog << "Extract " << string(Itm.Name,End) << endl;
  225. /* if (Count != 0)
  226. return _error->Error(_("Done"));*/
  227. return true;
  228. }
  229. /*}}}*/
  230. // Extract::Finished - Sequence finished, erase the temp files /*{{{*/
  231. // ---------------------------------------------------------------------
  232. /* */
  233. APT_CONST bool pkgExtract::Finished()
  234. {
  235. return true;
  236. }
  237. /*}}}*/
  238. // Extract::Aborted - Sequence aborted, undo all our unpacking /*{{{*/
  239. // ---------------------------------------------------------------------
  240. /* This undoes everything that was done by all calls to the DoItem method
  241. and restores the File Listing cache to its original form. It bases its
  242. actions on the flags value for each node in the cache. */
  243. bool pkgExtract::Aborted()
  244. {
  245. if (Debug == true)
  246. clog << "Aborted, backing out" << endl;
  247. pkgFLCache::NodeIterator Files = FLPkg.Files();
  248. map_ptrloc *Last = &FLPkg->Files;
  249. /* Loop over all files, restore those that have been unpacked from their
  250. dpkg-tmp entries */
  251. while (Files.end() == false)
  252. {
  253. // Locate the hash bucket for the node and locate its group head
  254. pkgFLCache::NodeIterator Nde(FLCache,FLCache.HashNode(Files));
  255. for (; Nde.end() == false && Files->File != Nde->File; Nde++);
  256. if (Nde.end() == true)
  257. return _error->Error(_("Failed to locate node in its hash bucket"));
  258. if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
  259. Nde.DirN(),Nde.File()) <= 0)
  260. return _error->Error(_("The path is too long"));
  261. // Deal with diversions
  262. if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
  263. {
  264. pkgFLCache::DiverIterator Div = Nde.Diversion();
  265. // See if it is us and we are following it in the right direction
  266. if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
  267. {
  268. Nde = Div.DivertTo();
  269. if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
  270. Nde.DirN(),Nde.File()) <= 0)
  271. return _error->Error(_("The diversion path is too long"));
  272. }
  273. }
  274. // Deal with overwrites+replaces
  275. for (; Nde.end() == false && Files->File == Nde->File; Nde++)
  276. {
  277. if ((Nde->Flags & pkgFLCache::Node::Replaced) ==
  278. pkgFLCache::Node::Replaced)
  279. {
  280. if (Debug == true)
  281. clog << "De-replaced " << FileName << " from " << Nde.RealPackage()->Name << endl;
  282. Nde->Flags &= ~pkgFLCache::Node::Replaced;
  283. }
  284. }
  285. // Undo the change in the filesystem
  286. if (Debug == true)
  287. clog << "Backing out " << FileName;
  288. // Remove a new node
  289. if ((Files->Flags & pkgFLCache::Node::NewFile) ==
  290. pkgFLCache::Node::NewFile)
  291. {
  292. if (Debug == true)
  293. clog << " [new node]" << endl;
  294. pkgFLCache::Node *Tmp = Files;
  295. Files++;
  296. *Last = Tmp->NextPkg;
  297. Tmp->NextPkg = 0;
  298. FLCache.DropNode(Tmp - FLCache.NodeP);
  299. }
  300. else
  301. {
  302. if (Debug == true)
  303. clog << endl;
  304. Last = &Files->NextPkg;
  305. Files++;
  306. }
  307. }
  308. return true;
  309. }
  310. /*}}}*/
  311. // Extract::Fail - Extraction of a file Failed /*{{{*/
  312. // ---------------------------------------------------------------------
  313. /* */
  314. bool pkgExtract::Fail(Item &Itm,int Fd)
  315. {
  316. return pkgDirStream::Fail(Itm,Fd);
  317. }
  318. /*}}}*/
  319. // Extract::FinishedFile - Finished a file /*{{{*/
  320. // ---------------------------------------------------------------------
  321. /* */
  322. bool pkgExtract::FinishedFile(Item &Itm,int Fd)
  323. {
  324. return pkgDirStream::FinishedFile(Itm,Fd);
  325. }
  326. /*}}}*/
  327. // Extract::HandleOverwrites - See if a replaces covers this overwrite /*{{{*/
  328. // ---------------------------------------------------------------------
  329. /* Check if the file is in a package that is being replaced by this
  330. package or if the file is being overwritten. Note that if the file
  331. is really a directory but it has been erased from the filesystem
  332. this will fail with an overwrite message. This is a limitation of the
  333. dpkg file information format.
  334. XX If a new package installs and another package replaces files in this
  335. package what should we do? */
  336. bool pkgExtract::HandleOverwrites(pkgFLCache::NodeIterator Nde,
  337. bool DiverCheck)
  338. {
  339. pkgFLCache::NodeIterator TmpNde = Nde;
  340. unsigned long DiverOwner = 0;
  341. unsigned long FileGroup = Nde->File;
  342. for (; Nde.end() == false && FileGroup == Nde->File; Nde++)
  343. {
  344. if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
  345. {
  346. /* Store the diversion owner if this is the forward direction
  347. of the diversion */
  348. if (DiverCheck == true)
  349. DiverOwner = Nde.Diversion()->OwnerPkg;
  350. continue;
  351. }
  352. pkgFLCache::PkgIterator FPkg(FLCache,Nde.RealPackage());
  353. if (FPkg.end() == true || FPkg == FLPkg)
  354. continue;
  355. /* This tests trips when we are checking a diversion to see
  356. if something has already been diverted by this diversion */
  357. if (FPkg.Offset() == DiverOwner)
  358. continue;
  359. // Now see if this package matches one in a replace depends
  360. pkgCache::DepIterator Dep = Ver.DependsList();
  361. bool Ok = false;
  362. for (; Dep.end() == false; ++Dep)
  363. {
  364. if (Dep->Type != pkgCache::Dep::Replaces)
  365. continue;
  366. // Does the replaces apply to this package?
  367. if (strcmp(Dep.TargetPkg().Name(),FPkg.Name()) != 0)
  368. continue;
  369. /* Check the version for match. I do not think CurrentVer can be
  370. 0 if we are here.. */
  371. pkgCache::PkgIterator Pkg = Dep.TargetPkg();
  372. if (Pkg->CurrentVer == 0)
  373. {
  374. _error->Warning(_("Overwrite package match with no version for %s"),Pkg.Name());
  375. continue;
  376. }
  377. // Replaces is met
  378. if (debVS.CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,Dep.TargetVer()) == true)
  379. {
  380. if (Debug == true)
  381. clog << "Replaced file " << Nde.DirN() << '/' << Nde.File() << " from " << Pkg.Name() << endl;
  382. Nde->Flags |= pkgFLCache::Node::Replaced;
  383. Ok = true;
  384. break;
  385. }
  386. }
  387. // Negative Hit
  388. if (Ok == false)
  389. return _error->Error(_("File %s/%s overwrites the one in the package %s"),
  390. Nde.DirN(),Nde.File(),FPkg.Name());
  391. }
  392. /* If this is a diversion we might have to recurse to process
  393. the other side of it */
  394. if ((TmpNde->Flags & pkgFLCache::Node::Diversion) != 0)
  395. {
  396. pkgFLCache::DiverIterator Div = TmpNde.Diversion();
  397. if (Div.DivertTo() == TmpNde)
  398. return HandleOverwrites(Div.DivertFrom(),true);
  399. }
  400. return true;
  401. }
  402. /*}}}*/
  403. // Extract::CheckDirReplace - See if this directory can be erased /*{{{*/
  404. // ---------------------------------------------------------------------
  405. /* If this directory is owned by a single package and that package is
  406. replacing it with something non-directoryish then dpkg allows this.
  407. We increase the requirement to be that the directory is non-empty after
  408. the package is removed */
  409. bool pkgExtract::CheckDirReplace(string Dir,unsigned int Depth)
  410. {
  411. // Looping?
  412. if (Depth > 40)
  413. return false;
  414. if (Dir[Dir.size() - 1] != '/')
  415. Dir += '/';
  416. DIR *D = opendir(Dir.c_str());
  417. if (D == 0)
  418. return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
  419. string File;
  420. for (struct dirent *Dent = readdir(D); Dent != 0; Dent = readdir(D))
  421. {
  422. // Skip some files
  423. if (strcmp(Dent->d_name,".") == 0 ||
  424. strcmp(Dent->d_name,"..") == 0)
  425. continue;
  426. // Look up the node
  427. File = Dir + Dent->d_name;
  428. pkgFLCache::NodeIterator Nde = FLCache.GetNode(File.c_str(),
  429. File.c_str() + File.length(),0,false,false);
  430. // The file is not owned by this package
  431. if (Nde.end() != false || Nde.RealPackage() != FLPkg)
  432. {
  433. closedir(D);
  434. return false;
  435. }
  436. // See if it is a directory
  437. struct stat St;
  438. if (lstat(File.c_str(),&St) != 0)
  439. {
  440. closedir(D);
  441. return _error->Errno("lstat",_("Unable to stat %s"),File.c_str());
  442. }
  443. // Recurse down directories
  444. if (S_ISDIR(St.st_mode) != 0)
  445. {
  446. if (CheckDirReplace(File,Depth + 1) == false)
  447. {
  448. closedir(D);
  449. return false;
  450. }
  451. }
  452. }
  453. // No conflicts
  454. closedir(D);
  455. return true;
  456. }
  457. /*}}}*/