15504318 action upgrade needs to consider a missing origin s12b54
authorXiaobo Shen <xiaobo.shen@oracle.com>
Fri, 01 Aug 2014 11:33:40 -0700
changeset 3109 62e30e758ebb
parent 3108 b4e104db9d4a
child 3110 5590234ea9b2
15504318 action upgrade needs to consider a missing origin
src/modules/actions/directory.py
src/modules/actions/file.py
src/tests/cli/t_pkg_install.py
--- 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."""