示例#1
0
    def verify(self, img, pfmri, **args):
        """Returns a tuple of lists of the form (errors, warnings,
                info).  The error list will be empty if the action has been
                correctly installed in the given image."""

        errors = []
        warnings = []
        info = []

        path = os.path.join(img.get_license_dir(pfmri),
                            "license." + quote(self.attrs["license"], ""))

        hash_attr, hash_val, hash_func = \
            digest.get_preferred_hash(self)
        if args["forever"] == True:
            try:
                chash, cdata = misc.get_data_digest(path, hash_func=hash_func)
            except EnvironmentError as e:
                if e.errno == errno.ENOENT:
                    errors.append(
                        _("License file {0} does "
                          "not exist.").format(path))
                    return errors, warnings, info
                raise

            if chash != hash_val:
                errors.append(
                    _("Hash: '{found}' should be "
                      "'{expected}'").format(found=chash, expected=hash_val))
        return errors, warnings, info
示例#2
0
    def install(self, pkgplan, orig):
        """Client-side method that installs the license."""
        owner = 0
        group = 0

        # ensure "path" is initialized.  it may not be if we've loaded
        # a plan that was previously prepared.
        self.preinstall(pkgplan, orig)

        stream = self.data()

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

        # make sure the directory exists and the file is writable
        if not os.path.exists(os.path.dirname(path)):
            self.makedirs(os.path.dirname(path),
                          mode=misc.PKG_DIR_MODE,
                          fmri=pkgplan.destination_fmri)
        elif os.path.exists(path):
            os.chmod(path, misc.PKG_FILE_MODE)

        lfile = open(path, "wb")
        try:
            hash_attr, hash_val, hash_func = \
                digest.get_preferred_hash(self)
            shasum = misc.gunzip_from_stream(stream,
                                             lfile,
                                             hash_func=hash_func)
        except zlib.error as e:
            raise ActionExecutionError(
                self,
                details=_("Error "
                          "decompressing payload: {0}").format(" ".join(
                              [str(a) for a in e.args])),
                error=e)
        finally:
            lfile.close()
            stream.close()

        if shasum != hash_val:
            raise ActionExecutionError(
                self,
                details=_("Action "
                          "data hash verification failure: expected: "
                          "{expected} computed: {actual} action: "
                          "{action}").format(expected=hash_val,
                                             actual=shasum,
                                             action=self))

        os.chmod(path, misc.PKG_RO_FILE_MODE)

        try:
            portable.chown(path, owner, group)
        except OSError as e:
            if e.errno != errno.EPERM:
                raise
示例#3
0
    def remove(self, pkgplan):
        path = self.get_installed_path(pkgplan.image.get_root())

        # Are we supposed to save this file to restore it elsewhere
        # or in another pkg? 'save_file' is set by the imageplan.
        save_file = self.attrs.get("save_file")
        if save_file:
            # 'save_file' contains a tuple of (orig_name,
            # remove_file).
            remove = save_file[1]
            self.save_file(pkgplan.image, path)
            if remove != "true":
                # File must be left in place (this file is
                # likely overlaid and is moving).
                return

        if self.attrs.get("preserve") == "abandon":
            return

        try:
            # Make file writable so it can be deleted.
            os.chmod(path, stat.S_IWRITE | stat.S_IREAD)
        except OSError as e:
            if e.errno == errno.ENOENT:
                # Already gone; don't care.
                return
            raise

        if not pkgplan.destination_fmri and \
            self.attrs.get("preserve", "false").lower() != "false":
            # Preserved files are salvaged if they have been
            # modified since they were installed and this is
            # not an upgrade.
            try:
                hash_attr, hash_val, hash_func  = \
                    digest.get_preferred_hash(self)
                ihash, cdata = misc.get_data_digest(path, hash_func=hash_func)
                if ihash != hash_val:
                    pkgplan.salvage(path)
                    # Nothing more to do.
                    return
            except EnvironmentError as e:
                if e.errno == errno.ENOENT:
                    # Already gone; don't care.
                    return
                raise

        # Attempt to remove the file.
        self.remove_fsobj(pkgplan, path)
示例#4
0
    def get_chain_certs_chashes(self, least_preferred=False):
        """Return a list of the chain certificates needed to validate
                this signature."""

        if least_preferred:
            chain_chash_attr, chain_chash_val, hash_func = \
                digest.get_least_preferred_hash(self,
                hash_type=digest.CHAIN_CHASH)
        else:
            chain_chash_attr, chain_chash_val, hash_func = \
                digest.get_preferred_hash(self,
                hash_type=digest.CHAIN_CHASH)
        if not chain_chash_val:
            return []
        return chain_chash_val.split()
