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