From 247bd9256db7433b3befa1f045c19856ebb02378 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 6 Mar 2010 20:09:18 +0000 Subject: [PATCH] fixed bug in fallocate usage and avoid trying to allocate and truncate files that don't need it to not update their modification time for no good reason --- src/file.cpp | 59 +++++++++++++++++++++++++++++++++---------------- src/storage.cpp | 5 ++--- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/file.cpp b/src/file.cpp index 4ce277073..e49698847 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -82,9 +82,14 @@ POSSIBILITY OF SUCH DAMAGE. static int my_fallocate(int fd, int mode, loff_t offset, loff_t len) { #ifdef __NR_fallocate + // the man page on fallocate differes between versions of linux. + // it appears that fallocate in fact sets errno and returns -1 + // on failure. return syscall(__NR_fallocate, fd, mode, offset, len); #else - return EOPNOTSUPP; + // pretend that the system call doesn't exist + errno = ENOSYS; + return -1; #endif } @@ -1464,14 +1469,33 @@ namespace libtorrent return false; } #else - if (ftruncate(m_fd, s) < 0) + struct stat st; + if (fstat(m_fd, &st) != 0) { ec.assign(errno, get_posix_category()); return false; } - if ((m_open_mode & sparse) == 0) + + // only truncate the file if it doesn't already + // have the right size. We don't want to update + if (st.st_size != s && ftruncate(m_fd, s) < 0) { - // if we're not in sparse mode, allocate the storage + ec.assign(errno, get_posix_category()); + return false; + } + + // if we're not in sparse mode, allocate the storage + // but only if the number of allocated blocks for the file + // is less than the file size. Otherwise we would just + // update the modification time of the file for no good + // reason. + if ((m_open_mode & sparse) == 0 + && st.st_blocks < (s + st.st_blksize - 1) / st.st_blksize) + { + // How do we know that the file is already allocated? + // if we always try to allocate the space, we'll update + // the modification time without actually changing the file + // but if we don't do anything if the file size is #ifdef F_PREALLOCATE fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0}; if (fcntl(m_fd, F_PREALLOCATE, &f) < 0) @@ -1481,24 +1505,21 @@ namespace libtorrent } #elif defined TORRENT_LINUX int ret = my_fallocate(m_fd, 0, 0, s); - if (ret == 0 && errno != ENOSYS) return true; - if (ret != EOPNOTSUPP) ec.assign(ret, get_posix_category()); - // if fallocate failed, we have to use posix_fallocate - // which can be painfully slow, so only use it if we really - // have to - struct stat st; - if (fstat(m_fd, &st) != 0) + // if we return 0, everything went fine + // the fallocate call succeeded + if (ret == 0) return true; + // otherwise, something went wrong. If the error + // is ENOSYS, just keep going and do it the old-fashioned + // way. If fallocate failed with some other error, it + // probably means the user should know about it, error out + // and report it. + if (errno != ENOSYS) { - ec.assign(errno, get_posix_category()); + ec.assign(ret, get_posix_category()); return false; } - // if the number of allocated blocks already matches - // the size of the file. No need to allocate it - if (st.st_blocks >= (s + st.st_blksize - 1) / st.st_blksize) - { - ec.clear(); - return true; - } + // if fallocate failed, we have to use posix_fallocate + // which can be painfully slow ret = posix_fallocate(m_fd, 0, s); if (ret != 0) { diff --git a/src/storage.cpp b/src/storage.cpp index 29398f473..2785ba4d6 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -545,12 +545,11 @@ namespace libtorrent { ec.clear(); boost::shared_ptr f = open_file(*file_iter, file::read_write, ec); - std::string path = combine_path(m_save_path, file_iter->path); - if (ec) set_error(path, ec); + if (ec) set_error(file_path, ec); else if (f) { f->set_size(file_iter->size, ec); - if (ec) set_error(path, ec); + if (ec) set_error(file_path, ec); } }