1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
}
|