示例#5
0
    def get_chain_certs(self, least_preferred=False):
        """Return a list of the chain certificates needed to validate
                this signature. When retrieving the content from the
                repository, we use the "least preferred" hash for backwards
                compatibility, but when verifying the content, we use the
                "most preferred" hash."""

        if least_preferred:
            chain_attr, chain_val, hash_func = \
                digest.get_least_preferred_hash(self,
                hash_type=digest.CHAIN)
        else:
            chain_attr, chain_val, hash_func = \
                digest.get_preferred_hash(self,
                hash_type=digest.CHAIN)
        if not chain_val:
            return []
        return chain_val.split()
示例#6
0
    def remove(self, pkgplan):
        path = self.get_installed_path(pkgplan.image.get_root())

        # Are we supposed to save this file to restore it elsewhere
        # or in another pkg? 'save_file' is set by the imageplan.
        save_file = self.attrs.get("save_file")
        if save_file:
            # 'save_file' contains a tuple of (orig_name,
            # remove_file).
            remove = save_file[1]
            self.save_file(pkgplan.image, path)
            if remove != "true":
                # File must be left in place (this file is
                # likely overlaid and is moving).
                return

        if self.attrs.get("preserve") in ("abandon", "install-only"):
            return

        if not pkgplan.destination_fmri and \
            self.attrs.get("preserve", "false").lower() != "false":
            # Preserved files are salvaged if they have been
            # modified since they were installed and this is
            # not an upgrade.
            try:
                hash_attr, hash_val, hash_func  = \
                    digest.get_preferred_hash(self)
                ihash, cdata = misc.get_data_digest(path, hash_func=hash_func)
                if ihash != hash_val:
                    pkgplan.salvage(path)
                    # Nothing more to do.
                    return
            except EnvironmentError as e:
                if e.errno == errno.ENOENT:
                    # Already gone; don't care.
                    return
                raise

        # Attempt to remove the file.
        rm_exc = None
        try:
            self.remove_fsobj(pkgplan, path)
            return
        except Exception as e:
            if e.errno != errno.EACCES:
                raise
            rm_exc = e

        # There are only two likely reasons we couldn't remove the file;
        # either because the parent directory isn't writable, or
        # because the file is read-only and the OS isn't allowing its
        # removal.  Assume both and try making both the parent directory
        # and the file writable, removing the file, and finally
        # resetting the directory to its original mode.
        pdir = os.path.dirname(path)
        pmode = None
        try:
            if pdir != pkgplan.image.get_root():
                # Parent directory is not image root (e.g. '/').
                ps = os.lstat(pdir)
                pmode = ps.st_mode
                os.chmod(pdir, misc.PKG_DIR_MODE)

            # Make file writable and try removing it again; required
            # on some operating systems or potentially for some
            # filesystems?
            os.chmod(path, stat.S_IWRITE | stat.S_IREAD)
            self.remove_fsobj(pkgplan, path)
        except Exception as e:
            # Raise new exception chained to old.
            six.raise_from(e, rm_exc)
        finally:
            # If parent directory wasn't image root, then assume
            # mode needs reset.
            if pmode is not None:
                try:
                    os.chmod(pdir, pmode)
                except Exception as e:
                    # Ignore failure to reset parent mode.
                    pass
