class CreateFSItemOperation(FSOperationBase): """Create a directory or file with the given access permissions and ownership. In case of a file, you may specify an initial content. For this operation to succeed, the destination path must not exist yet!""" __slots__ = ("_path", "_content", "_mode", "_uid", "_gid") name = "CreateFSItem" def __init__(self, transaction, path, initial_file_content=None, mode=None, uid=None, gid=None): """Initialize the operation with a path to create. If initial_file_content is set, to a string, it will be written in binary mode to a file. If it is unset, a directory will be created. Non-existing parent-directories will be created. After creation, the mode will be set if not None, and uid and gid will be set as well to the given numerical ID if of of them is not None""" super(CreateFSItemOperation, self).__init__(transaction) self._assert_posix() self._path = Path(path) self._content = initial_file_content self._mode = mode self._uid = uid self._gid = gid def apply(self): if self._content and self._path.isdir() or not self._content and self._path.isfile(): raise AssertionError( "Cannot create item of type directory or file as the an equally named item of different type exists") # END sanity check if self._dry_run(): return if self._path.exists(): return # end ignore existing items of the same type # we don't do it the most efficient way, as we could specify certain things in # at creation. For now, we don't do it though as it shouldn't matter if self._content: self.log.info("creating file %s", self._path) self._path.write_bytes(self._content) else: self.log.info("creating directory %s", self._path) self._path.makedirs() # END initial creation self._operation_performed = True if self._mode is not None: self._path.chmod(self._mode) # END handle mode self.set_user_group(self._path, self._gid, self._uid) def rollback(self): try: if not self._operation_performed or not self._path.exists(): return if self._content: self.log.info("Removing file %s", self._path) self._path.remove() else: self.log.info("Removing single directory %s", self._path) self._path.rmdir() # END handle removal, safely as we don't recursively delete anything finally: self._reset_state()