Esempio n. 1
0
    def needsdata(self, orig, pkgplan):
        if self.replace_required:
            return True

        # import goes here to prevent circular import
        from pkg.client.imageconfig import CONTENT_UPDATE_POLICY

        use_content_hash = orig and pkgplan.image.cfg.get_policy_str(
            CONTENT_UPDATE_POLICY) == "when-required"

        # If content update policy allows it, check for a common
        # preferred content hash.
        if use_content_hash:
            content_hash_attr, content_hash_val, \
            orig_content_hash_val, content_hash_func = \
                digest.get_common_preferred_hash(
                    self, orig, hash_type=digest.HASH_GELF)

        hash_attr, hash_val, orig_hash_val, hash_func = \
            digest.get_common_preferred_hash(self, orig)

        if not orig:
            changed_hash = True
        elif orig and (orig_hash_val is None or hash_val is None):
            # we have no common hash so we have to treat this as a
            # changed action
            changed_hash = True
        else:
            changed_hash = hash_val != orig_hash_val

        if (changed_hash and (not use_content_hash
                              or content_hash_val != orig_content_hash_val)):
            if ("preserve" not in self.attrs or not pkgplan.origin_fmri):
                return True
        elif orig:
            # It's possible that the file content hasn't changed
            # for an upgrade case, but the file is missing.  This
            # ensures that for cases where the mode or some other
            # attribute of the file has changed that the file will
            # be installed.
            path = self.get_installed_path(pkgplan.image.get_root())
            if not os.path.isfile(path):
                return True

        pres_type = self._check_preserve(orig, pkgplan)
        if pres_type not in (None, True, "abandon"):
            # Preserved files only need data if they're being
            # changed (e.g. "renameold", etc.).
            return True

        return False
Esempio n. 2
0
    def _check_preserve(self, orig, pkgplan, orig_path=None):
        """Return the type of preservation needed for this action.

                Returns None if preservation is not defined by the action.
                Returns False if it is, but no preservation is necessary.
                Returns True for the normal preservation form.  Returns one of
                the strings 'renameold', 'renameold.update', 'renamenew',
                'legacy', or 'abandon' for each of the respective forms of
                preservation.
                """

        # If the logic in this function ever changes, all callers will
        # need to be updated to reflect how they interpret return
        # values.

        try:
            pres_type = self.attrs["preserve"]
        except KeyError:
            return

        # Should ultimately be conditioned on file type
        if "elfhash" in self.attrs:
            # Don't allow preserve logic to be applied to elf files;
            # if we ever stop tagging elf binaries with this
            # attribute, this will need to be updated.
            return

        if pres_type == "abandon":
            return pres_type

        final_path = self.get_installed_path(pkgplan.image.get_root())

        # 'legacy' preservation is very different than other forms of
        # preservation as it doesn't account for the on-disk state of
        # the action's payload.
        if pres_type == "legacy":
            if not orig:
                # This is an initial install or a repair, so
                # there's nothing to deliver.
                return True
            return pres_type

        # If action has been marked with a preserve attribute, the
        # hash of the preserved file has changed between versions,
        # and the package being installed is older than the package
        # that was installed, and the version on disk is different
        # than the installed package's original version, then preserve
        # the installed file by renaming it.
        #
        # If pkgplan.origin_fmri isn't set, but there is an orig action,
        # then this file is moving between packages and it can't be
        # a downgrade since that isn't allowed across rename or obsolete
        # boundaries.
        is_file = os.path.isfile(final_path)

        # 'install-only' preservation has very specific semantics as
        # well; if there's an 'orig' or this is an initial install and
        # the file exists, we should not modify the file content.
        if pres_type == "install-only":
            if orig or is_file:
                return True
            return False

        changed_hash = False
        if orig:
            # We must use the same hash algorithm when comparing old
            # and new actions. Look for the most-preferred common
            # hash between old and new. Since the two actions may
            # not share a common hash (in which case, we get a tuple
            # of 'None' objects) we also need to know the preferred
            # hash to use when examining the old action on its own.
            common_hash_attr, common_hash_val, \
                common_orig_hash_val, common_hash_func = \
                digest.get_common_preferred_hash(self, orig)

            hattr, orig_hash_val, orig_hash_func = \
                digest.get_preferred_hash(orig)

            if common_orig_hash_val and common_hash_val:
                changed_hash = common_hash_val != common_orig_hash_val
            else:
                # we don't have a common hash, so we must treat
                # this as a changed action
                changed_hash = True

            if pkgplan.destination_fmri and \
                changed_hash and \
                pkgplan.origin_fmri and \
                pkgplan.destination_fmri.version < pkgplan.origin_fmri.version:
                # Installed, preserved file is for a package
                # newer than what will be installed. So check if
                # the version on disk is different than what
                # was originally delivered, and if so, preserve
                # it.
                if not is_file:
                    return False

                preserve_version = self.__check_preserve_version(orig)
                if not preserve_version:
                    return False

                ihash, cdata = misc.get_data_digest(final_path,
                                                    hash_func=orig_hash_func)
                if ihash != orig_hash_val:
                    return preserve_version

                return True

        if (orig and orig_path):
            # Comparison will be based on a file being moved.
            is_file = os.path.isfile(orig_path)

        # If the action has been marked with a preserve attribute, and
        # the file exists and has a content hash different from what the
        # system expected it to be, then we preserve the original file
        # in some way, depending on the value of preserve.
        if is_file:
            # if we had an action installed, then we know what hash
            # function was used to compute it's hash attribute.
            if orig:
                if not orig_path:
                    orig_path = final_path
                chash, cdata = misc.get_data_digest(orig_path,
                                                    hash_func=orig_hash_func)
            if not orig or chash != orig_hash_val:
                if pres_type in ("renameold", "renamenew"):
                    return pres_type
                return True
            elif not changed_hash and chash == orig_hash_val:
                # If packaged content has not changed since last
                # version and on-disk content matches the last
                # version, preserve on-disk file.
                return True

        return False
