Alon Bar-Lev has uploaded a new change for review. Change subject: core: filetransaction: support unsafe visible file transaction ......................................................................
core: filetransaction: support unsafe visible file transaction In some cases there are utilities that need to use the file that is writing during transaction, this implies that we must modify the original file. In the unsafe visible mode we do exactly that, and revert by moving the backup file to original file. Change-Id: I66af1694922730a377257a907a9a80f4d071ec74 Signed-off-by: Alon Bar-Lev <[email protected]> --- M src/otopi/filetransaction.py 1 file changed, 70 insertions(+), 22 deletions(-) git pull ssh://gerrit.ovirt.org:29418/otopi refs/changes/15/11515/1 diff --git a/src/otopi/filetransaction.py b/src/otopi/filetransaction.py index 6396e4a..15834c5 100644 --- a/src/otopi/filetransaction.py +++ b/src/otopi/filetransaction.py @@ -109,6 +109,7 @@ downer=None, dgroup=None, enforcePermissions=False, + visibleButUnsafe=False, modifiedList=[], ): """Constructor. @@ -130,6 +131,7 @@ dgroup -- directory group (name) if directory is to be created. enforcePermissions -- if True permissions are enforced also if previous file was exists. + visibleButUnsafe -- if True during transaction new content is visible. modifiedList -- a list to add file name if was changed. """ @@ -150,6 +152,7 @@ self._downer = -1 self._dgroup = -1 self._enforcePermissions = enforcePermissions + self._visibleButUnsafe = visibleButUnsafe self._modifiedList = modifiedList if owner is not None: self._owner, self._group = pwd.getpwnam(owner)[2:4] @@ -160,6 +163,8 @@ if dgroup is not None: self._dgroup = grp.getgrnam(dgroup)[2] self._tmpname = None + self._backup = None + self._originalFileWasMissing = not os.path.exists(self._name) self._prepared = False def __str__(self): @@ -169,7 +174,9 @@ def prepare(self): doit = True - if os.path.exists(self._name): + if self._originalFileWasMissing: + self.logger.debug("file '%s' missing" % self._name) + else: self.logger.debug("file '%s' exists" % self._name) with open(self._name, 'r') as f: if f.read() == self._content: @@ -177,32 +184,43 @@ "file '%s' already has content" % self._name ) doit = False - else: - self.logger.debug("file '%s' missing" % self._name) if doit: mydir = os.path.dirname(self._name) - if os.path.exists(self._name): + if self._originalFileWasMissing: + if not os.path.exists(mydir): + self._createDirRecursive(mydir) + else: # check we can open file for write with open(self._name, 'a'): pass - # backup file - newname = "%s.%s" % ( - self._name, - datetime.datetime.now().strftime('%Y%m%d%H%M%S') - ) - self.logger.debug("backup '%s'->'%s'" % (self._name, newname)) - shutil.copyfile(self._name, newname) - + currentStat = os.stat(self._name) if not self._enforcePermissions: - # get current file stats - currentStat = os.stat(self._name) self._mode = currentStat.st_mode self._owner = currentStat.st_uid self._group = currentStat.st_gid - elif not os.path.exists(mydir): - self._createDirRecursive(mydir) + + # + # backup the file + # + self._backup = "%s.%s" % ( + self._name, + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ) + self.logger.debug( + "backup '%s'->'%s'" % ( + self._name, + self._backup + ) + ) + shutil.copyfile(self._name, self._backup) + shutil.copystat(self._name, self._backup) + os.chown( + self._backup, + currentStat.st_uid, + currentStat.st_gid + ) fd = -1 try: @@ -228,6 +246,13 @@ os.write(fd, self._content) os.fsync(fd) + + if self._visibleButUnsafe: + type(self)._atomicMove( + source=self._tmpname, + destination=self._name, + ) + self._prepared = True finally: if fd != -1: @@ -238,15 +263,38 @@ fd = -1 def abort(self): - if self._tmpname is not None: - try: - os.unlink(self._tmpname) - except OSError: - pass + try: + if self._visibleButUnsafe: + if ( + self._originalFileWasMissing and + os.path.exists(self._name) + ): + os.unlink(self._name) + elif ( + self._backup is not None and + os.path.exists(self._backup) + ): + type(self)._atomicMove( + source=self._backup, + destination=self._name, + ) + else: + if ( + self._tmpname is not None and + os.path.exists(self._tmpname) + ): + os.unlink(self._tmpname) + except OSError: + self.logger.debug('Exception during abort', exc_info=True) + pass def commit(self): if self._prepared: - type(self)._atomicMove(self._tmpname, self._name) + if not self._visibleButUnsafe: + type(self)._atomicMove( + source=self._tmpname, + destination=self._name, + ) self._modifiedList.append(self._name) -- To view, visit http://gerrit.ovirt.org/11515 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I66af1694922730a377257a907a9a80f4d071ec74 Gerrit-PatchSet: 1 Gerrit-Project: otopi Gerrit-Branch: master Gerrit-Owner: Alon Bar-Lev <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