示例#7
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
示例#8
0
    def verify(self, img, **args):
        """Returns a tuple of lists of the form (errors, warnings,
                info).  The error list will be empty if the action has been
                correctly installed in the given image.

                In detail, this verifies that the file is present, and if
                the preserve attribute is not present, that the hashes
                and other attributes of the file match."""

        if self.attrs.get("preserve") == "abandon":
            return [], [], []

        path = self.get_installed_path(img.get_root())

        lstat, errors, warnings, info, abort = \
            self.verify_fsobj_common(img, stat.S_IFREG)
        if lstat:
            if not stat.S_ISREG(lstat.st_mode):
                self.replace_required = True

        if abort:
            assert errors
            self.replace_required = True
            return errors, warnings, info

        if path.lower().endswith("/bobcat") and args["verbose"] == True:
            # Returned as a purely informational (untranslated)
            # message so that no client should interpret it as a
            # reason to fail verification.
            info.append("Warning: package may contain bobcat!  "
                        "(http://xkcd.com/325/)")

        preserve = self.attrs.get("preserve")

        if (preserve is None and "timestamp" in self.attrs and lstat.st_mtime
                != misc.timestamp_to_time(self.attrs["timestamp"])):
            errors.append(
                _("Timestamp: {found} should be "
                  "{expected}").format(found=misc.time_to_timestamp(
                      lstat.st_mtime),
                                       expected=self.attrs["timestamp"]))

        # avoid checking pkg.size if we have any content-hashes present;
        # different size files may have the same content-hash
        pkg_size = int(self.attrs.get("pkg.size", 0))
        if preserve is None and pkg_size > 0 and \
            not set(digest.DEFAULT_GELF_HASH_ATTRS).intersection(
            set(self.attrs.keys())) and \
            lstat.st_size != pkg_size:
            errors.append(
                _("Size: {found:d} bytes should be "
                  "{expected:d}").format(found=lstat.st_size,
                                         expected=pkg_size))

        if (preserve is not None and args["verbose"] == False
                or lstat is None):
            return errors, warnings, info

        if args["forever"] != True:
            return errors, warnings, info

        #
        # Check file contents.
        #
        try:
            # This is a generic mechanism, but only used for libc on
            # x86, where the "best" version of libc is lofs-mounted
            # on the canonical path, foiling the standard verify
            # checks.
            is_mtpt = self.attrs.get("mountpoint", "").lower() == "true"
            elfhash = None
            elferror = None
            elf_hash_attr, elf_hash_val, \
                elf_hash_func = \
                digest.get_preferred_hash(self,
                    hash_type=pkg.digest.HASH_GELF)
            if elf_hash_attr and haveelf and not is_mtpt:
                #
                # It's possible for the elf module to
                # throw while computing the hash,
                # especially if the file is badly
                # corrupted or truncated.
                #
                try:
                    # On path, only calculate the
                    # content hash that matches
                    # the preferred one on the
                    # action
                    get_elfhash = \
                        elf_hash_attr == "elfhash"
                    get_sha256 = (not get_elfhash and elf_hash_func
                                  == digest.GELF_HASH_ALGS["gelf:sha256"])
                    get_sha512t_256 = (
                        not get_elfhash and elf_hash_func
                        == digest.GELF_HASH_ALGS["gelf:sha512t_256"])
                    elfhash = elf.get_hashes(
                        path,
                        elfhash=get_elfhash,
                        sha256=get_sha256,
                        sha512t_256=get_sha512t_256)[elf_hash_attr]

                    if get_elfhash:
                        elfhash = [elfhash]
                    else:
                        elfhash = list(digest.ContentHash(elfhash).values())
                except elf.ElfError as e:
                    # Any ELF error means there is something bad
                    # with the file, mark as needing to be replaced.
                    elferror = _("ELF failure: {0}").format(e)

                if (elfhash is not None and elf_hash_val != elfhash[0]):
                    elferror = _("ELF content hash: "
                                 "{found} "
                                 "should be {expected}").format(
                                     found=elfhash[0], expected=elf_hash_val)

            # Always check on the file hash because the ELF hash
            # check only checks on the ELF parts and does not
            # check for some other file integrity issues.
            if not is_mtpt:
                hash_attr, hash_val, hash_func = \
                    digest.get_preferred_hash(self)
                sha_hash, data = misc.get_data_digest(path,
                                                      hash_func=hash_func)
                if sha_hash != hash_val:
                    # Prefer the ELF content hash error message.
                    if preserve is not None:
                        info.append(_("editable file has " "been changed"))
                    elif elferror:
                        errors.append(elferror)
                        self.replace_required = True
                    else:
                        errors.append(
                            _("Hash: "
                              "{found} should be "
                              "{expected}").format(found=sha_hash,
                                                   expected=hash_val))
                        self.replace_required = True

            # Check system attributes.
            # Since some attributes like 'archive' or 'av_modified'
            # are set automatically by the FS, it makes no sense to
            # check for 1:1 matches. So we only check that the
            # system attributes specified in the action are still
            # set on the file.
            sattr = self.attrs.get("sysattr", None)
            if sattr:
                if isinstance(sattr, list):
                    sattr = ",".join(sattr)
                sattrs = sattr.split(",")
                if len(sattrs) == 1 and \
                    sattrs[0] not in portable.get_sysattr_dict():
                    # not a verbose attr, try as a compact
                    set_attrs = portable.fgetattr(path, compact=True)
                    sattrs = sattrs[0]
                else:
                    set_attrs = portable.fgetattr(path)

                for a in sattrs:
                    if a not in set_attrs:
                        errors.append(
                            _("System attribute '{0}' "
                              "not set").format(a))

        except EnvironmentError as e:
            if e.errno == errno.EACCES:
                errors.append(_("Skipping: Permission Denied"))
            else:
                errors.append(_("Unexpected Error: {0}").format(e))
        except Exception as e:
            errors.append(_("Unexpected Exception: {0}").format(e))

        return errors, warnings, info
