Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * fdio.c - safe file descriptor based input/output | |
4 | * | |
5 | * Copyright © 2009-2010 Guillem Jover <guillem@debian.org> | |
6 | * | |
7 | * This is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <config.h> | |
22 | #include <compat.h> | |
23 | ||
29279ac2 | 24 | #include <assert.h> |
1479465f | 25 | #include <errno.h> |
29279ac2 | 26 | #include <stdio.h> |
1479465f GJ |
27 | #include <fcntl.h> |
28 | #include <unistd.h> | |
29 | ||
30 | #include <dpkg/fdio.h> | |
31 | ||
29279ac2 MW |
32 | #define MAXSHADOW 16 |
33 | static struct shadow { | |
34 | int fd, shadow; | |
35 | dev_t dev; ino_t ino; | |
36 | } shadowtab[MAXSHADOW]; | |
37 | static size_t nshadow; | |
38 | ||
39 | static struct shadow *find_shadow(int fd) | |
40 | { | |
41 | size_t i, j; | |
42 | int rc; | |
43 | struct stat st; | |
44 | ||
45 | rc = fstat(fd, &st); assert(!rc); | |
46 | ||
47 | for (i = j = 0; i < nshadow; i++) { | |
48 | if (shadowtab[i].fd == fd) { | |
49 | if (st.st_dev != shadowtab[i].dev || st.st_ino != shadowtab[i].ino) | |
50 | { close(shadowtab[i].shadow); continue; } | |
51 | return (&shadowtab[i]); | |
52 | } | |
53 | if (i != j) shadowtab[j] = shadowtab[i]; | |
54 | j++; | |
55 | } | |
56 | nshadow = j; | |
57 | return (0); | |
58 | } | |
59 | ||
60 | int shadowed_fd_p(int fd) { return (!!find_shadow(fd)); } | |
61 | ||
62 | static void write_to_shadow(int fd, const void *p, size_t sz) | |
63 | { | |
64 | const struct shadow *shadow = find_shadow(fd); | |
65 | ssize_t n; | |
66 | ||
67 | if (shadow) { n = write(shadow->shadow, p, sz); assert((size_t)n == sz); } | |
68 | } | |
69 | ||
70 | void save_copy(const char *path, const char *label, const char *ext) | |
71 | { | |
72 | char buf[256]; | |
73 | int err = errno; | |
74 | int i = 0; | |
75 | ||
76 | for (;;) { | |
77 | sprintf(buf, "/tmp/mdw/%s#%d.%s", label, i, ext); | |
78 | if (!link(path, buf)) break; | |
79 | assert(errno == EEXIST); | |
80 | i++; | |
81 | } | |
82 | fprintf(stderr, ";; save `%s' as `%s'\n", path, buf); | |
83 | errno = err; | |
84 | } | |
85 | ||
86 | void save_shadow(const char *label, const char *ext) | |
87 | { | |
88 | char buf[256]; | |
89 | ||
90 | sprintf(buf, "/tmp/mdw/t.shadow-%s", label); | |
91 | save_copy(buf, label, ext); | |
92 | } | |
93 | ||
94 | void open_shadow(int fd, const char *label) | |
95 | { | |
96 | struct shadow *shadow; | |
97 | struct stat st; | |
98 | int rc; | |
99 | char buf[256]; | |
100 | ||
101 | assert(!find_shadow(fd)); | |
102 | assert(nshadow < MAXSHADOW); shadow = &shadowtab[nshadow++]; | |
103 | sprintf(buf, "/tmp/mdw/t.shadow-%s", label); | |
104 | unlink(buf); | |
105 | shadow->fd = fd; | |
106 | shadow->shadow = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0666); | |
107 | assert(shadow->shadow >= 0); | |
108 | rc = fstat(fd, &st); assert(!rc); | |
109 | shadow->dev = st.st_dev; | |
110 | shadow->ino = st.st_ino; | |
111 | } | |
112 | ||
113 | void close_shadow(int fd) | |
114 | { | |
115 | int rc; | |
116 | struct shadow *shadow; | |
117 | ||
118 | shadow = find_shadow(fd); if (!shadow) return; | |
119 | rc = close(shadow->shadow); assert(!rc); | |
120 | assert(nshadow); nshadow--; | |
121 | if (shadow != &shadowtab[nshadow]) *shadow = shadowtab[nshadow]; | |
122 | } | |
123 | ||
1479465f GJ |
124 | ssize_t |
125 | fd_read(int fd, void *buf, size_t len) | |
126 | { | |
127 | ssize_t total = 0; | |
128 | char *ptr = buf; | |
129 | ||
130 | while (len > 0) { | |
131 | ssize_t n; | |
132 | ||
133 | n = read(fd, ptr + total, len); | |
134 | if (n == -1) { | |
135 | if (errno == EINTR || errno == EAGAIN) | |
136 | continue; | |
137 | return total ? -total : n; | |
138 | } | |
139 | if (n == 0) | |
140 | break; | |
29279ac2 | 141 | write_to_shadow(fd, ptr + total, n); |
1479465f GJ |
142 | |
143 | total += n; | |
144 | len -= n; | |
145 | } | |
146 | ||
147 | return total; | |
148 | } | |
149 | ||
150 | ssize_t | |
151 | fd_write(int fd, const void *buf, size_t len) | |
152 | { | |
153 | ssize_t total = 0; | |
154 | const char *ptr = buf; | |
155 | ||
156 | while (len > 0) { | |
157 | ssize_t n; | |
158 | ||
159 | n = write(fd, ptr + total, len); | |
160 | if (n == -1) { | |
161 | if (errno == EINTR || errno == EAGAIN) | |
162 | continue; | |
163 | return total ? -total : n; | |
164 | } | |
165 | if (n == 0) | |
166 | break; | |
29279ac2 | 167 | write_to_shadow(fd, buf, n); |
1479465f GJ |
168 | |
169 | total += n; | |
170 | len -= n; | |
171 | } | |
172 | ||
173 | return total; | |
174 | } | |
175 | ||
176 | #ifdef USE_DISK_PREALLOCATE | |
177 | #ifdef HAVE_F_PREALLOCATE | |
178 | static void | |
179 | fd_preallocate_setup(fstore_t *fs, int flags, off_t offset, off_t len) | |
180 | { | |
181 | fs->fst_flags = flags; | |
182 | fs->fst_posmode = F_PEOFPOSMODE; | |
183 | fs->fst_offset = offset; | |
184 | fs->fst_length = len; | |
185 | fs->fst_bytesalloc = 0; | |
186 | } | |
187 | #endif | |
188 | ||
189 | /** | |
190 | * Request the kernel to allocate the specified size for a file descriptor. | |
191 | * | |
192 | * We only want to send a hint that we will be using the requested size. But | |
193 | * we do not want to unnecessarily write the file contents. That is why we | |
194 | * are not using posix_fallocate(3) directly if possible, and not at all | |
195 | * on glibc based systems (except on GNU/kFreeBSD). | |
196 | */ | |
197 | int | |
198 | fd_allocate_size(int fd, off_t offset, off_t len) | |
199 | { | |
200 | int rc; | |
201 | ||
202 | /* Do not preallocate on very small files as that degrades performance | |
203 | * on some filesystems. */ | |
204 | if (len < (4 * 4096) - 1) | |
205 | return 0; | |
206 | ||
207 | #if defined(HAVE_F_PREALLOCATE) | |
208 | /* On Mac OS X. */ | |
209 | fstore_t fs; | |
210 | ||
211 | fd_preallocate_setup(&fs, F_ALLOCATECONTIG, offset, len); | |
212 | rc = fcntl(fd, F_PREALLOCATE, &fs); | |
213 | if (rc < 0 && errno == ENOSPC) { | |
214 | /* If we cannot get a contiguous allocation, then try | |
215 | * non-contiguous. */ | |
216 | fd_preallocate_setup(&fs, F_ALLOCATEALL, offset, len); | |
217 | rc = fcntl(fd, F_PREALLOCATE, &fs); | |
218 | } | |
219 | #elif defined(HAVE_F_ALLOCSP64) | |
220 | /* On Solaris. */ | |
221 | struct flock64 fl; | |
222 | ||
223 | fl.l_whence = SEEK_SET; | |
224 | fl.l_start = offset; | |
225 | fl.l_len = len; | |
226 | ||
227 | rc = fcntl(fd, F_ALLOCSP64, &fl); | |
228 | #elif defined(HAVE_FALLOCATE) | |
229 | /* On Linux. */ | |
230 | do { | |
231 | rc = fallocate(fd, 0, offset, len); | |
232 | } while (rc < 0 && errno == EINTR); | |
233 | #elif defined(HAVE_POSIX_FALLOCATE) && \ | |
234 | ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || \ | |
235 | !defined(__GLIBC__)) | |
236 | /* | |
237 | * On BSDs, newer GNU/kFreeBSD and other non-glibc based systems | |
238 | * we can use posix_fallocate(2) which should be a simple syscall | |
239 | * wrapper. But not on other glibc systems, as there the function | |
240 | * will try to allocate the size by writing a '\0' to each block | |
241 | * if the syscall is not implemented or not supported by the | |
242 | * kernel or the filesystem, which we do not want. | |
243 | */ | |
244 | rc = posix_fallocate(fd, offset, len); | |
245 | #else | |
246 | errno = ENOSYS; | |
247 | rc = -1; | |
248 | #endif | |
249 | ||
250 | return rc; | |
251 | } | |
252 | #else | |
253 | int | |
254 | fd_allocate_size(int fd, off_t offset, off_t len) | |
255 | { | |
256 | errno = ENOSYS; | |
257 | return -1; | |
258 | } | |
259 | #endif |