9f9230833b50b8271840dc2c12bd1e94d9df7d12Alexander Pyhalov#!@PYTHON@
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# CDDL HEADER START
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# The contents of this file are subject to the terms of the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Common Development and Distribution License (the "License").
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# You may not use this file except in compliance with the License.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# or http://www.opensolaris.org/os/licensing.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# See the License for the specific language governing permissions
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# and limitations under the License.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# When distributing Covered Code, include this CDDL HEADER in each
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# If applicable, add the following below this CDDL HEADER, with the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# fields enclosed by brackets "[]" replaced with your own identifying
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# information: Portions Copyright [yyyy] [name of copyright owner]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# CDDL HEADER END
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Use is subject to license terms.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Compare the content generated by a build to a set of manifests
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# describing how that content is to be delivered.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaimport getopt
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaimport os
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaimport stat
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaimport sys
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazafrom pkg import actions
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazafrom pkg import manifest
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Dictionary used to map action names to output format. Each entry is
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# indexed by action name, and consists of a list of tuples that map
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# FileInfo class members to output labels.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza#
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaOUTPUTMAP = {
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "dir": [
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("group", "group="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("mode", "mode="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("owner", "owner="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("path", "path=")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ],
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "file": [
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("hash", ""),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("group", "group="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("mode", "mode="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("owner", "owner="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("path", "path=")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ],
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "link": [
3be32116258babbbdd231a5bba8e6e58e87daa99Alexander Pyhalov ("mediator", "mediator="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("path", "path="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("target", "target=")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ],
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "hardlink": [
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("path", "path="),
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ("hardkey", "target=")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ],
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza}
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza# Mode checks used to validate safe file and directory permissions
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaALLMODECHECKS = frozenset(("m", "w", "s", "o"))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaDEFAULTMODECHECKS = frozenset(("m", "w", "o"))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass FileInfo(object):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Base class to represent a file.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza Subclassed according to whether the file represents an actual filesystem
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza object (RealFileInfo) or an IPS manifest action (ActionInfo).
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.path = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.isdir = False
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.target = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.owner = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.group = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mode = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hardkey = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hardpaths = set()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.editable = False
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def name(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Return the IPS action name of a FileInfo object.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if self.isdir:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return "dir"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if self.target:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return "link"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if self.hardkey:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return "hardlink"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return "file"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def checkmodes(self, modechecks):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Check for and report on unsafe permissions.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza Returns a potentially empty list of warning strings.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza t = self.name()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if t in ("link", "hardlink"):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return w
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza m = int(self.mode, 8)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza o = self.owner
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza p = self.path
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "s" in modechecks and t == "file":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if m & (stat.S_ISUID | stat.S_ISGID):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if m & (stat.S_IRGRP | stat.S_IROTH):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w.extend(["%s: 0%o: setuid/setgid file should not be " \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "readable by group or other" % (p, m)])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "o" in modechecks and o != "root" and ((m & stat.S_ISUID) == 0):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mu = (m & stat.S_IRWXU) >> 6
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mg = (m & stat.S_IRWXG) >> 3
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mo = m & stat.S_IRWXO
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza e = self.editable
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if (((mu & 02) == 0 and (mo & mg & 04) == 04) or
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (t == "file" and mo & 01 == 1) or
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (mg, mo) == (mu, mu) or
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ((t == "file" and not e or t == "dir" and o == "bin") and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (mg & 05 == mo & 05)) or
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (t == "file" and o == "bin" and mu & 01 == 01) or
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (m & 0105 != 0 and p.startswith("etc/security/dev/"))):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w.extend(["%s: owner \"%s\" may be safely " \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "changed to \"root\"" % (p, o)])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "w" in modechecks and t == "file" and o != "root":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza uwx = stat.S_IWUSR | stat.S_IXUSR
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if m & uwx == uwx:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w.extend(["%s: non-root-owned executable should not " \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza "also be writable by owner." % p])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if ("m" in modechecks and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza m & (stat.S_IWGRP | stat.S_IWOTH) != 0 and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza m & stat.S_ISVTX == 0):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w.extend(["%s: 0%o: should not be writable by group or other" %
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (p, m)])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return w
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __ne__(self, other):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Compare two FileInfo objects.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza Note this is the "not equal" comparison, so a return value of False
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza indicates that the objects are functionally equivalent.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Map the objects such that the lhs is always the ActionInfo,
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # and the rhs is always the RealFileInfo.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # It's only really important that the rhs not be an
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # ActionInfo; if we're comparing FileInfo the RealFileInfo, it
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # won't actually matter what we choose.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if isinstance(self, ActionInfo):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza lhs = self
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza rhs = other
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza else:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza lhs = other
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza rhs = self
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Because the manifest may legitimately translate a relative
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # path from the proto area into a different path on the installed
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # system, we don't compare paths here. We only expect this comparison
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # to be invoked on items with identical relative paths in
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # first place.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # All comparisons depend on type. For symlink and directory, they
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # must be the same. For file and hardlink, see below.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza typelhs = lhs.name()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza typerhs = rhs.name()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if typelhs in ("link", "dir"):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if typelhs != typerhs:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # For symlinks, all that's left is the link target.
3be32116258babbbdd231a5bba8e6e58e87daa99Alexander Pyhalov # For mediated symlinks targets can differ.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if typelhs == "link":
3be32116258babbbdd231a5bba8e6e58e87daa99Alexander Pyhalov return (lhs.mediator is None) and (lhs.target != rhs.target)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # For a directory, it's important that both be directories,
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # the modes be identical, and the paths are identical. We already
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # checked all but the modes above.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # If both objects are files, then we're in the same boat.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if typelhs == "dir" or (typelhs == "file" and typerhs == "file"):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return lhs.mode != rhs.mode
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # For files or hardlinks:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Since the key space is different (inodes for real files and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # actual link targets for hard links), and since the proto area will
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # identify all N occurrences as hardlinks, but the manifests as one
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # file and N-1 hardlinks, we have to compare files to hardlinks.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # If they're both hardlinks, we just make sure that
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # the same target path appears in both sets of
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # possible targets.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if typelhs == "hardlink" and typerhs == "hardlink":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return len(lhs.hardpaths.intersection(rhs.hardpaths)) == 0
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Otherwise, we have a mix of file and hardlink, so we
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # need to make sure that the file path appears in the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # set of possible target paths for the hardlink.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # We already know that the ActionInfo, if present, is the lhs
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # operator. So it's the rhs operator that's guaranteed to
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # have a set of hardpaths.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return lhs.path not in rhs.hardpaths
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __str__(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Return an action-style representation of a FileInfo object.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza We don't currently quote items with embedded spaces. If we
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ever decide to parse this output, we'll want to revisit that.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza name = self.name()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza out = name
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for member, label in OUTPUTMAP[name]:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza out += " " + label + str(getattr(self, member))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return out
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def protostr(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Return a protolist-style representation of a FileInfo object.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza target = "-"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza major = "-"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza minor = "-"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mode = self.mode
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza owner = self.owner
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza group = self.group
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza name = self.name()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if name == "dir":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ftype = "d"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif name in ("file", "hardlink"):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ftype = "f"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif name == "link":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ftype = "s"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza target = self.target
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mode = "777"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza owner = "root"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza group = "other"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza out = "%c %-30s %-20s %4s %-5s %-5s %6d %2ld - -" % \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (ftype, self.path, target, mode, owner, group, 0, 1)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return out
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass ActionInfo(FileInfo):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Object to track information about manifest actions.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza This currently understands file, link, dir, and hardlink actions.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, action):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza FileInfo.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Currently, all actions that we support have a "path"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # attribute. If that changes, then we'll need to
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # catch a KeyError from this assignment.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.path = action.attrs["path"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if action.name == "file":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.owner = action.attrs["owner"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.group = action.attrs["group"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mode = action.attrs["mode"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hash = action.hash
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "preserve" in action.attrs:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.editable = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif action.name == "link":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza target = action.attrs["target"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.target = os.path.normpath(target)
3be32116258babbbdd231a5bba8e6e58e87daa99Alexander Pyhalov self.mediator = action.attrs.get("mediator")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif action.name == "dir":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.owner = action.attrs["owner"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.group = action.attrs["group"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mode = action.attrs["mode"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.isdir = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif action.name == "hardlink":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza target = os.path.normpath(action.get_target_path())
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hardkey = target
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hardpaths.add(target)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza @staticmethod
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def supported(action):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Indicates whether the specified IPS action time is
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza correctly handled by the ActionInfo constructor.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return action in frozenset(("file", "dir", "link", "hardlink"))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass UnsupportedFileFormatError(Exception):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """This means that the stat.S_IFMT returned something we don't
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza support, ie a pipe or socket. If it's appropriate for such an
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza object to be in the proto area, then the RealFileInfo constructor
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza will need to evolve to support it, or it will need to be in the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exception list.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, path, mode):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza Exception.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.path = path
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mode = mode
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __str__(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return '%s: unsupported S_IFMT %07o' % (self.path, self.mode)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass RealFileInfo(FileInfo):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Object to track important-to-packaging file information.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza This currently handles regular files, directories, and symbolic links.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza For multiple RealFileInfo objects with identical hardkeys, there
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza is no way to determine which of the hard links should be
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza delivered as a file, and which as hardlinks.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, root=None, path=None):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza FileInfo.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.path = path
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza path = os.path.join(root, path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza lstat = os.lstat(path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mode = lstat.st_mode
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Per stat.py, these cases are mutually exclusive.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if stat.S_ISREG(mode):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hash = self.path
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif stat.S_ISDIR(mode):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.isdir = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif stat.S_ISLNK(mode):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.target = os.path.normpath(os.readlink(path))
3be32116258babbbdd231a5bba8e6e58e87daa99Alexander Pyhalov self.mediator = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza else:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza raise UnsupportedFileFormatError(path, mode)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if not stat.S_ISLNK(mode):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mode = "%04o" % stat.S_IMODE(mode)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Instead of reading the group and owner from the proto area after
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # a non-root build, just drop in dummy values. Since we don't
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # compare them anywhere, this should allow at least marginally
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # useful comparisons of protolist-style output.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.owner = "owner"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.group = "group"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # refcount > 1 indicates a hard link
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if lstat.st_nlink > 1:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # This could get ugly if multiple proto areas reside
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # on different filesystems.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.hardkey = lstat.st_ino
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass DirectoryTree(dict):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Meant to be subclassed according to population method.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, name):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza dict.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.name = name
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def compare(self, other):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Compare two different sets of FileInfo objects.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza keys1 = frozenset(self.keys())
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza keys2 = frozenset(other.keys())
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza common = keys1.intersection(keys2)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza onlykeys1 = keys1.difference(common)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza onlykeys2 = keys2.difference(common)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if onlykeys1:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "Entries present in %s but not %s:" % \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (self.name, other.name)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for path in sorted(onlykeys1):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print("\t%s" % str(self[path]))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if onlykeys2:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "Entries present in %s but not %s:" % \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (other.name, self.name)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for path in sorted(onlykeys2):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print("\t%s" % str(other[path]))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza nodifferences = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for path in sorted(common):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if self[path] != other[path]:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if nodifferences:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza nodifferences = False
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "Entries that differ between %s and %s:" \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza % (self.name, other.name)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print("%14s %s" % (self.name, self[path]))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print("%14s %s" % (other.name, other[path]))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if not nodifferences:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass BadProtolistFormat(Exception):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """This means that the user supplied a file via -l, but at least
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza one line from that file doesn't have the right number of fields to
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza parse as protolist output.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __str__(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return 'bad proto list entry: "%s"' % Exception.__str__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass ProtoTree(DirectoryTree):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Describes one or more proto directories as a dictionary of
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza RealFileInfo objects, indexed by relative path.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def adddir(self, proto, exceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Extends the ProtoTree dictionary with RealFileInfo
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza objects describing the proto dir, indexed by relative
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza path.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza newentries = {}
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza pdir = os.path.normpath(proto)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza strippdir = lambda r, n: os.path.join(r, n)[len(pdir)+1:]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for root, dirs, files in os.walk(pdir):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for name in dirs + files:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza path = strippdir(root, name)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if path not in exceptions:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza newentries[path] = RealFileInfo(pdir, path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except OSError, e:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write("Warning: unable to stat %s: %s\n" %
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza (path, e))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza continue
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza else:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exceptions.remove(path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if name in dirs:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza dirs.remove(name)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Find the sets of paths in this proto dir that are hardlinks
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # to the same inode.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # It seems wasteful to store this in each FileInfo, but we
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # otherwise need a linking mechanism. With this information
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # here, FileInfo object comparison can be self contained.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # We limit this aggregation to a single proto dir, as
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # represented by newentries. That means we don't need to care
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # about proto dirs on separate filesystems, or about hardlinks
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # that cross proto dir boundaries.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza hk2path = {}
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for path, fileinfo in newentries.iteritems():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if fileinfo.hardkey:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza hk2path.setdefault(fileinfo.hardkey, set()).add(path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for fileinfo in newentries.itervalues():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if fileinfo.hardkey:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.hardpaths.update(hk2path[fileinfo.hardkey])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.update(newentries)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def addprotolist(self, protolist, exceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Read in the specified file, assumed to be the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza output of protolist.
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza This has been tested minimally, and is potentially useful for
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza comparing across the transition period, but should ultimately
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza go away.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza plist = open(protolist)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza raise IOError("cannot open proto list: %s" % str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza newentries = {}
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for pline in plist:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza pline = pline.split()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Use a FileInfo() object instead of a RealFileInfo()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # object because we want to avoid the RealFileInfo
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # constructor, because there's nothing to actually stat().
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo = FileInfo()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if pline[1] in exceptions:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exceptions.remove(pline[1])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza continue
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if pline[0] == "d":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.isdir = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.path = pline[1]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if pline[2] != "-":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.target = os.path.normpath(pline[2])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.mode = int("0%s" % pline[3])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.owner = pline[4]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.group = pline[5]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if pline[6] != "0":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.hardkey = pline[6]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza newentries[pline[1]] = fileinfo
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IndexError:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza raise BadProtolistFormat(pline)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza plist.close()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza hk2path = {}
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for path, fileinfo in newentries.iteritems():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if fileinfo.hardkey:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza hk2path.setdefault(fileinfo.hardkey, set()).add(path)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for fileinfo in newentries.itervalues():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if fileinfo.hardkey:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza fileinfo.hardpaths.update(hk2path[fileinfo.hardkey])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.update(newentries)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass ManifestParsingError(Exception):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """This means that the Manifest.set_content() raised an
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza ActionError. We raise this, instead, to tell us which manifest
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza could not be parsed, rather than what action error we hit.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, mfile, error):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza Exception.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.mfile = mfile
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.error = error
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __str__(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return "unable to parse manifest %s: %s" % (self.mfile, self.error)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass ManifestTree(DirectoryTree):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Describes one or more directories containing arbitrarily
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza many manifests as a dictionary of ActionInfo objects, indexed
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza by the relative path of the data source within the proto area.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza That path may or may not be the same as the path attribute of the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza given action.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def addmanifest(self, root, mfile, arch, modechecks, exceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Treats the specified input file as a pkg(5) package
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifest, and extends the ManifestTree dictionary with entries
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for the actions therein.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mfest = manifest.Manifest()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza mfest.set_content(open(os.path.join(root, mfile)).read())
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza raise IOError("cannot read manifest: %s" % str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except actions.ActionError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza raise ManifestParsingError(mfile, str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # Make sure the manifest is applicable to the user-specified
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # architecture. Assumption: if variant.arch is not an
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # attribute of the manifest, then the package should be
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # installed on all architectures.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if arch not in mfest.attributes.get("variant.arch", (arch,)):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modewarnings = set()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for action in mfest.gen_actions():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "path" not in action.attrs or \
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza not ActionInfo.supported(action.name):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza continue
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # The dir action is currently fully specified, in that it
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # lists owner, group, and mode attributes. If that
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # changes in pkg(5) code, we'll need to revisit either this
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # code or the ActionInfo() constructor. It's possible
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # that the pkg(5) system could be extended to provide a
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # mechanism for specifying directory permissions outside
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # of the individual manifests that deliver files into
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # those directories. Doing so at time of manifest
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # processing would mean that validate_pkg continues to work,
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # but doing so at time of publication would require updates.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # See pkgsend(1) for the use of NOHASH for objects with
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # datastreams. Currently, that means "files," but this
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # should work for any other such actions.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if getattr(action, "hash", "NOHASH") != "NOHASH":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza path = action.hash
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza else:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza path = action.attrs["path"]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # This is the wrong tool in which to enforce consistency
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # on a set of manifests. So instead of comparing the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # different actions with the same "path" attribute, we
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # use the first one.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if path in self:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza continue
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe # As with the manifest itself, if an action has specified
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # variant.arch, we look for the target architecture
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza # therein.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza #
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe var = None
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe #
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe # The name of this method changed in pkg(5) build 150, we need to
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe # work with both sets.
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe #
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe if hasattr(action, 'get_variants'):
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe var = action.get_variants()
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe else:
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe var = action.get_variant_template()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if "variant.arch" in var and arch not in var["variant.arch"]:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza return
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self[path] = ActionInfo(action)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if modechecks is not None and path not in exceptions:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modewarnings.update(self[path].checkmodes(modechecks))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(modewarnings) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "warning: unsafe permissions in %s" % mfile
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for w in sorted(modewarnings):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print w
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def adddir(self, mdir, arch, modechecks, exceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Walks the specified directory looking for pkg(5) manifests.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for mfile in os.listdir(mdir):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if (mfile.endswith(".mog") and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza stat.S_ISREG(os.lstat(os.path.join(mdir, mfile)).st_mode)):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.addmanifest(mdir, mfile, arch, modechecks, exceptions)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write("warning: %s\n" % str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def resolvehardlinks(self):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Populates mode, group, and owner for resolved (ie link target
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza is present in the manifest tree) hard links.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for info in self.values():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if info.name() == "hardlink":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza tgt = info.hardkey
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if tgt in self:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza tgtinfo = self[tgt]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza info.owner = tgtinfo.owner
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza info.group = tgtinfo.group
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza info.mode = tgtinfo.mode
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaclass ExceptionList(set):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Keep track of an exception list as a set of paths to be excluded
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza from any other lists we build.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def __init__(self, files, arch):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza set.__init__(self)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for fname in files:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.readexceptionfile(fname, arch)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write("warning: cannot read exception file: %s\n" %
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza def readexceptionfile(self, efile, arch):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Build a list of all pathnames from the specified file that
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza either apply to all architectures (ie which have no trailing
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza architecture tokens), or to the specified architecture (ie
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza which have the value of the arch arg as a trailing
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza architecture token.)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza excfile = open(efile)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for exc in excfile:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exc = exc.split()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(exc) and exc[0][0] != "#":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if arch in (exc[1:] or arch):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza self.add(os.path.normpath(exc[0]))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza excfile.close()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaUSAGE = """%s [-v] -a arch [-e exceptionfile]... [-L|-M [-X check]...] input_1 [input_2]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazawhere input_1 and input_2 may specify proto lists, proto areas,
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaor manifest directories. For proto lists, use one or more
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza -l file
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaarguments. For proto areas, use one or more
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza -p dir
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaarguments. For manifest directories, use one or more
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza -m dir
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaarguments.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaIf -L or -M is specified, then only one input source is allowed, and
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazait should be one or more manifest directories. These two options are
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazamutually exclusive.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaThe -L option is used to generate a proto list to stdout.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaThe -M option is used to check for safe file and directory modes.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane PrazaBy default, this causes all mode checks to be performed. Individual
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazamode checks may be turned off using "-X check," where "check" comes
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazafrom the following set of checks:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza m check for group or other write permissions
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza w check for user write permissions on files and directories
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza not owned by root
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza s check for group/other read permission on executable files
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza that have setuid/setgid bit(s)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza o check for files that could be safely owned by root
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza""" % sys.argv[0]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazadef usage(msg=None):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Try to give the user useful information when they don't get the
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza command syntax right.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if msg:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write(USAGE)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.exit(2)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazadef main(argv):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """Compares two out of three possible data sources: a proto list, a
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza set of proto areas, and a set of manifests.
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza """
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza opts, args = getopt.getopt(argv, 'a:e:Ll:Mm:p:vX:')
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except getopt.GetoptError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage(str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if args:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza arch = None
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exceptionlists = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza listonly = False
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifestdirs = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifesttree = ManifestTree("manifests")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protodirs = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza prototree = ProtoTree("proto area")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protolists = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protolist = ProtoTree("proto list")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modechecks = set()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza togglemodechecks = set()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza trees = []
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza comparing = set()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza verbose = False
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for opt, arg in opts:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if opt == "-a":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if arch:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("may only specify one architecture")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza else:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza arch = arg
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-e":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exceptionlists.append(arg)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-L":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza listonly = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-l":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza comparing.add("protolist")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protolists.append(os.path.normpath(arg))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-M":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modechecks.update(DEFAULTMODECHECKS)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-m":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza comparing.add("manifests")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifestdirs.append(os.path.normpath(arg))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-p":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza comparing.add("proto area")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protodirs.append(os.path.normpath(arg))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-v":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza verbose = True
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif opt == "-X":
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza togglemodechecks.add(arg)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if listonly or len(modechecks) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(comparing) != 1 or "manifests" not in comparing:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("-L and -M require one or more -m args, and no -l or -p")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if listonly and len(modechecks) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("-L and -M are mutually exclusive")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza elif len(comparing) != 2:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("must specify exactly two of -l, -m, and -p")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(togglemodechecks) > 0 and len(modechecks) == 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("-X requires -M")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for s in togglemodechecks:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if s not in ALLMODECHECKS:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("unknown mode check %s" % s)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modechecks.symmetric_difference_update((s))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(modechecks) == 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza modechecks = None
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if not arch:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usage("must specify architecture")
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza exceptions = ExceptionList(exceptionlists, arch)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza originalexceptions = exceptions.copy()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(manifestdirs) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for mdir in manifestdirs:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifesttree.adddir(mdir, arch, modechecks, exceptions)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if listonly:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza manifesttree.resolvehardlinks()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for info in manifesttree.values():
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "%s" % info.protostr()
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.exit(0)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if modechecks is not None:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.exit(0)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza trees.append(manifesttree)
fe89515e649436ba27844b63b5f18b41113d99c8Richard Lowe
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(protodirs) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for pdir in protodirs:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza prototree.adddir(pdir, exceptions)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza trees.append(prototree)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if len(protolists) > 0:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for plist in protolists:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza protolist.addprotolist(plist, exceptions)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError, exc:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.stderr.write("warning: %s\n" % str(exc))
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza trees.append(protolist)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if verbose and exceptions:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "Entries present in exception list but missing from proto area:"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for exc in sorted(exceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "\t%s" % exc
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza usedexceptions = originalexceptions.difference(exceptions)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza harmfulexceptions = usedexceptions.intersection(manifesttree)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza if harmfulexceptions:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "Entries present in exception list but also in manifests:"
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza for exc in sorted(harmfulexceptions):
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print "\t%s" % exc
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza del manifesttree[exc]
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza print ""
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza trees[0].compare(trees[1])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Prazaif __name__ == '__main__':
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza try:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza main(sys.argv[1:])
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except KeyboardInterrupt:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.exit(1)
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza except IOError:
ead1f93ee620d7580f7e53350fe5a884fc4f158aLiane Praza sys.exit(1)