1516N/A#!/usr/bin/python
38N/A#
38N/A# CDDL HEADER START
38N/A#
38N/A# The contents of this file are subject to the terms of the
38N/A# Common Development and Distribution License (the "License").
38N/A# You may not use this file except in compliance with the License.
38N/A#
38N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
38N/A# or http://www.opensolaris.org/os/licensing.
38N/A# See the License for the specific language governing permissions
38N/A# and limitations under the License.
38N/A#
38N/A# When distributing Covered Code, include this CDDL HEADER in each
38N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
38N/A# If applicable, add the following below this CDDL HEADER, with the
38N/A# fields enclosed by brackets "[]" replaced with your own identifying
38N/A# information: Portions Copyright [yyyy] [name of copyright owner]
38N/A#
38N/A# CDDL HEADER END
38N/A#
38N/A
38N/A#
3356N/A# Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
38N/A#
38N/A
38N/Aimport os
3234N/Aimport six
3234N/A
2456N/Aimport pkg.bundle
443N/Aimport pkg.misc as misc
2456N/A
49N/Afrom pkg.actions import *
2276N/Afrom pkg.actions.attribute import AttributeAction
2276N/Afrom pkg.actions.legacy import LegacyAction
3234N/Afrom pkg.cpiofile import CpioFile
3234N/Afrom pkg.sysvpkg import SolarisPackage
38N/A
2456N/A
2456N/Aclass SolarisPackageDirBundle(pkg.bundle.Bundle):
38N/A
2276N/A hollow_attr = "pkg.send.convert.sunw-pkg-hollow"
2276N/A
3155N/A def __init__(self, filename, data=True, **kwargs):
2456N/A filename = os.path.normpath(filename)
38N/A self.pkg = SolarisPackage(filename)
38N/A self.pkgname = self.pkg.pkginfo["PKG"]
38N/A self.filename = filename
1292N/A self.data = data
38N/A
2276N/A # map the path name to the SVR4 class it belongs to and
2276N/A # maintain a set of pre/post install/remove and class action
2276N/A # scripts this package uses.
2276N/A self.class_actions_dir = {}
2276N/A self.class_action_names = set()
2276N/A self.scripts = set()
2276N/A
2276N/A self.hollow = self.pkg.pkginfo.get("SUNW_PKG_HOLLOW",
2276N/A "").lower() == "true"
2276N/A # A list of pkg.action.AttributeActions with pkginfo
3356N/A # attributes for items that don't map to pkg(7) equivalents
2276N/A self.pkginfo_actions = self.get_pkginfo_actions(self.pkg.pkginfo)
2276N/A
2456N/A def _walk_bundle(self):
42N/A faspac = []
42N/A if "faspac" in self.pkg.pkginfo:
42N/A faspac = self.pkg.pkginfo["faspac"]
42N/A
42N/A # Want to access the manifest as a dict.
42N/A pkgmap = {}
38N/A for p in self.pkg.manifest:
42N/A pkgmap[p.pathname] = p
2276N/A self.class_actions_dir[p.pathname] = p.klass
2276N/A self.class_action_names.add(p.klass)
2276N/A
2276N/A for act in self.pkginfo_actions:
2456N/A yield act.attrs.get("path"), act
42N/A
1292N/A if not self.data:
1292N/A for p in self.pkg.manifest:
1420N/A act = self.action(p, None)
1420N/A if act:
2456N/A yield act.attrs.get("path"), act
1292N/A return
1292N/A
83N/A def j(path):
83N/A return os.path.join(self.pkg.basedir, path)
83N/A
338N/A faspac_contents = set()
338N/A
42N/A for klass in faspac:
504N/A fpath = os.path.join(self.filename, "archive", klass)
504N/A # We accept either bz2 or 7zip'd files
504N/A for x in [".bz2", ".7z"]:
504N/A if os.path.exists(fpath + x):
504N/A cf = CpioFile.open(fpath + x)
504N/A break
2304N/A
42N/A for ci in cf:
338N/A faspac_contents.add(j(ci.name))
1420N/A act = self.action(pkgmap[j(ci.name)],
49N/A ci.extractfile())
1420N/A if act:
2456N/A yield act.attrs.get("path"), act
42N/A
2304N/A # Remove BASEDIR from a relocatable path. The extra work is
2304N/A # because if BASEDIR is not empty (non-"/"), then we probably
2304N/A # need to strip an extra slash from the beginning of the path,
2304N/A # but if BASEDIR is "" ("/" in the pkginfo file), then we don't
2304N/A # need to do anything extra.
2456N/A def r(path, ptype):
2456N/A if ptype == "i":
83N/A return path
2304N/A if path[0] == "/":
2304N/A return path[1:]
83N/A p = path[len(self.pkg.basedir):]
83N/A if p[0] == "/":
83N/A p = p[1:]
83N/A return p
83N/A
42N/A for p in self.pkg.manifest:
42N/A # Just do the files that remain. Only regular file
42N/A # types end up compressed; so skip them and only them.
338N/A # Files with special characters in their names may not
338N/A # end up in the faspac archive, so we still need to emit
338N/A # the ones that aren't.
338N/A if p.type in "fev" and p.klass in faspac and \
338N/A p.pathname in faspac_contents:
42N/A continue
42N/A
40N/A # These are the only valid file types in SysV packages
2456N/A if p.type in "ifevbcdxpls":
2456N/A if p.type == "i":
2456N/A d = "install"
2456N/A elif p.pathname[0] == "/":
2304N/A d = "root"
2304N/A else:
2304N/A d = "reloc"
1420N/A act = self.action(p, os.path.join(self.filename,
2304N/A d, r(p.pathname, p.type)))
1420N/A if act:
2456N/A if act.name == "license":
2456N/A # This relies on the fact that
2456N/A # license actions have their
2456N/A # hash set to the package path.
2456N/A yield act.hash, act
2456N/A else:
2456N/A yield os.path.join(d, act.attrs.get(
2456N/A "path", "")), act
2456N/A
2456N/A def __iter__(self):
2456N/A for entry in self._walk_bundle():
2456N/A yield entry[-1]
49N/A
49N/A def action(self, mapline, data):
1292N/A preserve_dict = {
2456N/A "renameold": "renameold",
2456N/A "renamenew": "renamenew",
2456N/A "preserve": "true",
2456N/A "svmpreserve": "true"
2456N/A }
1420N/A
2276N/A act = None
2276N/A
1420N/A # If any one of the mode, owner, or group is "?", then we're
1420N/A # clearly not capable of delivering the object correctly, so
1420N/A # ignore it.
1420N/A if mapline.type in "fevdx" and (mapline.mode == "?" or
1420N/A mapline.owner == "?" or mapline.group == "?"):
1420N/A return None
1420N/A
2304N/A if mapline.type in "fev":
3253N/A # false positive
3253N/A # file-builtin; pylint: disable=W1607
2276N/A act = file.FileAction(data, mode=mapline.mode,
1292N/A owner=mapline.owner, group=mapline.group,
2456N/A path=mapline.pathname,
1292N/A timestamp=misc.time_to_timestamp(int(mapline.modtime)))
443N/A
2304N/A # Add a preserve attribute if klass is known to be used
2304N/A # for preservation. For editable and volatile files,
2304N/A # always do at least basic preservation.
2304N/A preserve = preserve_dict.get(mapline.klass, None)
2304N/A if preserve or mapline.type in "ev":
2304N/A if not preserve:
2304N/A preserve = "true"
2304N/A act.attrs["preserve"] = preserve
2304N/A
2304N/A if act.hash == "NOHASH" and \
3234N/A isinstance(data, six.string_types) and \
2304N/A data.startswith(self.filename):
2304N/A act.hash = data[len(self.filename) + 1:]
49N/A elif mapline.type in "dx":
2276N/A act = directory.DirectoryAction(mode=mapline.mode,
49N/A owner=mapline.owner, group=mapline.group,
49N/A path=mapline.pathname)
49N/A elif mapline.type == "s":
2276N/A act = link.LinkAction(path=mapline.pathname,
49N/A target=mapline.target)
49N/A elif mapline.type == "l":
2276N/A act = hardlink.HardLinkAction(path=mapline.pathname,
49N/A target=mapline.target)
2304N/A elif mapline.type == "i" and mapline.pathname == "copyright":
2304N/A act = license.LicenseAction(data,
3158N/A license="{0}.copyright".format(self.pkgname))
2456N/A if act.hash == "NOHASH" and \
3234N/A isinstance(data, six.string_types) and \
2456N/A data.startswith(self.filename):
2456N/A act.hash = data[len(self.filename) + 1:]
1765N/A elif mapline.type == "i":
2276N/A if mapline.pathname not in ["depend", "pkginfo"]:
2276N/A # check to see if we've seen this script
2276N/A # before
2276N/A script = mapline.pathname
2276N/A if script.startswith("i.") and \
2276N/A script.replace("i.", "", 1) \
2276N/A in self.class_action_names:
2276N/A pass
2276N/A elif script.startswith("r.") and \
2276N/A script.replace("r.", "", 1) in \
2276N/A self.class_action_names:
2276N/A pass
2276N/A else:
2276N/A self.scripts.add(script)
1765N/A return None
49N/A else:
2276N/A act = unknown.UnknownAction(path=mapline.pathname)
2276N/A
2276N/A if self.hollow and act:
2276N/A act.attrs["pkg.send.convert.sunw-pkg-hollow"] = "true"
2276N/A return act
2276N/A
2276N/A def get_pkginfo_actions(self, pkginfo):
2276N/A """Creates a list of pkg.action.AttributeActions corresponding
3356N/A to pkginfo fields that aren't directly mapped to pkg(7)
2276N/A equivalents."""
2276N/A
2276N/A # these keys get converted to a legacy action
2276N/A legacy_keys = [
2276N/A "arch",
2276N/A "category",
2276N/A "name",
2276N/A "desc",
2276N/A "hotline",
2276N/A "pkg",
2276N/A "vendor",
2276N/A "version"
2276N/A ]
2276N/A
2276N/A # parameters defined in pkginfo(4) that we always ignore.
2276N/A # by default, we also ignore SUNW_*
2276N/A ignored_keys = [
2276N/A "pstamp",
2276N/A "pkginst",
2276N/A "maxinst",
2276N/A "classes",
2276N/A "basedir",
2276N/A "intonly",
2276N/A "istates",
2276N/A "order",
2276N/A "rstates",
2276N/A "ulimit",
2276N/A # XXX pkg.sysvpkg adds this, ignoring for now.
2276N/A "pkg.plat",
2276N/A ]
2276N/A ignored_keys.extend(legacy_keys)
2276N/A
2276N/A actions = []
2276N/A for key in pkginfo:
2276N/A if not pkginfo[key]:
2276N/A continue
2276N/A
2276N/A name = key.lower()
2276N/A if name in ignored_keys or "SUNW_" in key:
2276N/A continue
3158N/A name = "pkg.send.convert.{0}".format(name)
2276N/A name = name.replace("_", "-")
2276N/A actions.append(AttributeAction(name=name,
2276N/A value=pkginfo[key]))
2276N/A
2276N/A legacy_attrs = {}
2276N/A for key in pkginfo:
2276N/A name = key.lower()
2276N/A if name in legacy_keys:
2276N/A name = name.replace("_", "-")
2276N/A legacy_attrs[name] = pkginfo[key]
2276N/A
2639N/A actions.append(LegacyAction(**legacy_attrs))
2276N/A
2276N/A if "DESC" in pkginfo:
2276N/A actions.append(AttributeAction(name="pkg.description",
2276N/A value=pkginfo["DESC"]))
2276N/A if "NAME" in pkginfo:
2276N/A actions.append(AttributeAction(name="pkg.summary",
2276N/A value=pkginfo["NAME"]))
2276N/A if self.hollow:
2276N/A for act in actions:
2276N/A act.attrs[self.hollow_attr] = "true"
2276N/A
2276N/A return actions
38N/A
38N/Adef test(filename):
38N/A if os.path.isfile(os.path.join(filename, "pkginfo")) and \
42N/A os.path.isfile(os.path.join(filename, "pkgmap")):
38N/A return True
38N/A
38N/A return False