def listCached(self): items = [] for name in os.listdir(self.cacheDir): if len(name) != (40 + len(self.suffix)) or not name.endswith(self.suffix): continue fingerprint = sha1FromString(name[:40]) st = util.lstat(os.path.join(self.cacheDir, name, '.used')) if st: atime = st.st_mtime else: st = util.lstat(os.path.join(self.cacheDir, name)) if st: atime = st.st_atime else: # Disappeared continue size = 0 items.append(CachedItem(fingerprint, atime, size)) return items
def listCached(self): items = [] for name in os.listdir(self.cacheDir): if len(name) != (40 + len(self.suffix)) or not name.endswith( self.suffix): continue fingerprint = sha1FromString(name[:40]) st = util.lstat(os.path.join(self.cacheDir, name, '.used')) if st: atime = st.st_mtime else: st = util.lstat(os.path.join(self.cacheDir, name)) if st: atime = st.st_atime else: # Disappeared continue size = 0 items.append(CachedItem(fingerprint, atime, size)) return items
def install(self, flags, troveCs): ACTION_RESTORE = 1 ACTION_SKIP = 2 ACTION_CONFLICT = 3 rc = SingleCapsuleOperation.install(self, flags, troveCs) if rc is None: # parent class thinks we should just ignore this troveCs; I'm # not going to argue with it (it's probably because the capsule # hasn't changed return None (oldTrv, trv) = rc trvInfo = troveCs.getNewNameVersionFlavor() oldTrvInfo = troveCs.getOldNameVersionFlavor() hasCapsule = troveCs.hasCapsule() # Updates the fsJob metadata for installing the current trove. # It assumes files are replaced on install, and complains if something # is in the way unless the appropriate flags are set. This is a very # much simplified version of FilesystemJob._singleTrove() which maps # out a complete install strategy for native packages. Note that # we walk all of the files in this trove, not just the new files # or the changed files, because RPM installs all of the files. toRestore = [] changedByPathId = dict((x[0], x) for x in troveCs.getChangedFileList()) # things which aren't change, new, or removed are unchanged unchangedByPathId = (set(x[0] for x in trv.iterFileList()) - set(changedByPathId.iterkeys()) - set(x[0] for x in troveCs.getNewFileList()) - set(troveCs.getOldFileList())) l = [] for oldFileInfo in troveCs.getChangedFileList(): oldFileId, oldVersion = oldTrv.getFile(oldFileInfo[0])[1:3] l.append((oldFileInfo[0], oldFileId, oldVersion)) for unchangedPathId in unchangedByPathId: unchangedFileId, unchangedFileVersion = \ trv.getFile(unchangedPathId)[1:3] l.append((unchangedPathId, unchangedFileId, unchangedFileVersion)) fileObjs = self.db.getFileVersions(l) fileObjsByPathId = dict( [ (x[0], y) for x, y in itertools.izip(l, fileObjs) ] ) for fileInfo in trv.iterFileList(): pathId, path, fileId, version = fileInfo if os.path.dirname(path) in self.netSharedPath: # we do nothing. really. nothing. # # we don't back it up. we don't mark it as removed in # our database. we don't look for conflicts. nothing. continue if pathId in changedByPathId: oldFileId = oldTrv.getFile(pathId)[1] fileChange = self.changeSet.getFileChange(oldFileId, fileId) if (oldFileId == fileId): # only the version number changed; we don't need # to merge anything here fileObj = fileObjsByPathId[pathId] elif fileChange[0] == '\x01': fileObj = fileObjsByPathId[pathId] fileObj.twm(fileChange, fileObj) else: fileObj = files.ThawFile(fileChange, pathId) elif pathId in unchangedByPathId: fileObj = fileObjsByPathId[pathId] else: # if it's not changed and it's not unchanged, it must be new fileStream = self.changeSet.getFileChange(None, fileId) fileObj = files.ThawFile(fileStream, pathId) absolutePath = util.joinPaths(self.root, path) if (fileObj.flags.isCapsuleAddition()): # this was added to the package outside of the RPM; we don't # have any responsibility for it continue elif (trove.conaryContents(hasCapsule, pathId, fileObj) and fileObj.lsTag != 'd'): # this content isn't part of the capsule; remember to put # it back when RPM is done self.preservePath(path) continue s = util.lstat(absolutePath) if not s: # there is nothing in the way, so there is nothing which # concerns us here. Track the file for later. toRestore.append((fileInfo, fileObj)) continue action = ACTION_CONFLICT existingOwners = list( self.db.iterFindPathReferences(path, justPresent = True, withStream = True)) if existingOwners: # Don't complain about files owned by the previous version # of this trove. l = [ x for x in existingOwners if x[0:3] == oldTrvInfo ] if l: existingOwners.remove(l[0]) if not existingOwners: action = ACTION_RESTORE elif stat.S_ISDIR(s.st_mode) and fileObj.lsTag == 'd': # Don't let existing directories stop us from taking over # ownership of the directory action = ACTION_RESTORE elif fileObj.flags.isInitialContents(): # Initial contents files may be restored on top of things # already in the filesystem. They're ghosts or config files # and RPM will get the contents right either way, and we # should remove them either way. action = ACTION_RESTORE if action == ACTION_CONFLICT and existingOwners: if fileId in [ x[4] for x in existingOwners ]: # The files share metadata same. Whatever it looks like on # disk, RPM is going to blow it away with the new one. for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif path.startswith('/usr/share/doc/'): # Mirror badness Red Hat patches into RPM for rhel4 # and rhel5 action = ACTION_RESTORE else: existingFiles = [ files.ThawFile(x[5], pathId) for x in existingOwners ] compatibility = [ 1 for x in existingFiles if fileObj.compatibleWith(x) ] if 1 in compatibility: # files can be shared even though the fileId's # are different for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif 1 in [ files.rpmFileColorCmp(x, fileObj) for x in existingFiles ]: # rpm file colors and the default rpm setting for # file color policy make elf64 files silently replace # elf32 files. follow that behavior here. # # no, i'm not making this up # # yes, really action = ACTION_SKIP elif (self._checkReplaceManagedFiles(flags, path) or 1 in [ files.rpmFileColorCmp(fileObj, x) for x in existingFiles ]): # The files are different. Bail unless we're supposed # to replace managed files. existingFile = files.FileFromFilesystem(absolutePath, pathId) for info in existingOwners: self.fsJob.userRemoval( fileObj = existingFile, content = filecontents.FromFilesystem(absolutePath), *info[0:4]) action = ACTION_RESTORE else: # it's not up to us to decide if this is a true # conflict; the database layer will do that for # us (see checkPathConflicts) action = ACTION_RESTORE elif flags.replaceUnmanagedFiles: # we don't own it, but it's on disk. RPM will just write over # it and we have the flag saying we're good with that action = ACTION_RESTORE if action == ACTION_RESTORE: # We may proceed, and RPM will replace this file for us. We # need to track that it's being restored to avoid conflicts # with other restorations though. toRestore.append((fileInfo, fileObj)) elif action == ACTION_CONFLICT: # The file exists already, we can't share it, and we're not # allowed to overwrite it. self._error(errors.FileInWayError(util.normpath(path), troveCs.getName(), troveCs.getNewVersion(), troveCs.getNewFlavor())) else: assert(action == ACTION_SKIP) self.preservePath(path) self.fsJob.userRemoval(trv.getName(), trv.getVersion(), trv.getFlavor(), pathId) # toRestore is the list of what is going to be restored. We need to get # the fileObjects which will be created so we can track them in the # filesystem job. This lets the filesystem job look for resolveable # conflicts within this update. We handle newly created files first # and files which have changed (so we have to look up the diff) # a bit later. for fileInfo, fileObj in toRestore: fullPath = util.joinPaths(self.root, path) self.fsJob._restore(fileObj, fullPath, trvInfo, "restoring %s from RPM", restoreFile = False, fileId = fileId)
def install(self, flags, troveCs): ACTION_RESTORE = 1 ACTION_SKIP = 2 ACTION_CONFLICT = 3 rc = SingleCapsuleOperation.install(self, flags, troveCs) if rc is None: # parent class thinks we should just ignore this troveCs; I'm # not going to argue with it (it's probably because the capsule # hasn't changed return None (oldTrv, trv) = rc trvInfo = troveCs.getNewNameVersionFlavor() oldTrvInfo = troveCs.getOldNameVersionFlavor() hasCapsule = troveCs.hasCapsule() # Updates the fsJob metadata for installing the current trove. # It assumes files are replaced on install, and complains if something # is in the way unless the appropriate flags are set. This is a very # much simplified version of FilesystemJob._singleTrove() which maps # out a complete install strategy for native packages. Note that # we walk all of the files in this trove, not just the new files # or the changed files, because RPM installs all of the files. toRestore = [] changedByPathId = dict((x[0], x) for x in troveCs.getChangedFileList()) # things which aren't change, new, or removed are unchanged unchangedByPathId = (set(x[0] for x in trv.iterFileList()) - set(changedByPathId.iterkeys()) - set(x[0] for x in troveCs.getNewFileList()) - set(troveCs.getOldFileList())) l = [] for oldFileInfo in troveCs.getChangedFileList(): oldFileId, oldVersion = oldTrv.getFile(oldFileInfo[0])[1:3] l.append((oldFileInfo[0], oldFileId, oldVersion)) for unchangedPathId in unchangedByPathId: unchangedFileId, unchangedFileVersion = \ trv.getFile(unchangedPathId)[1:3] l.append((unchangedPathId, unchangedFileId, unchangedFileVersion)) fileObjs = self.db.getFileVersions(l) fileObjsByPathId = dict([(x[0], y) for x, y in itertools.izip(l, fileObjs)]) for fileInfo in trv.iterFileList(): pathId, path, fileId, version = fileInfo if os.path.dirname(path) in self.netSharedPath: # we do nothing. really. nothing. # # we don't back it up. we don't mark it as removed in # our database. we don't look for conflicts. nothing. continue if pathId in changedByPathId: oldFileId = oldTrv.getFile(pathId)[1] fileChange = self.changeSet.getFileChange(oldFileId, fileId) if (oldFileId == fileId): # only the version number changed; we don't need # to merge anything here fileObj = fileObjsByPathId[pathId] elif fileChange[0] == '\x01': fileObj = fileObjsByPathId[pathId] fileObj.twm(fileChange, fileObj) else: fileObj = files.ThawFile(fileChange, pathId) elif pathId in unchangedByPathId: fileObj = fileObjsByPathId[pathId] else: # if it's not changed and it's not unchanged, it must be new fileStream = self.changeSet.getFileChange(None, fileId) fileObj = files.ThawFile(fileStream, pathId) absolutePath = util.joinPaths(self.root, path) if (fileObj.flags.isCapsuleAddition()): # this was added to the package outside of the RPM; we don't # have any responsibility for it continue elif (trove.conaryContents(hasCapsule, pathId, fileObj) and fileObj.lsTag != 'd'): # this content isn't part of the capsule; remember to put # it back when RPM is done self.preservePath(path, unlink=True) continue s = util.lstat(absolutePath) if not s: # there is nothing in the way, so there is nothing which # concerns us here. Track the file for later. toRestore.append((fileInfo, fileObj)) continue action = ACTION_CONFLICT existingOwners = list( self.db.iterFindPathReferences(path, justPresent=True, withStream=True)) if existingOwners: # Don't complain about files owned by the previous version # of this trove. l = [x for x in existingOwners if x[0:3] == oldTrvInfo] if l: existingOwners.remove(l[0]) if not existingOwners: action = ACTION_RESTORE elif stat.S_ISDIR(s.st_mode) and fileObj.lsTag == 'd': # Don't let existing directories stop us from taking over # ownership of the directory action = ACTION_RESTORE elif fileObj.flags.isInitialContents(): # Initial contents files may be restored on top of things # already in the filesystem. They're ghosts or config files # and RPM will get the contents right either way, and we # should remove them either way. action = ACTION_RESTORE if action == ACTION_CONFLICT and not existingOwners: # Check for "conflicts" that might just be a view across a # symlink. if self.fsJob.findAliasedRemovals(absolutePath): action = ACTION_RESTORE if action == ACTION_CONFLICT and existingOwners: if fileId in [x[4] for x in existingOwners]: # The files share metadata same. Whatever it looks like on # disk, RPM is going to blow it away with the new one. for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif path.startswith('/usr/share/doc/'): # Mirror badness Red Hat patches into RPM for rhel4 # and rhel5 action = ACTION_RESTORE else: existingFiles = [ files.ThawFile(x[5], pathId) for x in existingOwners ] compatibility = [ 1 for x in existingFiles if fileObj.compatibleWith(x) ] if 1 in compatibility: # files can be shared even though the fileId's # are different for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif 1 in [ files.rpmFileColorCmp(x, fileObj) for x in existingFiles ]: # rpm file colors and the default rpm setting for # file color policy make elf64 files silently replace # elf32 files. follow that behavior here. # # no, i'm not making this up # # yes, really action = ACTION_SKIP elif (self._checkReplaceManagedFiles(flags, path) or 1 in [ files.rpmFileColorCmp(fileObj, x) for x in existingFiles ]): # The files are different. Bail unless we're supposed # to replace managed files. existingFile = files.FileFromFilesystem( absolutePath, pathId) for info in existingOwners: self.fsJob.userRemoval( fileObj=existingFile, content=filecontents.FromFilesystem( absolutePath), *info[0:4]) action = ACTION_RESTORE else: # it's not up to us to decide if this is a true # conflict; the database layer will do that for # us (see checkPathConflicts) action = ACTION_RESTORE elif flags.replaceUnmanagedFiles: # we don't own it, but it's on disk. RPM will just write over # it and we have the flag saying we're good with that action = ACTION_RESTORE if action == ACTION_RESTORE: # We may proceed, and RPM will replace this file for us. We # need to track that it's being restored to avoid conflicts # with other restorations though. toRestore.append((fileInfo, fileObj)) elif action == ACTION_CONFLICT: # The file exists already, we can't share it, and we're not # allowed to overwrite it. self._error( errors.FileInWayError(util.normpath(path), troveCs.getName(), troveCs.getNewVersion(), troveCs.getNewFlavor())) else: assert (action == ACTION_SKIP) self.preservePath(path, unlink=False) self.fsJob.userRemoval(trv.getName(), trv.getVersion(), trv.getFlavor(), pathId) # toRestore is the list of what is going to be restored. We need to get # the fileObjects which will be created so we can track them in the # filesystem job. This lets the filesystem job look for resolveable # conflicts within this update. We handle newly created files first # and files which have changed (so we have to look up the diff) # a bit later. for fileInfo, fileObj in toRestore: fullPath = util.joinPaths(self.root, path) self.fsJob._restore(fileObj, fullPath, trvInfo, "restoring %s from RPM", restoreFile=False, fileId=fileId)