diff options
author | Guillem Jover <guillem@debian.org> | 2014-07-15 21:00:52 +0200 |
---|---|---|
committer | Guillem Jover <guillem@debian.org> | 2014-08-09 22:48:11 +0200 |
commit | 87b0b20b86407baf1deb4e91b3fd839e01228ac8 (patch) | |
tree | 2b984a34f0e7bcbbbf309d0fb576aea93bf68f73 /lib | |
parent | 101e5beaf233e3df0eef71a88e005d79633b19bf (diff) | |
download | dpkg-87b0b20b86407baf1deb4e91b3fd839e01228ac8.tar.gz |
dpkg: Try to preallocate the disk size for extracted files
This might help in avoiding filesystem fragmentation, and possibly
improve performance on some filesystems.
We use the system specific methods if available, and only use
posix_fallocate() if nothing else is available, because on some systems
its semantics are counter to what we want to obtain here, as the libc
library will fallback to manually writing '\0' to each block to force
the preallocation, instead of just failing and leaving the application
to do so if desired.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dpkg/fdio.c | 75 | ||||
-rw-r--r-- | lib/dpkg/fdio.h | 3 | ||||
-rw-r--r-- | lib/dpkg/libdpkg.map | 1 |
3 files changed, 79 insertions, 0 deletions
diff --git a/lib/dpkg/fdio.c b/lib/dpkg/fdio.c index 9df068061..9ac2128e7 100644 --- a/lib/dpkg/fdio.c +++ b/lib/dpkg/fdio.c @@ -22,6 +22,7 @@ #include <compat.h> #include <errno.h> +#include <fcntl.h> #include <unistd.h> #include <dpkg/fdio.h> @@ -75,3 +76,77 @@ fd_write(int fd, const void *buf, size_t len) return total; } + +#ifdef HAVE_F_PREALLOCATE +static void +fd_preallocate_setup(fstore_t *fs, int flags, off_t offset, off_t len) +{ + fs->fst_flags = flags; + fs->fst_posmode = F_PEOFPOSMODE; + fs->fst_offset = offset; + fs->fst_length = len; + fs->fst_bytesalloc = 0; +} +#endif + +/** + * Request the kernel to allocate the specified size for a file descriptor. + * + * We only want to send a hint that we will be using the requested size. But + * we do not want to unnecessarily write the file contents. That is why we + * are not using posix_fallocate(3) directly if possible, and not at all + * on glibc based systems (except on GNU/kFreeBSD). + */ +int +fd_allocate_size(int fd, off_t offset, off_t len) +{ + int rc; + + if (len == 0) + return 0; + +#if defined(HAVE_F_PREALLOCATE) + /* On Mac OS X. */ + fstore_t fs; + + fs_preallocate_setup(&fs, F_ALLOCATECONTIG, offset, len); + rc = fcntl(fd, F_PREALLOCATE, &fs) + if (rc < 0 && errno == ENOSPC) { + /* If we cannot get a contiguous allocation, then try + * non-contiguous. */ + fs_preallocate_setup(&fs, F_ALLOCATEALL, offset, len); + rc = fcntl(fd, F_PREALLOCATE, &fs) + } +#elif defined(HAVE_F_ALLOCSP64) + /* On Solaris. */ + struct flock64 fl; + + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = len; + + rc = fcntl(fd, F_ALLOCSP64, &fl); +#elif defined(HAVE_FALLOCATE) + /* On Linux. */ + do { + rc = fallocate(fd, 0, offset, len); + } while (rc < 0 && errno == EINTR); +#elif defined(HAVE_POSIX_FALLOCATE) && \ + ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || \ + !defined(__GLIBC__)) + /* + * On BSDs, newer GNU/kFreeBSD and other non-glibc based systems + * we can use posix_fallocate(2) which should be a simple syscall + * wrapper. But not on other glibc systems, as there the function + * will try to allocate the size by writing a '\0' to each block + * if the syscall is not implemented or not supported by the + * kernel or the filesystem, which we do not want. + */ + rc = posix_fallocate(fd, offset, len); +#else + errno = ENOSYS; + rc = -1; +#endif + + return rc; +} diff --git a/lib/dpkg/fdio.h b/lib/dpkg/fdio.h index 3f26ac065..d714dbdad 100644 --- a/lib/dpkg/fdio.h +++ b/lib/dpkg/fdio.h @@ -36,6 +36,9 @@ DPKG_BEGIN_DECLS ssize_t fd_read(int fd, void *buf, size_t len); ssize_t fd_write(int fd, const void *buf, size_t len); +int +fd_allocate_size(int fd, off_t offset, off_t len); + /** @} */ DPKG_END_DECLS diff --git a/lib/dpkg/libdpkg.map b/lib/dpkg/libdpkg.map index 6368d31ee..fb5e7d9c0 100644 --- a/lib/dpkg/libdpkg.map +++ b/lib/dpkg/libdpkg.map @@ -125,6 +125,7 @@ LIBDPKG_PRIVATE { # Buffer I/O functions fd_read; fd_write; + fd_allocate_size; buffer_filter; buffer_skip_*; buffer_copy_*; |