hardlink.py revision 3158
0N/A#!/usr/bin/python
0N/A#
0N/A# CDDL HEADER START
0N/A#
0N/A# The contents of this file are subject to the terms of the
0N/A# Common Development and Distribution License (the "License").
0N/A# You may not use this file except in compliance with the License.
0N/A#
0N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0N/A# or http://www.opensolaris.org/os/licensing.
0N/A# See the License for the specific language governing permissions
0N/A# and limitations under the License.
0N/A#
0N/A# When distributing Covered Code, include this CDDL HEADER in each
0N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
0N/A# If applicable, add the following below this CDDL HEADER, with the
0N/A# fields enclosed by brackets "[]" replaced with your own identifying
0N/A# information: Portions Copyright [yyyy] [name of copyright owner]
0N/A#
0N/A# CDDL HEADER END
0N/A#
0N/A
0N/A#
0N/A# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
0N/A#
0N/A
0N/A"""module describing a (hard) link packaging object
0N/A
0N/AThis module contains the HardLinkAction class, which represents a hardlink-type
0N/Apackaging object."""
0N/A
0N/Aimport errno
0N/Aimport generic
0N/Aimport link
0N/Aimport os
0N/Aimport stat
0N/A
0N/Afrom pkg import misc
0N/Afrom pkg.client.api_errors import ActionExecutionError
0N/A
0N/Aclass HardLinkAction(link.LinkAction):
0N/A """Class representing a hardlink-type packaging object."""
0N/A
0N/A __slots__ = []
0N/A
0N/A name = "hardlink"
0N/A ordinality = generic._orderdict[name]
0N/A
0N/A def get_target_path(self):
0N/A """ return a path for target that is relative to image"""
0N/A
0N/A target = self.attrs["target"]
0N/A
0N/A # paths are either relative to path or absolute;
0N/A # both need to be passed through os.path.normpath to ensure
0N/A # that all ".." are removed to constrain target to image
0N/A
0N/A if target[0] != "/":
0N/A path = self.attrs["path"]
0N/A target = os.path.normpath(
0N/A os.path.join(os.path.split(path)[0], target))
0N/A else:
0N/A target = os.path.normpath(target)[1:]
0N/A
0N/A return target
0N/A
0N/A def install(self, pkgplan, orig):
0N/A """Client-side method that installs a hard link."""
0N/A
0N/A target = self.get_target_path()
0N/A path = self.get_installed_path(pkgplan.image.get_root())
0N/A
0N/A # Don't allow installation through symlinks.
0N/A self.fsobj_checkpath(pkgplan, path)
0N/A
0N/A if not os.path.exists(os.path.dirname(path)):
0N/A self.makedirs(os.path.dirname(path),
0N/A mode=misc.PKG_DIR_MODE,
0N/A fmri=pkgplan.destination_fmri)
0N/A elif os.path.exists(path):
0N/A self.remove(pkgplan)
0N/A
0N/A fulltarget = os.path.normpath(os.path.sep.join(
0N/A (pkgplan.image.get_root(), target)))
0N/A
0N/A try:
0N/A os.link(fulltarget, path)
0N/A except EnvironmentError, e:
0N/A if e.errno != errno.ENOENT:
0N/A raise ActionExecutionError(self, error=e)
0N/A
0N/A # User or another process has removed target for
0N/A # hardlink, a package hasn't declared correct
0N/A # dependencies, or the target hasn't been installed
0N/A # yet.
0N/A err_txt = _("Unable to create hard link {path}; "
0N/A "target {target} is missing.").format(
0N/A path=path, target=fulltarget)
0N/A raise ActionExecutionError(self, details=err_txt,
0N/A error=e, fmri=pkgplan.destination_fmri)
0N/A
0N/A def verify(self, img, **args):
0N/A """Returns a tuple of lists of the form (errors, warnings,
0N/A info). The error list will be empty if the action has been
correctly installed in the given image."""
#
# We only allow hard links to regular files, so the hard
# link should lstat() as a regular file.
#
lstat, errors, warnings, info, abort = \
self.verify_fsobj_common(img, stat.S_IFREG)
if abort:
assert errors
return errors, warnings, info
target = self.get_target_path()
path = self.get_installed_path(img.get_root())
target = os.path.normpath(os.path.sep.join(
(img.get_root(), target)))
if not os.path.exists(target):
errors.append(_("Target '{0}' does not exist").format(
self.attrs["target"]))
# No point in continuing if no target
if errors:
return errors, warnings, info
try:
if os.stat(path).st_ino != os.stat(target).st_ino:
errors.append(_("Broken: Path and Target ({0}) "
"inodes not the same").format(
self.get_target_path()))
except OSError, e:
errors.append(_("Unexpected Error: {0}").format(e))
return errors, warnings, info