Esempio n. 3
0
        def needsdata(self, orig, pkgplan):
                if self.replace_required:
                        return True

                # import goes here to prevent circular import
                from pkg.client.imageconfig import CONTENT_UPDATE_POLICY

                use_content_hash = pkgplan.image.cfg.get_policy_str(
                    CONTENT_UPDATE_POLICY) == "when-required"

                # If content update policy allows it, check for the presence of
                # a simple elfhash attribute, and if that's present, look for
                # the common preferred elfhash.  For now, this is sufficient,
                # but when additional content types are supported (and we stop
                # publishing SHA-1 hashes) more work will be needed to compute
                # 'bothelf'.
                bothelf = use_content_hash and orig and \
                    "elfhash" in orig.attrs and "elfhash" in self.attrs
                if bothelf:
                        common_elf_attr, common_elfhash, common_orig_elfhash, \
                            common_elf_func = \
                            digest.get_common_preferred_hash(self, orig,
                            hash_type=digest.CONTENT_HASH)

                common_hash_attr, common_hash_val, \
                    common_orig_hash_val, common_hash_func = \
                    digest.get_common_preferred_hash(self, orig)

                if not orig:
                        changed_hash = True
                elif orig and (common_orig_hash_val is None or
                    common_hash_val is None):
                        # we have no common hash so we have to treat this as a
                        # changed action
                        changed_hash = True
                else:
                        changed_hash = common_hash_val != common_orig_hash_val

                if (changed_hash and (not bothelf or
                    common_orig_elfhash != common_elfhash)):
                        if ("preserve" not in self.attrs or
                            not pkgplan.origin_fmri or
                            (pkgplan.destination_fmri.version <
                            pkgplan.origin_fmri.version)):
                                return True
                elif orig:
                        # It's possible that the file content hasn't changed
                        # for an upgrade case, but the file is missing.  This
                        # ensures that for cases where the mode or some other
                        # attribute of the file has changed that the file will
                        # be installed.
                        path = self.get_installed_path(pkgplan.image.get_root())
                        if not os.path.isfile(path):
                                return True

                pres_type = self._check_preserve(orig, pkgplan)
                if pres_type not in (None, True, "abandon"):
                        # Preserved files only need data if they're being
                        # changed (e.g. "renameold", etc.).
                        return True

                return False