def __apply_change(self, change_id, change_tuple): """Apply changes to our filesystem reported by GD. All we do is remove the current record components, if it's valid, and then reload it with what we were given. Note that since we don't necessarily know about the entries that have been changed, this also allows us to slowly increase our knowledge of the filesystem (of, obviously, only those things that change). """ (entry_id, was_deleted, entry) = change_tuple is_visible = entry.is_visible if entry else None _logger.info( "Applying change with change-ID (%d), entry-ID [%s], " "and is-visible of [%s]", change_id, entry_id, is_visible) # First, remove any current knowledge from the system. _logger.debug( "Removing all trace of entry with ID [%s] " "(apply_change).", entry_id) PathRelations.get_instance().remove_entry_all(entry_id) # If it wasn't deleted, add it back. _logger.debug("Registering changed entry with ID [%s].", entry_id) if is_visible: path_relations = PathRelations.get_instance() path_relations.register_entry(entry)
def __apply_change(self, change_id, change_tuple): """Apply changes to our filesystem reported by GD. All we do is remove the current record components, if it's valid, and then reload it with what we were given. Note that since we don't necessarily know about the entries that have been changed, this also allows us to slowly increase our knowledge of the filesystem (of, obviously, only those things that change). """ (entry_id, was_deleted, entry) = change_tuple is_visible = entry.is_visible if entry else None _logger.info("Applying change with change-ID (%d), entry-ID [%s], " "and is-visible of [%s]", change_id, entry_id, is_visible) # First, remove any current knowledge from the system. _logger.debug("Removing all trace of entry with ID [%s] " "(apply_change).", entry_id) PathRelations.get_instance().remove_entry_all(entry_id) # If it wasn't deleted, add it back. _logger.debug("Registering changed entry with ID [%s].", entry_id) if is_visible: path_relations = PathRelations.get_instance() path_relations.register_entry(entry)
def rename(self, filepath_old, filepath_new): # Make sure the old filepath exists. (entry, path, filename_old) = get_entry_or_raise(filepath_old) # At this point, decorations, the is-hidden prefix, etc.. haven't been # stripped. (path, filename_new_raw) = split(filepath_new) # Make sure the new filepath doesn't exist. try: get_entry_or_raise(filepath_new, True) except GdNotFoundError: pass gd = get_gdrive() try: entry = gd.rename(entry, filename_new_raw) except: _logger.exception("Could not update entry [%s] for rename.", entry) raise FuseOSError(EIO) # Update our knowledge of the entry. path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: _logger.exception("Could not register renamed entry: %s", entry) raise FuseOSError(EIO)
def get_by_path(raw_path): try: result = split_path(raw_path, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except: print("Could not process file-path [%s]." % (raw_path)) exit() filepath = build_filepath(path, filename) path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: print("Could not retrieve clause for non-existent file-path [%s]." % (filepath)) exit() except: print("Could not retrieve clause for path [%s]. " % (filepath)) exit() if not entry_clause: print("Path [%s] does not exist for stat()." % (filepath)) exit() return entry_clause[CLAUSE_ENTRY]
def __get_entry_or_raise(self, raw_path): try: result = split_path(raw_path, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except: self.__log.exception("Could not process file-path [%s]." % (raw_path)) raise FuseOSError(EIO) filepath = build_filepath(path, filename) path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: self.__log.exception("Could not retrieve clause for non-existent " "file-path [%s]." % (filepath)) raise FuseOSError(ENOENT) except: self.__log.exception("Could not retrieve clause for path [%s]. " % (filepath)) raise FuseOSError(EIO) if not entry_clause: self.__log.debug("Path [%s] does not exist for stat()." % (filepath)) raise FuseOSError(ENOENT) return (entry_clause[CLAUSE_ENTRY], path, filename)
def __create(self, filepath, mode=None): """Create a new file. We don't implement "mode" (permissions) because the model doesn't agree with GD. """ # TODO: Fail if it already exists. try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: _logger.exception("Could not process [%s] (i-create).", filepath) raise FuseOSError(ENOENT) except: _logger.exception("Could not split path [%s] (i-create).", filepath) raise FuseOSError(EIO) distilled_filepath = build_filepath(path, filename) # Try to guess at a mime-type, if not otherwise given. if mime_type is None: (mimetype_guess, _) = guess_type(filename, True) if mimetype_guess is not None: mime_type = mimetype_guess else: mime_type = Conf.get('default_mimetype') gd = get_gdrive() try: entry = gd.create_file( filename, [parent_clause[3]], mime_type, is_hidden=is_hidden) except: _logger.exception("Could not create empty file [%s] under " "parent with ID [%s].", filename, parent_clause[3]) raise FuseOSError(EIO) path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: _logger.exception("Could not register created file in cache.") raise FuseOSError(EIO) _logger.info("Inner-create of [%s] completed.", distilled_filepath) return (entry, path, filename, mime_type)
def rmdir(self, filepath): """Remove a directory.""" path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: _logger.exception("Could not process [%s] (rmdir).", filepath) raise FuseOSError(ENOENT) except: _logger.exception("Could not get clause from file-path [%s] " "(rmdir).", filepath) raise FuseOSError(EIO) if not entry_clause: _logger.error("Path [%s] does not exist for rmdir().", filepath) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if not a directory. if not normalized_entry.is_directory: _logger.error("Can not rmdir() non-directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(ENOTDIR) # Ensure the folder is empty. gd = get_gdrive() try: found = gd.get_children_under_parent_id( entry_id, max_results=1) except: _logger.exception("Could not determine if directory to be removed " "has children.", entry_id) raise FuseOSError(EIO) if found: raise FuseOSError(ENOTEMPTY) try: gd.remove_entry(normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: _logger.exception("Could not remove directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(EIO)
def rmdir(self, filepath): """Remove a directory.""" path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: _logger.exception("Could not process [%s] (rmdir).", filepath) raise FuseOSError(ENOENT) except: _logger.exception( "Could not get clause from file-path [%s] " "(rmdir).", filepath) raise FuseOSError(EIO) if not entry_clause: _logger.error("Path [%s] does not exist for rmdir().", filepath) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if not a directory. if not normalized_entry.is_directory: _logger.error("Can not rmdir() non-directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(ENOTDIR) # Ensure the folder is empty. gd = get_gdrive() try: found = gd.get_children_under_parent_id(entry_id, max_results=1) except: _logger.exception( "Could not determine if directory to be removed " "has children.", entry_id) raise FuseOSError(EIO) if found: raise FuseOSError(ENOTEMPTY) try: gd.remove_entry(normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: _logger.exception("Could not remove directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(EIO)
def __create(self, filepath, mode=None): """Create a new file. We don't implement "mode" (permissions) because the model doesn't agree with GD. """ # TODO: Fail if it already exists. try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: _logger.exception("Could not process [%s] (i-create).", filepath) raise FuseOSError(ENOENT) except: _logger.exception("Could not split path [%s] (i-create).", filepath) raise FuseOSError(EIO) distilled_filepath = build_filepath(path, filename) # Try to guess at a mime-type, if not otherwise given. if mime_type is None: (mimetype_guess, _) = guess_type(filename, True) if mimetype_guess is not None: mime_type = mimetype_guess else: mime_type = Conf.get('default_mimetype') gd = get_gdrive() try: entry = gd.create_file(filename, [parent_clause[3]], mime_type, is_hidden=is_hidden) except: _logger.exception( "Could not create empty file [%s] under " "parent with ID [%s].", filename, parent_clause[3]) raise FuseOSError(EIO) path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: _logger.exception("Could not register created file in cache.") raise FuseOSError(EIO) _logger.info("Inner-create of [%s] completed.", distilled_filepath) return (entry, path, filename, mime_type)
def __create(self, filepath, mode=None): """Create a new file. We don't implement "mode" (permissions) because the model doesn't agree with GD. """ # TODO: Fail if it already exists. try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: _logger.exception("Could not process [%s] (i-create).", filepath) raise FuseOSError(ENOENT) except: _logger.exception("Could not split path [%s] (i-create).", filepath) raise FuseOSError(EIO) if mime_type is None: _, ext = os.path.splitext(filename) if ext != '': ext = ext[1:] mime_type = utility.get_first_mime_type_by_extension(ext) distilled_filepath = build_filepath(path, filename) gd = get_gdrive() try: entry = gd.create_file( filename, [parent_clause[3]], mime_type, is_hidden=is_hidden) except: _logger.exception("Could not create empty file [%s] under " "parent with ID [%s].", filename, parent_clause[3]) raise FuseOSError(EIO) path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: _logger.exception("Could not register created file in cache.") raise FuseOSError(EIO) _logger.info("Inner-create of [%s] completed.", distilled_filepath) return (entry, path, filename, mime_type)
def __create(self, filepath, mode=None): """Create a new file. We don't implement "mode" (permissions) because the model doesn't agree with GD. """ # TODO: Fail if it already exists. try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: _logger.exception("Could not process [%s] (i-create).", filepath) raise FuseOSError(ENOENT) except: _logger.exception("Could not split path [%s] (i-create).", filepath) raise FuseOSError(EIO) if mime_type is None: _, ext = os.path.splitext(filename) if ext != '': ext = ext[1:] mime_type = utility.get_first_mime_type_by_extension(ext) distilled_filepath = build_filepath(path, filename) gd = get_gdrive() try: entry = gd.create_file(filename, [parent_clause[3]], mime_type, is_hidden=is_hidden) except: _logger.exception( "Could not create empty file [%s] under " "parent with ID [%s].", filename, parent_clause[3]) raise FuseOSError(EIO) path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: _logger.exception("Could not register created file in cache.") raise FuseOSError(EIO) _logger.info("Inner-create of [%s] completed.", distilled_filepath) return (entry, path, filename, mime_type)
def readdir(self, path, offset): """A generator returning one base filename at a time.""" # We expect "offset" to always be (0). if offset != 0: self.__log.warning( "readdir() has been invoked for path [%s] and non-" "zero offset (%d). This is not allowed." % (path, offset)) # TODO: Once we start working on the cache, make sure we don't make this call, # constantly. path_relations = PathRelations.get_instance() self.__log.debug("Listing files.") try: entry_clause = path_relations.get_clause_from_path(path) except GdNotFoundError: self.__log.exception("Could not process [%s] (readdir).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from path [%s] " "(readdir)." % (path)) raise FuseOSError(EIO) if not entry_clause: self.__log.debug("Path [%s] does not exist for readdir()." % (path)) raise FuseOSError(ENOENT) try: entry_tuples = path_relations.get_children_entries_from_entry_id \ (entry_clause[CLAUSE_ID]) except: self.__log.exception( "Could not render list of filenames under path " "[%s]." % (path)) raise FuseOSError(EIO) yield '.' yield '..' for (filename, entry) in entry_tuples: # Decorate any file that -requires- a mime-type (all files can # merely accept a mime-type) if entry.requires_mimetype: filename += '#' yield filename
def readdir(self, path, offset): """A generator returning one base filename at a time.""" # We expect "offset" to always be (0). if offset != 0: self.__log.warning("readdir() has been invoked for path [%s] and non-" "zero offset (%d). This is not allowed." % (path, offset)) # TODO: Once we start working on the cache, make sure we don't make this call, # constantly. path_relations = PathRelations.get_instance() self.__log.debug("Listing files.") try: entry_clause = path_relations.get_clause_from_path(path) except GdNotFoundError: self.__log.exception("Could not process [%s] (readdir).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from path [%s] " "(readdir)." % (path)) raise FuseOSError(EIO) if not entry_clause: self.__log.debug("Path [%s] does not exist for readdir()." % (path)) raise FuseOSError(ENOENT) try: entry_tuples = path_relations.get_children_entries_from_entry_id \ (entry_clause[CLAUSE_ID]) except: self.__log.exception("Could not render list of filenames under path " "[%s]." % (path)) raise FuseOSError(EIO) yield '.' yield '..' for (filename, entry) in entry_tuples: # Decorate any file that -requires- a mime-type (all files can # merely accept a mime-type) if entry.requires_mimetype: filename += '#' yield filename
def readdir(self, path, offset): """A generator returning one base filename at a time.""" # We expect "offset" to always be (0). if offset != 0: _logger.warning( "readdir() has been invoked for path [%s] and " "non-zero offset (%d). This is not allowed.", path, offset, ) # TODO: Once we start working on the cache, make sure we don't make this call, # constantly. path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(path) except GdNotFoundError: _logger.exception("Could not process [%s] (readdir).") raise FuseOSError(ENOENT) except: _logger.exception("Could not get clause from path [%s] " "(readdir)." % (path)) raise FuseOSError(EIO) if not entry_clause: raise FuseOSError(ENOENT) try: entry_tuples = path_relations.get_children_entries_from_entry_id(entry_clause[CLAUSE_ID]) except: _logger.exception("Could not render list of filenames under path " "[%s].", path) raise FuseOSError(EIO) yield utility.translate_filename_charset(".") yield utility.translate_filename_charset("..") for (filename, entry) in entry_tuples: # Decorate any file that -requires- a mime-type (all files can # merely accept a mime-type) if entry.requires_mimetype: filename += utility.translate_filename_charset("#") yield (filename, self.__build_stat_from_entry(entry), 0)
def __get_entry_or_raise(self, raw_path, allow_normal_for_missing=False): try: result = split_path(raw_path, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: self.__log.exception("Could not retrieve clause for non-existent " "file-path [%s] (parent does not exist)." % (raw_path)) if allow_normal_for_missing is True: raise else: raise FuseOSError(ENOENT) except: self.__log.exception("Could not process file-path [%s]." % (raw_path)) raise FuseOSError(EIO) filepath = build_filepath(path, filename) path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: self.__log.exception("Could not retrieve clause for non-existent " "file-path [%s] (parent exists)." % (filepath)) if allow_normal_for_missing is True: raise else: raise FuseOSError(ENOENT) except: self.__log.exception("Could not retrieve clause for path [%s]. " % (filepath)) raise FuseOSError(EIO) if not entry_clause: self.__log.debug("Path [%s] does not exist for stat()." % (filepath)) if allow_normal_for_missing is True: raise GdNotFoundError() else: raise FuseOSError(ENOENT) return (entry_clause[CLAUSE_ENTRY], path, filename)
def flush(self): """The OS wants to effect any changes made to the file.""" _logger.debug("Flushing opened-file.") entry = self.__cache.get(self.__entry_id) if self.__is_dirty is False: _logger.debug("Flush will be skipped for [%s] because there " "are no changes: [%s] IS_LOADED=[%s] " "IS_DIRTY=[%d]", entry.id, self.file_path, self.__is_loaded, self.__is_dirty) return else: st = os.stat(self.__temp_filepath) _logger.debug("Pushing (%d) bytes for entry with ID from [%s] to " "GD for file-path [%s].", st.st_size, entry.id, self.__temp_filepath) # TODO: Make sure we sync the mtime to remote. gd = get_gdrive() entry = gd.update_entry( entry, filename=entry.title, data_filepath=self.__temp_filepath, mime_type=self.mime_type, parents=entry.parents, is_hidden=self.__is_hidden) self.__is_dirty = False # TODO(dustin): For now, we don't cleanup the temporary file. We need to # schedule this using LRU-semantics. # Immediately update our current cached entry. _logger.debug("Update successful. Updating local cache.") path_relations = PathRelations.get_instance() path_relations.register_entry(entry) _logger.info("Update complete on entry with ID [%s].", entry.id)
def mkdir(self, filepath, mode): """Create the given directory.""" # TODO: Implement the "mode". try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: self.__log.exception("Could not process [%s] (mkdir).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not split path [%s] (mkdir)." % (filepath)) raise FuseOSError(EIO) parent_id = parent_clause[CLAUSE_ID] self.__log.debug("Creating directory [%s] under parent [%s] with ID " "[%s]." % (filename, path, parent_id)) try: entry = drive_proxy('create_directory', filename=filename, parents=[parent_id], is_hidden=is_hidden) except: self.__log.exception("Could not create directory with name [%s] " "and parent with ID [%s]." % (filename, parent_clause[0].id)) raise FuseOSError(EIO) self.__log.info("Directory [%s] created as ID [%s] under parent with " "ID [%s]." % (filepath, entry.id, parent_id)) #parent_clause[4] = False path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: self.__log.exception("Could not register new directory in cache.") raise FuseOSError(EIO)
def flush(self): """The OS wants to effect any changes made to the file.""" _logger.debug("Flushing opened-file.") entry = self.__cache.get(self.__entry_id) if self.__is_dirty is False: _logger.debug( "Flush will be skipped for [%s] because there " "are no changes: [%s] IS_LOADED=[%s] " "IS_DIRTY=[%d]", entry.id, self.file_path, self.__is_loaded, self.__is_dirty) return else: st = os.stat(self.__temp_filepath) _logger.debug( "Pushing (%d) bytes for entry with ID from [%s] to " "GD for file-path [%s].", st.st_size, entry.id, self.__temp_filepath) # TODO: Make sure we sync the mtime to remote. gd = get_gdrive() entry = gd.update_entry(entry, filename=entry.title, data_filepath=self.__temp_filepath, mime_type=self.mime_type, parents=entry.parents, is_hidden=self.__is_hidden) self.__is_dirty = False # TODO(dustin): For now, we don't cleanup the temporary file. We need to # schedule this using LRU-semantics. # Immediately update our current cached entry. _logger.debug("Update successful. Updating local cache.") path_relations = PathRelations.get_instance() path_relations.register_entry(entry) _logger.info("Update complete on entry with ID [%s].", entry.id)
def get_entry_or_raise(raw_path, allow_normal_for_missing=False): try: result = split_path(raw_path, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: _logger.exception("Could not retrieve clause for non-existent " "file-path [%s] (parent does not exist)." % (raw_path)) if allow_normal_for_missing is True: raise else: raise FuseOSError(ENOENT) except: _logger.exception("Could not process file-path [%s]." % (raw_path)) raise FuseOSError(EIO) filepath = build_filepath(path, filename) path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: _logger.exception("Could not retrieve clause for non-existent " "file-path [%s] (parent exists)." % (filepath)) if allow_normal_for_missing is True: raise else: raise FuseOSError(ENOENT) except: _logger.exception("Could not retrieve clause for path [%s]. " % (filepath)) raise FuseOSError(EIO) if not entry_clause: if allow_normal_for_missing is True: raise GdNotFoundError() else: raise FuseOSError(ENOENT) return (entry_clause[CLAUSE_ENTRY], path, filename)
def rename(self, filepath_old, filepath_new): self.__log.debug("Renaming [%s] to [%s]." % (filepath_old, filepath_new)) # Make sure the old filepath exists. (entry, path, filename_old) = self.__get_entry_or_raise(filepath_old) # At this point, decorations, the is-hidden prefix, etc.. haven't been # stripped. (path, filename_new_raw) = split(filepath_new) # Make sure the new filepath doesn't exist. try: self.__get_entry_or_raise(filepath_new, True) except GdNotFoundError: pass try: entry = drive_proxy('rename', normalized_entry=entry, new_filename=filename_new_raw) except: self.__log.exception("Could not update entry [%s] for rename." % (entry)) raise FuseOSError(EIO) # Update our knowledge of the entry. path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: self.__log.exception("Could not register renamed entry: %s" % (entry)) raise FuseOSError(EIO)
def unlink(self, file_path): """Remove a file.""" # TODO: Change to simply move to "trash". Have a FUSE option to elect this # behavior. path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(file_path) except GdNotFoundError: _logger.exception("Could not process [%s] (unlink).", file_path) raise FuseOSError(ENOENT) except: _logger.exception( "Could not get clause from file-path [%s] " "(unlink).", file_path) raise FuseOSError(EIO) if not entry_clause: _logger.error("Path [%s] does not exist for unlink().", file_path) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if a directory. if normalized_entry.is_directory: _logger.error( "Can not unlink() directory [%s] with ID [%s]. " "Must be file.", file_path, entry_id) raise FuseOSError(errno.EISDIR) # Remove online. Complements local removal (if not found locally, a # follow-up request checks online). gd = get_gdrive() try: gd.remove_entry(normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: _logger.exception("Could not remove file [%s] with ID [%s].", file_path, entry_id) raise FuseOSError(EIO) # Remove from cache. Will no longer be able to be found, locally. try: PathRelations.get_instance().remove_entry_all(entry_id) except: _logger.exception( "There was a problem removing entry [%s] " "from the caches.", normalized_entry) raise # Remove from among opened-files. om = gdrivefs.gdfs.opened_file.get_om() try: opened_file = om.remove_by_filepath(file_path) except: _logger.exception( "There was an error while removing all " "opened-file instances for file [%s] " "(remove).", file_path) raise FuseOSError(EIO)
def __create(self, filepath, mode=None): """Create a new file. We don't implement "mode" (permissions) because the model doesn't agree with GD. """ # TODO: Fail if it already exists. self.__log.debug("Splitting file-path [%s] for inner create." % (filepath)) try: result = split_path(filepath, path_resolver) (parent_clause, path, filename, mime_type, is_hidden) = result except GdNotFoundError: self.__log.exception("Could not process [%s] (i-create).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not split path [%s] (i-create)." % (filepath)) raise FuseOSError(EIO) distilled_filepath = build_filepath(path, filename) self.__log.debug("Acquiring file-handle.") # Try to guess at a mime-type, if not otherwise given. if mime_type is None: (mimetype_guess, _) = guess_type(filename, True) if mimetype_guess is not None: mime_type = mimetype_guess else: mime_type = Conf.get('default_mimetype') self.__log.debug("Creating empty file [%s] under parent with ID " "[%s]." % (filename, parent_clause[3])) try: entry = drive_proxy('create_file', filename=filename, data_filepath='/dev/null', parents=[parent_clause[3]], mime_type=mime_type, is_hidden=is_hidden) except: self.__log.exception("Could not create empty file [%s] under " "parent with ID [%s]." % (filename, parent_clause[3])) raise FuseOSError(EIO) self.__log.debug("Registering created file in cache.") path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: self.__log.exception("Could not register created file in cache.") raise FuseOSError(EIO) self.__log.info("Inner-create of [%s] completed." % (distilled_filepath)) return (entry, path, filename, mime_type)
def rmdir(self, filepath): """Remove a directory.""" path_relations = PathRelations.get_instance() self.__log.debug("Removing directory [%s]." % (filepath)) try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: self.__log.exception("Could not process [%s] (rmdir).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from file-path [%s] " "(rmdir)." % (filepath)) raise FuseOSError(EIO) if not entry_clause: self.__log.error("Path [%s] does not exist for rmdir()." % (filepath)) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if not a directory. self.__log.debug("Ensuring it is a directory.") if not normalized_entry.is_directory: self.__log.error("Can not rmdir() non-directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(ENOTDIR) # Ensure the folder is empty. self.__log.debug("Checking if empty.") try: found = drive_proxy('get_children_under_parent_id', parent_id=entry_id, max_results=1) except: self.__log.exception("Could not determine if directory to be removed " "has children." % (entry_id)) raise FuseOSError(EIO) if found: raise FuseOSError(ENOTEMPTY) self.__log.debug("Doing remove of directory [%s] with ID [%s]." % (filepath, entry_id)) try: drive_proxy('remove_entry', normalized_entry=normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: self.__log.exception("Could not remove directory [%s] with ID [%s]." % (filepath, entry_id)) raise FuseOSError(EIO) # TODO: Remove from cache. self.__log.debug("Directory removal complete.")
def rmdir(self, filepath): """Remove a directory.""" path_relations = PathRelations.get_instance() self.__log.debug("Removing directory [%s]." % (filepath)) try: entry_clause = path_relations.get_clause_from_path(filepath) except GdNotFoundError: self.__log.exception("Could not process [%s] (rmdir).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from file-path [%s] " "(rmdir)." % (filepath)) raise FuseOSError(EIO) if not entry_clause: self.__log.error("Path [%s] does not exist for rmdir()." % (filepath)) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if not a directory. self.__log.debug("Ensuring it is a directory.") if not normalized_entry.is_directory: self.__log.error( "Can not rmdir() non-directory [%s] with ID [%s].", filepath, entry_id) raise FuseOSError(ENOTDIR) # Ensure the folder is empty. self.__log.debug("Checking if empty.") try: found = drive_proxy('get_children_under_parent_id', parent_id=entry_id, max_results=1) except: self.__log.exception( "Could not determine if directory to be removed " "has children." % (entry_id)) raise FuseOSError(EIO) if found: raise FuseOSError(ENOTEMPTY) self.__log.debug("Doing remove of directory [%s] with ID [%s]." % (filepath, entry_id)) try: drive_proxy('remove_entry', normalized_entry=normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: self.__log.exception( "Could not remove directory [%s] with ID [%s]." % (filepath, entry_id)) raise FuseOSError(EIO) # TODO: Remove from cache. self.__log.debug("Directory removal complete.")
def create_for_existing_filepath(filepath): """Process the file/path that was requested (potential export-type directive, dot-prefix, etc..), and build an opened-file object using the information. """ _logger.debug("Creating OpenedFile for [%s].", filepath) # Process/distill the requested file-path. try: result = split_path(filepath, path_resolver) except GdNotFoundError: _logger.exception("Could not process [%s] (create_for_requested).", filepath) raise fuse.FuseOSError(ENOENT) (parent_clause, path, filename, mime_type, is_hidden) = result distilled_filepath = build_filepath(path, filename) # Look-up the requested entry. path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path( distilled_filepath) except: _logger.exception("Could not try to get clause from path [%s] " "(OpenedFile).", distilled_filepath) raise fuse.FuseOSError(EIO) if not entry_clause: _logger.debug("Path [%s] does not exist for stat().", path) raise fuse.FuseOSError(ENOENT) entry = entry_clause[CLAUSE_ENTRY] # Normalize the mime-type by considering what's available for download. # We're going to let the requests that didn't provide a mime-type fail # right here. It will give us the opportunity to try a few options to # get the file. try: final_mimetype = entry.normalize_download_mimetype(mime_type) except ExportFormatError: _logger.exception("There was an export-format error " "(create_for_requested_filesystem).") raise fuse.FuseOSError(ENOENT) except: _logger.exception("Could not normalize mime-type [%s] for entry" "[%s].", mime_type, entry) raise fuse.FuseOSError(EIO) if final_mimetype != mime_type: _logger.info("Entry being opened will be opened as [%s] rather " "than [%s].", final_mimetype, mime_type) # Build the object. return OpenedFile( entry_clause[CLAUSE_ID], path, filename, is_hidden, final_mimetype)
def unlink(self, file_path): """Remove a file.""" # TODO: Change to simply move to "trash". Have a FUSE option to elect this # behavior. path_relations = PathRelations.get_instance() self.__log.debug("Removing file [%s]." % (file_path)) try: entry_clause = path_relations.get_clause_from_path(file_path) except GdNotFoundError: self.__log.exception("Could not process [%s] (unlink).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from file-path [%s] " "(unlink)." % (file_path)) raise FuseOSError(EIO) if not entry_clause: self.__log.error("Path [%s] does not exist for unlink()." % (file_path)) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if a directory. self.__log.debug("Ensuring it is a file (not a directory).") if normalized_entry.is_directory: self.__log.error( "Can not unlink() directory [%s] with ID [%s]. " "Must be file.", file_path, entry_id) raise FuseOSError(errno.EISDIR) self.__log.debug("Doing remove of directory [%s] with ID [%s]." % (file_path, entry_id)) # Remove online. Complements local removal (if not found locally, a # follow-up request checks online). try: drive_proxy('remove_entry', normalized_entry=normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: self.__log.exception("Could not remove file [%s] with ID [%s]." % (file_path, entry_id)) raise FuseOSError(EIO) # Remove from cache. Will no longer be able to be found, locally. self.__log.debug("Removing all trace of entry [%s] from cache " "(unlink)." % (normalized_entry)) try: PathRelations.get_instance().remove_entry_all(entry_id) except: self.__log.exception("There was a problem removing entry [%s] " "from the caches." % (normalized_entry)) raise # Remove from among opened-files. self.__log.debug("Removing all opened-files for [%s]." % (file_path)) try: opened_file = OpenedManager.get_instance().\ remove_by_filepath(file_path) except: self.__log.exception("There was an error while removing all " "opened-file instances for file [%s] " "(remove)." % (file_path)) raise FuseOSError(EIO) self.__log.debug("File removal complete.")
def flush(self): """The OS wants to effect any changes made to the file.""" self.__log.debug("Retrieving entry for write-flush.") entry = self.__get_entry_or_raise() cache_fault = self.__load_base_from_remote() with self.__class__.__update_lock: if self.__is_dirty is False: self.__log.debug("Flush will be skipped because there are no " "changes.") # TODO: Raise an exception? return # Write back out to the temporary file. self.__log.debug("Writing buffer to temporary file.") # TODO: Make sure to uncache the temp data if self.temp_file_path is not None. mime_type = self.mime_type # If we've already opened a work file, use it. Else, use a # temporary file that we'll close at the end of the method. if self.__is_loaded: is_temp = False temp_file_path = get_temp_filepath(entry, mime_type) with file(temp_file_path, 'w') as f: for block in self.__buffer.read(): f.write(block) write_filepath = temp_file_path else: is_temp = True with NamedTemporaryFile(delete=False) as f: write_filepath = f.name for block in self.__buffer.read(): f.write(block) # Push to GD. self.__log.debug("Pushing (%d) bytes for entry with ID from [%s] " "to GD for file-path [%s]." % (self.__buffer.length, entry.id, write_filepath)) # print("Sending updates.") # TODO: Update mtime? try: entry = drive_proxy('update_entry', normalized_entry=entry, filename=entry.title, data_filepath=write_filepath, mime_type=mime_type, parents=entry.parents, is_hidden=self.__is_hidden) except: self.__log.exception("Could not localize displaced file with " "entry having ID [%s]." % (entry.id)) raise if not is_temp: unlink(write_filepath) else: # Update the write-cache file to the official mtime. We won't # redownload it on the next flush if it wasn't changed, # elsewhere. self.__log.debug("Updating local write-cache file to official " "mtime [%s]." % (entry.modified_date_epoch)) try: utime( write_filepath, (entry.modified_date_epoch, entry.modified_date_epoch)) except: self.__log.exception("Could not update mtime of write-" "cache [%s] for entry with ID [%s], " "post-flush." % (entry.modified_date_epoch, entry.id)) raise # Immediately update our current cached entry. self.__log.debug("Update successful. Updating local cache.") path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: self.__log.exception("Could not register updated file in cache.") raise self.__is_dirty = False self.__log.info("Update complete on entry with ID [%s]." % (entry.id))
def unlink(self, file_path): """Remove a file.""" # TODO: Change to simply move to "trash". Have a FUSE option to elect this # behavior. path_relations = PathRelations.get_instance() self.__log.debug("Removing file [%s]." % (file_path)) try: entry_clause = path_relations.get_clause_from_path(file_path) except GdNotFoundError: self.__log.exception("Could not process [%s] (unlink).") raise FuseOSError(ENOENT) except: self.__log.exception("Could not get clause from file-path [%s] " "(unlink)." % (file_path)) raise FuseOSError(EIO) if not entry_clause: self.__log.error("Path [%s] does not exist for unlink()." % (file_path)) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if a directory. self.__log.debug("Ensuring it is a file (not a directory).") if normalized_entry.is_directory: self.__log.error("Can not unlink() directory [%s] with ID [%s]. " "Must be file.", file_path, entry_id) raise FuseOSError(errno.EISDIR) self.__log.debug("Doing remove of directory [%s] with ID [%s]." % (file_path, entry_id)) # Remove online. Complements local removal (if not found locally, a # follow-up request checks online). try: drive_proxy('remove_entry', normalized_entry=normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: self.__log.exception("Could not remove file [%s] with ID [%s]." % (file_path, entry_id)) raise FuseOSError(EIO) # Remove from cache. Will no longer be able to be found, locally. self.__log.debug("Removing all trace of entry [%s] from cache " "(unlink)." % (normalized_entry)) try: PathRelations.get_instance().remove_entry_all(entry_id) except: self.__log.exception("There was a problem removing entry [%s] " "from the caches." % (normalized_entry)) raise # Remove from among opened-files. self.__log.debug("Removing all opened-files for [%s]." % (file_path)) try: opened_file = OpenedManager.get_instance().\ remove_by_filepath(file_path) except: self.__log.exception("There was an error while removing all " "opened-file instances for file [%s] " "(remove)." % (file_path)) raise FuseOSError(EIO) self.__log.debug("File removal complete.")
def create_for_existing_filepath(filepath): """Process the file/path that was requested (potential export-type directive, dot-prefix, etc..), and build an opened-file object using the information. """ _logger.debug("Creating OpenedFile for [%s].", filepath) # Process/distill the requested file-path. try: result = split_path(filepath, path_resolver) except GdNotFoundError: _logger.exception("Could not process [%s] (create_for_requested).", filepath) raise fuse.FuseOSError(ENOENT) (parent_clause, path, filename, mime_type, is_hidden) = result distilled_filepath = build_filepath(path, filename) # Look-up the requested entry. path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(distilled_filepath) except: _logger.exception( "Could not try to get clause from path [%s] " "(OpenedFile).", distilled_filepath) raise fuse.FuseOSError(EIO) if not entry_clause: _logger.debug("Path [%s] does not exist for stat().", path) raise fuse.FuseOSError(ENOENT) entry = entry_clause[CLAUSE_ENTRY] # Normalize the mime-type by considering what's available for download. # We're going to let the requests that didn't provide a mime-type fail # right here. It will give us the opportunity to try a few options to # get the file. try: final_mimetype = entry.normalize_download_mimetype(mime_type) except ExportFormatError: _logger.exception("There was an export-format error " "(create_for_requested_filesystem).") raise fuse.FuseOSError(ENOENT) except: _logger.exception( "Could not normalize mime-type [%s] for entry" "[%s].", mime_type, entry) raise fuse.FuseOSError(EIO) if final_mimetype != mime_type: _logger.info( "Entry being opened will be opened as [%s] rather " "than [%s].", final_mimetype, mime_type) # Build the object. return OpenedFile(entry_clause[CLAUSE_ID], path, filename, is_hidden, final_mimetype)
def flush(self): """The OS wants to effect any changes made to the file.""" self.__log.debug("Retrieving entry for write-flush.") entry = self.__get_entry_or_raise() cache_fault = self.__load_base_from_remote() with self.__class__.__update_lock: if self.__is_dirty is False: self.__log.debug("Flush will be skipped because there are no " "changes.") # TODO: Raise an exception? return # Write back out to the temporary file. self.__log.debug("Writing buffer to temporary file.") # TODO: Make sure to uncache the temp data if self.temp_file_path is not None. mime_type = self.mime_type # If we've already opened a work file, use it. Else, use a # temporary file that we'll close at the end of the method. if self.__is_loaded: is_temp = False temp_file_path = get_temp_filepath(entry, mime_type) with file(temp_file_path, 'w') as f: for block in self.__buffer.read(): f.write(block) write_filepath = temp_file_path else: is_temp = True with NamedTemporaryFile(delete=False) as f: write_filepath = f.name for block in self.__buffer.read(): f.write(block) # Push to GD. self.__log.debug("Pushing (%d) bytes for entry with ID from [%s] " "to GD for file-path [%s]." % (self.__buffer.length, entry.id, write_filepath)) # print("Sending updates.") # TODO: Update mtime? try: entry = drive_proxy('update_entry', normalized_entry=entry, filename=entry.title, data_filepath=write_filepath, mime_type=mime_type, parents=entry.parents, is_hidden=self.__is_hidden) except: self.__log.exception("Could not localize displaced file with " "entry having ID [%s]." % (entry.id)) raise if not is_temp: unlink(write_filepath) else: # Update the write-cache file to the official mtime. We won't # redownload it on the next flush if it wasn't changed, # elsewhere. self.__log.debug("Updating local write-cache file to official " "mtime [%s]." % (entry.modified_date_epoch)) try: utime(write_filepath, (entry.modified_date_epoch, entry.modified_date_epoch)) except: self.__log.exception("Could not update mtime of write-" "cache [%s] for entry with ID [%s], " "post-flush." % (entry.modified_date_epoch, entry.id)) raise # Immediately update our current cached entry. self.__log.debug("Update successful. Updating local cache.") path_relations = PathRelations.get_instance() try: path_relations.register_entry(entry) except: self.__log.exception("Could not register updated file in cache.") raise self.__is_dirty = False self.__log.info("Update complete on entry with ID [%s]." % (entry.id))
def unlink(self, file_path): """Remove a file.""" # TODO: Change to simply move to "trash". Have a FUSE option to elect this # behavior. path_relations = PathRelations.get_instance() try: entry_clause = path_relations.get_clause_from_path(file_path) except GdNotFoundError: _logger.exception("Could not process [%s] (unlink).", file_path) raise FuseOSError(ENOENT) except: _logger.exception("Could not get clause from file-path [%s] " "(unlink).", file_path) raise FuseOSError(EIO) if not entry_clause: _logger.error("Path [%s] does not exist for unlink().", file_path) raise FuseOSError(ENOENT) entry_id = entry_clause[CLAUSE_ID] normalized_entry = entry_clause[CLAUSE_ENTRY] # Check if a directory. if normalized_entry.is_directory: _logger.error("Can not unlink() directory [%s] with ID [%s]. " "Must be file.", file_path, entry_id) raise FuseOSError(errno.EISDIR) # Remove online. Complements local removal (if not found locally, a # follow-up request checks online). gd = get_gdrive() try: gd.remove_entry(normalized_entry) except (NameError): raise FuseOSError(ENOENT) except: _logger.exception("Could not remove file [%s] with ID [%s].", file_path, entry_id) raise FuseOSError(EIO) # Remove from cache. Will no longer be able to be found, locally. try: PathRelations.get_instance().remove_entry_all(entry_id) except: _logger.exception("There was a problem removing entry [%s] " "from the caches.", normalized_entry) raise # Remove from among opened-files. om = gdrivefs.gdfs.opened_file.get_om() try: opened_file = om.remove_by_filepath(file_path) except: _logger.exception("There was an error while removing all " "opened-file instances for file [%s] " "(remove).", file_path) raise FuseOSError(EIO)