1516N/A#!/usr/bin/python
535N/A#
535N/A# CDDL HEADER START
535N/A#
535N/A# The contents of this file are subject to the terms of the
535N/A# Common Development and Distribution License (the "License").
535N/A# You may not use this file except in compliance with the License.
535N/A#
535N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
535N/A# or http://www.opensolaris.org/os/licensing.
535N/A# See the License for the specific language governing permissions
535N/A# and limitations under the License.
535N/A#
535N/A# When distributing Covered Code, include this CDDL HEADER in each
535N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
535N/A# If applicable, add the following below this CDDL HEADER, with the
535N/A# fields enclosed by brackets "[]" replaced with your own identifying
535N/A# information: Portions Copyright [yyyy] [name of copyright owner]
535N/A#
535N/A# CDDL HEADER END
535N/A#
535N/A
535N/A#
3356N/A# Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
535N/A#
535N/A
3155N/Aimport grp
535N/Aimport os
3155N/Aimport pwd
535N/Aimport stat
535N/Aimport pkg.misc
535N/A
1839N/Aimport pkg.bundle
1839N/Aimport pkg.actions.file
1839N/Aimport pkg.actions.link
1839N/Aimport pkg.actions.hardlink
535N/A
2456N/Aclass DirectoryBundle(pkg.bundle.Bundle):
535N/A """The DirectoryBundle class assists in the conversion of a directory
3356N/A tree to a pkg(7) package by traversing the tree and emitting actions for
535N/A all files, directories, and links found therein.
535N/A
535N/A Paths are published relative to the given directory. Hardlinks are
535N/A resolved as long as their companions are in the tree as well.
535N/A
535N/A All owners are set to "root" and groups to "bin", as the ownership
535N/A information is not considered to be valid. These can be set by the
535N/A caller once the action has been emitted.
535N/A """
535N/A
3155N/A def __init__(self, path, targetpaths=(), use_default_owner=True):
535N/A # XXX This could be more intelligent. Or get user input. Or
535N/A # extend API to take FMRI.
2456N/A path = os.path.normpath(path)
2456N/A self.filename = path
2456N/A self.rootdir = path
1839N/A self.pkgname = os.path.basename(self.rootdir)
2456N/A self.inodes = None
1955N/A self.targetpaths = targetpaths
2276N/A self.pkg = None
3155N/A self.use_default_owner = use_default_owner
535N/A
2456N/A def _walk_bundle(self):
1955N/A # Pre-populate self.inodes with the paths of known targets
2456N/A if self.inodes is None:
2456N/A self.inodes = {}
2456N/A for p in self.targetpaths:
2456N/A fp = os.path.join(self.rootdir, p)
2456N/A pstat = os.lstat(fp)
2456N/A self.inodes[pstat.st_ino] = fp
1955N/A
535N/A for root, dirs, files in os.walk(self.rootdir):
535N/A for obj in dirs + files:
2456N/A path = os.path.join(root, obj)
2456N/A yield path, (path,)
2456N/A
2456N/A def __iter__(self):
2456N/A for path, data in self._walk_bundle():
2456N/A act = self.action(*data)
2456N/A if act:
2456N/A yield act
535N/A
535N/A def action(self, path):
1839N/A rootdir = self.rootdir
2112N/A pubpath = pkg.misc.relpath(path, rootdir)
535N/A pstat = os.lstat(path)
535N/A mode = oct(stat.S_IMODE(pstat.st_mode))
535N/A timestamp = pkg.misc.time_to_timestamp(pstat.st_mtime)
535N/A
3155N/A # Set default root and group.
3155N/A owner = "root"
3155N/A group = "bin"
3155N/A
3155N/A # Check whether need to change owner.
3155N/A if not self.use_default_owner:
3155N/A try:
3155N/A owner = pwd.getpwuid(pstat.st_uid).pw_name
3171N/A except KeyError as e:
3155N/A owner = None
3155N/A try:
3155N/A group = grp.getgrgid(pstat.st_gid).gr_name
3171N/A except KeyError as e:
3155N/A group = None
3155N/A
3155N/A if not owner and not group:
3155N/A raise pkg.bundle.InvalidOwnershipException(
3155N/A path, uid=pstat.st_uid, gid=pstat.st_gid)
3155N/A elif not owner:
3155N/A raise pkg.bundle.InvalidOwnershipException(
3155N/A path, uid=pstat.st_uid)
3155N/A elif not group:
3155N/A raise pkg.bundle.InvalidOwnershipException(
3155N/A path, gid=pstat.st_gid)
3155N/A
535N/A if stat.S_ISREG(pstat.st_mode):
535N/A inode = pstat.st_ino
1955N/A # Any inode in self.inodes will either have been visited
1955N/A # before or will have been pre-populated from the list
1955N/A # of known targets. Create file actions for known
1955N/A # targets and unvisited inodes.
1955N/A if pubpath in self.targetpaths or \
1955N/A inode not in self.inodes:
535N/A if pstat.st_nlink > 1:
1955N/A self.inodes.setdefault(inode, path)
1839N/A return pkg.actions.file.FileAction(
3155N/A open(path, "rb"), mode=mode, owner=owner,
3155N/A group=group, path=pubpath,
535N/A timestamp=timestamp)
535N/A else:
535N/A # Find the relative path to the link target.
2112N/A target = pkg.misc.relpath(self.inodes[inode],
1839N/A os.path.dirname(path))
1839N/A return pkg.actions.hardlink.HardLinkAction(
535N/A path=pubpath, target=target)
535N/A elif stat.S_ISLNK(pstat.st_mode):
1839N/A return pkg.actions.link.LinkAction(
535N/A target=os.readlink(path), path=pubpath)
535N/A elif stat.S_ISDIR(pstat.st_mode):
1839N/A return pkg.actions.directory.DirectoryAction(
3155N/A timestamp=timestamp, mode=mode, owner=owner,
3155N/A group=group, path=pubpath)
535N/A
535N/Adef test(filename):
1944N/A return stat.S_ISDIR(os.stat(filename).st_mode)