hardlink.py revision 1846
1516N/A#!/usr/bin/python
49N/A#
49N/A# CDDL HEADER START
49N/A#
49N/A# The contents of this file are subject to the terms of the
49N/A# Common Development and Distribution License (the "License").
49N/A# You may not use this file except in compliance with the License.
49N/A#
49N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
49N/A# or http://www.opensolaris.org/os/licensing.
49N/A# See the License for the specific language governing permissions
49N/A# and limitations under the License.
49N/A#
49N/A# When distributing Covered Code, include this CDDL HEADER in each
49N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
49N/A# If applicable, add the following below this CDDL HEADER, with the
49N/A# fields enclosed by brackets "[]" replaced with your own identifying
49N/A# information: Portions Copyright [yyyy] [name of copyright owner]
49N/A#
49N/A# CDDL HEADER END
49N/A#
49N/A
49N/A#
1685N/A# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
49N/A# Use is subject to license terms.
49N/A#
49N/A
49N/A"""module describing a (hard) link packaging object
49N/A
49N/AThis module contains the HardLinkAction class, which represents a hardlink-type
49N/Apackaging object."""
49N/A
1050N/Aimport errno
1507N/Aimport link
96N/Aimport os
1281N/Aimport stat
1050N/A
1507N/Afrom pkg import misc
1050N/Afrom pkg.client.api_errors import ActionExecutionError
1507N/A
49N/A
49N/Aclass HardLinkAction(link.LinkAction):
49N/A """Class representing a hardlink-type packaging object."""
49N/A
1846N/A __slots__ = []
1846N/A
51N/A name = "hardlink"
1713N/A globally_unique = True
51N/A
49N/A def __init__(self, data=None, **attrs):
49N/A link.LinkAction.__init__(self, data, **attrs)
96N/A
237N/A def get_target_path(self):
237N/A """ return a path for target that is relative to image"""
1507N/A
237N/A target = self.attrs["target"]
237N/A
462N/A # paths are either relative to path or absolute;
462N/A # both need to be passed through os.path.normpath to ensure
462N/A # that all ".." are removed to constrain target to image
237N/A
237N/A if target[0] != "/":
462N/A path = self.attrs["path"]
237N/A target = os.path.normpath(
237N/A os.path.join(os.path.split(path)[0], target))
237N/A else:
237N/A target = os.path.normpath(target)[1:]
237N/A
237N/A return target
583N/A
136N/A def install(self, pkgplan, orig):
96N/A """Client-side method that installs a hard link."""
96N/A
96N/A path = self.attrs["path"]
237N/A target = self.get_target_path()
96N/A
96N/A path = os.path.normpath(os.path.sep.join(
136N/A (pkgplan.image.get_root(), path)))
96N/A
462N/A if not os.path.exists(os.path.dirname(path)):
1507N/A self.makedirs(os.path.dirname(path),
1507N/A mode=misc.PKG_DIR_MODE)
462N/A elif os.path.exists(path):
96N/A os.unlink(path)
96N/A
1050N/A fulltarget = os.path.normpath(os.path.sep.join(
462N/A (pkgplan.image.get_root(), target)))
96N/A
1050N/A try:
1050N/A os.link(fulltarget, path)
1050N/A except EnvironmentError, e:
1050N/A if e.errno == errno.ENOENT:
1050N/A raise ActionExecutionError(self, e,
1050N/A "missing hard link target '%s'" % target)
1050N/A else:
1050N/A raise ActionExecutionError(self, e)
222N/A
237N/A def verify(self, img, **args):
1685N/A """Returns a tuple of lists of the form (errors, warnings,
1685N/A info). The error list will be empty if the action has been
1685N/A correctly installed in the given image."""
1281N/A
1281N/A #
1281N/A # We only allow hard links to regular files, so the hard
1281N/A # link should lstat() as a regular file.
1281N/A #
1685N/A lstat, errors, warnings, info, abort = \
1281N/A self.verify_fsobj_common(img, stat.S_IFREG)
1281N/A if abort:
1281N/A assert errors
1685N/A return errors, warnings, info
1281N/A
237N/A target = self.get_target_path()
222N/A path = os.path.normpath(os.path.sep.join(
1281N/A (img.get_root(), self.attrs["path"])))
462N/A target = os.path.normpath(os.path.sep.join(
462N/A (img.get_root(), target)))
237N/A
237N/A if not os.path.exists(target):
1685N/A errors.append(_("Target '%s' does not exist") %
583N/A self.attrs["target"])
583N/A
1281N/A # No point in continuing if no target
583N/A if errors:
1685N/A return errors, warnings, info
222N/A
237N/A try:
1281N/A if os.stat(path).st_ino != os.stat(target).st_ino:
1685N/A errors.append(_("Broken: Path and Target (%s) "
1685N/A "inodes not the same") %
583N/A self.get_target_path())
1685N/A except OSError, e:
1685N/A errors.append(_("Unexpected Error: %s") % e)
222N/A
1685N/A return errors, warnings, info