From 2bcc4551e9ad7c3827a52af7edd1888775ab5acc Mon Sep 17 00:00:00 2001 From: Jochen Henneberg Date: Sat, 2 Nov 2019 09:20:17 +0100 Subject: [PATCH] Added support for get/sendPartialObject If the caps flags are enabled for both get and send of partial object this method is used instead of file push and pull. This makes many file operations much faster and is widely supported throughout mobile devices. We still use the tmp file, not for storing data but to keep a unique file handler for all the fuse file operations. No caching happens at the moment, we could cache in the tmp file and keep a table of offsets and sizes of the file that we already cached, however, using the kernel file system cache is much better. --- src/simple-mtpfs-fuse.cpp | 54 ++++++++++++++++++++++-------- src/simple-mtpfs-fuse.h | 1 + src/simple-mtpfs-mtp-device.cpp | 58 +++++++++++++++++++++++++++++++++ src/simple-mtpfs-mtp-device.h | 2 ++ 4 files changed, 102 insertions(+), 13 deletions(-) diff --git a/src/simple-mtpfs-fuse.cpp b/src/simple-mtpfs-fuse.cpp index 588968d..796c492 100644 --- a/src/simple-mtpfs-fuse.cpp +++ b/src/simple-mtpfs-fuse.cpp @@ -596,11 +596,19 @@ int SMTPFileSystem::open(const char *path, struct fuse_file_info *file_info) } else { tmp_path = m_tmp_files_pool.makeTmpPath(std_path); - int rval = m_device.filePull(std_path, tmp_path); - if (rval != 0) - return -rval; + // only copy the file if needed + if (!hasPartialObjectSupport()) { + int rval = m_device.filePull(std_path, tmp_path); + if (rval != 0) + return -rval; + } else { + int fd = ::creat(tmp_path.c_str(), S_IRUSR | S_IWUSR); + ::close(fd); + } } + // we create the tmp file even if we can use partial get/send to + // have a valid file descriptor int fd = ::open(tmp_path.c_str(), file_info->flags); if (fd < 0) { ::unlink(tmp_path.c_str()); @@ -620,24 +628,38 @@ int SMTPFileSystem::open(const char *path, struct fuse_file_info *file_info) int SMTPFileSystem::read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *file_info) { - int rval = ::pread(file_info->fh, buf, size, offset); - if (rval < 0) - return -errno; + int rval = 0; + if (hasPartialObjectSupport()) { + const std::string std_path(path); + rval = m_device.fileRead(std_path, buf, size, offset); + } else { + rval = ::pread(file_info->fh, buf, size, offset); + if (rval < 0) + return -errno; + } + return rval; } int SMTPFileSystem::write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *file_info) { - const TypeTmpFile *tmp_file = m_tmp_files_pool.getFile(std::string(path)); - if (!tmp_file) - return -EINVAL; + int rval = 0; + if (hasPartialObjectSupport()) { + const std::string std_path(path); + rval = m_device.fileWrite(std_path, buf, size, offset); + } else { + const TypeTmpFile *tmp_file = m_tmp_files_pool.getFile(std::string(path)); + if (!tmp_file) + return -EINVAL; - int rval = ::pwrite(file_info->fh, buf, size, offset); - if (rval < 0) - return -errno; + rval = ::pwrite(file_info->fh, buf, size, offset); + if (rval < 0) + return -errno; + + const_cast(tmp_file)->setModified(); + } - const_cast(tmp_file)->setModified(); return rval; } @@ -764,3 +786,9 @@ int SMTPFileSystem::ftruncate(const char *path, off_t offset, const_cast(tmp_file)->setModified(); return 0; } + +bool SMTPFileSystem::hasPartialObjectSupport() +{ + MTPDevice::Capabilities caps = m_device.getCapabilities(); + return (caps.canGetPartialObject() && caps.canSendPartialObject()); +} diff --git a/src/simple-mtpfs-fuse.h b/src/simple-mtpfs-fuse.h index 7c6d8e4..b10683b 100644 --- a/src/simple-mtpfs-fuse.h +++ b/src/simple-mtpfs-fuse.h @@ -104,6 +104,7 @@ class SMTPFileSystem int create(const char *path, mode_t mode, fuse_file_info *file_info); private: + bool hasPartialObjectSupport(); static std::unique_ptr s_instance; struct fuse_args m_args; diff --git a/src/simple-mtpfs-mtp-device.cpp b/src/simple-mtpfs-mtp-device.cpp index f5ab838..58e120a 100644 --- a/src/simple-mtpfs-mtp-device.cpp +++ b/src/simple-mtpfs-mtp-device.cpp @@ -467,6 +467,64 @@ int MTPDevice::rename(const std::string &oldpath, const std::string &newpath) #endif } +int MTPDevice::fileRead(const std::string &path, char *buf, size_t size, + off_t offset) +{ + const std::string path_basename(smtpfs_basename(path)); + const std::string path_dirname(smtpfs_dirname(path)); + const TypeDir *dir_parent = dirFetchContent(path_dirname); + const TypeFile *file_to_fetch = dir_parent ? + dir_parent->file(path_basename) : nullptr; + if (!dir_parent) { + logerr("Can not fetch '", path, "'.\n"); + return -EINVAL; + } + if (!file_to_fetch) { + logerr("No such file '", path, "'.\n"); + return -ENOENT; + } + + // all systems clear + unsigned char *tmp_buf; + unsigned int tmp_size; + int rval = LIBMTP_GetPartialObject(m_device, file_to_fetch->id(), + offset, size, &tmp_buf, &tmp_size); + if (tmp_size > 0) { + memcpy(buf, tmp_buf, tmp_size); + free(tmp_buf); + } + + if (rval != 0) + return -EIO; + return tmp_size; +} + +int MTPDevice::fileWrite(const std::string &path, const char *buf, size_t size, + off_t offset) +{ + const std::string path_basename(smtpfs_basename(path)); + const std::string path_dirname(smtpfs_dirname(path)); + const TypeDir *dir_parent = dirFetchContent(path_dirname); + const TypeFile *file_to_fetch = dir_parent ? + dir_parent->file(path_basename) : nullptr; + if (!dir_parent) { + logerr("Can not fetch '", path, "'.\n"); + return -EINVAL; + } + if (!file_to_fetch) { + logerr("No such file '", path, "'.\n"); + return -ENOENT; + } + + // all systems clear + int rval = LIBMTP_SendPartialObject(m_device, file_to_fetch->id(), + offset, (unsigned char *) buf, size); + + if (rval < 0) + return -EIO; + return size; +} + int MTPDevice::filePull(const std::string &src, const std::string &dst) { const std::string src_basename(smtpfs_basename(src)); diff --git a/src/simple-mtpfs-mtp-device.h b/src/simple-mtpfs-mtp-device.h index ffab1b8..087fce2 100644 --- a/src/simple-mtpfs-mtp-device.h +++ b/src/simple-mtpfs-mtp-device.h @@ -78,6 +78,8 @@ class MTPDevice int rename(const std::string &oldpath, const std::string &newpath); + int fileRead(const std::string &path, char *buf, size_t size, off_t offset); + int fileWrite(const std::string &path, const char *buf, size_t size, off_t offset); int filePull(const std::string &src, const std::string &dst); int filePush(const std::string &src, const std::string &dst); int fileRemove(const std::string &path);