--- a/src/modules/actions/directory.py Thu Jul 31 21:20:23 2014 +0100
+++ b/src/modules/actions/directory.py Fri Aug 01 11:33:40 2014 -0700
@@ -71,6 +71,60 @@
def directory_references(self):
return [os.path.normpath(self.attrs["path"])]
+ def __create_directory(self, pkgplan, path, mode):
+ """Create a directory."""
+
+ try:
+ self.makedirs(path, mode=mode,
+ fmri=pkgplan.destination_fmri)
+ except OSError, e:
+ if e.filename != path:
+ # makedirs failed for some component
+ # of the path.
+ raise
+
+ fs = os.lstat(path)
+ fs_mode = stat.S_IFMT(fs.st_mode)
+ if e.errno == errno.EROFS:
+ # Treat EROFS like EEXIST if both are
+ # applicable, since we'll end up with
+ # EROFS instead.
+ if stat.S_ISDIR(fs_mode):
+ return
+ raise
+ elif e.errno != errno.EEXIST:
+ raise
+
+ if stat.S_ISLNK(fs_mode):
+ # User has replaced directory with a
+ # link, or a package has been poorly
+ # implemented. It isn't safe to
+ # simply re-create the directory as
+ # that won't restore the files that
+ # are supposed to be contained within.
+ err_txt = _("Unable to create "
+ "directory %s; it has been "
+ "replaced with a link. To "
+ "continue, please remove the "
+ "link or restore the directory "
+ "to its original location and "
+ "try again.") % path
+ raise apx.ActionExecutionError(
+ self, details=err_txt, error=e,
+ fmri=pkgplan.destination_fmri)
+ elif stat.S_ISREG(fs_mode):
+ # User has replaced directory with a
+ # file, or a package has been poorly
+ # implemented. Salvage what's there,
+ # and drive on.
+ pkgplan.salvage(path)
+ os.mkdir(path, mode)
+ elif stat.S_ISDIR(fs_mode):
+ # The directory already exists, but
+ # ensure that the mode matches what's
+ # expected.
+ os.chmod(path, mode)
+
def install(self, pkgplan, orig):
"""Client-side method that installs a directory."""
@@ -105,56 +159,7 @@
mode |= stat.S_IWUSR
if not orig:
- try:
- self.makedirs(path, mode=mode,
- fmri=pkgplan.destination_fmri)
- except OSError, e:
- if e.filename != path:
- # makedirs failed for some component
- # of the path.
- raise
-
- fs = os.lstat(path)
- fs_mode = stat.S_IFMT(fs.st_mode)
- if e.errno == errno.EROFS:
- # Treat EROFS like EEXIST if both are
- # applicable, since we'll end up with
- # EROFS instead.
- if stat.S_ISDIR(fs_mode):
- return
- raise
- elif e.errno != errno.EEXIST:
- raise
-
- if stat.S_ISLNK(fs_mode):
- # User has replaced directory with a
- # link, or a package has been poorly
- # implemented. It isn't safe to
- # simply re-create the directory as
- # that won't restore the files that
- # are supposed to be contained within.
- err_txt = _("Unable to create "
- "directory %s; it has been "
- "replaced with a link. To "
- "continue, please remove the "
- "link or restore the directory "
- "to its original location and "
- "try again.") % path
- raise apx.ActionExecutionError(
- self, details=err_txt, error=e,
- fmri=pkgplan.destination_fmri)
- elif stat.S_ISREG(fs_mode):
- # User has replaced directory with a
- # file, or a package has been poorly
- # implemented. Salvage what's there,
- # and drive on.
- pkgplan.salvage(path)
- os.mkdir(path, mode)
- elif stat.S_ISDIR(fs_mode):
- # The directory already exists, but
- # ensure that the mode matches what's
- # expected.
- os.chmod(path, mode)
+ self.__create_directory(pkgplan, path, mode)
# The downside of chmodding the directory is that as a non-root
# user, if we set perms u-w, we won't be able to put anything in
@@ -168,7 +173,16 @@
# other failures. Or can we require that everyone simply have
# file_dac_write who wants to use the tools. Probably not.
elif mode != omode:
- os.chmod(path, mode)
+ try:
+ os.chmod(path, mode)
+ except Exception, e:
+ if e.errno != errno.EPERM and e.errno != \
+ errno.ENOSYS:
+ # Assume chmod failed due to a
+ # recoverable error.
+ self.__create_directory(pkgplan, path,
+ mode)
+ omode = oowner = ogroup = None
# if we're salvaging contents, move 'em now.
# directories with "salvage-from" attribute
--- a/src/modules/actions/file.py Thu Jul 31 21:20:23 2014 +0100
+++ b/src/modules/actions/file.py Fri Aug 01 11:33:40 2014 -0700
@@ -243,7 +243,10 @@
raise
# This is safe even if temp == final_path.
- portable.rename(temp, final_path)
+ try:
+ portable.rename(temp, final_path)
+ except OSError, e:
+ raise api_errors.FileInUseException(final_path)
# Handle timestamp if specified (and content was installed).
if do_content and "timestamp" in self.attrs:
--- a/src/tests/cli/t_pkg_install.py Thu Jul 31 21:20:23 2014 +0100
+++ b/src/tests/cli/t_pkg_install.py Fri Aug 01 11:33:40 2014 -0700
@@ -2532,6 +2532,20 @@
close
"""
+ dumdir10 = """
+ open [email protected]
+ add dir path=etc mode=0755 owner=root group=bin
+ add file tmp/amber1 mode=0755 owner=root group=bin path=etc/amber1
+ close
+ """
+
+ dumdir20 = """
+ open [email protected]
+ add dir path=etc mode=0700 owner=root group=bin
+ add file tmp/amber1 mode=0444 owner=root group=bin path=etc/amber1
+ close
+ """
+
misc_files1 = [
"tmp/amber1", "tmp/amber2", "tmp/bronzeA1", "tmp/bronzeA2",
"tmp/bronze1", "tmp/bronze2",
@@ -2912,6 +2926,21 @@
self.pkg("install [email protected]")
self.pkg("verify -v")
+ def test_upgrade5(self):
+ """Test manually removed directory and files will be restored
+ during update, if mode are different."""
+
+ self.pkgsend_bulk(self.rurl, (self.dumdir10, self.dumdir20))
+ self.image_create(self.rurl)
+
+ self.pkg("install -vvv [email protected]")
+ self.pkg("verify -v")
+ dirpath = os.path.join(self.test_root, "image0", "etc")
+ shutil.rmtree(dirpath)
+
+ self.pkg("update -vvv [email protected]")
+ self.pkg("verify -v")
+
def test_upgrade_liveroot(self):
"""Test to make sure upgrade of package fails if on live root
and reboot is needed."""