示例#9
0
    def install(self, pkgplan, orig):
        """Client-side method that installs a file."""

        mode = None
        try:
            mode = int(self.attrs.get("mode", None), 8)
        except (TypeError, ValueError):
            # Mode isn't valid, so let validate raise a more
            # informative error.
            self.validate(fmri=pkgplan.destination_fmri)

        owner, group = self.get_fsobj_uid_gid(pkgplan,
                                              pkgplan.destination_fmri)

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

        # Don't allow installation through symlinks.
        self.fsobj_checkpath(pkgplan, final_path)

        if not os.path.exists(os.path.dirname(final_path)):
            self.makedirs(os.path.dirname(final_path),
                          mode=misc.PKG_DIR_MODE,
                          fmri=pkgplan.destination_fmri)
        elif (not orig and not pkgplan.origin_fmri and "preserve" in self.attrs
              and self.attrs["preserve"] not in ("abandon", "install-only")
              and os.path.isfile(final_path)):
            # Unpackaged editable file is already present during
            # initial install; salvage it before continuing.
            pkgplan.salvage(final_path)

        # XXX If we're upgrading, do we need to preserve file perms from
        # existing file?

        # check if we have a save_file active; if so, simulate file
        # being already present rather than installed from scratch

        if "save_file" in self.attrs:
            orig = self.restore_file(pkgplan.image)

        # See if we need to preserve the file, and if so, set that up.
        #
        # XXX What happens when we transition from preserve to
        # non-preserve or vice versa? Do we want to treat a preserve
        # attribute as turning the action into a critical action?
        #
        # XXX We should save the originally installed file.  It can be
        # used as an ancestor for a three-way merge, for example.  Where
        # should it be stored?
        pres_type = self._check_preserve(orig, pkgplan)
        do_content = True
        old_path = None
        if pres_type == True or (pres_type and pkgplan.origin_fmri
                                 == pkgplan.destination_fmri):
            # File is marked to be preserved and exists so don't
            # reinstall content.
            do_content = False
        elif pres_type == "legacy":
            # Only rename old file if this is a transition to
            # preserve=legacy from something else.
            if orig.attrs.get("preserve", None) != "legacy":
                old_path = final_path + ".legacy"
        elif pres_type == "renameold.update":
            old_path = final_path + ".update"
        elif pres_type == "renameold":
            old_path = final_path + ".old"
        elif pres_type == "renamenew":
            final_path = final_path + ".new"
        elif pres_type == "abandon":
            return

        # If it is a directory (and not empty) then we should
        # salvage the contents.
        if os.path.exists(final_path) and \
            not os.path.islink(final_path) and \
            os.path.isdir(final_path):
            try:
                os.rmdir(final_path)
            except OSError as e:
                if e.errno == errno.ENOENT:
                    pass
                elif e.errno in (errno.EEXIST, errno.ENOTEMPTY):
                    pkgplan.salvage(final_path)
                elif e.errno != errno.EACCES:
                    # this happens on Windows
                    raise

        # XXX This needs to be modularized.
        if do_content and self.needsdata(orig, pkgplan):
            tfilefd, temp = tempfile.mkstemp(dir=os.path.dirname(final_path))
            if not self.data:
                # The state of the filesystem changed after the
                # plan was prepared; attempt a one-off
                # retrieval of the data.
                self.data = self.__set_data(pkgplan)
            stream = self.data()
            tfile = os.fdopen(tfilefd, "wb")
            try:
                # Always verify using the most preferred hash
                hash_attr, hash_val, hash_func  = \
                    digest.get_preferred_hash(self)
                shasum = misc.gunzip_from_stream(stream, tfile, hash_func)
            except zlib.error as e:
                raise ActionExecutionError(
                    self,
                    details=_("Error decompressing payload: "
                              "{0}").format(" ".join([str(a)
                                                      for a in e.args])),
                    error=e)
            finally:
                tfile.close()
                stream.close()

            if shasum != hash_val:
                raise ActionExecutionError(
                    self,
                    details=_("Action data hash verification "
                              "failure: expected: {expected} computed: "
                              "{actual} action: {action}").format(
                                  expected=hash_val,
                                  actual=shasum,
                                  action=self))

        else:
            temp = final_path

        try:
            os.chmod(temp, mode)
        except OSError as e:
            # If the file didn't exist, assume that's intentional,
            # and drive on.
            if e.errno != errno.ENOENT:
                raise
            else:
                return

        try:
            portable.chown(temp, owner, group)
        except OSError as e:
            if e.errno != errno.EPERM:
                raise

        # XXX There's a window where final_path doesn't exist, but we
        # probably don't care.
        if do_content and old_path:
            try:
                portable.rename(final_path, old_path)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    # Only care if file isn't gone already.
                    raise

        # This is safe even if temp == final_path.
        try:
            portable.rename(temp, final_path)
        except OSError as e:
            raise api_errors.FileInUseException(final_path)

        # Handle timestamp if specified (and content was installed).
        if do_content and "timestamp" in self.attrs:
            t = misc.timestamp_to_time(self.attrs["timestamp"])
            try:
                os.utime(final_path, (t, t))
            except OSError as e:
                if e.errno != errno.EACCES:
                    raise

                # On Windows, the time cannot be changed on a
                # read-only file
                os.chmod(final_path, stat.S_IRUSR | stat.S_IWUSR)
                os.utime(final_path, (t, t))
                os.chmod(final_path, mode)

        # Handle system attributes.
        sattr = self.attrs.get("sysattr")
        if sattr:
            if isinstance(sattr, list):
                sattr = ",".join(sattr)
            sattrs = sattr.split(",")
            if len(sattrs) == 1 and \
                sattrs[0] not in portable.get_sysattr_dict():
                # not a verbose attr, try as a compact attr seq
                arg = sattrs[0]
            else:
                arg = sattrs

            try:
                portable.fsetattr(final_path, arg)
            except OSError as e:
                if e.errno != errno.EINVAL:
                    raise
                warn = _("System attributes are not supported "
                         "on the target image filesystem; 'sysattr'"
                         " ignored for {0}").format(self.attrs["path"])
                pkgplan.image.imageplan.pd.add_item_message(
                    pkgplan.destination_fmri,
                    misc.time_to_timestamp(time.time()), MSG_WARNING, warn)
            except ValueError as e:
                warn = _("Could not set system attributes for {path}"
                         "'{attrlist}': {err}").format(attrlist=sattr,
                                                       err=e,
                                                       path=self.attrs["path"])
                pkgplan.image.imageplan.pd.add_item_message(
                    pkgplan.destination_fmri,
                    misc.time_to_timestamp(time.time()), MSG_WARNING, warn)
