LinkageMap.findConsumers catches shadowed obj keys.
[revdep-rebuild-reimplementation.git] / pym / portage / dbapi / vartree.py
blobc96fbacb655e650e3a3b788fd8515e2573d0af1a
1 # Copyright 1998-2007 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id: vartree.py 11392 2008-08-10 10:30:20Z zmedico $
5 __all__ = ["PreservedLibsRegistry", "LinkageMap",
6 "vardbapi", "vartree", "dblink"] + \
7 ["write_contents", "tar_contents"]
9 from portage.checksum import perform_md5
10 from portage.const import CACHE_PATH, CONFIG_MEMORY_FILE, PORTAGE_BIN_PATH, \
11 PRIVATE_PATH, VDB_PATH
12 from portage.data import portage_gid, portage_uid, secpass
13 from portage.dbapi import dbapi
14 from portage.dep import dep_getslot, use_reduce, paren_reduce, isvalidatom, \
15 isjustname, dep_getkey, match_from_list
16 from portage.exception import InvalidAtom, InvalidData, InvalidPackageName, \
17 FileNotFound, PermissionDenied, UnsupportedAPIException
18 from portage.locks import lockdir, unlockdir
19 from portage.output import bold, red, green
20 from portage.update import fixdbentries
21 from portage.util import apply_secpass_permissions, ConfigProtect, ensure_dirs, \
22 writemsg, writemsg_stdout, writemsg_level, \
23 write_atomic, atomic_ofstream, writedict, \
24 grabfile, grabdict, normalize_path, new_protect_filename, getlibpaths
25 from portage.versions import pkgsplit, catpkgsplit, catsplit, best, pkgcmp
27 from portage import listdir, dep_expand, flatten, key_expand, \
28 doebuild_environment, doebuild, env_update, prepare_build_dirs, \
29 abssymlink, movefile, _movefile, bsd_chflags, cpv_getkey
31 from portage.elog import elog_process
32 from portage.elog.filtering import filter_mergephases, filter_unmergephases
34 import os, re, sys, stat, errno, commands, copy, time, subprocess
35 import logging
36 import shlex
37 from itertools import izip
39 try:
40 import cPickle
41 except ImportError:
42 import pickle as cPickle
44 class PreservedLibsRegistry(object):
45 """ This class handles the tracking of preserved library objects """
46 def __init__(self, filename, autocommit=True):
47 """ @param filename: absolute path for saving the preserved libs records
48 @type filename: String
49 @param autocommit: determines if the file is written after every update
50 @type autocommit: Boolean
51 """
52 self._filename = filename
53 self._autocommit = autocommit
54 self.load()
55 self.pruneNonExisting()
57 def load(self):
58 """ Reload the registry data from file """
59 try:
60 self._data = cPickle.load(open(self._filename, "r"))
61 except (EOFError, IOError), e:
62 if isinstance(e, EOFError) or e.errno == errno.ENOENT:
63 self._data = {}
64 elif e.errno == PermissionDenied.errno:
65 raise PermissionDenied(self._filename)
66 else:
67 raise e
69 def store(self):
70 """ Store the registry data to file. No need to call this if autocommit
71 was enabled.
72 """
73 if os.environ.get("SANDBOX_ON") == "1":
74 return
75 try:
76 f = atomic_ofstream(self._filename)
77 cPickle.dump(self._data, f)
78 f.close()
79 except EnvironmentError, e:
80 if e.errno != PermissionDenied.errno:
81 writemsg("!!! %s %s\n" % (e, self._filename), noiselevel=-1)
83 def register(self, cpv, slot, counter, paths):
84 """ Register new objects in the registry. If there is a record with the
85 same packagename (internally derived from cpv) and slot it is
86 overwritten with the new data.
87 @param cpv: package instance that owns the objects
88 @type cpv: CPV (as String)
89 @param slot: the value of SLOT of the given package instance
90 @type slot: String
91 @param counter: vdb counter value for the package instace
92 @type counter: Integer
93 @param paths: absolute paths of objects that got preserved during an update
94 @type paths: List
95 """
96 cp = "/".join(catpkgsplit(cpv)[:2])
97 cps = cp+":"+slot
98 if len(paths) == 0 and cps in self._data \
99 and self._data[cps][0] == cpv and int(self._data[cps][1]) == int(counter):
100 del self._data[cps]
101 elif len(paths) > 0:
102 self._data[cps] = (cpv, counter, paths)
103 if self._autocommit:
104 self.store()
106 def unregister(self, cpv, slot, counter):
107 """ Remove a previous registration of preserved objects for the given package.
108 @param cpv: package instance whose records should be removed
109 @type cpv: CPV (as String)
110 @param slot: the value of SLOT of the given package instance
111 @type slot: String
113 self.register(cpv, slot, counter, [])
115 def pruneNonExisting(self):
116 """ Remove all records for objects that no longer exist on the filesystem. """
117 for cps in self._data.keys():
118 cpv, counter, paths = self._data[cps]
119 paths = [f for f in paths if os.path.exists(f)]
120 if len(paths) > 0:
121 self._data[cps] = (cpv, counter, paths)
122 else:
123 del self._data[cps]
124 if self._autocommit:
125 self.store()
127 def hasEntries(self):
128 """ Check if this registry contains any records. """
129 return len(self._data) > 0
131 def getPreservedLibs(self):
132 """ Return a mapping of packages->preserved objects.
133 @returns mapping of package instances to preserved objects
134 @rtype Dict cpv->list-of-paths
136 rValue = {}
137 for cps in self._data:
138 rValue[self._data[cps][0]] = self._data[cps][2]
139 return rValue
141 class LinkageMap(object):
143 """Models dynamic linker dependencies."""
145 def __init__(self, vardbapi):
146 self._dbapi = vardbapi
147 self._libs = {}
148 self._obj_properties = {}
149 self._defpath = set(getlibpaths())
150 self._obj_key_cache = {}
152 class _ObjectKey(object):
154 """Helper class used as _obj_properties keys for objects."""
156 def __init__(self, object):
158 This takes a path to an object.
160 @param object: path to a file
161 @type object: string (example: '/usr/bin/bar')
164 self._key = self._generate_object_key(object)
166 def __hash__(self):
167 return hash(self._key)
169 def __eq__(self, other):
170 return self._key == other._key
172 def __ne__(self, other):
173 return self._key != other._key
175 def _generate_object_key(self, object):
177 Generate object key for a given object.
179 @param object: path to a file
180 @type object: string (example: '/usr/bin/bar')
181 @rtype: 2-tuple of types (long, int) if object exists. string if
182 object does not exist.
183 @return:
184 1. 2-tuple of object's inode and device from a stat call, if object
185 exists.
186 2. realpath of object if object does not exist.
189 try:
190 object_stat = os.stat(object)
191 except OSError:
192 # Use the realpath as the key if the file does not exists on the
193 # filesystem.
194 return os.path.realpath(object)
195 # Return a tuple of the device and inode.
196 return (object_stat.st_dev, object_stat.st_ino)
198 def file_exists(self):
200 Determine if the file for this key exists on the filesystem.
202 @rtype: Boolean
203 @return:
204 1. True if the file exists.
205 2. False if the file does not exist or is a broken symlink.
208 return isinstance(self._key, tuple)
210 def rebuild(self, include_file=None):
211 libs = {}
212 obj_key_cache = {}
213 obj_properties = {}
214 lines = []
215 for cpv in self._dbapi.cpv_all():
216 lines += self._dbapi.aux_get(cpv, ["NEEDED.ELF.2"])[0].split('\n')
217 # Cache NEEDED.* files avoid doing excessive IO for every rebuild.
218 self._dbapi.flush_cache()
220 if include_file:
221 lines += grabfile(include_file)
223 # have to call scanelf for preserved libs here as they aren't
224 # registered in NEEDED.ELF.2 files
225 if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs():
226 args = ["/usr/bin/scanelf", "-qF", "%a;%F;%S;%r;%n"]
227 for items in self._dbapi.plib_registry.getPreservedLibs().values():
228 args += [x.lstrip(".") for x in items]
229 proc = subprocess.Popen(args, stdout=subprocess.PIPE)
230 output = [l[3:] for l in proc.communicate()[0].split("\n")]
231 lines += output
233 for l in lines:
234 if l.strip() == "":
235 continue
236 fields = l.strip("\n").split(";")
237 if len(fields) < 5:
238 print "Error", fields
239 # insufficient field length
240 continue
241 arch = fields[0]
242 obj = fields[1]
243 obj_key = self._ObjectKey(obj)
244 soname = fields[2]
245 path = set([normalize_path(x)
246 for x in filter(None, fields[3].replace(
247 "${ORIGIN}", os.path.dirname(obj)).replace(
248 "$ORIGIN", os.path.dirname(obj)).split(":"))])
249 needed = filter(None, fields[4].split(","))
250 if soname:
251 libs.setdefault(soname, \
252 {arch: {"providers": set(), "consumers": set()}})
253 libs[soname].setdefault(arch, \
254 {"providers": set(), "consumers": set()})
255 libs[soname][arch]["providers"].add(obj_key)
256 for x in needed:
257 libs.setdefault(x, \
258 {arch: {"providers": set(), "consumers": set()}})
259 libs[x].setdefault(arch, {"providers": set(), "consumers": set()})
260 libs[x][arch]["consumers"].add(obj_key)
261 obj_key_cache.setdefault(obj, obj_key)
262 # All object paths are added into the obj_properties tuple
263 obj_properties.setdefault(obj_key, \
264 (arch, needed, path, soname, set()))[4].add(obj)
266 self._libs = libs
267 self._obj_properties = obj_properties
268 self._obj_key_cache = obj_key_cache
270 def listBrokenBinaries(self, debug=False):
272 Find binaries and their needed sonames, which have no providers.
274 @param debug: Boolean to enable debug output
275 @type debug: Boolean
276 @rtype: dict (example: {'/usr/bin/foo': set(['libbar.so'])})
277 @return: The return value is an object -> set-of-sonames mapping, where
278 object is a broken binary and the set consists of sonames needed by
279 object that have no corresponding libraries to fulfill the dependency.
282 class _LibraryCache(object):
285 Caches properties associated with paths.
287 The purpose of this class is to prevent multiple instances of
288 _ObjectKey for the same paths.
292 def __init__(cache_self):
293 cache_self.cache = {}
295 def get(cache_self, obj):
297 Caches and returns properties associated with an object.
299 @param obj: absolute path (can be symlink)
300 @type obj: string (example: '/usr/lib/libfoo.so')
301 @rtype: 4-tuple with types
302 (string or None, string or None, 2-tuple, Boolean)
303 @return: 4-tuple with the following components:
304 1. arch as a string or None if it does not exist,
305 2. soname as a string or None if it does not exist,
306 3. obj_key as 2-tuple,
307 4. Boolean representing whether the object exists.
308 (example: ('libfoo.so.1', (123L, 456L), True))
311 if obj in cache_self.cache:
312 return cache_self.cache[obj]
313 else:
314 if obj in self._obj_key_cache:
315 obj_key = self._obj_key_cache.get(obj)
316 else:
317 obj_key = self._ObjectKey(obj)
318 # Check that the library exists on the filesystem.
319 if obj_key.file_exists():
320 # Get the arch and soname from LinkageMap._obj_properties if
321 # it exists. Otherwise, None.
322 arch, _, _, soname, _ = \
323 self._obj_properties.get(obj_key, (None,)*5)
324 return cache_self.cache.setdefault(obj, \
325 (arch, soname, obj_key, True))
326 else:
327 return cache_self.cache.setdefault(obj, \
328 (None, None, obj_key, False))
330 rValue = {}
331 cache = _LibraryCache()
332 providers = self.listProviders()
334 # Iterate over all obj_keys and their providers.
335 for obj_key, sonames in providers.items():
336 arch, _, path, _, objs = self._obj_properties[obj_key]
337 path = path.union(self._defpath)
338 # Iterate over each needed soname and the set of library paths that
339 # fulfill the soname to determine if the dependency is broken.
340 for soname, libraries in sonames.items():
341 # validLibraries is used to store libraries, which satisfy soname,
342 # so if no valid libraries are found, the soname is not satisfied
343 # for obj_key. If unsatisfied, objects associated with obj_key
344 # must be emerged.
345 validLibraries = set()
346 # It could be the case that the library to satisfy the soname is
347 # not in the obj's runpath, but a symlink to the library is (eg
348 # libnvidia-tls.so.1 in nvidia-drivers). Also, since LinkageMap
349 # does not catalog symlinks, broken or missing symlinks may go
350 # unnoticed. As a result of these cases, check that a file with
351 # the same name as the soname exists in obj's runpath.
352 # XXX If we catalog symlinks in LinkageMap, this could be improved.
353 for directory in path:
354 cachedArch, cachedSoname, cachedKey, cachedExists = \
355 cache.get(os.path.join(directory, soname))
356 # Check that this library provides the needed soname. Doing
357 # this, however, will cause consumers of libraries missing
358 # sonames to be unnecessarily emerged. (eg libmix.so)
359 if cachedSoname == soname and cachedArch == arch:
360 validLibraries.add(cachedKey)
361 if debug and cachedKey not in \
362 set(map(self._obj_key_cache.get, libraries)):
363 # XXX This is most often due to soname symlinks not in
364 # a library's directory. We could catalog symlinks in
365 # LinkageMap to avoid checking for this edge case here.
366 print "Found provider outside of findProviders:", \
367 os.path.join(directory, soname), "->", \
368 self._obj_properties[cachedKey][4], libraries
369 # A valid library has been found, so there is no need to
370 # continue.
371 break
372 if debug and cachedArch == arch and \
373 cachedKey in self._obj_properties:
374 print "Broken symlink or missing/bad soname:", \
375 os.path.join(directory, soname), '->', \
376 self._obj_properties[cachedKey], "with soname", \
377 cachedSoname, "but expecting", soname
378 # This conditional checks if there are no libraries to satisfy the
379 # soname (empty set).
380 if not validLibraries:
381 for obj in objs:
382 rValue.setdefault(obj, set()).add(soname)
383 # If no valid libraries have been found by this point, then
384 # there are no files named with the soname within obj's runpath,
385 # but if there are libraries (from the providers mapping), it is
386 # likely that soname symlinks or the actual libraries are
387 # missing or broken. Thus those libraries are added to rValue
388 # in order to emerge corrupt library packages.
389 for lib in libraries:
390 rValue.setdefault(lib, set()).add(soname)
391 if debug:
392 if not os.path.isfile(lib):
393 print "Missing library:", lib
394 else:
395 print "Possibly missing symlink:", \
396 os.path.join(os.path.dirname(lib), soname)
397 return rValue
399 def listProviders(self):
401 Find the providers for all object keys in LinkageMap.
403 @rtype: dict (example:
404 {(123L, 456L): {'libbar.so': set(['/lib/libbar.so.1.5'])}})
405 @return: The return value is an object key -> providers mapping, where
406 providers is a mapping of soname -> set-of-library-paths returned
407 from the findProviders method.
410 rValue = {}
411 if not self._libs:
412 self.rebuild()
413 # Iterate over all object keys within LinkageMap.
414 for obj_key in self._obj_properties:
415 rValue.setdefault(obj_key, self.findProviders(obj_key))
416 return rValue
418 def isMasterLink(self, obj):
420 Determine whether an object is a master link.
422 @param obj: absolute path to an object
423 @type obj: string (example: '/usr/bin/foo')
424 @rtype: Boolean
425 @return:
426 1. True if obj is a master link
427 2. False if obj is not a master link
430 basename = os.path.basename(obj)
431 obj_key = self._ObjectKey(obj)
432 if obj_key not in self._obj_properties:
433 raise KeyError("%s (%s) not in object list" % (obj_key, obj))
434 soname = self._obj_properties[obj_key][3]
435 return (len(basename) < len(soname))
437 def listLibraryObjects(self):
439 Return a list of library objects.
441 Known limitation: library objects lacking an soname are not included.
443 @rtype: list of strings
444 @return: list of paths to all providers
447 rValue = []
448 if not self._libs:
449 self.rebuild()
450 for soname in self._libs:
451 for arch in self._libs[soname]:
452 for obj_key in self._libs[soname][arch]["providers"]:
453 rValue.extend(self._obj_properties[obj_key][4])
454 return rValue
456 def getSoname(self, obj):
458 Return the soname associated with an object.
460 @param obj: absolute path to an object
461 @type obj: string (example: '/usr/bin/bar')
462 @rtype: string
463 @return: soname as a string
466 if not self._libs:
467 self.rebuild()
468 if obj not in self._obj_key_cache:
469 raise KeyError("%s not in object list" % obj)
470 return self._obj_properties[self._obj_key_cache[obj]][3]
472 def findProviders(self, obj):
474 Find providers for an object or object key.
476 This method may be called with a key from _obj_properties.
478 In some cases, not all valid libraries are returned. This may occur when
479 an soname symlink referencing a library is in an object's runpath while
480 the actual library is not. We should consider cataloging symlinks within
481 LinkageMap as this would avoid those cases and would be a better model of
482 library dependencies (since the dynamic linker actually searches for
483 files named with the soname in the runpaths).
485 @param obj: absolute path to an object or a key from _obj_properties
486 @type obj: string (example: '/usr/bin/bar') or _ObjectKey
487 @rtype: dict (example: {'libbar.so': set(['/lib/libbar.so.1.5'])})
488 @return: The return value is a soname -> set-of-library-paths, where
489 set-of-library-paths satisfy soname.
492 rValue = {}
494 if not self._libs:
495 self.rebuild()
497 # Determine the obj_key from the arguments.
498 if isinstance(obj, self._ObjectKey):
499 obj_key = obj
500 if obj_key not in self._obj_properties:
501 raise KeyError("%s not in object list" % obj_key)
502 else:
503 obj_key = self._obj_key_cache.get(obj)
504 if obj_key not in self._obj_properties:
505 obj_key = self._ObjectKey(obj)
506 if obj_key not in self._obj_properties:
507 raise KeyError("%s (%s) not in object list" % (obj_key, obj))
509 arch, needed, path, _, _ = self._obj_properties[obj_key]
510 path = path.union(self._defpath)
511 for soname in needed:
512 rValue[soname] = set()
513 if soname not in self._libs or arch not in self._libs[soname]:
514 continue
515 # For each potential provider of the soname, add it to rValue if it
516 # resides in the obj's runpath.
517 for provider_key in self._libs[soname][arch]["providers"]:
518 providers = self._obj_properties[provider_key][4]
519 for provider in providers:
520 if os.path.dirname(provider) in path:
521 rValue[soname].add(provider)
522 return rValue
524 def findConsumers(self, obj):
526 Find consumers of an object or object key.
528 This method may be called with a key from _obj_properties.
530 In some cases, not all consumers are returned. This may occur when
531 an soname symlink referencing a library is in an object's runpath while
532 the actual library is not.
534 @param obj: absolute path to an object or a key from _obj_properties
535 @type obj: string (example: '/usr/bin/bar') or _ObjectKey
536 @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar']))
537 @return: The return value is a soname -> set-of-library-paths, where
538 set-of-library-paths satisfy soname.
541 rValue = set()
543 if not self._libs:
544 self.rebuild()
546 # Determine the obj_key and the set of objects matching the arguments.
547 if isinstance(obj, self._ObjectKey):
548 obj_key = obj
549 if obj_key not in self._obj_properties:
550 raise KeyError("%s not in object list" % obj_key)
551 objs = self._obj_properties[obj_key][4]
552 else:
553 objs = set([obj])
554 obj_key = self._obj_key_cache.get(obj)
555 if obj_key not in self._obj_properties:
556 obj_key = self._ObjectKey(obj)
557 if obj_key not in self._obj_properties:
558 raise KeyError("%s (%s) not in object list" % (obj_key, obj))
560 # Determine the directory(ies) from the set of objects.
561 objs_dirs = set([os.path.dirname(x) for x in objs])
563 # If there is another version of this lib with the
564 # same soname and the master link points to that
565 # other version, this lib will be shadowed and won't
566 # have any consumers.
567 soname = self._obj_properties[obj_key][3]
568 shadowed_library = True
569 for obj_dir in objs_dirs:
570 master_link = os.path.join(obj_dir, soname)
571 master_link_obj_key = self._ObjectKey(master_link)
572 if obj_key == master_link_obj_key:
573 shadowed_library = False
574 break
575 if shadowed_library:
576 return set()
578 arch, _, _, soname, _ = self._obj_properties[obj_key]
579 if soname in self._libs and arch in self._libs[soname]:
580 # For each potential consumer, add it to rValue if an object from the
581 # arguments resides in the consumer's runpath.
582 for consumer_key in self._libs[soname][arch]["consumers"]:
583 _, _, path, _, consumer_objs = \
584 self._obj_properties[consumer_key]
585 path = path.union(self._defpath)
586 if objs_dirs.intersection(path):
587 rValue.update(consumer_objs)
588 return rValue
590 class vardbapi(dbapi):
592 _excluded_dirs = ["CVS", "lost+found"]
593 _excluded_dirs = [re.escape(x) for x in _excluded_dirs]
594 _excluded_dirs = re.compile(r'^(\..*|-MERGING-.*|' + \
595 "|".join(_excluded_dirs) + r')$')
597 _aux_cache_version = "1"
598 _owners_cache_version = "1"
600 # Number of uncached packages to trigger cache update, since
601 # it's wasteful to update it for every vdb change.
602 _aux_cache_threshold = 5
604 _aux_cache_keys_re = re.compile(r'^NEEDED\..*$')
605 _aux_multi_line_re = re.compile(r'^(CONTENTS|NEEDED\..*)$')
607 def __init__(self, root, categories=None, settings=None, vartree=None):
609 The categories parameter is unused since the dbapi class
610 now has a categories property that is generated from the
611 available packages.
613 self.root = root[:]
615 #cache for category directory mtimes
616 self.mtdircache = {}
618 #cache for dependency checks
619 self.matchcache = {}
621 #cache for cp_list results
622 self.cpcache = {}
624 self.blockers = None
625 if settings is None:
626 from portage import settings
627 self.settings = settings
628 if vartree is None:
629 from portage import db
630 vartree = db[root]["vartree"]
631 self.vartree = vartree
632 self._aux_cache_keys = set(
633 ["CHOST", "COUNTER", "DEPEND", "DESCRIPTION",
634 "EAPI", "HOMEPAGE", "IUSE", "KEYWORDS",
635 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
636 "repository", "RESTRICT" , "SLOT", "USE"])
637 self._aux_cache_obj = None
638 self._aux_cache_filename = os.path.join(self.root,
639 CACHE_PATH.lstrip(os.path.sep), "vdb_metadata.pickle")
640 self._counter_path = os.path.join(root,
641 CACHE_PATH.lstrip(os.path.sep), "counter")
643 try:
644 self.plib_registry = PreservedLibsRegistry(
645 os.path.join(self.root, PRIVATE_PATH, "preserved_libs_registry"))
646 except PermissionDenied:
647 # apparently this user isn't allowed to access PRIVATE_PATH
648 self.plib_registry = None
650 self.linkmap = LinkageMap(self)
651 self._owners = self._owners_db(self)
653 def getpath(self, mykey, filename=None):
654 rValue = os.path.join(self.root, VDB_PATH, mykey)
655 if filename != None:
656 rValue = os.path.join(rValue, filename)
657 return rValue
659 def cpv_exists(self, mykey):
660 "Tells us whether an actual ebuild exists on disk (no masking)"
661 return os.path.exists(self.getpath(mykey))
663 def cpv_counter(self, mycpv):
664 "This method will grab the COUNTER. Returns a counter value."
665 try:
666 return long(self.aux_get(mycpv, ["COUNTER"])[0])
667 except (KeyError, ValueError):
668 pass
669 cdir = self.getpath(mycpv)
670 cpath = self.getpath(mycpv, filename="COUNTER")
672 # We write our new counter value to a new file that gets moved into
673 # place to avoid filesystem corruption on XFS (unexpected reboot.)
674 corrupted = 0
675 if os.path.exists(cpath):
676 cfile = open(cpath, "r")
677 try:
678 counter = long(cfile.readline())
679 except ValueError:
680 print "portage: COUNTER for", mycpv, "was corrupted; resetting to value of 0"
681 counter = long(0)
682 corrupted = 1
683 cfile.close()
684 elif os.path.exists(cdir):
685 mys = pkgsplit(mycpv)
686 myl = self.match(mys[0], use_cache=0)
687 print mys, myl
688 if len(myl) == 1:
689 try:
690 # Only one package... Counter doesn't matter.
691 write_atomic(cpath, "1")
692 counter = 1
693 except SystemExit, e:
694 raise
695 except Exception, e:
696 writemsg("!!! COUNTER file is missing for "+str(mycpv)+" in /var/db.\n",
697 noiselevel=-1)
698 writemsg("!!! Please run %s/fix-db.py or\n" % PORTAGE_BIN_PATH,
699 noiselevel=-1)
700 writemsg("!!! unmerge this exact version.\n", noiselevel=-1)
701 writemsg("!!! %s\n" % e, noiselevel=-1)
702 sys.exit(1)
703 else:
704 writemsg("!!! COUNTER file is missing for "+str(mycpv)+" in /var/db.\n",
705 noiselevel=-1)
706 writemsg("!!! Please run %s/fix-db.py or\n" % PORTAGE_BIN_PATH,
707 noiselevel=-1)
708 writemsg("!!! remerge the package.\n", noiselevel=-1)
709 sys.exit(1)
710 else:
711 counter = long(0)
712 if corrupted:
713 # update new global counter file
714 write_atomic(cpath, str(counter))
715 return counter
717 def cpv_inject(self, mycpv):
718 "injects a real package into our on-disk database; assumes mycpv is valid and doesn't already exist"
719 os.makedirs(self.getpath(mycpv))
720 counter = self.counter_tick(self.root, mycpv=mycpv)
721 # write local package counter so that emerge clean does the right thing
722 write_atomic(self.getpath(mycpv, filename="COUNTER"), str(counter))
724 def isInjected(self, mycpv):
725 if self.cpv_exists(mycpv):
726 if os.path.exists(self.getpath(mycpv, filename="INJECTED")):
727 return True
728 if not os.path.exists(self.getpath(mycpv, filename="CONTENTS")):
729 return True
730 return False
732 def move_ent(self, mylist):
733 origcp = mylist[1]
734 newcp = mylist[2]
736 # sanity check
737 for cp in [origcp, newcp]:
738 if not (isvalidatom(cp) and isjustname(cp)):
739 raise InvalidPackageName(cp)
740 origmatches = self.match(origcp, use_cache=0)
741 moves = 0
742 if not origmatches:
743 return moves
744 for mycpv in origmatches:
745 mycpsplit = catpkgsplit(mycpv)
746 mynewcpv = newcp + "-" + mycpsplit[2]
747 mynewcat = newcp.split("/")[0]
748 if mycpsplit[3] != "r0":
749 mynewcpv += "-" + mycpsplit[3]
750 mycpsplit_new = catpkgsplit(mynewcpv)
751 origpath = self.getpath(mycpv)
752 if not os.path.exists(origpath):
753 continue
754 moves += 1
755 if not os.path.exists(self.getpath(mynewcat)):
756 #create the directory
757 os.makedirs(self.getpath(mynewcat))
758 newpath = self.getpath(mynewcpv)
759 if os.path.exists(newpath):
760 #dest already exists; keep this puppy where it is.
761 continue
762 _movefile(origpath, newpath, mysettings=self.settings)
764 # We need to rename the ebuild now.
765 old_pf = catsplit(mycpv)[1]
766 new_pf = catsplit(mynewcpv)[1]
767 if new_pf != old_pf:
768 try:
769 os.rename(os.path.join(newpath, old_pf + ".ebuild"),
770 os.path.join(newpath, new_pf + ".ebuild"))
771 except EnvironmentError, e:
772 if e.errno != errno.ENOENT:
773 raise
774 del e
775 write_atomic(os.path.join(newpath, "PF"), new_pf+"\n")
776 write_atomic(os.path.join(newpath, "CATEGORY"), mynewcat+"\n")
777 fixdbentries([mylist], newpath)
778 return moves
780 def cp_list(self, mycp, use_cache=1):
781 mysplit=catsplit(mycp)
782 if mysplit[0] == '*':
783 mysplit[0] = mysplit[0][1:]
784 try:
785 mystat = os.stat(self.getpath(mysplit[0]))[stat.ST_MTIME]
786 except OSError:
787 mystat = 0
788 if use_cache and mycp in self.cpcache:
789 cpc = self.cpcache[mycp]
790 if cpc[0] == mystat:
791 return cpc[1][:]
792 cat_dir = self.getpath(mysplit[0])
793 try:
794 dir_list = os.listdir(cat_dir)
795 except EnvironmentError, e:
796 if e.errno == PermissionDenied.errno:
797 raise PermissionDenied(cat_dir)
798 del e
799 dir_list = []
801 returnme = []
802 for x in dir_list:
803 if self._excluded_dirs.match(x) is not None:
804 continue
805 ps = pkgsplit(x)
806 if not ps:
807 self.invalidentry(os.path.join(self.getpath(mysplit[0]), x))
808 continue
809 if len(mysplit) > 1:
810 if ps[0] == mysplit[1]:
811 returnme.append(mysplit[0]+"/"+x)
812 self._cpv_sort_ascending(returnme)
813 if use_cache:
814 self.cpcache[mycp] = [mystat, returnme[:]]
815 elif mycp in self.cpcache:
816 del self.cpcache[mycp]
817 return returnme
819 def cpv_all(self, use_cache=1):
821 Set use_cache=0 to bypass the portage.cachedir() cache in cases
822 when the accuracy of mtime staleness checks should not be trusted
823 (generally this is only necessary in critical sections that
824 involve merge or unmerge of packages).
826 returnme = []
827 basepath = os.path.join(self.root, VDB_PATH) + os.path.sep
829 if use_cache:
830 from portage import listdir
831 else:
832 def listdir(p, **kwargs):
833 try:
834 return [x for x in os.listdir(p) \
835 if os.path.isdir(os.path.join(p, x))]
836 except EnvironmentError, e:
837 if e.errno == PermissionDenied.errno:
838 raise PermissionDenied(p)
839 del e
840 return []
842 for x in listdir(basepath, EmptyOnError=1, ignorecvs=1, dirsonly=1):
843 if self._excluded_dirs.match(x) is not None:
844 continue
845 if not self._category_re.match(x):
846 continue
847 for y in listdir(basepath + x, EmptyOnError=1, dirsonly=1):
848 if self._excluded_dirs.match(y) is not None:
849 continue
850 subpath = x + "/" + y
851 # -MERGING- should never be a cpv, nor should files.
852 try:
853 if catpkgsplit(subpath) is None:
854 self.invalidentry(os.path.join(self.root, subpath))
855 continue
856 except InvalidData:
857 self.invalidentry(os.path.join(self.root, subpath))
858 continue
859 returnme.append(subpath)
860 return returnme
862 def cp_all(self, use_cache=1):
863 mylist = self.cpv_all(use_cache=use_cache)
864 d={}
865 for y in mylist:
866 if y[0] == '*':
867 y = y[1:]
868 try:
869 mysplit = catpkgsplit(y)
870 except InvalidData:
871 self.invalidentry(self.getpath(y))
872 continue
873 if not mysplit:
874 self.invalidentry(self.getpath(y))
875 continue
876 d[mysplit[0]+"/"+mysplit[1]] = None
877 return d.keys()
879 def checkblockers(self, origdep):
880 pass
882 def _clear_cache(self):
883 self.mtdircache.clear()
884 self.matchcache.clear()
885 self.cpcache.clear()
886 self._aux_cache_obj = None
888 def _add(self, pkg_dblink):
889 self._clear_pkg_cache(pkg_dblink)
891 def _remove(self, pkg_dblink):
892 self._clear_pkg_cache(pkg_dblink)
894 def _clear_pkg_cache(self, pkg_dblink):
895 # Due to 1 second mtime granularity in <python-2.5, mtime checks
896 # are not always sufficient to invalidate vardbapi caches. Therefore,
897 # the caches need to be actively invalidated here.
898 self.mtdircache.pop(pkg_dblink.cat, None)
899 self.matchcache.pop(pkg_dblink.cat, None)
900 self.cpcache.pop(pkg_dblink.mysplit[0], None)
901 from portage import dircache
902 dircache.pop(pkg_dblink.dbcatdir, None)
904 def match(self, origdep, use_cache=1):
905 "caching match function"
906 mydep = dep_expand(
907 origdep, mydb=self, use_cache=use_cache, settings=self.settings)
908 mykey = dep_getkey(mydep)
909 mycat = catsplit(mykey)[0]
910 if not use_cache:
911 if mycat in self.matchcache:
912 del self.mtdircache[mycat]
913 del self.matchcache[mycat]
914 return list(self._iter_match(mydep,
915 self.cp_list(mydep.cp, use_cache=use_cache)))
916 try:
917 curmtime = os.stat(self.root+VDB_PATH+"/"+mycat).st_mtime
918 except (IOError, OSError):
919 curmtime=0
921 if mycat not in self.matchcache or \
922 self.mtdircache[mycat] != curmtime:
923 # clear cache entry
924 self.mtdircache[mycat] = curmtime
925 self.matchcache[mycat] = {}
926 if mydep not in self.matchcache[mycat]:
927 mymatch = list(self._iter_match(mydep,
928 self.cp_list(mydep.cp, use_cache=use_cache)))
929 self.matchcache[mycat][mydep] = mymatch
930 return self.matchcache[mycat][mydep][:]
932 def findname(self, mycpv):
933 return self.getpath(str(mycpv), filename=catsplit(mycpv)[1]+".ebuild")
935 def flush_cache(self):
936 """If the current user has permission and the internal aux_get cache has
937 been updated, save it to disk and mark it unmodified. This is called
938 by emerge after it has loaded the full vdb for use in dependency
939 calculations. Currently, the cache is only written if the user has
940 superuser privileges (since that's required to obtain a lock), but all
941 users have read access and benefit from faster metadata lookups (as
942 long as at least part of the cache is still valid)."""
943 if self._aux_cache is not None and \
944 len(self._aux_cache["modified"]) >= self._aux_cache_threshold and \
945 secpass >= 2:
946 self._owners.populate() # index any unindexed contents
947 valid_nodes = set(self.cpv_all())
948 for cpv in self._aux_cache["packages"].keys():
949 if cpv not in valid_nodes:
950 del self._aux_cache["packages"][cpv]
951 del self._aux_cache["modified"]
952 try:
953 f = atomic_ofstream(self._aux_cache_filename)
954 cPickle.dump(self._aux_cache, f, -1)
955 f.close()
956 apply_secpass_permissions(
957 self._aux_cache_filename, gid=portage_gid, mode=0644)
958 except (IOError, OSError), e:
959 pass
960 self._aux_cache["modified"] = set()
962 @property
963 def _aux_cache(self):
964 if self._aux_cache_obj is None:
965 self._aux_cache_init()
966 return self._aux_cache_obj
968 def _aux_cache_init(self):
969 aux_cache = None
970 try:
971 f = open(self._aux_cache_filename)
972 mypickle = cPickle.Unpickler(f)
973 mypickle.find_global = None
974 aux_cache = mypickle.load()
975 f.close()
976 del f
977 except (IOError, OSError, EOFError, cPickle.UnpicklingError), e:
978 if isinstance(e, cPickle.UnpicklingError):
979 writemsg("!!! Error loading '%s': %s\n" % \
980 (self._aux_cache_filename, str(e)), noiselevel=-1)
981 del e
983 if not aux_cache or \
984 not isinstance(aux_cache, dict) or \
985 aux_cache.get("version") != self._aux_cache_version or \
986 not aux_cache.get("packages"):
987 aux_cache = {"version": self._aux_cache_version}
988 aux_cache["packages"] = {}
990 owners = aux_cache.get("owners")
991 if owners is not None:
992 if not isinstance(owners, dict):
993 owners = None
994 elif "version" not in owners:
995 owners = None
996 elif owners["version"] != self._owners_cache_version:
997 owners = None
998 elif "base_names" not in owners:
999 owners = None
1000 elif not isinstance(owners["base_names"], dict):
1001 owners = None
1003 if owners is None:
1004 owners = {
1005 "base_names" : {},
1006 "version" : self._owners_cache_version
1008 aux_cache["owners"] = owners
1010 aux_cache["modified"] = set()
1011 self._aux_cache_obj = aux_cache
1013 def aux_get(self, mycpv, wants):
1014 """This automatically caches selected keys that are frequently needed
1015 by emerge for dependency calculations. The cached metadata is
1016 considered valid if the mtime of the package directory has not changed
1017 since the data was cached. The cache is stored in a pickled dict
1018 object with the following format:
1020 {version:"1", "packages":{cpv1:(mtime,{k1,v1, k2,v2, ...}), cpv2...}}
1022 If an error occurs while loading the cache pickle or the version is
1023 unrecognized, the cache will simple be recreated from scratch (it is
1024 completely disposable).
1026 cache_these_wants = self._aux_cache_keys.intersection(wants)
1027 for x in wants:
1028 if self._aux_cache_keys_re.match(x) is not None:
1029 cache_these_wants.add(x)
1031 if not cache_these_wants:
1032 return self._aux_get(mycpv, wants)
1034 cache_these = set(self._aux_cache_keys)
1035 cache_these.update(cache_these_wants)
1037 mydir = self.getpath(mycpv)
1038 mydir_stat = None
1039 try:
1040 mydir_stat = os.stat(mydir)
1041 except OSError, e:
1042 if e.errno != errno.ENOENT:
1043 raise
1044 raise KeyError(mycpv)
1045 mydir_mtime = long(mydir_stat.st_mtime)
1046 pkg_data = self._aux_cache["packages"].get(mycpv)
1047 pull_me = cache_these.union(wants)
1048 mydata = {"_mtime_" : mydir_mtime}
1049 cache_valid = False
1050 cache_incomplete = False
1051 cache_mtime = None
1052 metadata = None
1053 if pkg_data is not None:
1054 if not isinstance(pkg_data, tuple) or len(pkg_data) != 2:
1055 pkg_data = None
1056 else:
1057 cache_mtime, metadata = pkg_data
1058 if not isinstance(cache_mtime, (long, int)) or \
1059 not isinstance(metadata, dict):
1060 pkg_data = None
1062 if pkg_data:
1063 cache_mtime, metadata = pkg_data
1064 cache_valid = cache_mtime == mydir_mtime
1065 if cache_valid:
1066 mydata.update(metadata)
1067 pull_me.difference_update(mydata)
1069 if pull_me:
1070 # pull any needed data and cache it
1071 aux_keys = list(pull_me)
1072 for k, v in izip(aux_keys,
1073 self._aux_get(mycpv, aux_keys, st=mydir_stat)):
1074 mydata[k] = v
1075 if not cache_valid or cache_these.difference(metadata):
1076 cache_data = {}
1077 if cache_valid and metadata:
1078 cache_data.update(metadata)
1079 for aux_key in cache_these:
1080 cache_data[aux_key] = mydata[aux_key]
1081 self._aux_cache["packages"][mycpv] = (mydir_mtime, cache_data)
1082 self._aux_cache["modified"].add(mycpv)
1083 return [mydata[x] for x in wants]
1085 def _aux_get(self, mycpv, wants, st=None):
1086 mydir = self.getpath(mycpv)
1087 if st is None:
1088 try:
1089 st = os.stat(mydir)
1090 except OSError, e:
1091 if e.errno == errno.ENOENT:
1092 raise KeyError(mycpv)
1093 elif e.errno == PermissionDenied.errno:
1094 raise PermissionDenied(mydir)
1095 else:
1096 raise
1097 if not stat.S_ISDIR(st.st_mode):
1098 raise KeyError(mycpv)
1099 results = []
1100 for x in wants:
1101 if x == "_mtime_":
1102 results.append(st.st_mtime)
1103 continue
1104 try:
1105 myf = open(os.path.join(mydir, x), "r")
1106 try:
1107 myd = myf.read()
1108 finally:
1109 myf.close()
1110 # Preserve \n for metadata that is known to
1111 # contain multiple lines.
1112 if self._aux_multi_line_re.match(x) is None:
1113 myd = " ".join(myd.split())
1114 except IOError:
1115 myd = ""
1116 if x == "EAPI" and not myd:
1117 results.append("0")
1118 else:
1119 results.append(myd)
1120 return results
1122 def aux_update(self, cpv, values):
1123 cat, pkg = catsplit(cpv)
1124 mylink = dblink(cat, pkg, self.root, self.settings,
1125 treetype="vartree", vartree=self.vartree)
1126 if not mylink.exists():
1127 raise KeyError(cpv)
1128 for k, v in values.iteritems():
1129 if v:
1130 mylink.setfile(k, v)
1131 else:
1132 try:
1133 os.unlink(os.path.join(self.getpath(cpv), k))
1134 except EnvironmentError:
1135 pass
1137 def counter_tick(self, myroot, mycpv=None):
1138 return self.counter_tick_core(myroot, incrementing=1, mycpv=mycpv)
1140 def get_counter_tick_core(self, myroot, mycpv=None):
1142 Use this method to retrieve the counter instead
1143 of having to trust the value of a global counter
1144 file that can lead to invalid COUNTER
1145 generation. When cache is valid, the package COUNTER
1146 files are not read and we rely on the timestamp of
1147 the package directory to validate cache. The stat
1148 calls should only take a short time, so performance
1149 is sufficient without having to rely on a potentially
1150 corrupt global counter file.
1152 The global counter file located at
1153 $CACHE_PATH/counter serves to record the
1154 counter of the last installed package and
1155 it also corresponds to the total number of
1156 installation actions that have occurred in
1157 the history of this package database.
1159 cp_list = self.cp_list
1160 max_counter = 0
1161 for cp in self.cp_all():
1162 for cpv in cp_list(cp):
1163 try:
1164 counter = int(self.aux_get(cpv, ["COUNTER"])[0])
1165 except (KeyError, OverflowError, ValueError):
1166 continue
1167 if counter > max_counter:
1168 max_counter = counter
1170 new_vdb = False
1171 counter = -1
1172 try:
1173 cfile = open(self._counter_path, "r")
1174 except EnvironmentError, e:
1175 new_vdb = not bool(self.cpv_all())
1176 if not new_vdb:
1177 writemsg("!!! Unable to read COUNTER file: '%s'\n" % \
1178 self._counter_path, noiselevel=-1)
1179 writemsg("!!! %s\n" % str(e), noiselevel=-1)
1180 del e
1181 else:
1182 try:
1183 try:
1184 counter = long(cfile.readline().strip())
1185 finally:
1186 cfile.close()
1187 except (OverflowError, ValueError), e:
1188 writemsg("!!! COUNTER file is corrupt: '%s'\n" % \
1189 self._counter_path, noiselevel=-1)
1190 writemsg("!!! %s\n" % str(e), noiselevel=-1)
1191 del e
1193 # We must ensure that we return a counter
1194 # value that is at least as large as the
1195 # highest one from the installed packages,
1196 # since having a corrupt value that is too low
1197 # can trigger incorrect AUTOCLEAN behavior due
1198 # to newly installed packages having lower
1199 # COUNTERs than the previous version in the
1200 # same slot.
1201 if counter > max_counter:
1202 max_counter = counter
1204 if counter < 0 and not new_vdb:
1205 writemsg("!!! Initializing COUNTER to " + \
1206 "value of %d\n" % max_counter, noiselevel=-1)
1208 return max_counter + 1
1210 def counter_tick_core(self, myroot, incrementing=1, mycpv=None):
1211 "This method will grab the next COUNTER value and record it back to the global file. Returns new counter value."
1212 counter = self.get_counter_tick_core(myroot, mycpv=mycpv) - 1
1213 if incrementing:
1214 #increment counter
1215 counter += 1
1216 # update new global counter file
1217 write_atomic(self._counter_path, str(counter))
1218 return counter
1220 def _dblink(self, cpv):
1221 category, pf = catsplit(cpv)
1222 return dblink(category, pf, self.root,
1223 self.settings, vartree=self.vartree, treetype="vartree")
1225 def removeFromContents(self, pkg, paths, relative_paths=True):
1227 @param pkg: cpv for an installed package
1228 @type pkg: string
1229 @param paths: paths of files to remove from contents
1230 @type paths: iterable
1232 if not hasattr(pkg, "getcontents"):
1233 pkg = self._dblink(pkg)
1234 root = self.root
1235 root_len = len(root) - 1
1236 new_contents = pkg.getcontents().copy()
1237 removed = 0
1239 for filename in paths:
1240 filename = normalize_path(filename)
1241 if relative_paths:
1242 relative_filename = filename
1243 else:
1244 relative_filename = filename[root_len:]
1245 contents_key = pkg._match_contents(relative_filename, root)
1246 if contents_key:
1247 del new_contents[contents_key]
1248 removed += 1
1250 if removed:
1251 f = atomic_ofstream(os.path.join(pkg.dbdir, "CONTENTS"))
1252 write_contents(new_contents, root, f)
1253 f.close()
1255 class _owners_cache(object):
1257 This class maintains an hash table that serves to index package
1258 contents by mapping the basename of file to a list of possible
1259 packages that own it. This is used to optimize owner lookups
1260 by narrowing the search down to a smaller number of packages.
1262 try:
1263 from hashlib import md5 as _new_hash
1264 except ImportError:
1265 from md5 import new as _new_hash
1267 _hash_bits = 16
1268 _hex_chars = _hash_bits / 4
1270 def __init__(self, vardb):
1271 self._vardb = vardb
1273 def add(self, cpv):
1274 root_len = len(self._vardb.root)
1275 contents = self._vardb._dblink(cpv).getcontents()
1276 pkg_hash = self._hash_pkg(cpv)
1277 if not contents:
1278 # Empty path is a code used to represent empty contents.
1279 self._add_path("", pkg_hash)
1280 for x in contents:
1281 self._add_path(x[root_len:], pkg_hash)
1282 self._vardb._aux_cache["modified"].add(cpv)
1284 def _add_path(self, path, pkg_hash):
1286 Empty path is a code that represents empty contents.
1288 if path:
1289 name = os.path.basename(path.rstrip(os.path.sep))
1290 if not name:
1291 return
1292 else:
1293 name = path
1294 name_hash = self._hash_str(name)
1295 base_names = self._vardb._aux_cache["owners"]["base_names"]
1296 pkgs = base_names.get(name_hash)
1297 if pkgs is None:
1298 pkgs = {}
1299 base_names[name_hash] = pkgs
1300 pkgs[pkg_hash] = None
1302 def _hash_str(self, s):
1303 h = self._new_hash()
1304 h.update(s)
1305 h = h.hexdigest()
1306 h = h[-self._hex_chars:]
1307 h = int(h, 16)
1308 return h
1310 def _hash_pkg(self, cpv):
1311 counter, mtime = self._vardb.aux_get(
1312 cpv, ["COUNTER", "_mtime_"])
1313 try:
1314 counter = int(counter)
1315 except ValueError:
1316 counter = 0
1317 return (cpv, counter, mtime)
1319 class _owners_db(object):
1321 def __init__(self, vardb):
1322 self._vardb = vardb
1324 def populate(self):
1325 self._populate()
1327 def _populate(self):
1328 owners_cache = vardbapi._owners_cache(self._vardb)
1329 cached_hashes = set()
1330 base_names = self._vardb._aux_cache["owners"]["base_names"]
1332 # Take inventory of all cached package hashes.
1333 for name, hash_values in base_names.items():
1334 if not isinstance(hash_values, dict):
1335 del base_names[name]
1336 continue
1337 cached_hashes.update(hash_values)
1339 # Create sets of valid package hashes and uncached packages.
1340 uncached_pkgs = set()
1341 hash_pkg = owners_cache._hash_pkg
1342 valid_pkg_hashes = set()
1343 for cpv in self._vardb.cpv_all():
1344 hash_value = hash_pkg(cpv)
1345 valid_pkg_hashes.add(hash_value)
1346 if hash_value not in cached_hashes:
1347 uncached_pkgs.add(cpv)
1349 # Cache any missing packages.
1350 for cpv in uncached_pkgs:
1351 owners_cache.add(cpv)
1353 # Delete any stale cache.
1354 stale_hashes = cached_hashes.difference(valid_pkg_hashes)
1355 if stale_hashes:
1356 for base_name_hash, bucket in base_names.items():
1357 for hash_value in stale_hashes.intersection(bucket):
1358 del bucket[hash_value]
1359 if not bucket:
1360 del base_names[base_name_hash]
1362 return owners_cache
1364 def get_owners(self, path_iter):
1366 @return the owners as a dblink -> set(files) mapping.
1368 owners = {}
1369 for owner, f in self.iter_owners(path_iter):
1370 owned_files = owners.get(owner)
1371 if owned_files is None:
1372 owned_files = set()
1373 owners[owner] = owned_files
1374 owned_files.add(f)
1375 return owners
1377 def getFileOwnerMap(self, path_iter):
1378 owners = self.get_owners(path_iter)
1379 file_owners = {}
1380 for pkg_dblink, files in owners.iteritems():
1381 for f in files:
1382 owner_set = file_owners.get(f)
1383 if owner_set is None:
1384 owner_set = set()
1385 file_owners[f] = owner_set
1386 owner_set.add(pkg_dblink)
1387 return file_owners
1389 def iter_owners(self, path_iter):
1391 Iterate over tuples of (dblink, path). In order to avoid
1392 consuming too many resources for too much time, resources
1393 are only allocated for the duration of a given iter_owners()
1394 call. Therefore, to maximize reuse of resources when searching
1395 for multiple files, it's best to search for them all in a single
1396 call.
1399 owners_cache = self._populate()
1401 vardb = self._vardb
1402 root = vardb.root
1403 hash_pkg = owners_cache._hash_pkg
1404 hash_str = owners_cache._hash_str
1405 base_names = self._vardb._aux_cache["owners"]["base_names"]
1407 dblink_cache = {}
1409 def dblink(cpv):
1410 x = dblink_cache.get(cpv)
1411 if x is None:
1412 x = self._vardb._dblink(cpv)
1413 dblink_cache[cpv] = x
1414 return x
1416 for path in path_iter:
1417 name = os.path.basename(path.rstrip(os.path.sep))
1418 if not name:
1419 continue
1421 name_hash = hash_str(name)
1422 pkgs = base_names.get(name_hash)
1423 if pkgs is not None:
1424 for hash_value in pkgs:
1425 if not isinstance(hash_value, tuple) or \
1426 len(hash_value) != 3:
1427 continue
1428 cpv, counter, mtime = hash_value
1429 if not isinstance(cpv, basestring):
1430 continue
1431 try:
1432 current_hash = hash_pkg(cpv)
1433 except KeyError:
1434 continue
1436 if current_hash != hash_value:
1437 continue
1438 if dblink(cpv).isowner(path, root):
1439 yield dblink(cpv), path
1441 class vartree(object):
1442 "this tree will scan a var/db/pkg database located at root (passed to init)"
1443 def __init__(self, root="/", virtual=None, clone=None, categories=None,
1444 settings=None):
1445 if clone:
1446 writemsg("vartree.__init__(): deprecated " + \
1447 "use of clone parameter\n", noiselevel=-1)
1448 self.root = clone.root[:]
1449 self.dbapi = copy.deepcopy(clone.dbapi)
1450 self.populated = 1
1451 from portage import config
1452 self.settings = config(clone=clone.settings)
1453 else:
1454 self.root = root[:]
1455 if settings is None:
1456 from portage import settings
1457 self.settings = settings # for key_expand calls
1458 if categories is None:
1459 categories = settings.categories
1460 self.dbapi = vardbapi(self.root, categories=categories,
1461 settings=settings, vartree=self)
1462 self.populated = 1
1464 def getpath(self, mykey, filename=None):
1465 return self.dbapi.getpath(mykey, filename=filename)
1467 def zap(self, mycpv):
1468 return
1470 def inject(self, mycpv):
1471 return
1473 def get_provide(self, mycpv):
1474 myprovides = []
1475 mylines = None
1476 try:
1477 mylines, myuse = self.dbapi.aux_get(mycpv, ["PROVIDE", "USE"])
1478 if mylines:
1479 myuse = myuse.split()
1480 mylines = flatten(use_reduce(paren_reduce(mylines), uselist=myuse))
1481 for myprovide in mylines:
1482 mys = catpkgsplit(myprovide)
1483 if not mys:
1484 mys = myprovide.split("/")
1485 myprovides += [mys[0] + "/" + mys[1]]
1486 return myprovides
1487 except SystemExit, e:
1488 raise
1489 except Exception, e:
1490 mydir = os.path.join(self.root, VDB_PATH, mycpv)
1491 writemsg("\nParse Error reading PROVIDE and USE in '%s'\n" % mydir,
1492 noiselevel=-1)
1493 if mylines:
1494 writemsg("Possibly Invalid: '%s'\n" % str(mylines),
1495 noiselevel=-1)
1496 writemsg("Exception: %s\n\n" % str(e), noiselevel=-1)
1497 return []
1499 def get_all_provides(self):
1500 myprovides = {}
1501 for node in self.getallcpv():
1502 for mykey in self.get_provide(node):
1503 if mykey in myprovides:
1504 myprovides[mykey] += [node]
1505 else:
1506 myprovides[mykey] = [node]
1507 return myprovides
1509 def dep_bestmatch(self, mydep, use_cache=1):
1510 "compatibility method -- all matches, not just visible ones"
1511 #mymatch=best(match(dep_expand(mydep,self.dbapi),self.dbapi))
1512 mymatch = best(self.dbapi.match(
1513 dep_expand(mydep, mydb=self.dbapi, settings=self.settings),
1514 use_cache=use_cache))
1515 if mymatch is None:
1516 return ""
1517 else:
1518 return mymatch
1520 def dep_match(self, mydep, use_cache=1):
1521 "compatibility method -- we want to see all matches, not just visible ones"
1522 #mymatch = match(mydep,self.dbapi)
1523 mymatch = self.dbapi.match(mydep, use_cache=use_cache)
1524 if mymatch is None:
1525 return []
1526 else:
1527 return mymatch
1529 def exists_specific(self, cpv):
1530 return self.dbapi.cpv_exists(cpv)
1532 def getallcpv(self):
1533 """temporary function, probably to be renamed --- Gets a list of all
1534 category/package-versions installed on the system."""
1535 return self.dbapi.cpv_all()
1537 def getallnodes(self):
1538 """new behavior: these are all *unmasked* nodes. There may or may not be available
1539 masked package for nodes in this nodes list."""
1540 return self.dbapi.cp_all()
1542 def exists_specific_cat(self, cpv, use_cache=1):
1543 cpv = key_expand(cpv, mydb=self.dbapi, use_cache=use_cache,
1544 settings=self.settings)
1545 a = catpkgsplit(cpv)
1546 if not a:
1547 return 0
1548 mylist = listdir(self.getpath(a[0]), EmptyOnError=1)
1549 for x in mylist:
1550 b = pkgsplit(x)
1551 if not b:
1552 self.dbapi.invalidentry(self.getpath(a[0], filename=x))
1553 continue
1554 if a[1] == b[0]:
1555 return 1
1556 return 0
1558 def getebuildpath(self, fullpackage):
1559 cat, package = catsplit(fullpackage)
1560 return self.getpath(fullpackage, filename=package+".ebuild")
1562 def getnode(self, mykey, use_cache=1):
1563 mykey = key_expand(mykey, mydb=self.dbapi, use_cache=use_cache,
1564 settings=self.settings)
1565 if not mykey:
1566 return []
1567 mysplit = catsplit(mykey)
1568 mydirlist = listdir(self.getpath(mysplit[0]),EmptyOnError=1)
1569 returnme = []
1570 for x in mydirlist:
1571 mypsplit = pkgsplit(x)
1572 if not mypsplit:
1573 self.dbapi.invalidentry(self.getpath(mysplit[0], filename=x))
1574 continue
1575 if mypsplit[0] == mysplit[1]:
1576 appendme = [mysplit[0]+"/"+x, [mysplit[0], mypsplit[0], mypsplit[1], mypsplit[2]]]
1577 returnme.append(appendme)
1578 return returnme
1581 def getslot(self, mycatpkg):
1582 "Get a slot for a catpkg; assume it exists."
1583 try:
1584 return self.dbapi.aux_get(mycatpkg, ["SLOT"])[0]
1585 except KeyError:
1586 return ""
1588 def hasnode(self, mykey, use_cache):
1589 """Does the particular node (cat/pkg key) exist?"""
1590 mykey = key_expand(mykey, mydb=self.dbapi, use_cache=use_cache,
1591 settings=self.settings)
1592 mysplit = catsplit(mykey)
1593 mydirlist = listdir(self.getpath(mysplit[0]), EmptyOnError=1)
1594 for x in mydirlist:
1595 mypsplit = pkgsplit(x)
1596 if not mypsplit:
1597 self.dbapi.invalidentry(self.getpath(mysplit[0], filename=x))
1598 continue
1599 if mypsplit[0] == mysplit[1]:
1600 return 1
1601 return 0
1603 def populate(self):
1604 self.populated=1
1606 class dblink(object):
1608 This class provides an interface to the installed package database
1609 At present this is implemented as a text backend in /var/db/pkg.
1612 import re
1613 _normalize_needed = re.compile(r'.*//.*|^[^/]|.+/$|(^|.*/)\.\.?(/.*|$)')
1614 _contents_split_counts = {
1615 "dev": 2,
1616 "dir": 2,
1617 "fif": 2,
1618 "obj": 4,
1619 "sym": 5
1622 # When looping over files for merge/unmerge, temporarily yield to the
1623 # scheduler each time this many files are processed.
1624 _file_merge_yield_interval = 20
1626 def __init__(self, cat, pkg, myroot, mysettings, treetype=None,
1627 vartree=None, blockers=None, scheduler=None):
1629 Creates a DBlink object for a given CPV.
1630 The given CPV may not be present in the database already.
1632 @param cat: Category
1633 @type cat: String
1634 @param pkg: Package (PV)
1635 @type pkg: String
1636 @param myroot: Typically ${ROOT}
1637 @type myroot: String (Path)
1638 @param mysettings: Typically portage.config
1639 @type mysettings: An instance of portage.config
1640 @param treetype: one of ['porttree','bintree','vartree']
1641 @type treetype: String
1642 @param vartree: an instance of vartree corresponding to myroot.
1643 @type vartree: vartree
1646 self.cat = cat
1647 self.pkg = pkg
1648 self.mycpv = self.cat + "/" + self.pkg
1649 self.mysplit = list(catpkgsplit(self.mycpv)[1:])
1650 self.mysplit[0] = "%s/%s" % (self.cat, self.mysplit[0])
1651 self.treetype = treetype
1652 if vartree is None:
1653 from portage import db
1654 vartree = db[myroot]["vartree"]
1655 self.vartree = vartree
1656 self._blockers = blockers
1657 self._scheduler = scheduler
1659 self.dbroot = normalize_path(os.path.join(myroot, VDB_PATH))
1660 self.dbcatdir = self.dbroot+"/"+cat
1661 self.dbpkgdir = self.dbcatdir+"/"+pkg
1662 self.dbtmpdir = self.dbcatdir+"/-MERGING-"+pkg
1663 self.dbdir = self.dbpkgdir
1665 self._lock_vdb = None
1667 self.settings = mysettings
1668 if self.settings == 1:
1669 raise ValueError
1671 self.myroot=myroot
1672 protect_obj = ConfigProtect(myroot,
1673 mysettings.get("CONFIG_PROTECT","").split(),
1674 mysettings.get("CONFIG_PROTECT_MASK","").split())
1675 self.updateprotect = protect_obj.updateprotect
1676 self.isprotected = protect_obj.isprotected
1677 self._installed_instance = None
1678 self.contentscache = None
1679 self._contents_inodes = None
1680 self._contents_basenames = None
1682 def lockdb(self):
1683 if self._lock_vdb:
1684 raise AssertionError("Lock already held.")
1685 # At least the parent needs to exist for the lock file.
1686 ensure_dirs(self.dbroot)
1687 self._lock_vdb = lockdir(self.dbroot)
1689 def unlockdb(self):
1690 if self._lock_vdb:
1691 unlockdir(self._lock_vdb)
1692 self._lock_vdb = None
1694 def getpath(self):
1695 "return path to location of db information (for >>> informational display)"
1696 return self.dbdir
1698 def exists(self):
1699 "does the db entry exist? boolean."
1700 return os.path.exists(self.dbdir)
1702 def delete(self):
1704 Remove this entry from the database
1706 if not os.path.exists(self.dbdir):
1707 return
1709 # Check validity of self.dbdir before attempting to remove it.
1710 if not self.dbdir.startswith(self.dbroot):
1711 writemsg("portage.dblink.delete(): invalid dbdir: %s\n" % \
1712 self.dbdir, noiselevel=-1)
1713 return
1714 import shutil
1715 shutil.rmtree(self.dbdir)
1716 self.vartree.dbapi._remove(self)
1718 def clearcontents(self):
1720 For a given db entry (self), erase the CONTENTS values.
1722 if os.path.exists(self.dbdir+"/CONTENTS"):
1723 os.unlink(self.dbdir+"/CONTENTS")
1725 def _clear_contents_cache(self):
1726 self.contentscache = None
1727 self._contents_inodes = None
1728 self._contents_basenames = None
1730 def getcontents(self):
1732 Get the installed files of a given package (aka what that package installed)
1734 contents_file = os.path.join(self.dbdir, "CONTENTS")
1735 if self.contentscache is not None:
1736 return self.contentscache
1737 pkgfiles = {}
1738 try:
1739 myc = open(contents_file,"r")
1740 except EnvironmentError, e:
1741 if e.errno != errno.ENOENT:
1742 raise
1743 del e
1744 self.contentscache = pkgfiles
1745 return pkgfiles
1746 mylines = myc.readlines()
1747 myc.close()
1748 null_byte = "\0"
1749 normalize_needed = self._normalize_needed
1750 contents_split_counts = self._contents_split_counts
1751 myroot = self.myroot
1752 if myroot == os.path.sep:
1753 myroot = None
1754 pos = 0
1755 errors = []
1756 for pos, line in enumerate(mylines):
1757 if null_byte in line:
1758 # Null bytes are a common indication of corruption.
1759 errors.append((pos + 1, "Null byte found in CONTENTS entry"))
1760 continue
1761 line = line.rstrip("\n")
1762 # Split on " " so that even file paths that
1763 # end with spaces can be handled.
1764 mydat = line.split(" ")
1765 entry_type = mydat[0] # empty string if line is empty
1766 correct_split_count = contents_split_counts.get(entry_type)
1767 if correct_split_count and len(mydat) > correct_split_count:
1768 # Apparently file paths contain spaces, so reassemble
1769 # the split have the correct_split_count.
1770 newsplit = [entry_type]
1771 spaces_total = len(mydat) - correct_split_count
1772 if entry_type == "sym":
1773 try:
1774 splitter = mydat.index("->", 2, len(mydat) - 2)
1775 except ValueError:
1776 errors.append((pos + 1, "Unrecognized CONTENTS entry"))
1777 continue
1778 spaces_in_path = splitter - 2
1779 spaces_in_target = spaces_total - spaces_in_path
1780 newsplit.append(" ".join(mydat[1:splitter]))
1781 newsplit.append("->")
1782 target_end = splitter + spaces_in_target + 2
1783 newsplit.append(" ".join(mydat[splitter + 1:target_end]))
1784 newsplit.extend(mydat[target_end:])
1785 else:
1786 path_end = spaces_total + 2
1787 newsplit.append(" ".join(mydat[1:path_end]))
1788 newsplit.extend(mydat[path_end:])
1789 mydat = newsplit
1791 # we do this so we can remove from non-root filesystems
1792 # (use the ROOT var to allow maintenance on other partitions)
1793 try:
1794 if normalize_needed.match(mydat[1]):
1795 mydat[1] = normalize_path(mydat[1])
1796 if not mydat[1].startswith(os.path.sep):
1797 mydat[1] = os.path.sep + mydat[1]
1798 if myroot:
1799 mydat[1] = os.path.join(myroot, mydat[1].lstrip(os.path.sep))
1800 if mydat[0] == "obj":
1801 #format: type, mtime, md5sum
1802 pkgfiles[mydat[1]] = [mydat[0], mydat[3], mydat[2]]
1803 elif mydat[0] == "dir":
1804 #format: type
1805 pkgfiles[mydat[1]] = [mydat[0]]
1806 elif mydat[0] == "sym":
1807 #format: type, mtime, dest
1808 pkgfiles[mydat[1]] = [mydat[0], mydat[4], mydat[3]]
1809 elif mydat[0] == "dev":
1810 #format: type
1811 pkgfiles[mydat[1]] = [mydat[0]]
1812 elif mydat[0]=="fif":
1813 #format: type
1814 pkgfiles[mydat[1]] = [mydat[0]]
1815 else:
1816 errors.append((pos + 1, "Unrecognized CONTENTS entry"))
1817 except (KeyError, IndexError):
1818 errors.append((pos + 1, "Unrecognized CONTENTS entry"))
1819 if errors:
1820 writemsg("!!! Parse error in '%s'\n" % contents_file, noiselevel=-1)
1821 for pos, e in errors:
1822 writemsg("!!! line %d: %s\n" % (pos, e), noiselevel=-1)
1823 self.contentscache = pkgfiles
1824 return pkgfiles
1826 def unmerge(self, pkgfiles=None, trimworld=1, cleanup=1,
1827 ldpath_mtimes=None, others_in_slot=None):
1829 Calls prerm
1830 Unmerges a given package (CPV)
1831 calls postrm
1832 calls cleanrm
1833 calls env_update
1835 @param pkgfiles: files to unmerge (generally self.getcontents() )
1836 @type pkgfiles: Dictionary
1837 @param trimworld: Remove CPV from world file if True, not if False
1838 @type trimworld: Boolean
1839 @param cleanup: cleanup to pass to doebuild (see doebuild)
1840 @type cleanup: Boolean
1841 @param ldpath_mtimes: mtimes to pass to env_update (see env_update)
1842 @type ldpath_mtimes: Dictionary
1843 @param others_in_slot: all dblink instances in this slot, excluding self
1844 @type others_in_slot: list
1845 @rtype: Integer
1846 @returns:
1847 1. os.EX_OK if everything went well.
1848 2. return code of the failed phase (for prerm, postrm, cleanrm)
1850 Notes:
1851 The caller must ensure that lockdb() and unlockdb() are called
1852 before and after this method.
1854 showMessage = self._display_merge
1855 if self.vartree.dbapi._categories is not None:
1856 self.vartree.dbapi._categories = None
1857 # When others_in_slot is supplied, the security check has already been
1858 # done for this slot, so it shouldn't be repeated until the next
1859 # replacement or unmerge operation.
1860 if others_in_slot is None:
1861 slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
1862 slot_matches = self.vartree.dbapi.match(
1863 "%s:%s" % (dep_getkey(self.mycpv), slot))
1864 others_in_slot = []
1865 for cur_cpv in slot_matches:
1866 if cur_cpv == self.mycpv:
1867 continue
1868 others_in_slot.append(dblink(self.cat, catsplit(cur_cpv)[1],
1869 self.vartree.root, self.settings, vartree=self.vartree,
1870 treetype="vartree"))
1872 retval = self._security_check([self] + others_in_slot)
1873 if retval:
1874 return retval
1876 contents = self.getcontents()
1877 # Now, don't assume that the name of the ebuild is the same as the
1878 # name of the dir; the package may have been moved.
1879 myebuildpath = None
1880 ebuild_phase = "prerm"
1881 log_path = None
1882 mystuff = os.listdir(self.dbdir)
1883 for x in mystuff:
1884 if x.endswith(".ebuild"):
1885 myebuildpath = os.path.join(self.dbdir, self.pkg + ".ebuild")
1886 if x[:-7] != self.pkg:
1887 # Clean up after vardbapi.move_ent() breakage in
1888 # portage versions before 2.1.2
1889 os.rename(os.path.join(self.dbdir, x), myebuildpath)
1890 write_atomic(os.path.join(self.dbdir, "PF"), self.pkg+"\n")
1891 break
1893 self.settings.setcpv(self.mycpv, mydb=self.vartree.dbapi)
1894 if myebuildpath:
1895 try:
1896 doebuild_environment(myebuildpath, "prerm", self.myroot,
1897 self.settings, 0, 0, self.vartree.dbapi)
1898 except UnsupportedAPIException, e:
1899 # Sometimes this happens due to corruption of the EAPI file.
1900 writemsg("!!! FAILED prerm: %s\n" % \
1901 os.path.join(self.dbdir, "EAPI"), noiselevel=-1)
1902 writemsg("%s\n" % str(e), noiselevel=-1)
1903 myebuildpath = None
1904 else:
1905 catdir = os.path.dirname(self.settings["PORTAGE_BUILDDIR"])
1906 ensure_dirs(os.path.dirname(catdir), uid=portage_uid,
1907 gid=portage_gid, mode=070, mask=0)
1909 builddir_lock = None
1910 catdir_lock = None
1911 scheduler = self._scheduler
1912 retval = -1
1913 try:
1914 if myebuildpath:
1915 catdir_lock = lockdir(catdir)
1916 ensure_dirs(catdir,
1917 uid=portage_uid, gid=portage_gid,
1918 mode=070, mask=0)
1919 builddir_lock = lockdir(
1920 self.settings["PORTAGE_BUILDDIR"])
1921 try:
1922 unlockdir(catdir_lock)
1923 finally:
1924 catdir_lock = None
1926 prepare_build_dirs(self.myroot, self.settings, 1)
1927 log_path = self.settings.get("PORTAGE_LOG_FILE")
1929 if scheduler is None:
1930 retval = doebuild(myebuildpath, ebuild_phase, self.myroot,
1931 self.settings, cleanup=cleanup, use_cache=0,
1932 mydbapi=self.vartree.dbapi, tree=self.treetype,
1933 vartree=self.vartree)
1934 else:
1935 retval = scheduler.dblinkEbuildPhase(
1936 self, self.vartree.dbapi, myebuildpath, ebuild_phase)
1938 # XXX: Decide how to handle failures here.
1939 if retval != os.EX_OK:
1940 writemsg("!!! FAILED prerm: %s\n" % retval, noiselevel=-1)
1942 self._unmerge_pkgfiles(pkgfiles, others_in_slot)
1944 # Remove the registration of preserved libs for this pkg instance
1945 plib_registry = self.vartree.dbapi.plib_registry
1946 plib_registry.unregister(self.mycpv, self.settings["SLOT"],
1947 self.vartree.dbapi.cpv_counter(self.mycpv))
1949 if myebuildpath:
1950 ebuild_phase = "postrm"
1951 if scheduler is None:
1952 retval = doebuild(myebuildpath, ebuild_phase, self.myroot,
1953 self.settings, use_cache=0, tree=self.treetype,
1954 mydbapi=self.vartree.dbapi, vartree=self.vartree)
1955 else:
1956 retval = scheduler.dblinkEbuildPhase(
1957 self, self.vartree.dbapi, myebuildpath, ebuild_phase)
1959 # XXX: Decide how to handle failures here.
1960 if retval != os.EX_OK:
1961 writemsg("!!! FAILED postrm: %s\n" % retval, noiselevel=-1)
1963 # regenerate reverse NEEDED map
1964 self.vartree.dbapi.linkmap.rebuild()
1966 # remove preserved libraries that don't have any consumers left
1967 # FIXME: this code is quite ugly and can likely be optimized in several ways
1968 plib_dict = plib_registry.getPreservedLibs()
1969 for cpv in plib_dict:
1970 plib_dict[cpv].sort()
1971 # for the loop below to work correctly, we need all
1972 # symlinks to come before the actual files, such that
1973 # the recorded symlinks (sonames) will be resolved into
1974 # their real target before the object is found not to be
1975 # in the reverse NEEDED map
1976 def symlink_compare(x, y):
1977 if os.path.islink(x):
1978 if os.path.islink(y):
1979 return 0
1980 else:
1981 return -1
1982 elif os.path.islink(y):
1983 return 1
1984 else:
1985 return 0
1987 plib_dict[cpv].sort(symlink_compare)
1988 for f in plib_dict[cpv]:
1989 if not os.path.exists(f):
1990 continue
1991 unlink_list = []
1992 consumers = self.vartree.dbapi.linkmap.findConsumers(f)
1993 if not consumers:
1994 unlink_list.append(f)
1995 else:
1996 keep=False
1997 for c in consumers:
1998 if c not in self.getcontents():
1999 keep=True
2000 break
2001 if not keep:
2002 unlink_list.append(f)
2003 for obj in unlink_list:
2004 try:
2005 if os.path.islink(obj):
2006 obj_type = "sym"
2007 else:
2008 obj_type = "obj"
2009 os.unlink(obj)
2010 showMessage("<<< !needed %s %s\n" % (obj_type, obj))
2011 except OSError, e:
2012 if e.errno == errno.ENOENT:
2013 pass
2014 else:
2015 raise e
2016 plib_registry.pruneNonExisting()
2018 finally:
2019 if builddir_lock:
2020 try:
2021 if myebuildpath:
2022 if retval != os.EX_OK:
2023 msg_lines = []
2024 msg = ("The '%s' " % ebuild_phase) + \
2025 ("phase of the '%s' package " % self.mycpv) + \
2026 ("has failed with exit value %s." % retval)
2027 from textwrap import wrap
2028 msg_lines.extend(wrap(msg, 72))
2029 msg_lines.append("")
2031 ebuild_name = os.path.basename(myebuildpath)
2032 ebuild_dir = os.path.dirname(myebuildpath)
2033 msg = "The problem occurred while executing " + \
2034 ("the ebuild file named '%s' " % ebuild_name) + \
2035 ("located in the '%s' directory. " \
2036 % ebuild_dir) + \
2037 "If necessary, manually remove " + \
2038 "the environment.bz2 file and/or the " + \
2039 "ebuild file located in that directory."
2040 msg_lines.extend(wrap(msg, 72))
2041 msg_lines.append("")
2043 msg = "Removal " + \
2044 "of the environment.bz2 file is " + \
2045 "preferred since it may allow the " + \
2046 "removal phases to execute successfully. " + \
2047 "The ebuild will be " + \
2048 "sourced and the eclasses " + \
2049 "from the current portage tree will be used " + \
2050 "when necessary. Removal of " + \
2051 "the ebuild file will cause the " + \
2052 "pkg_prerm() and pkg_postrm() removal " + \
2053 "phases to be skipped entirely."
2054 msg_lines.extend(wrap(msg, 72))
2056 self._eerror(ebuild_phase, msg_lines)
2058 # process logs created during pre/postrm
2059 elog_process(self.mycpv, self.settings, phasefilter=filter_unmergephases)
2060 if retval == os.EX_OK:
2061 doebuild(myebuildpath, "cleanrm", self.myroot,
2062 self.settings, tree="vartree",
2063 mydbapi=self.vartree.dbapi,
2064 vartree=self.vartree)
2065 finally:
2066 unlockdir(builddir_lock)
2067 try:
2068 if myebuildpath and not catdir_lock:
2069 # Lock catdir for removal if empty.
2070 catdir_lock = lockdir(catdir)
2071 finally:
2072 if catdir_lock:
2073 try:
2074 os.rmdir(catdir)
2075 except OSError, e:
2076 if e.errno not in (errno.ENOENT,
2077 errno.ENOTEMPTY, errno.EEXIST):
2078 raise
2079 del e
2080 unlockdir(catdir_lock)
2082 if log_path is not None and os.path.exists(log_path):
2083 # Restore this since it gets lost somewhere above and it
2084 # needs to be set for _display_merge() to be able to log.
2085 # Note that the log isn't necessarily supposed to exist
2086 # since if PORT_LOGDIR is unset then it's a temp file
2087 # so it gets cleaned above.
2088 self.settings["PORTAGE_LOG_FILE"] = log_path
2089 else:
2090 self.settings.pop("PORTAGE_LOG_FILE", None)
2092 env_update(target_root=self.myroot, prev_mtimes=ldpath_mtimes,
2093 contents=contents, env=self.settings.environ(),
2094 writemsg_level=self._display_merge)
2095 return os.EX_OK
2097 def _display_merge(self, msg, level=0, noiselevel=0):
2098 if self._scheduler is not None:
2099 self._scheduler.dblinkDisplayMerge(self, msg,
2100 level=level, noiselevel=noiselevel)
2101 return
2102 writemsg_level(msg, level=level, noiselevel=noiselevel)
2104 def _unmerge_pkgfiles(self, pkgfiles, others_in_slot):
2107 Unmerges the contents of a package from the liveFS
2108 Removes the VDB entry for self
2110 @param pkgfiles: typically self.getcontents()
2111 @type pkgfiles: Dictionary { filename: [ 'type', '?', 'md5sum' ] }
2112 @param others_in_slot: all dblink instances in this slot, excluding self
2113 @type others_in_slot: list
2114 @rtype: None
2117 showMessage = self._display_merge
2118 scheduler = self._scheduler
2120 if not pkgfiles:
2121 showMessage("No package files given... Grabbing a set.\n")
2122 pkgfiles = self.getcontents()
2124 if others_in_slot is None:
2125 others_in_slot = []
2126 slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
2127 slot_matches = self.vartree.dbapi.match(
2128 "%s:%s" % (dep_getkey(self.mycpv), slot))
2129 for cur_cpv in slot_matches:
2130 if cur_cpv == self.mycpv:
2131 continue
2132 others_in_slot.append(dblink(self.cat, catsplit(cur_cpv)[1],
2133 self.vartree.root, self.settings,
2134 vartree=self.vartree, treetype="vartree"))
2136 dest_root = normalize_path(self.vartree.root).rstrip(os.path.sep) + \
2137 os.path.sep
2138 dest_root_len = len(dest_root) - 1
2140 conf_mem_file = os.path.join(dest_root, CONFIG_MEMORY_FILE)
2141 cfgfiledict = grabdict(conf_mem_file)
2142 stale_confmem = []
2144 unmerge_orphans = "unmerge-orphans" in self.settings.features
2146 if pkgfiles:
2147 self.updateprotect()
2148 mykeys = pkgfiles.keys()
2149 mykeys.sort()
2150 mykeys.reverse()
2152 #process symlinks second-to-last, directories last.
2153 mydirs = []
2154 ignored_unlink_errnos = (
2155 errno.EBUSY, errno.ENOENT,
2156 errno.ENOTDIR, errno.EISDIR)
2157 ignored_rmdir_errnos = (
2158 errno.EEXIST, errno.ENOTEMPTY,
2159 errno.EBUSY, errno.ENOENT,
2160 errno.ENOTDIR, errno.EISDIR)
2161 modprotect = os.path.join(self.vartree.root, "lib/modules/")
2163 def unlink(file_name, lstatobj):
2164 if bsd_chflags:
2165 if lstatobj.st_flags != 0:
2166 bsd_chflags.lchflags(file_name, 0)
2167 parent_name = os.path.dirname(file_name)
2168 # Use normal stat/chflags for the parent since we want to
2169 # follow any symlinks to the real parent directory.
2170 pflags = os.stat(parent_name).st_flags
2171 if pflags != 0:
2172 bsd_chflags.chflags(parent_name, 0)
2173 try:
2174 if not stat.S_ISLNK(lstatobj.st_mode):
2175 # Remove permissions to ensure that any hardlinks to
2176 # suid/sgid files are rendered harmless.
2177 os.chmod(file_name, 0)
2178 os.unlink(file_name)
2179 finally:
2180 if bsd_chflags and pflags != 0:
2181 # Restore the parent flags we saved before unlinking
2182 bsd_chflags.chflags(parent_name, pflags)
2184 def show_unmerge(zing, desc, file_type, file_name):
2185 showMessage("%s %s %s %s\n" % \
2186 (zing, desc.ljust(8), file_type, file_name))
2187 for i, objkey in enumerate(mykeys):
2189 if scheduler is not None and \
2190 0 == i % self._file_merge_yield_interval:
2191 scheduler.scheduleYield()
2193 obj = normalize_path(objkey)
2194 file_data = pkgfiles[objkey]
2195 file_type = file_data[0]
2196 statobj = None
2197 try:
2198 statobj = os.stat(obj)
2199 except OSError:
2200 pass
2201 lstatobj = None
2202 try:
2203 lstatobj = os.lstat(obj)
2204 except (OSError, AttributeError):
2205 pass
2206 islink = lstatobj is not None and stat.S_ISLNK(lstatobj.st_mode)
2207 if lstatobj is None:
2208 show_unmerge("---", "!found", file_type, obj)
2209 continue
2210 if obj.startswith(dest_root):
2211 relative_path = obj[dest_root_len:]
2212 is_owned = False
2213 for dblnk in others_in_slot:
2214 if dblnk.isowner(relative_path, dest_root):
2215 is_owned = True
2216 break
2217 if is_owned:
2218 # A new instance of this package claims the file, so
2219 # don't unmerge it.
2220 show_unmerge("---", "replaced", file_type, obj)
2221 continue
2222 elif relative_path in cfgfiledict:
2223 stale_confmem.append(relative_path)
2224 # next line includes a tweak to protect modules from being unmerged,
2225 # but we don't protect modules from being overwritten if they are
2226 # upgraded. We effectively only want one half of the config protection
2227 # functionality for /lib/modules. For portage-ng both capabilities
2228 # should be able to be independently specified.
2229 if obj.startswith(modprotect):
2230 show_unmerge("---", "cfgpro", file_type, obj)
2231 continue
2233 # Don't unlink symlinks to directories here since that can
2234 # remove /lib and /usr/lib symlinks.
2235 if unmerge_orphans and \
2236 lstatobj and not stat.S_ISDIR(lstatobj.st_mode) and \
2237 not (islink and statobj and stat.S_ISDIR(statobj.st_mode)) and \
2238 not self.isprotected(obj):
2239 try:
2240 unlink(obj, lstatobj)
2241 except EnvironmentError, e:
2242 if e.errno not in ignored_unlink_errnos:
2243 raise
2244 del e
2245 show_unmerge("<<<", "", file_type, obj)
2246 continue
2248 lmtime = str(lstatobj[stat.ST_MTIME])
2249 if (pkgfiles[objkey][0] not in ("dir", "fif", "dev")) and (lmtime != pkgfiles[objkey][1]):
2250 show_unmerge("---", "!mtime", file_type, obj)
2251 continue
2253 if pkgfiles[objkey][0] == "dir":
2254 if statobj is None or not stat.S_ISDIR(statobj.st_mode):
2255 show_unmerge("---", "!dir", file_type, obj)
2256 continue
2257 mydirs.append(obj)
2258 elif pkgfiles[objkey][0] == "sym":
2259 if not islink:
2260 show_unmerge("---", "!sym", file_type, obj)
2261 continue
2262 # Go ahead and unlink symlinks to directories here when
2263 # they're actually recorded as symlinks in the contents.
2264 # Normally, symlinks such as /lib -> lib64 are not recorded
2265 # as symlinks in the contents of a package. If a package
2266 # installs something into ${D}/lib/, it is recorded in the
2267 # contents as a directory even if it happens to correspond
2268 # to a symlink when it's merged to the live filesystem.
2269 try:
2270 unlink(obj, lstatobj)
2271 show_unmerge("<<<", "", file_type, obj)
2272 except (OSError, IOError),e:
2273 if e.errno not in ignored_unlink_errnos:
2274 raise
2275 del e
2276 show_unmerge("!!!", "", file_type, obj)
2277 elif pkgfiles[objkey][0] == "obj":
2278 if statobj is None or not stat.S_ISREG(statobj.st_mode):
2279 show_unmerge("---", "!obj", file_type, obj)
2280 continue
2281 mymd5 = None
2282 try:
2283 mymd5 = perform_md5(obj, calc_prelink=1)
2284 except FileNotFound, e:
2285 # the file has disappeared between now and our stat call
2286 show_unmerge("---", "!obj", file_type, obj)
2287 continue
2289 # string.lower is needed because db entries used to be in upper-case. The
2290 # string.lower allows for backwards compatibility.
2291 if mymd5 != pkgfiles[objkey][2].lower():
2292 show_unmerge("---", "!md5", file_type, obj)
2293 continue
2294 try:
2295 unlink(obj, lstatobj)
2296 except (OSError, IOError), e:
2297 if e.errno not in ignored_unlink_errnos:
2298 raise
2299 del e
2300 show_unmerge("<<<", "", file_type, obj)
2301 elif pkgfiles[objkey][0] == "fif":
2302 if not stat.S_ISFIFO(lstatobj[stat.ST_MODE]):
2303 show_unmerge("---", "!fif", file_type, obj)
2304 continue
2305 show_unmerge("---", "", file_type, obj)
2306 elif pkgfiles[objkey][0] == "dev":
2307 show_unmerge("---", "", file_type, obj)
2309 mydirs.sort()
2310 mydirs.reverse()
2312 for obj in mydirs:
2313 try:
2314 if bsd_chflags:
2315 lstatobj = os.lstat(obj)
2316 if lstatobj.st_flags != 0:
2317 bsd_chflags.lchflags(obj, 0)
2318 parent_name = os.path.dirname(obj)
2319 # Use normal stat/chflags for the parent since we want to
2320 # follow any symlinks to the real parent directory.
2321 pflags = os.stat(parent_name).st_flags
2322 if pflags != 0:
2323 bsd_chflags.chflags(parent_name, 0)
2324 try:
2325 os.rmdir(obj)
2326 finally:
2327 if bsd_chflags and pflags != 0:
2328 # Restore the parent flags we saved before unlinking
2329 bsd_chflags.chflags(parent_name, pflags)
2330 show_unmerge("<<<", "", "dir", obj)
2331 except EnvironmentError, e:
2332 if e.errno not in ignored_rmdir_errnos:
2333 raise
2334 if e.errno != errno.ENOENT:
2335 show_unmerge("---", "!empty", "dir", obj)
2336 del e
2338 # Remove stale entries from config memory.
2339 if stale_confmem:
2340 for filename in stale_confmem:
2341 del cfgfiledict[filename]
2342 writedict(cfgfiledict, conf_mem_file)
2344 #remove self from vartree database so that our own virtual gets zapped if we're the last node
2345 self.vartree.zap(self.mycpv)
2347 def isowner(self, filename, destroot):
2348 """
2349 Check if a file belongs to this package. This may
2350 result in a stat call for the parent directory of
2351 every installed file, since the inode numbers are
2352 used to work around the problem of ambiguous paths
2353 caused by symlinked directories. The results of
2354 stat calls are cached to optimize multiple calls
2355 to this method.
2357 @param filename:
2358 @type filename:
2359 @param destroot:
2360 @type destroot:
2361 @rtype: Boolean
2362 @returns:
2363 1. True if this package owns the file.
2364 2. False if this package does not own the file.
2366 return bool(self._match_contents(filename, destroot))
2368 def _match_contents(self, filename, destroot):
2370 The matching contents entry is returned, which is useful
2371 since the path may differ from the one given by the caller,
2372 due to symlinks.
2374 @rtype: String
2375 @return: the contents entry corresponding to the given path, or False
2376 if the file is not owned by this package.
2379 destfile = normalize_path(
2380 os.path.join(destroot, filename.lstrip(os.path.sep)))
2382 pkgfiles = self.getcontents()
2383 if pkgfiles and destfile in pkgfiles:
2384 return destfile
2385 if pkgfiles:
2386 basename = os.path.basename(destfile)
2387 if self._contents_basenames is None:
2388 self._contents_basenames = set(
2389 os.path.basename(x) for x in pkgfiles)
2390 if basename not in self._contents_basenames:
2391 # This is a shortcut that, in most cases, allows us to
2392 # eliminate this package as an owner without the need
2393 # to examine inode numbers of parent directories.
2394 return False
2396 # Use stat rather than lstat since we want to follow
2397 # any symlinks to the real parent directory.
2398 parent_path = os.path.dirname(destfile)
2399 try:
2400 parent_stat = os.stat(parent_path)
2401 except EnvironmentError, e:
2402 if e.errno != errno.ENOENT:
2403 raise
2404 del e
2405 return False
2406 if self._contents_inodes is None:
2407 self._contents_inodes = {}
2408 parent_paths = set()
2409 for x in pkgfiles:
2410 p_path = os.path.dirname(x)
2411 if p_path in parent_paths:
2412 continue
2413 parent_paths.add(p_path)
2414 try:
2415 s = os.stat(p_path)
2416 except OSError:
2417 pass
2418 else:
2419 inode_key = (s.st_dev, s.st_ino)
2420 # Use lists of paths in case multiple
2421 # paths reference the same inode.
2422 p_path_list = self._contents_inodes.get(inode_key)
2423 if p_path_list is None:
2424 p_path_list = []
2425 self._contents_inodes[inode_key] = p_path_list
2426 if p_path not in p_path_list:
2427 p_path_list.append(p_path)
2428 p_path_list = self._contents_inodes.get(
2429 (parent_stat.st_dev, parent_stat.st_ino))
2430 if p_path_list:
2431 for p_path in p_path_list:
2432 x = os.path.join(p_path, basename)
2433 if x in pkgfiles:
2434 return x
2436 return False
2438 def _preserve_libs(self, srcroot, destroot, mycontents, counter, inforoot):
2439 showMessage = self._display_merge
2440 # read global reverse NEEDED map
2441 linkmap = self.vartree.dbapi.linkmap
2442 linkmap.rebuild(include_file=os.path.join(inforoot, "NEEDED.ELF.2"))
2443 liblist = linkmap.listLibraryObjects()
2445 # get list of libraries from old package instance
2446 root_len = len(self.myroot) - 1
2447 old_contents = set(p[root_len:] \
2448 for p in self._installed_instance.getcontents())
2449 old_libs = old_contents.intersection(liblist)
2451 # get list of libraries from new package instance
2452 mylibs = set([os.path.join(os.sep, x) for x in mycontents]).intersection(liblist)
2454 # check which libs are present in the old, but not the new package instance
2455 candidates = old_libs.difference(mylibs)
2457 for x in old_contents:
2458 if os.path.islink(x) and os.path.realpath(x) in candidates and x not in mycontents:
2459 candidates.add(x)
2461 provider_cache = {}
2462 consumer_cache = {}
2464 # ignore any libs that are only internally used by the package
2465 def has_external_consumers(lib, contents, otherlibs):
2466 consumers = consumer_cache.get(lib)
2467 if consumers is None:
2468 consumers = linkmap.findConsumers(lib)
2469 consumer_cache[lib] = consumers
2470 contents_without_libs = [x for x in contents if x not in otherlibs]
2472 # just used by objects that will be autocleaned
2473 if len(consumers.difference(contents_without_libs)) == 0:
2474 return False
2475 # used by objects that are referenced as well, need to check those
2476 # recursively to break any reference cycles
2477 elif len(consumers.difference(contents)) == 0:
2478 otherlibs = set(otherlibs)
2479 for ol in otherlibs.intersection(consumers):
2480 if has_external_consumers(ol, contents, otherlibs.difference([lib])):
2481 return True
2482 return False
2483 # used by external objects directly
2484 else:
2485 return True
2487 for lib in list(candidates):
2488 if not has_external_consumers(lib, old_contents, candidates):
2489 candidates.remove(lib)
2490 continue
2491 if linkmap.isMasterLink(lib):
2492 candidates.remove(lib)
2493 continue
2494 # only preserve the lib if there is no other copy to use for each consumer
2495 keep = False
2497 lib_consumers = consumer_cache.get(lib)
2498 if lib_consumers is None:
2499 lib_consumers = linkmap.findConsumers(lib)
2500 consumer_cache[lib] = lib_consumers
2502 for c in lib_consumers:
2503 localkeep = True
2504 providers = provider_cache.get(c)
2505 if providers is None:
2506 providers = linkmap.findProviders(c)
2507 provider_cache[c] = providers
2509 for soname in providers:
2510 if lib in providers[soname]:
2511 for p in providers[soname]:
2512 if p not in candidates or os.path.exists(os.path.join(srcroot, p.lstrip(os.sep))):
2513 localkeep = False
2514 break
2515 break
2516 if localkeep:
2517 keep = True
2518 if not keep:
2519 candidates.remove(lib)
2520 continue
2522 del mylibs, mycontents, old_contents, liblist
2524 # inject files that should be preserved into our image dir
2525 import shutil
2526 preserve_paths = []
2527 candidates_stack = list(candidates)
2528 while candidates_stack:
2529 x = candidates_stack.pop()
2530 # skip existing files so the 'new' libs aren't overwritten
2531 if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))):
2532 continue
2533 showMessage("injecting %s into %s\n" % (x, srcroot),
2534 noiselevel=-1)
2535 if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))):
2536 showMessage("%s does not exist so can't be preserved\n" % x,
2537 noiselevel=-1)
2538 continue
2539 mydir = os.path.join(srcroot, os.path.dirname(x).lstrip(os.sep))
2540 if not os.path.exists(mydir):
2541 os.makedirs(mydir)
2543 # resolve symlinks and extend preserve list
2544 # NOTE: we're extending the list in the loop to emulate recursion to
2545 # also get indirect symlinks
2546 if os.path.islink(x):
2547 linktarget = os.readlink(x)
2548 os.symlink(linktarget, os.path.join(srcroot, x.lstrip(os.sep)))
2549 if linktarget[0] != os.sep:
2550 linktarget = os.path.join(os.path.dirname(x), linktarget)
2551 if linktarget not in candidates:
2552 candidates.add(linktarget)
2553 candidates_stack.append(linktarget)
2554 else:
2555 shutil.copy2(os.path.join(destroot, x.lstrip(os.sep)),
2556 os.path.join(srcroot, x.lstrip(os.sep)))
2557 preserve_paths.append(x)
2559 del candidates
2561 # keep track of the libs we preserved
2562 self.vartree.dbapi.plib_registry.register(self.mycpv, self.settings["SLOT"], counter, preserve_paths)
2564 del preserve_paths
2566 def _collision_protect(self, srcroot, destroot, mypkglist, mycontents):
2567 collision_ignore = set([normalize_path(myignore) for myignore in \
2568 shlex.split(self.settings.get("COLLISION_IGNORE", ""))])
2570 showMessage = self._display_merge
2571 scheduler = self._scheduler
2572 stopmerge = False
2573 collisions = []
2574 destroot = normalize_path(destroot).rstrip(os.path.sep) + \
2575 os.path.sep
2576 showMessage("%s checking %d files for package collisions\n" % \
2577 (green("*"), len(mycontents)))
2578 for i, f in enumerate(mycontents):
2579 if i % 1000 == 0 and i != 0:
2580 showMessage("%d files checked ...\n" % i)
2582 if scheduler is not None and \
2583 0 == i % self._file_merge_yield_interval:
2584 scheduler.scheduleYield()
2586 dest_path = normalize_path(
2587 os.path.join(destroot, f.lstrip(os.path.sep)))
2588 try:
2589 dest_lstat = os.lstat(dest_path)
2590 except EnvironmentError, e:
2591 if e.errno == errno.ENOENT:
2592 del e
2593 continue
2594 elif e.errno == errno.ENOTDIR:
2595 del e
2596 # A non-directory is in a location where this package
2597 # expects to have a directory.
2598 dest_lstat = None
2599 parent_path = dest_path
2600 while len(parent_path) > len(destroot):
2601 parent_path = os.path.dirname(parent_path)
2602 try:
2603 dest_lstat = os.lstat(parent_path)
2604 break
2605 except EnvironmentError, e:
2606 if e.errno != errno.ENOTDIR:
2607 raise
2608 del e
2609 if not dest_lstat:
2610 raise AssertionError(
2611 "unable to find non-directory " + \
2612 "parent for '%s'" % dest_path)
2613 dest_path = parent_path
2614 f = os.path.sep + dest_path[len(destroot):]
2615 if f in collisions:
2616 continue
2617 else:
2618 raise
2619 if f[0] != "/":
2620 f="/"+f
2621 isowned = False
2622 for ver in [self] + mypkglist:
2623 if (ver.isowner(f, destroot) or ver.isprotected(f)):
2624 isowned = True
2625 break
2626 if not isowned:
2627 stopmerge = True
2628 if collision_ignore:
2629 if f in collision_ignore:
2630 stopmerge = False
2631 else:
2632 for myignore in collision_ignore:
2633 if f.startswith(myignore + os.path.sep):
2634 stopmerge = False
2635 break
2636 if stopmerge:
2637 collisions.append(f)
2638 return collisions
2640 def _security_check(self, installed_instances):
2641 if not installed_instances:
2642 return 0
2644 showMessage = self._display_merge
2645 scheduler = self._scheduler
2647 file_paths = set()
2648 for dblnk in installed_instances:
2649 file_paths.update(dblnk.getcontents())
2650 inode_map = {}
2651 real_paths = set()
2652 for i, path in enumerate(file_paths):
2654 if scheduler is not None and \
2655 0 == i % self._file_merge_yield_interval:
2656 scheduler.scheduleYield()
2658 try:
2659 s = os.lstat(path)
2660 except OSError, e:
2661 if e.errno not in (errno.ENOENT, errno.ENOTDIR):
2662 raise
2663 del e
2664 continue
2665 if not stat.S_ISREG(s.st_mode):
2666 continue
2667 path = os.path.realpath(path)
2668 if path in real_paths:
2669 continue
2670 real_paths.add(path)
2671 if s.st_nlink > 1 and \
2672 s.st_mode & (stat.S_ISUID | stat.S_ISGID):
2673 k = (s.st_dev, s.st_ino)
2674 inode_map.setdefault(k, []).append((path, s))
2675 suspicious_hardlinks = []
2676 for path_list in inode_map.itervalues():
2677 path, s = path_list[0]
2678 if len(path_list) == s.st_nlink:
2679 # All hardlinks seem to be owned by this package.
2680 continue
2681 suspicious_hardlinks.append(path_list)
2682 if not suspicious_hardlinks:
2683 return 0
2685 msg = []
2686 msg.append("suid/sgid file(s) " + \
2687 "with suspicious hardlink(s):")
2688 msg.append("")
2689 for path_list in suspicious_hardlinks:
2690 for path, s in path_list:
2691 msg.append("\t%s" % path)
2692 msg.append("")
2693 msg.append("See the Gentoo Security Handbook " + \
2694 "guide for advice on how to proceed.")
2696 self._eerror("preinst", msg)
2698 return 1
2700 def _eerror(self, phase, lines):
2701 from portage.elog.messages import eerror as _eerror
2702 if self._scheduler is None:
2703 for l in lines:
2704 _eerror(l, phase=phase, key=self.settings.mycpv)
2705 else:
2706 self._scheduler.dblinkElog(self,
2707 phase, _eerror, lines)
2709 def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
2710 mydbapi=None, prev_mtimes=None):
2713 This function does the following:
2715 calls self._preserve_libs if FEATURES=preserve-libs
2716 calls self._collision_protect if FEATURES=collision-protect
2717 calls doebuild(mydo=pkg_preinst)
2718 Merges the package to the livefs
2719 unmerges old version (if required)
2720 calls doebuild(mydo=pkg_postinst)
2721 calls env_update
2722 calls elog_process
2724 @param srcroot: Typically this is ${D}
2725 @type srcroot: String (Path)
2726 @param destroot: Path to merge to (usually ${ROOT})
2727 @type destroot: String (Path)
2728 @param inforoot: root of the vardb entry ?
2729 @type inforoot: String (Path)
2730 @param myebuild: path to the ebuild that we are processing
2731 @type myebuild: String (Path)
2732 @param mydbapi: dbapi which is handed to doebuild.
2733 @type mydbapi: portdbapi instance
2734 @param prev_mtimes: { Filename:mtime } mapping for env_update
2735 @type prev_mtimes: Dictionary
2736 @rtype: Boolean
2737 @returns:
2738 1. 0 on success
2739 2. 1 on failure
2741 secondhand is a list of symlinks that have been skipped due to their target
2742 not existing; we will merge these symlinks at a later time.
2745 showMessage = self._display_merge
2746 scheduler = self._scheduler
2748 srcroot = normalize_path(srcroot).rstrip(os.path.sep) + os.path.sep
2749 destroot = normalize_path(destroot).rstrip(os.path.sep) + os.path.sep
2751 if not os.path.isdir(srcroot):
2752 showMessage("!!! Directory Not Found: D='%s'\n" % srcroot,
2753 level=logging.ERROR, noiselevel=-1)
2754 return 1
2756 inforoot_slot_file = os.path.join(inforoot, "SLOT")
2757 slot = None
2758 try:
2759 f = open(inforoot_slot_file)
2760 try:
2761 slot = f.read().strip()
2762 finally:
2763 f.close()
2764 except EnvironmentError, e:
2765 if e.errno != errno.ENOENT:
2766 raise
2767 del e
2769 if slot is None:
2770 slot = ""
2772 def eerror(lines):
2773 self._eerror("preinst", lines)
2775 if slot != self.settings["SLOT"]:
2776 showMessage("!!! WARNING: Expected SLOT='%s', got '%s'\n" % \
2777 (self.settings["SLOT"], slot), level=logging.WARN)
2779 if not os.path.exists(self.dbcatdir):
2780 os.makedirs(self.dbcatdir)
2782 otherversions = []
2783 for v in self.vartree.dbapi.cp_list(self.mysplit[0]):
2784 otherversions.append(v.split("/")[1])
2786 # filter any old-style virtual matches
2787 slot_matches = [cpv for cpv in self.vartree.dbapi.match(
2788 "%s:%s" % (cpv_getkey(self.mycpv), slot)) \
2789 if cpv_getkey(cpv) == cpv_getkey(self.mycpv)]
2791 if self.mycpv not in slot_matches and \
2792 self.vartree.dbapi.cpv_exists(self.mycpv):
2793 # handle multislot or unapplied slotmove
2794 slot_matches.append(self.mycpv)
2796 others_in_slot = []
2797 from portage import config
2798 for cur_cpv in slot_matches:
2799 # Clone the config in case one of these has to be unmerged since
2800 # we need it to have private ${T} etc... for things like elog.
2801 others_in_slot.append(dblink(self.cat, catsplit(cur_cpv)[1],
2802 self.vartree.root, config(clone=self.settings),
2803 vartree=self.vartree, treetype="vartree",
2804 scheduler=self._scheduler))
2806 retval = self._security_check(others_in_slot)
2807 if retval:
2808 return retval
2810 if slot_matches:
2811 # Used by self.isprotected().
2812 max_dblnk = None
2813 max_counter = -1
2814 for dblnk in others_in_slot:
2815 cur_counter = self.vartree.dbapi.cpv_counter(dblnk.mycpv)
2816 if cur_counter > max_counter:
2817 max_counter = cur_counter
2818 max_dblnk = dblnk
2819 self._installed_instance = max_dblnk
2821 # get current counter value (counter_tick also takes care of incrementing it)
2822 # XXX Need to make this destroot, but it needs to be initialized first. XXX
2823 # XXX bis: leads to some invalidentry() call through cp_all().
2824 # Note: The counter is generated here but written later because preserve_libs
2825 # needs the counter value but has to be before dbtmpdir is made (which
2826 # has to be before the counter is written) - genone
2827 counter = self.vartree.dbapi.counter_tick(self.myroot, mycpv=self.mycpv)
2829 # Save this for unregistering preserved-libs if the merge fails.
2830 self.settings["COUNTER"] = str(counter)
2831 self.settings.backup_changes("COUNTER")
2833 myfilelist = []
2834 mylinklist = []
2835 def onerror(e):
2836 raise
2837 for parent, dirs, files in os.walk(srcroot, onerror=onerror):
2838 for f in files:
2839 file_path = os.path.join(parent, f)
2840 file_mode = os.lstat(file_path).st_mode
2841 if stat.S_ISREG(file_mode):
2842 myfilelist.append(file_path[len(srcroot):])
2843 elif stat.S_ISLNK(file_mode):
2844 # Note: os.walk puts symlinks to directories in the "dirs"
2845 # list and it does not traverse them since that could lead
2846 # to an infinite recursion loop.
2847 mylinklist.append(file_path[len(srcroot):])
2849 # If there are no files to merge, and an installed package in the same
2850 # slot has files, it probably means that something went wrong.
2851 if self.settings.get("PORTAGE_PACKAGE_EMPTY_ABORT") == "1" and \
2852 not myfilelist and not mylinklist and others_in_slot:
2853 installed_files = None
2854 for other_dblink in others_in_slot:
2855 installed_files = other_dblink.getcontents()
2856 if not installed_files:
2857 continue
2858 from textwrap import wrap
2859 wrap_width = 72
2860 msg = []
2861 d = (
2862 self.mycpv,
2863 other_dblink.mycpv
2865 msg.extend(wrap(("The '%s' package will not install " + \
2866 "any files, but the currently installed '%s'" + \
2867 " package has the following files: ") % d, wrap_width))
2868 msg.append("")
2869 msg.extend(sorted(installed_files))
2870 msg.append("")
2871 msg.append("package %s NOT merged" % self.mycpv)
2872 msg.append("")
2873 msg.extend(wrap(
2874 ("Manually run `emerge --unmerge =%s` " % \
2875 other_dblink.mycpv) + "if you really want to " + \
2876 "remove the above files. Set " + \
2877 "PORTAGE_PACKAGE_EMPTY_ABORT=\"0\" in " + \
2878 "/etc/make.conf if you do not want to " + \
2879 "abort in cases like this.",
2880 wrap_width))
2881 eerror(msg)
2882 if installed_files:
2883 return 1
2885 # Preserve old libs if they are still in use
2886 if slot_matches and "preserve-libs" in self.settings.features:
2887 self._preserve_libs(srcroot, destroot, myfilelist+mylinklist, counter, inforoot)
2889 # check for package collisions
2890 blockers = None
2891 if self._blockers is not None:
2892 # This is only supposed to be called when
2893 # the vdb is locked, like it is here.
2894 blockers = self._blockers()
2895 if blockers is None:
2896 blockers = []
2897 collisions = self._collision_protect(srcroot, destroot,
2898 others_in_slot + blockers, myfilelist + mylinklist)
2900 # Make sure the ebuild environment is initialized and that ${T}/elog
2901 # exists for logging of collision-protect eerror messages.
2902 if myebuild is None:
2903 myebuild = os.path.join(inforoot, self.pkg + ".ebuild")
2904 doebuild_environment(myebuild, "preinst", destroot,
2905 self.settings, 0, 0, mydbapi)
2906 prepare_build_dirs(destroot, self.settings, cleanup)
2908 if collisions:
2909 collision_protect = "collision-protect" in self.settings.features
2910 msg = "This package will overwrite one or more files that" + \
2911 " may belong to other packages (see list below)."
2912 if not collision_protect:
2913 msg += " Add \"collision-protect\" to FEATURES in" + \
2914 " make.conf if you would like the merge to abort" + \
2915 " in cases like this."
2916 if self.settings.get("PORTAGE_QUIET") != "1":
2917 msg += " You can use a command such as" + \
2918 " `portageq owners / <filename>` to identify the" + \
2919 " installed package that owns a file. If portageq" + \
2920 " reports that only one package owns a file then do NOT" + \
2921 " file a bug report. A bug report is only useful if it" + \
2922 " identifies at least two or more packages that are known" + \
2923 " to install the same file(s)." + \
2924 " If a collision occurs and you" + \
2925 " can not explain where the file came from then you" + \
2926 " should simply ignore the collision since there is not" + \
2927 " enough information to determine if a real problem" + \
2928 " exists. Please do NOT file a bug report at" + \
2929 " http://bugs.gentoo.org unless you report exactly which" + \
2930 " two packages install the same file(s). Once again," + \
2931 " please do NOT file a bug report unless you have" + \
2932 " completely understood the above message."
2934 self.settings["EBUILD_PHASE"] = "preinst"
2935 from textwrap import wrap
2936 msg = wrap(msg, 70)
2937 if collision_protect:
2938 msg.append("")
2939 msg.append("package %s NOT merged" % self.settings.mycpv)
2940 msg.append("")
2941 msg.append("Detected file collision(s):")
2942 msg.append("")
2944 for f in collisions:
2945 msg.append("\t%s" % \
2946 os.path.join(destroot, f.lstrip(os.path.sep)))
2948 eerror(msg)
2950 msg = []
2951 msg.append("")
2952 msg.append("Searching all installed" + \
2953 " packages for file collisions...")
2954 msg.append("")
2955 msg.append("Press Ctrl-C to Stop")
2956 msg.append("")
2957 eerror(msg)
2959 owners = self.vartree.dbapi._owners.get_owners(collisions)
2960 self.vartree.dbapi.flush_cache()
2962 for pkg, owned_files in owners.iteritems():
2963 cpv = pkg.mycpv
2964 msg = []
2965 msg.append("%s" % cpv)
2966 for f in sorted(owned_files):
2967 msg.append("\t%s" % os.path.join(destroot,
2968 f.lstrip(os.path.sep)))
2969 msg.append("")
2970 eerror(msg)
2972 if not owners:
2973 eerror(["None of the installed" + \
2974 " packages claim the file(s).", ""])
2976 # The explanation about the collision and how to solve
2977 # it may not be visible via a scrollback buffer, especially
2978 # if the number of file collisions is large. Therefore,
2979 # show a summary at the end.
2980 if collision_protect:
2981 msg = "Package '%s' NOT merged due to file collisions." % \
2982 self.settings.mycpv
2983 else:
2984 msg = "Package '%s' merged despite file collisions." % \
2985 self.settings.mycpv
2986 msg += " If necessary, refer to your elog " + \
2987 "messages for the whole content of the above message."
2988 eerror(wrap(msg, 70))
2990 if collision_protect:
2991 return 1
2993 # The merge process may move files out of the image directory,
2994 # which causes invalidation of the .installed flag.
2995 try:
2996 os.unlink(os.path.join(
2997 os.path.dirname(normalize_path(srcroot)), ".installed"))
2998 except OSError, e:
2999 if e.errno != errno.ENOENT:
3000 raise
3001 del e
3003 self.dbdir = self.dbtmpdir
3004 self.delete()
3005 ensure_dirs(self.dbtmpdir)
3007 # run preinst script
3008 if scheduler is None:
3009 showMessage(">>> Merging %s to %s\n" % (self.mycpv, destroot))
3010 a = doebuild(myebuild, "preinst", destroot, self.settings,
3011 use_cache=0, tree=self.treetype, mydbapi=mydbapi,
3012 vartree=self.vartree)
3013 else:
3014 a = scheduler.dblinkEbuildPhase(
3015 self, mydbapi, myebuild, "preinst")
3017 # XXX: Decide how to handle failures here.
3018 if a != os.EX_OK:
3019 showMessage("!!! FAILED preinst: "+str(a)+"\n",
3020 level=logging.ERROR, noiselevel=-1)
3021 return a
3023 # copy "info" files (like SLOT, CFLAGS, etc.) into the database
3024 for x in os.listdir(inforoot):
3025 self.copyfile(inforoot+"/"+x)
3027 # write local package counter for recording
3028 lcfile = open(os.path.join(self.dbtmpdir, "COUNTER"),"w")
3029 lcfile.write(str(counter))
3030 lcfile.close()
3032 # open CONTENTS file (possibly overwriting old one) for recording
3033 outfile = open(os.path.join(self.dbtmpdir, "CONTENTS"),"w")
3035 self.updateprotect()
3037 #if we have a file containing previously-merged config file md5sums, grab it.
3038 conf_mem_file = os.path.join(destroot, CONFIG_MEMORY_FILE)
3039 cfgfiledict = grabdict(conf_mem_file)
3040 if "NOCONFMEM" in self.settings:
3041 cfgfiledict["IGNORE"]=1
3042 else:
3043 cfgfiledict["IGNORE"]=0
3045 # Always behave like --noconfmem is enabled for downgrades
3046 # so that people who don't know about this option are less
3047 # likely to get confused when doing upgrade/downgrade cycles.
3048 pv_split = catpkgsplit(self.mycpv)[1:]
3049 for other in others_in_slot:
3050 if pkgcmp(pv_split, catpkgsplit(other.mycpv)[1:]) < 0:
3051 cfgfiledict["IGNORE"] = 1
3052 break
3054 # Don't bump mtimes on merge since some application require
3055 # preservation of timestamps. This means that the unmerge phase must
3056 # check to see if file belongs to an installed instance in the same
3057 # slot.
3058 mymtime = None
3060 # set umask to 0 for merging; back up umask, save old one in prevmask (since this is a global change)
3061 prevmask = os.umask(0)
3062 secondhand = []
3064 # we do a first merge; this will recurse through all files in our srcroot but also build up a
3065 # "second hand" of symlinks to merge later
3066 if self.mergeme(srcroot, destroot, outfile, secondhand, "", cfgfiledict, mymtime):
3067 return 1
3069 # now, it's time for dealing our second hand; we'll loop until we can't merge anymore. The rest are
3070 # broken symlinks. We'll merge them too.
3071 lastlen = 0
3072 while len(secondhand) and len(secondhand)!=lastlen:
3073 # clear the thirdhand. Anything from our second hand that
3074 # couldn't get merged will be added to thirdhand.
3076 thirdhand = []
3077 self.mergeme(srcroot, destroot, outfile, thirdhand, secondhand, cfgfiledict, mymtime)
3079 #swap hands
3080 lastlen = len(secondhand)
3082 # our thirdhand now becomes our secondhand. It's ok to throw
3083 # away secondhand since thirdhand contains all the stuff that
3084 # couldn't be merged.
3085 secondhand = thirdhand
3087 if len(secondhand):
3088 # force merge of remaining symlinks (broken or circular; oh well)
3089 self.mergeme(srcroot, destroot, outfile, None, secondhand, cfgfiledict, mymtime)
3091 #restore umask
3092 os.umask(prevmask)
3094 #if we opened it, close it
3095 outfile.flush()
3096 outfile.close()
3098 # write out our collection of md5sums
3099 cfgfiledict.pop("IGNORE", None)
3100 ensure_dirs(os.path.dirname(conf_mem_file),
3101 gid=portage_gid, mode=02750, mask=02)
3102 writedict(cfgfiledict, conf_mem_file)
3104 # These caches are populated during collision-protect and the data
3105 # they contain is now invalid. It's very important to invalidate
3106 # the contents_inodes cache so that FEATURES=unmerge-orphans
3107 # doesn't unmerge anything that belongs to this package that has
3108 # just been merged.
3109 others_in_slot.append(self) # self has just been merged
3110 for dblnk in others_in_slot:
3111 dblnk.contentscache = None
3112 dblnk._contents_inodes = None
3113 dblnk._contents_basenames = None
3115 # If portage is reinstalling itself, remove the old
3116 # version now since we want to use the temporary
3117 # PORTAGE_BIN_PATH that will be removed when we return.
3118 reinstall_self = False
3119 if self.myroot == "/" and \
3120 "sys-apps" == self.cat and \
3121 "portage" == pkgsplit(self.pkg)[0]:
3122 reinstall_self = True
3124 autoclean = self.settings.get("AUTOCLEAN", "yes") == "yes"
3125 for dblnk in list(others_in_slot):
3126 if dblnk is self:
3127 continue
3128 if not (autoclean or dblnk.mycpv == self.mycpv or reinstall_self):
3129 continue
3130 showMessage(">>> Safely unmerging already-installed instance...\n")
3131 others_in_slot.remove(dblnk) # dblnk will unmerge itself now
3132 dblnk.unmerge(trimworld=0, ldpath_mtimes=prev_mtimes,
3133 others_in_slot=others_in_slot)
3134 # TODO: Check status and abort if necessary.
3135 dblnk.delete()
3136 showMessage(">>> Original instance of package unmerged safely.\n")
3138 if len(others_in_slot) > 1:
3139 from portage.output import colorize
3140 showMessage(colorize("WARN", "WARNING:")
3141 + " AUTOCLEAN is disabled. This can cause serious"
3142 + " problems due to overlapping packages.\n",
3143 level=logging.WARN, noiselevel=-1)
3145 # We hold both directory locks.
3146 self.dbdir = self.dbpkgdir
3147 self.delete()
3148 _movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings)
3150 # Check for file collisions with blocking packages
3151 # and remove any colliding files from their CONTENTS
3152 # since they now belong to this package.
3153 self._clear_contents_cache()
3154 contents = self.getcontents()
3155 destroot_len = len(destroot) - 1
3156 for blocker in blockers:
3157 self.vartree.dbapi.removeFromContents(blocker, iter(contents),
3158 relative_paths=False)
3160 self.vartree.dbapi._add(self)
3161 contents = self.getcontents()
3163 # regenerate reverse NEEDED map
3164 self.vartree.dbapi.linkmap.rebuild()
3166 #do postinst script
3167 self.settings["PORTAGE_UPDATE_ENV"] = \
3168 os.path.join(self.dbpkgdir, "environment.bz2")
3169 self.settings.backup_changes("PORTAGE_UPDATE_ENV")
3170 try:
3171 if scheduler is None:
3172 a = doebuild(myebuild, "postinst", destroot, self.settings,
3173 use_cache=0, tree=self.treetype, mydbapi=mydbapi,
3174 vartree=self.vartree)
3175 if a == os.EX_OK:
3176 showMessage(">>> %s %s\n" % (self.mycpv, "merged."))
3177 else:
3178 a = scheduler.dblinkEbuildPhase(
3179 self, mydbapi, myebuild, "postinst")
3180 finally:
3181 self.settings.pop("PORTAGE_UPDATE_ENV", None)
3183 # XXX: Decide how to handle failures here.
3184 if a != os.EX_OK:
3185 showMessage("!!! FAILED postinst: "+str(a)+"\n",
3186 level=logging.ERROR, noiselevel=-1)
3187 return a
3189 downgrade = False
3190 for v in otherversions:
3191 if pkgcmp(catpkgsplit(self.pkg)[1:], catpkgsplit(v)[1:]) < 0:
3192 downgrade = True
3194 #update environment settings, library paths. DO NOT change symlinks.
3195 env_update(makelinks=(not downgrade),
3196 target_root=self.settings["ROOT"], prev_mtimes=prev_mtimes,
3197 contents=contents, env=self.settings.environ(),
3198 writemsg_level=self._display_merge)
3200 return os.EX_OK
3202 def mergeme(self, srcroot, destroot, outfile, secondhand, stufftomerge, cfgfiledict, thismtime):
3205 This function handles actual merging of the package contents to the livefs.
3206 It also handles config protection.
3208 @param srcroot: Where are we copying files from (usually ${D})
3209 @type srcroot: String (Path)
3210 @param destroot: Typically ${ROOT}
3211 @type destroot: String (Path)
3212 @param outfile: File to log operations to
3213 @type outfile: File Object
3214 @param secondhand: A set of items to merge in pass two (usually
3215 or symlinks that point to non-existing files that may get merged later)
3216 @type secondhand: List
3217 @param stufftomerge: Either a diretory to merge, or a list of items.
3218 @type stufftomerge: String or List
3219 @param cfgfiledict: { File:mtime } mapping for config_protected files
3220 @type cfgfiledict: Dictionary
3221 @param thismtime: The current time (typically long(time.time())
3222 @type thismtime: Long
3223 @rtype: None or Boolean
3224 @returns:
3225 1. True on failure
3226 2. None otherwise
3230 showMessage = self._display_merge
3231 scheduler = self._scheduler
3233 from os.path import sep, join
3234 srcroot = normalize_path(srcroot).rstrip(sep) + sep
3235 destroot = normalize_path(destroot).rstrip(sep) + sep
3237 # this is supposed to merge a list of files. There will be 2 forms of argument passing.
3238 if isinstance(stufftomerge, basestring):
3239 #A directory is specified. Figure out protection paths, listdir() it and process it.
3240 mergelist = os.listdir(join(srcroot, stufftomerge))
3241 offset = stufftomerge
3242 else:
3243 mergelist = stufftomerge
3244 offset = ""
3246 for i, x in enumerate(mergelist):
3248 if scheduler is not None and \
3249 0 == i % self._file_merge_yield_interval:
3250 scheduler.scheduleYield()
3252 mysrc = join(srcroot, offset, x)
3253 mydest = join(destroot, offset, x)
3254 # myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/")
3255 myrealdest = join(sep, offset, x)
3256 # stat file once, test using S_* macros many times (faster that way)
3257 try:
3258 mystat = os.lstat(mysrc)
3259 except OSError, e:
3260 writemsg("\n")
3261 writemsg(red("!!! ERROR: There appears to be ")+bold("FILE SYSTEM CORRUPTION.")+red(" A file that is listed\n"))
3262 writemsg(red("!!! as existing is not capable of being stat'd. If you are using an\n"))
3263 writemsg(red("!!! experimental kernel, please boot into a stable one, force an fsck,\n"))
3264 writemsg(red("!!! and ensure your filesystem is in a sane state. ")+bold("'shutdown -Fr now'\n"))
3265 writemsg(red("!!! File: ")+str(mysrc)+"\n", noiselevel=-1)
3266 writemsg(red("!!! Error: ")+str(e)+"\n", noiselevel=-1)
3267 sys.exit(1)
3268 except Exception, e:
3269 writemsg("\n")
3270 writemsg(red("!!! ERROR: An unknown error has occurred during the merge process.\n"))
3271 writemsg(red("!!! A stat call returned the following error for the following file:"))
3272 writemsg( "!!! Please ensure that your filesystem is intact, otherwise report\n")
3273 writemsg( "!!! this as a portage bug at bugs.gentoo.org. Append 'emerge info'.\n")
3274 writemsg( "!!! File: "+str(mysrc)+"\n", noiselevel=-1)
3275 writemsg( "!!! Error: "+str(e)+"\n", noiselevel=-1)
3276 sys.exit(1)
3279 mymode = mystat[stat.ST_MODE]
3280 # handy variables; mydest is the target object on the live filesystems;
3281 # mysrc is the source object in the temporary install dir
3282 try:
3283 mydstat = os.lstat(mydest)
3284 mydmode = mydstat.st_mode
3285 except OSError, e:
3286 if e.errno != errno.ENOENT:
3287 raise
3288 del e
3289 #dest file doesn't exist
3290 mydstat = None
3291 mydmode = None
3293 if stat.S_ISLNK(mymode):
3294 # we are merging a symbolic link
3295 myabsto = abssymlink(mysrc)
3296 if myabsto.startswith(srcroot):
3297 myabsto = myabsto[len(srcroot):]
3298 myabsto = myabsto.lstrip(sep)
3299 myto = os.readlink(mysrc)
3300 if self.settings and self.settings["D"]:
3301 if myto.startswith(self.settings["D"]):
3302 myto = myto[len(self.settings["D"]):]
3303 # myrealto contains the path of the real file to which this symlink points.
3304 # we can simply test for existence of this file to see if the target has been merged yet
3305 myrealto = normalize_path(os.path.join(destroot, myabsto))
3306 if mydmode!=None:
3307 #destination exists
3308 if not stat.S_ISLNK(mydmode):
3309 if stat.S_ISDIR(mydmode):
3310 # directory in the way: we can't merge a symlink over a directory
3311 # we won't merge this, continue with next file...
3312 continue
3314 if os.path.exists(mysrc) and stat.S_ISDIR(os.stat(mysrc)[stat.ST_MODE]):
3315 # Kill file blocking installation of symlink to dir #71787
3316 pass
3317 elif self.isprotected(mydest):
3318 # Use md5 of the target in ${D} if it exists...
3319 try:
3320 newmd5 = perform_md5(join(srcroot, myabsto))
3321 except FileNotFound:
3322 # Maybe the target is merged already.
3323 try:
3324 newmd5 = perform_md5(myrealto)
3325 except FileNotFound:
3326 newmd5 = None
3327 mydest = new_protect_filename(mydest, newmd5=newmd5)
3329 # if secondhand is None it means we're operating in "force" mode and should not create a second hand.
3330 if (secondhand != None) and (not os.path.exists(myrealto)):
3331 # either the target directory doesn't exist yet or the target file doesn't exist -- or
3332 # the target is a broken symlink. We will add this file to our "second hand" and merge
3333 # it later.
3334 secondhand.append(mysrc[len(srcroot):])
3335 continue
3336 # unlinking no longer necessary; "movefile" will overwrite symlinks atomically and correctly
3337 mymtime = movefile(mysrc, mydest, newmtime=thismtime, sstat=mystat, mysettings=self.settings)
3338 if mymtime != None:
3339 showMessage(">>> %s -> %s\n" % (mydest, myto))
3340 outfile.write("sym "+myrealdest+" -> "+myto+" "+str(mymtime)+"\n")
3341 else:
3342 print "!!! Failed to move file."
3343 print "!!!", mydest, "->", myto
3344 sys.exit(1)
3345 elif stat.S_ISDIR(mymode):
3346 # we are merging a directory
3347 if mydmode != None:
3348 # destination exists
3350 if bsd_chflags:
3351 # Save then clear flags on dest.
3352 dflags = mydstat.st_flags
3353 if dflags != 0:
3354 bsd_chflags.lchflags(mydest, 0)
3356 if not os.access(mydest, os.W_OK):
3357 pkgstuff = pkgsplit(self.pkg)
3358 writemsg("\n!!! Cannot write to '"+mydest+"'.\n", noiselevel=-1)
3359 writemsg("!!! Please check permissions and directories for broken symlinks.\n")
3360 writemsg("!!! You may start the merge process again by using ebuild:\n")
3361 writemsg("!!! ebuild "+self.settings["PORTDIR"]+"/"+self.cat+"/"+pkgstuff[0]+"/"+self.pkg+".ebuild merge\n")
3362 writemsg("!!! And finish by running this: env-update\n\n")
3363 return 1
3365 if stat.S_ISLNK(mydmode) or stat.S_ISDIR(mydmode):
3366 # a symlink to an existing directory will work for us; keep it:
3367 showMessage("--- %s/\n" % mydest)
3368 if bsd_chflags:
3369 bsd_chflags.lchflags(mydest, dflags)
3370 else:
3371 # a non-directory and non-symlink-to-directory. Won't work for us. Move out of the way.
3372 if movefile(mydest, mydest+".backup", mysettings=self.settings) is None:
3373 sys.exit(1)
3374 print "bak", mydest, mydest+".backup"
3375 #now create our directory
3376 if self.settings.selinux_enabled():
3377 import selinux
3378 sid = selinux.get_sid(mysrc)
3379 selinux.secure_mkdir(mydest,sid)
3380 else:
3381 os.mkdir(mydest)
3382 if bsd_chflags:
3383 bsd_chflags.lchflags(mydest, dflags)
3384 os.chmod(mydest, mystat[0])
3385 os.chown(mydest, mystat[4], mystat[5])
3386 showMessage(">>> %s/\n" % mydest)
3387 else:
3388 #destination doesn't exist
3389 if self.settings.selinux_enabled():
3390 import selinux
3391 sid = selinux.get_sid(mysrc)
3392 selinux.secure_mkdir(mydest, sid)
3393 else:
3394 os.mkdir(mydest)
3395 os.chmod(mydest, mystat[0])
3396 os.chown(mydest, mystat[4], mystat[5])
3397 showMessage(">>> %s/\n" % mydest)
3398 outfile.write("dir "+myrealdest+"\n")
3399 # recurse and merge this directory
3400 if self.mergeme(srcroot, destroot, outfile, secondhand,
3401 join(offset, x), cfgfiledict, thismtime):
3402 return 1
3403 elif stat.S_ISREG(mymode):
3404 # we are merging a regular file
3405 mymd5 = perform_md5(mysrc, calc_prelink=1)
3406 # calculate config file protection stuff
3407 mydestdir = os.path.dirname(mydest)
3408 moveme = 1
3409 zing = "!!!"
3410 mymtime = None
3411 if mydmode != None:
3412 # destination file exists
3413 if stat.S_ISDIR(mydmode):
3414 # install of destination is blocked by an existing directory with the same name
3415 moveme = 0
3416 showMessage("!!! %s\n" % mydest,
3417 level=logging.ERROR, noiselevel=-1)
3418 elif stat.S_ISREG(mydmode) or (stat.S_ISLNK(mydmode) and os.path.exists(mydest) and stat.S_ISREG(os.stat(mydest)[stat.ST_MODE])):
3419 cfgprot = 0
3420 # install of destination is blocked by an existing regular file,
3421 # or by a symlink to an existing regular file;
3422 # now, config file management may come into play.
3423 # we only need to tweak mydest if cfg file management is in play.
3424 if self.isprotected(mydest):
3425 # we have a protection path; enable config file management.
3426 destmd5 = perform_md5(mydest, calc_prelink=1)
3427 if mymd5 == destmd5:
3428 #file already in place; simply update mtimes of destination
3429 moveme = 1
3430 else:
3431 if mymd5 == cfgfiledict.get(myrealdest, [None])[0]:
3432 """ An identical update has previously been
3433 merged. Skip it unless the user has chosen
3434 --noconfmem."""
3435 moveme = cfgfiledict["IGNORE"]
3436 cfgprot = cfgfiledict["IGNORE"]
3437 if not moveme:
3438 zing = "---"
3439 mymtime = long(mystat.st_mtime)
3440 else:
3441 moveme = 1
3442 cfgprot = 1
3443 if moveme:
3444 # Merging a new file, so update confmem.
3445 cfgfiledict[myrealdest] = [mymd5]
3446 elif destmd5 == cfgfiledict.get(myrealdest, [None])[0]:
3447 """A previously remembered update has been
3448 accepted, so it is removed from confmem."""
3449 del cfgfiledict[myrealdest]
3450 if cfgprot:
3451 mydest = new_protect_filename(mydest, newmd5=mymd5)
3453 # whether config protection or not, we merge the new file the
3454 # same way. Unless moveme=0 (blocking directory)
3455 if moveme:
3456 mymtime = movefile(mysrc, mydest, newmtime=thismtime, sstat=mystat, mysettings=self.settings)
3457 if mymtime is None:
3458 sys.exit(1)
3459 zing = ">>>"
3461 if mymtime != None:
3462 outfile.write("obj "+myrealdest+" "+mymd5+" "+str(mymtime)+"\n")
3463 showMessage("%s %s\n" % (zing,mydest))
3464 else:
3465 # we are merging a fifo or device node
3466 zing = "!!!"
3467 if mydmode is None:
3468 # destination doesn't exist
3469 if movefile(mysrc, mydest, newmtime=thismtime, sstat=mystat, mysettings=self.settings) != None:
3470 zing = ">>>"
3471 else:
3472 sys.exit(1)
3473 if stat.S_ISFIFO(mymode):
3474 outfile.write("fif %s\n" % myrealdest)
3475 else:
3476 outfile.write("dev %s\n" % myrealdest)
3477 showMessage(zing + " " + mydest + "\n")
3479 def merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0,
3480 mydbapi=None, prev_mtimes=None):
3482 If portage is reinstalling itself, create temporary
3483 copies of PORTAGE_BIN_PATH and PORTAGE_PYM_PATH in order
3484 to avoid relying on the new versions which may be
3485 incompatible. Register an atexit hook to clean up the
3486 temporary directories. Pre-load elog modules here since
3487 we won't be able to later if they get unmerged (happens
3488 when namespace changes).
3490 if self.vartree.dbapi._categories is not None:
3491 self.vartree.dbapi._categories = None
3492 if self.myroot == "/" and \
3493 "sys-apps" == self.cat and \
3494 "portage" == pkgsplit(self.pkg)[0]:
3495 settings = self.settings
3496 base_path_orig = os.path.dirname(settings["PORTAGE_BIN_PATH"])
3497 from tempfile import mkdtemp
3498 import shutil
3499 # Make the temp directory inside PORTAGE_TMPDIR since, unlike
3500 # /tmp, it can't be mounted with the "noexec" option.
3501 base_path_tmp = mkdtemp("", "._portage_reinstall_.",
3502 settings["PORTAGE_TMPDIR"])
3503 from portage.process import atexit_register
3504 atexit_register(shutil.rmtree, base_path_tmp)
3505 dir_perms = 0755
3506 for subdir in "bin", "pym":
3507 var_name = "PORTAGE_%s_PATH" % subdir.upper()
3508 var_orig = settings[var_name]
3509 var_new = os.path.join(base_path_tmp, subdir)
3510 settings[var_name] = var_new
3511 settings.backup_changes(var_name)
3512 shutil.copytree(var_orig, var_new, symlinks=True)
3513 os.chmod(var_new, dir_perms)
3514 os.chmod(base_path_tmp, dir_perms)
3515 # This serves so pre-load the modules.
3516 elog_process(self.mycpv, self.settings,
3517 phasefilter=filter_mergephases)
3519 return self._merge(mergeroot, inforoot,
3520 myroot, myebuild=myebuild, cleanup=cleanup,
3521 mydbapi=mydbapi, prev_mtimes=prev_mtimes)
3523 def _merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0,
3524 mydbapi=None, prev_mtimes=None):
3525 retval = -1
3526 self.lockdb()
3527 try:
3528 retval = self.treewalk(mergeroot, myroot, inforoot, myebuild,
3529 cleanup=cleanup, mydbapi=mydbapi, prev_mtimes=prev_mtimes)
3530 # undo registrations of preserved libraries, bug #210501
3531 if retval != os.EX_OK:
3532 self.vartree.dbapi.plib_registry.unregister(self.mycpv, self.settings["SLOT"], self.settings["COUNTER"])
3533 # Process ebuild logfiles
3534 elog_process(self.mycpv, self.settings, phasefilter=filter_mergephases)
3535 if retval == os.EX_OK and "noclean" not in self.settings.features:
3536 if myebuild is None:
3537 myebuild = os.path.join(inforoot, self.pkg + ".ebuild")
3538 doebuild(myebuild, "clean", myroot, self.settings,
3539 tree=self.treetype, mydbapi=mydbapi, vartree=self.vartree)
3540 finally:
3541 self.unlockdb()
3542 return retval
3544 def getstring(self,name):
3545 "returns contents of a file with whitespace converted to spaces"
3546 if not os.path.exists(self.dbdir+"/"+name):
3547 return ""
3548 myfile = open(self.dbdir+"/"+name,"r")
3549 mydata = myfile.read().split()
3550 myfile.close()
3551 return " ".join(mydata)
3553 def copyfile(self,fname):
3554 import shutil
3555 shutil.copyfile(fname,self.dbdir+"/"+os.path.basename(fname))
3557 def getfile(self,fname):
3558 if not os.path.exists(self.dbdir+"/"+fname):
3559 return ""
3560 myfile = open(self.dbdir+"/"+fname,"r")
3561 mydata = myfile.read()
3562 myfile.close()
3563 return mydata
3565 def setfile(self,fname,data):
3566 write_atomic(os.path.join(self.dbdir, fname), data)
3568 def getelements(self,ename):
3569 if not os.path.exists(self.dbdir+"/"+ename):
3570 return []
3571 myelement = open(self.dbdir+"/"+ename,"r")
3572 mylines = myelement.readlines()
3573 myreturn = []
3574 for x in mylines:
3575 for y in x[:-1].split():
3576 myreturn.append(y)
3577 myelement.close()
3578 return myreturn
3580 def setelements(self,mylist,ename):
3581 myelement = open(self.dbdir+"/"+ename,"w")
3582 for x in mylist:
3583 myelement.write(x+"\n")
3584 myelement.close()
3586 def isregular(self):
3587 "Is this a regular package (does it have a CATEGORY file? A dblink can be virtual *and* regular)"
3588 return os.path.exists(os.path.join(self.dbdir, "CATEGORY"))
3590 def write_contents(contents, root, f):
3592 Write contents to any file like object. The file will be left open.
3594 root_len = len(root) - 1
3595 for filename in sorted(contents):
3596 entry_data = contents[filename]
3597 entry_type = entry_data[0]
3598 relative_filename = filename[root_len:]
3599 if entry_type == "obj":
3600 entry_type, mtime, md5sum = entry_data
3601 line = "%s %s %s %s\n" % \
3602 (entry_type, relative_filename, md5sum, mtime)
3603 elif entry_type == "sym":
3604 entry_type, mtime, link = entry_data
3605 line = "%s %s -> %s %s\n" % \
3606 (entry_type, relative_filename, link, mtime)
3607 else: # dir, dev, fif
3608 line = "%s %s\n" % (entry_type, relative_filename)
3609 f.write(line)
3611 def tar_contents(contents, root, tar, protect=None, onProgress=None):
3612 from portage.util import normalize_path
3613 import tarfile
3614 root = normalize_path(root).rstrip(os.path.sep) + os.path.sep
3615 id_strings = {}
3616 maxval = len(contents)
3617 curval = 0
3618 if onProgress:
3619 onProgress(maxval, 0)
3620 paths = contents.keys()
3621 paths.sort()
3622 for path in paths:
3623 curval += 1
3624 try:
3625 lst = os.lstat(path)
3626 except OSError, e:
3627 if e.errno != errno.ENOENT:
3628 raise
3629 del e
3630 if onProgress:
3631 onProgress(maxval, curval)
3632 continue
3633 contents_type = contents[path][0]
3634 if path.startswith(root):
3635 arcname = path[len(root):]
3636 else:
3637 raise ValueError("invalid root argument: '%s'" % root)
3638 live_path = path
3639 if 'dir' == contents_type and \
3640 not stat.S_ISDIR(lst.st_mode) and \
3641 os.path.isdir(live_path):
3642 # Even though this was a directory in the original ${D}, it exists
3643 # as a symlink to a directory in the live filesystem. It must be
3644 # recorded as a real directory in the tar file to ensure that tar
3645 # can properly extract it's children.
3646 live_path = os.path.realpath(live_path)
3647 tarinfo = tar.gettarinfo(live_path, arcname)
3648 # store numbers instead of real names like tar's --numeric-owner
3649 tarinfo.uname = id_strings.setdefault(tarinfo.uid, str(tarinfo.uid))
3650 tarinfo.gname = id_strings.setdefault(tarinfo.gid, str(tarinfo.gid))
3652 if stat.S_ISREG(lst.st_mode):
3653 # break hardlinks due to bug #185305
3654 tarinfo.type = tarfile.REGTYPE
3655 if protect and protect(path):
3656 # Create an empty file as a place holder in order to avoid
3657 # potential collision-protect issues.
3658 tarinfo.size = 0
3659 tar.addfile(tarinfo)
3660 else:
3661 f = open(path)
3662 try:
3663 tar.addfile(tarinfo, f)
3664 finally:
3665 f.close()
3666 else:
3667 tar.addfile(tarinfo)
3668 if onProgress:
3669 onProgress(maxval, curval)