def preinstall(self, pkgplan, orig): """If the file exists, check if it is in use.""" if not orig: return path = orig.get_installed_path(pkgplan.image.get_root()) if os.path.isfile(path) and self.in_use(path): raise api_errors.FileInUseException(path)
def preremove(self, pkgplan): path = self.get_installed_path(pkgplan.image.get_root()) if os.path.isfile(path) and self.in_use(path): raise api_errors.FileInUseException(path)
def install(self, pkgplan, orig): """Client-side method that installs a file.""" mode = None try: mode = int(self.attrs.get("mode", None), 8) except (TypeError, ValueError): # Mode isn't valid, so let validate raise a more # informative error. self.validate(fmri=pkgplan.destination_fmri) owner, group = self.get_fsobj_uid_gid(pkgplan, pkgplan.destination_fmri) final_path = self.get_installed_path(pkgplan.image.get_root()) # Don't allow installation through symlinks. self.fsobj_checkpath(pkgplan, final_path) if not os.path.exists(os.path.dirname(final_path)): self.makedirs(os.path.dirname(final_path), mode=misc.PKG_DIR_MODE, fmri=pkgplan.destination_fmri) elif (not orig and not pkgplan.origin_fmri and "preserve" in self.attrs and self.attrs["preserve"] not in ("abandon", "install-only") and os.path.isfile(final_path)): # Unpackaged editable file is already present during # initial install; salvage it before continuing. pkgplan.salvage(final_path) # XXX If we're upgrading, do we need to preserve file perms from # existing file? # check if we have a save_file active; if so, simulate file # being already present rather than installed from scratch if "save_file" in self.attrs: orig = self.restore_file(pkgplan.image) # See if we need to preserve the file, and if so, set that up. # # XXX What happens when we transition from preserve to # non-preserve or vice versa? Do we want to treat a preserve # attribute as turning the action into a critical action? # # XXX We should save the originally installed file. It can be # used as an ancestor for a three-way merge, for example. Where # should it be stored? pres_type = self._check_preserve(orig, pkgplan) do_content = True old_path = None if pres_type == True or (pres_type and pkgplan.origin_fmri == pkgplan.destination_fmri): # File is marked to be preserved and exists so don't # reinstall content. do_content = False elif pres_type == "legacy": # Only rename old file if this is a transition to # preserve=legacy from something else. if orig.attrs.get("preserve", None) != "legacy": old_path = final_path + ".legacy" elif pres_type == "renameold.update": old_path = final_path + ".update" elif pres_type == "renameold": old_path = final_path + ".old" elif pres_type == "renamenew": final_path = final_path + ".new" elif pres_type == "abandon": return # If it is a directory (and not empty) then we should # salvage the contents. if os.path.exists(final_path) and \ not os.path.islink(final_path) and \ os.path.isdir(final_path): try: os.rmdir(final_path) except OSError as e: if e.errno == errno.ENOENT: pass elif e.errno in (errno.EEXIST, errno.ENOTEMPTY): pkgplan.salvage(final_path) elif e.errno != errno.EACCES: # this happens on Windows raise # XXX This needs to be modularized. if do_content and self.needsdata(orig, pkgplan): tfilefd, temp = tempfile.mkstemp(dir=os.path.dirname(final_path)) if not self.data: # The state of the filesystem changed after the # plan was prepared; attempt a one-off # retrieval of the data. self.data = self.__set_data(pkgplan) stream = self.data() tfile = os.fdopen(tfilefd, "wb") try: # Always verify using the most preferred hash hash_attr, hash_val, hash_func = \ digest.get_preferred_hash(self) shasum = misc.gunzip_from_stream(stream, tfile, hash_func) except zlib.error as e: raise ActionExecutionError( self, details=_("Error decompressing payload: " "{0}").format(" ".join([str(a) for a in e.args])), error=e) finally: tfile.close() stream.close() if shasum != hash_val: raise ActionExecutionError( self, details=_("Action data hash verification " "failure: expected: {expected} computed: " "{actual} action: {action}").format( expected=hash_val, actual=shasum, action=self)) else: temp = final_path try: os.chmod(temp, mode) except OSError as e: # If the file didn't exist, assume that's intentional, # and drive on. if e.errno != errno.ENOENT: raise else: return try: portable.chown(temp, owner, group) except OSError as e: if e.errno != errno.EPERM: raise # XXX There's a window where final_path doesn't exist, but we # probably don't care. if do_content and old_path: try: portable.rename(final_path, old_path) except OSError as e: if e.errno != errno.ENOENT: # Only care if file isn't gone already. raise # This is safe even if temp == final_path. try: portable.rename(temp, final_path) except OSError as e: raise api_errors.FileInUseException(final_path) # Handle timestamp if specified (and content was installed). if do_content and "timestamp" in self.attrs: t = misc.timestamp_to_time(self.attrs["timestamp"]) try: os.utime(final_path, (t, t)) except OSError as e: if e.errno != errno.EACCES: raise # On Windows, the time cannot be changed on a # read-only file os.chmod(final_path, stat.S_IRUSR | stat.S_IWUSR) os.utime(final_path, (t, t)) os.chmod(final_path, mode) # Handle system attributes. sattr = self.attrs.get("sysattr") if sattr: if isinstance(sattr, list): sattr = ",".join(sattr) sattrs = sattr.split(",") if len(sattrs) == 1 and \ sattrs[0] not in portable.get_sysattr_dict(): # not a verbose attr, try as a compact attr seq arg = sattrs[0] else: arg = sattrs try: portable.fsetattr(final_path, arg) except OSError as e: if e.errno != errno.EINVAL: raise warn = _("System attributes are not supported " "on the target image filesystem; 'sysattr'" " ignored for {0}").format(self.attrs["path"]) pkgplan.image.imageplan.pd.add_item_message( pkgplan.destination_fmri, misc.time_to_timestamp(time.time()), MSG_WARNING, warn) except ValueError as e: warn = _("Could not set system attributes for {path}" "'{attrlist}': {err}").format(attrlist=sattr, err=e, path=self.attrs["path"]) pkgplan.image.imageplan.pd.add_item_message( pkgplan.destination_fmri, misc.time_to_timestamp(time.time()), MSG_WARNING, warn)