示例#10
0
文件: file.py 项目: ripudamank2/pkg5
        def verify(self, img, **args):
                """Returns a tuple of lists of the form (errors, warnings,
                info).  The error list will be empty if the action has been
                correctly installed in the given image.

                In detail, this verifies that the file is present, and if
                the preserve attribute is not present, that the hashes
                and other attributes of the file match."""

                if self.attrs.get("preserve") == "abandon":
                        return [], [], []

                path = self.get_installed_path(img.get_root())

                lstat, errors, warnings, info, abort = \
                    self.verify_fsobj_common(img, stat.S_IFREG)
                if lstat:
                        if not stat.S_ISREG(lstat.st_mode):
                                self.replace_required = True

                if abort:
                        assert errors
                        self.replace_required = True
                        return errors, warnings, info

                if path.lower().endswith("/bobcat") and args["verbose"] == True:
                        # Returned as a purely informational (untranslated)
                        # message so that no client should interpret it as a
                        # reason to fail verification.
                        info.append("Warning: package may contain bobcat!  "
                            "(http://xkcd.com/325/)")

                if "preserve" not in self.attrs and \
                    "timestamp" in self.attrs and lstat.st_mtime != \
                    misc.timestamp_to_time(self.attrs["timestamp"]):
                        errors.append(_("Timestamp: {found} should be "
                            "{expected}").format(
                            found=misc.time_to_timestamp(lstat.st_mtime),
                            expected=self.attrs["timestamp"]))

                # avoid checking pkg.size if we have any content-hashes present;
                # different size files may have the same content-hash
                if "preserve" not in self.attrs and \
                    "pkg.size" in self.attrs and    \
                    not set(digest.RANKED_CONTENT_HASH_ATTRS).intersection(
                    set(self.attrs.keys())) and \
                    lstat.st_size != int(self.attrs["pkg.size"]):
                        errors.append(_("Size: {found:d} bytes should be "
                            "{expected:d}").format(found=lstat.st_size,
                            expected=int(self.attrs["pkg.size"])))

                if "preserve" in self.attrs:
                        if args["verbose"] == False or lstat is None:
                                return errors, warnings, info

                if args["forever"] != True:
                        return errors, warnings, info

                #
                # Check file contents. At the moment, the only content-hash
                # supported in pkg(5) is for ELF files, so this will need work
                # when additional content-hashes are added.
                #
                try:
                        # This is a generic mechanism, but only used for libc on
                        # x86, where the "best" version of libc is lofs-mounted
                        # on the canonical path, foiling the standard verify
                        # checks.
                        is_mtpt = self.attrs.get("mountpoint", "").lower() == "true"
                        elfhash = None
                        elferror = None
                        ehash_attr, elfhash_val, hash_func = \
                            digest.get_preferred_hash(self,
                                hash_type=pkg.digest.CONTENT_HASH)
                        if ehash_attr and haveelf and not is_mtpt:
                                #
                                # It's possible for the elf module to
                                # throw while computing the hash,
                                # especially if the file is badly
                                # corrupted or truncated.
                                #
                                try:
                                        # Annoying that we have to hardcode this
                                        if ehash_attr == \
                                            "pkg.content-hash.sha256":
                                                get_sha256 = True
                                                get_sha1 = False
                                        else:
                                                get_sha256 = False
                                                get_sha1 = True
                                        elfhash = elf.get_dynamic(path,
                                            sha1=get_sha1,
                                            sha256=get_sha256)[ehash_attr]
                                except RuntimeError as e:
                                        errors.append(
                                            "ELF content hash: {0}".format(e))

                                if elfhash is not None and \
                                    elfhash != elfhash_val:
                                        elferror = _("ELF content hash: "
                                            "{found} "
                                            "should be {expected}").format(
                                            found=elfhash,
                                            expected=elfhash_val)

                        # If we failed to compute the content hash, or the
                        # content hash failed to verify, try the file hash.
                        # If the content hash fails to match but the file hash
                        # matches, it indicates that the content hash algorithm
                        # changed, since obviously the file hash is a superset
                        # of the content hash.
                        if (elfhash is None or elferror) and not is_mtpt:
                                hash_attr, hash_val, hash_func = \
                                    digest.get_preferred_hash(self)
                                sha_hash, data = misc.get_data_digest(path,
                                    hash_func=hash_func)
                                if sha_hash != hash_val:
                                        # Prefer the content hash error message.
                                        if "preserve" in self.attrs:
                                                info.append(_(
                                                    "editable file has "
                                                    "been changed"))
                                        elif elferror:
                                                errors.append(elferror)
                                                self.replace_required = True
                                        else:
                                                errors.append(_("Hash: "
                                                    "{found} should be "
                                                    "{expected}").format(
                                                    found=sha_hash,
                                                    expected=hash_val))
                                                self.replace_required = True

                        # Check system attributes.
                        # Since some attributes like 'archive' or 'av_modified'
                        # are set automatically by the FS, it makes no sense to
                        # check for 1:1 matches. So we only check that the
                        # system attributes specified in the action are still
                        # set on the file.
                        sattr = self.attrs.get("sysattr", None)
                        if sattr:
                                sattrs = sattr.split(",")
                                if len(sattrs) == 1 and \
                                    sattrs[0] not in portable.get_sysattr_dict():
                                        # not a verbose attr, try as a compact
                                        set_attrs = portable.fgetattr(path,
                                            compact=True)
                                        sattrs = sattrs[0]
                                else:
                                        set_attrs = portable.fgetattr(path)

                                for a in sattrs:
                                        if a not in set_attrs:
                                                errors.append(
                                                    _("System attribute '{0}' "
                                                    "not set").format(a))

                except EnvironmentError as e:
                        if e.errno == errno.EACCES:
                                errors.append(_("Skipping: Permission Denied"))
                        else:
                                errors.append(_("Unexpected Error: {0}").format(
                                    e))
                except Exception as e:
                        errors.append(_("Unexpected Exception: {0}").format(e))

                return errors, warnings, info