diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:36:47 +0000 |
commit | 0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch) | |
tree | 3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib/file-set-size.c | |
parent | Initial commit. (diff) | |
download | dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip |
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/file-set-size.c')
-rw-r--r-- | src/lib/file-set-size.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/lib/file-set-size.c b/src/lib/file-set-size.c new file mode 100644 index 0000000..1f5938f --- /dev/null +++ b/src/lib/file-set-size.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_POSIX_FALLOCATE +# define _XOPEN_SOURCE 600 /* Required by glibc, breaks Solaris 9 */ +#endif +#define _GNU_SOURCE /* for fallocate() */ +#include "lib.h" +#include "file-set-size.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#if defined(HAVE_LINUX_FALLOC_H) && !defined(FALLOC_FL_KEEP_SIZE) +/* Legacy Linux does not have the FALLOC_FL_* flags under fcntl.h */ +# include <linux/falloc.h> +#endif + +int file_set_size(int fd, off_t size) +{ +#ifdef HAVE_POSIX_FALLOCATE + static bool posix_fallocate_supported = TRUE; +#endif + char block[IO_BLOCK_SIZE]; + off_t offset; + ssize_t ret; + struct stat st; + + i_assert(size >= 0); + + if (fstat(fd, &st) < 0) { + i_error("fstat() failed: %m"); + return -1; + } + + if (size < st.st_size) { + if (ftruncate(fd, size) < 0) { + i_error("ftruncate() failed: %m"); + return -1; + } + return 0; + } + if (size == st.st_size) + return 0; + +#ifdef HAVE_POSIX_FALLOCATE + if (posix_fallocate_supported) { + int err; + + err = posix_fallocate(fd, st.st_size, size - st.st_size); + if (err == 0) + return 0; + + if (err != EINVAL /* Solaris */ && + err != EOPNOTSUPP /* AOX */) { + if (!ENOSPACE(err)) + i_error("posix_fallocate() failed: %m"); + return -1; + } + /* Not supported by kernel, fallback to writing. */ + posix_fallocate_supported = FALSE; + } +#endif + /* start growing the file */ + offset = st.st_size; + memset(block, 0, I_MIN((ssize_t)sizeof(block), size - offset)); + + while (offset < size) { + ret = pwrite(fd, block, + I_MIN((ssize_t)sizeof(block), size - offset), + offset); + if (ret < 0) { + if (!ENOSPACE(errno)) + i_error("pwrite() failed: %m"); + return -1; + } + offset += ret; + } + return 0; +} + +int file_preallocate(int fd ATTR_UNUSED, off_t size ATTR_UNUSED) +{ +#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_KEEP_SIZE) + /* Linux */ + if (fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, size) < 0) + return errno == ENOSYS ? 0 : -1; + return 1; +#elif defined (F_PREALLOCATE) + /* OSX */ + fstore_t fs; + + i_zero(&fs); + fs.fst_flags = F_ALLOCATECONTIG; + fs.fst_posmode = F_PEOFPOSMODE; + fs.fst_offset = 0; + fs.fst_length = size; + fs.fst_bytesalloc = 0; + if (fcntl(fd, F_PREALLOCATE, &fs) < 0) + return -1; + return fs.fst_bytesalloc > 0 ? 1 : 0; +#else + return 0; +#endif +} |