source src/filebuf.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
1 | - | /* | ||
2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
3 | - | * | ||
4 | - | * This file is part of libgit2, distributed under the GNU GPL v2 with | ||
5 | - | * a Linking Exception. For full terms see the included COPYING file. | ||
6 | - | */ | ||
7 | - | |||
8 | - | #include "filebuf.h" | ||
9 | - | |||
10 | - | #include "futils.h" | ||
11 | - | |||
12 | - | static const size_t WRITE_BUFFER_SIZE = (4096 * 2); | ||
13 | - | |||
14 | - | enum buferr_t { | ||
15 | - | BUFERR_OK = 0, | ||
16 | - | BUFERR_WRITE, | ||
17 | - | BUFERR_ZLIB, | ||
18 | - | BUFERR_MEM | ||
19 | - | }; | ||
20 | - | |||
21 | - | #define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; } | ||
22 | - | |||
23 | 12583 | 2 | static int verify_last_error(git_filebuf *file) | |
24 | - | { | ||
25 | 12583 | 2 | switch (file->last_error) { | |
26 | - | case BUFERR_WRITE: | ||
27 | ##### | 3 | git_error_set(GIT_ERROR_OS, "failed to write out file"); | |
28 | ##### | 4 | return -1; | |
29 | - | |||
30 | - | case BUFERR_MEM: | ||
31 | ##### | 5 | git_error_set_oom(); | |
32 | ##### | 6 | return -1; | |
33 | - | |||
34 | - | case BUFERR_ZLIB: | ||
35 | ##### | 7 | git_error_set(GIT_ERROR_ZLIB, | |
36 | - | "Buffer error when writing out ZLib data"); | ||
37 | ##### | 8 | return -1; | |
38 | - | |||
39 | - | default: | ||
40 | 12583 | 9 | return 0; | |
41 | - | } | ||
42 | - | } | ||
43 | - | |||
44 | 10356 | 2 | static int lock_file(git_filebuf *file, int flags, mode_t mode) | |
45 | - | { | ||
46 | 10356 | 2,3 | if (git_path_exists(file->path_lock) == true) { | |
47 | 30 | 4 | git_error_clear(); /* actual OS error code just confuses */ | |
48 | 30 | 5 | git_error_set(GIT_ERROR_OS, | |
49 | - | "failed to lock file '%s' for writing", file->path_lock); | ||
50 | 30 | 6 | return GIT_ELOCKED; | |
51 | - | } | ||
52 | - | |||
53 | - | /* create path to the file buffer is required */ | ||
54 | 10326 | 7 | if (flags & GIT_FILEBUF_CREATE_LEADING_DIRS) { | |
55 | - | /* XXX: Should dirmode here be configurable? Or is 0777 always fine? */ | ||
56 | 3495 | 8,9 | file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode); | |
57 | - | } else { | ||
58 | 6832 | 10,11 | file->fd = git_futils_creat_locked(file->path_lock, mode); | |
59 | - | } | ||
60 | - | |||
61 | 10326 | 12 | if (file->fd < 0) | |
62 | 3 | 13 | return file->fd; | |
63 | - | |||
64 | 10323 | 14 | file->fd_is_open = true; | |
65 | - | |||
66 | 10323 | 14-16 | if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) { | |
67 | - | git_file source; | ||
68 | - | char buffer[FILEIO_BUFSIZE]; | ||
69 | - | ssize_t read_bytes; | ||
70 | 151 | 17 | int error = 0; | |
71 | - | |||
72 | 151 | 17 | source = p_open(file->path_original, O_RDONLY); | |
73 | 151 | 18 | if (source < 0) { | |
74 | ##### | 19 | git_error_set(GIT_ERROR_OS, | |
75 | - | "failed to open file '%s' for reading", | ||
76 | - | file->path_original); | ||
77 | ##### | 20,37 | return -1; | |
78 | - | } | ||
79 | - | |||
80 | 230 | 21,27,28 | while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) { | |
81 | 79 | 22,23 | if ((error = p_write(file->fd, buffer, read_bytes)) < 0) | |
82 | ##### | 24 | break; | |
83 | 79 | 25 | if (file->compute_digest) | |
84 | ##### | 26 | git_hash_update(&file->digest, buffer, read_bytes); | |
85 | - | } | ||
86 | - | |||
87 | 151 | 29 | p_close(source); | |
88 | - | |||
89 | 151 | 30 | if (read_bytes < 0) { | |
90 | ##### | 31 | git_error_set(GIT_ERROR_OS, "failed to read file '%s'", file->path_original); | |
91 | ##### | 32 | return -1; | |
92 | 151 | 33 | } else if (error < 0) { | |
93 | ##### | 34 | git_error_set(GIT_ERROR_OS, "failed to write file '%s'", file->path_lock); | |
94 | 151 | 35,36 | return -1; | |
95 | - | } | ||
96 | - | } | ||
97 | - | |||
98 | 10323 | 38 | return 0; | |
99 | - | } | ||
100 | - | |||
101 | 21314 | 2 | void git_filebuf_cleanup(git_filebuf *file) | |
102 | - | { | ||
103 | 21314 | 2,3 | if (file->fd_is_open && file->fd >= 0) | |
104 | 2904 | 4 | p_close(file->fd); | |
105 | - | |||
106 | 21314 | 5-9 | if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock)) | |
107 | 2904 | 10 | p_unlink(file->path_lock); | |
108 | - | |||
109 | 21314 | 11 | if (file->compute_digest) { | |
110 | 4721 | 12 | git_hash_ctx_cleanup(&file->digest); | |
111 | 4721 | 13 | file->compute_digest = 0; | |
112 | - | } | ||
113 | - | |||
114 | 21314 | 14 | if (file->buffer) | |
115 | 14032 | 15 | git__free(file->buffer); | |
116 | - | |||
117 | - | /* use the presence of z_buf to decide if we need to deflateEnd */ | ||
118 | 21314 | 16 | if (file->z_buf) { | |
119 | 3779 | 17 | git__free(file->z_buf); | |
120 | 3779 | 18 | deflateEnd(&file->zs); | |
121 | - | } | ||
122 | - | |||
123 | 21314 | 19 | if (file->path_original) | |
124 | 12004 | 20 | git__free(file->path_original); | |
125 | 21314 | 21 | if (file->path_lock) | |
126 | 14144 | 22 | git__free(file->path_lock); | |
127 | - | |||
128 | 21314 | 23 | memset(file, 0x0, sizeof(git_filebuf)); | |
129 | 21314 | 23 | file->fd = -1; | |
130 | 21314 | 23 | } | |
131 | - | |||
132 | 19138 | 2 | GIT_INLINE(int) flush_buffer(git_filebuf *file) | |
133 | - | { | ||
134 | 19138 | 2 | int result = file->write(file, file->buffer, file->buf_pos); | |
135 | 19138 | 3 | file->buf_pos = 0; | |
136 | 19138 | 3 | return result; | |
137 | - | } | ||
138 | - | |||
139 | 4 | 2 | int git_filebuf_flush(git_filebuf *file) | |
140 | - | { | ||
141 | 4 | 2 | return flush_buffer(file); | |
142 | - | } | ||
143 | - | |||
144 | 11198 | 2 | static int write_normal(git_filebuf *file, void *source, size_t len) | |
145 | - | { | ||
146 | 11198 | 2 | if (len > 0) { | |
147 | 11080 | 3,4 | if (p_write(file->fd, (void *)source, len) < 0) { | |
148 | ##### | 5 | file->last_error = BUFERR_WRITE; | |
149 | ##### | 5 | return -1; | |
150 | - | } | ||
151 | - | |||
152 | 11080 | 6 | if (file->compute_digest) | |
153 | 6181 | 7 | git_hash_update(&file->digest, source, len); | |
154 | - | } | ||
155 | - | |||
156 | 11198 | 8 | return 0; | |
157 | - | } | ||
158 | - | |||
159 | 8053 | 2 | static int write_deflate(git_filebuf *file, void *source, size_t len) | |
160 | - | { | ||
161 | 8053 | 2 | z_stream *zs = &file->zs; | |
162 | - | |||
163 | 8053 | 2,3 | if (len > 0 || file->flush_mode == Z_FINISH) { | |
164 | 8053 | 4 | zs->next_in = source; | |
165 | 8053 | 4 | zs->avail_in = (uInt)len; | |
166 | - | |||
167 | - | do { | ||
168 | - | size_t have; | ||
169 | - | |||
170 | 8077 | 5 | zs->next_out = file->z_buf; | |
171 | 8077 | 5 | zs->avail_out = (uInt)file->buf_size; | |
172 | - | |||
173 | 8077 | 5,6 | if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) { | |
174 | ##### | 7 | file->last_error = BUFERR_ZLIB; | |
175 | ##### | 7 | return -1; | |
176 | - | } | ||
177 | - | |||
178 | 8077 | 8 | have = file->buf_size - (size_t)zs->avail_out; | |
179 | - | |||
180 | 8077 | 8,9 | if (p_write(file->fd, file->z_buf, have) < 0) { | |
181 | ##### | 10 | file->last_error = BUFERR_WRITE; | |
182 | ##### | 10 | return -1; | |
183 | - | } | ||
184 | - | |||
185 | 8077 | 11 | } while (zs->avail_out == 0); | |
186 | - | |||
187 | 8053 | 12,13 | assert(zs->avail_in == 0); | |
188 | - | |||
189 | 8053 | 14 | if (file->compute_digest) | |
190 | ##### | 15 | git_hash_update(&file->digest, source, len); | |
191 | - | } | ||
192 | - | |||
193 | 8053 | 16 | return 0; | |
194 | - | } | ||
195 | - | |||
196 | - | #define MAX_SYMLINK_DEPTH 5 | ||
197 | - | |||
198 | 10362 | 2 | static int resolve_symlink(git_buf *out, const char *path) | |
199 | - | { | ||
200 | - | int i, error, root; | ||
201 | - | ssize_t ret; | ||
202 | - | struct stat st; | ||
203 | 10362 | 2 | git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT; | |
204 | - | |||
205 | 10362 | 2-5 | if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 || | |
206 | - | (error = git_buf_puts(&curpath, path)) < 0) | ||
207 | ##### | 6 | return error; | |
208 | - | |||
209 | 10370 | 7,42,43 | for (i = 0; i < MAX_SYMLINK_DEPTH; i++) { | |
210 | 10369 | 8 | error = p_lstat(curpath.ptr, &st); | |
211 | 10369 | 9-11 | if (error < 0 && errno == ENOENT) { | |
212 | 3041 | 12 | error = git_buf_puts(out, curpath.ptr); | |
213 | 3041 | 13 | goto cleanup; | |
214 | - | } | ||
215 | - | |||
216 | 7328 | 14 | if (error < 0) { | |
217 | ##### | 15 | git_error_set(GIT_ERROR_OS, "failed to stat '%s'", curpath.ptr); | |
218 | ##### | 16 | error = -1; | |
219 | ##### | 16 | goto cleanup; | |
220 | - | } | ||
221 | - | |||
222 | 7328 | 17 | if (!S_ISLNK(st.st_mode)) { | |
223 | 7320 | 18 | error = git_buf_puts(out, curpath.ptr); | |
224 | 7320 | 19 | goto cleanup; | |
225 | - | } | ||
226 | - | |||
227 | 8 | 20 | ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX); | |
228 | 8 | 21 | if (ret < 0) { | |
229 | ##### | 22 | git_error_set(GIT_ERROR_OS, "failed to read symlink '%s'", curpath.ptr); | |
230 | ##### | 23 | error = -1; | |
231 | ##### | 23 | goto cleanup; | |
232 | - | } | ||
233 | - | |||
234 | 8 | 24 | if (ret == GIT_PATH_MAX) { | |
235 | ##### | 25 | git_error_set(GIT_ERROR_INVALID, "symlink target too long"); | |
236 | ##### | 26 | error = -1; | |
237 | ##### | 26 | goto cleanup; | |
238 | - | } | ||
239 | - | |||
240 | - | /* readlink(2) won't NUL-terminate for us */ | ||
241 | 8 | 27 | target.ptr[ret] = '\0'; | |
242 | 8 | 27 | target.size = ret; | |
243 | - | |||
244 | 8 | 27 | root = git_path_root(target.ptr); | |
245 | 8 | 28 | if (root >= 0) { | |
246 | 1 | 29,30 | if ((error = git_buf_sets(&curpath, target.ptr)) < 0) | |
247 | ##### | 31 | goto cleanup; | |
248 | - | } else { | ||
249 | 7 | 32 | git_buf dir = GIT_BUF_INIT; | |
250 | - | |||
251 | 7 | 32,33 | if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0) | |
252 | ##### | 34,41 | goto cleanup; | |
253 | - | |||
254 | 7 | 35 | git_buf_swap(&curpath, &dir); | |
255 | 7 | 36 | git_buf_dispose(&dir); | |
256 | - | |||
257 | 7 | 37,38 | if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0) | |
258 | 7 | 39,40 | goto cleanup; | |
259 | - | } | ||
260 | - | } | ||
261 | - | |||
262 | 1 | 44 | git_error_set(GIT_ERROR_INVALID, "maximum symlink depth reached"); | |
263 | 1 | 45 | error = -1; | |
264 | - | |||
265 | - | cleanup: | ||
266 | 10362 | 46 | git_buf_dispose(&curpath); | |
267 | 10362 | 47 | git_buf_dispose(&target); | |
268 | 10362 | 48 | return error; | |
269 | - | } | ||
270 | - | |||
271 | 14141 | 2 | int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode) | |
272 | - | { | ||
273 | 14141 | 2 | return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE); | |
274 | - | } | ||
275 | - | |||
276 | 14145 | 2 | int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size) | |
277 | - | { | ||
278 | 14145 | 2 | int compression, error = -1; | |
279 | - | size_t path_len, alloc_len; | ||
280 | - | |||
281 | - | /* opening an already open buffer is a programming error; | ||
282 | - | * assert that this never happens instead of returning | ||
283 | - | * an error code */ | ||
284 | 14145 | 2-5 | assert(file && path && file->buffer == NULL); | |
285 | - | |||
286 | 14145 | 6 | memset(file, 0x0, sizeof(git_filebuf)); | |
287 | - | |||
288 | 14145 | 6 | if (flags & GIT_FILEBUF_DO_NOT_BUFFER) | |
289 | 113 | 7 | file->do_not_buffer = true; | |
290 | - | |||
291 | 14145 | 8 | if (flags & GIT_FILEBUF_FSYNC) | |
292 | 43 | 9 | file->do_fsync = true; | |
293 | - | |||
294 | 14145 | 10 | file->buf_size = size; | |
295 | 14145 | 10 | file->buf_pos = 0; | |
296 | 14145 | 10 | file->fd = -1; | |
297 | 14145 | 10 | file->last_error = BUFERR_OK; | |
298 | - | |||
299 | - | /* Allocate the main cache buffer */ | ||
300 | 14145 | 10 | if (!file->do_not_buffer) { | |
301 | 14032 | 11 | file->buffer = git__malloc(file->buf_size); | |
302 | 14032 | 12,13 | GIT_ERROR_CHECK_ALLOC(file->buffer); | |
303 | - | } | ||
304 | - | |||
305 | - | /* If we are hashing on-write, allocate a new hash context */ | ||
306 | 14145 | 14 | if (flags & GIT_FILEBUF_HASH_CONTENTS) { | |
307 | 6102 | 15 | file->compute_digest = 1; | |
308 | - | |||
309 | 6102 | 15,16 | if (git_hash_ctx_init(&file->digest) < 0) | |
310 | ##### | 17 | goto cleanup; | |
311 | - | } | ||
312 | - | |||
313 | 14145 | 18 | compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT; | |
314 | - | |||
315 | - | /* If we are deflating on-write, */ | ||
316 | 14145 | 18 | if (compression != 0) { | |
317 | - | /* Initialize the ZLib stream */ | ||
318 | 3779 | 19,20 | if (deflateInit(&file->zs, compression) != Z_OK) { | |
319 | ##### | 21 | git_error_set(GIT_ERROR_ZLIB, "failed to initialize zlib"); | |
320 | ##### | 61 | goto cleanup; | |
321 | - | } | ||
322 | - | |||
323 | - | /* Allocate the Zlib cache buffer */ | ||
324 | 3779 | 22 | file->z_buf = git__malloc(file->buf_size); | |
325 | 3779 | 23,24 | GIT_ERROR_CHECK_ALLOC(file->z_buf); | |
326 | - | |||
327 | - | /* Never flush */ | ||
328 | 3779 | 25 | file->flush_mode = Z_NO_FLUSH; | |
329 | 3779 | 25 | file->write = &write_deflate; | |
330 | - | } else { | ||
331 | 10366 | 26 | file->write = &write_normal; | |
332 | - | } | ||
333 | - | |||
334 | - | /* If we are writing to a temp file */ | ||
335 | 14145 | 27 | if (flags & GIT_FILEBUF_TEMPORARY) { | |
336 | 3783 | 28 | git_buf tmp_path = GIT_BUF_INIT; | |
337 | - | |||
338 | - | /* Open the file as temporary for locking */ | ||
339 | 3783 | 28 | file->fd = git_futils_mktmp(&tmp_path, path, mode); | |
340 | - | |||
341 | 3783 | 29 | if (file->fd < 0) { | |
342 | ##### | 30 | git_buf_dispose(&tmp_path); | |
343 | ##### | 31 | goto cleanup; | |
344 | - | } | ||
345 | 3783 | 32 | file->fd_is_open = true; | |
346 | 3783 | 32 | file->created_lock = true; | |
347 | - | |||
348 | - | /* No original path */ | ||
349 | 3783 | 32 | file->path_original = NULL; | |
350 | 3783 | 32 | file->path_lock = git_buf_detach(&tmp_path); | |
351 | 3783 | 33-35 | GIT_ERROR_CHECK_ALLOC(file->path_lock); | |
352 | - | } else { | ||
353 | 10362 | 36 | git_buf resolved_path = GIT_BUF_INIT; | |
354 | - | |||
355 | 10362 | 36,37 | if ((error = resolve_symlink(&resolved_path, path)) < 0) | |
356 | 39 | 38,58 | goto cleanup; | |
357 | - | |||
358 | - | /* Save the original path of the file */ | ||
359 | 10361 | 39 | path_len = resolved_path.size; | |
360 | 10361 | 39 | file->path_original = git_buf_detach(&resolved_path); | |
361 | - | |||
362 | - | /* create the locking path by appending ".lock" to the original */ | ||
363 | 10361 | 40-46,59 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH); | |
364 | 10361 | 47 | file->path_lock = git__malloc(alloc_len); | |
365 | 10361 | 48,49 | GIT_ERROR_CHECK_ALLOC(file->path_lock); | |
366 | - | |||
367 | 10361 | 50 | memcpy(file->path_lock, file->path_original, path_len); | |
368 | 10361 | 50 | memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); | |
369 | - | |||
370 | 10361 | 50,51 | if (git_path_isdir(file->path_original)) { | |
371 | 5 | 52 | git_error_set(GIT_ERROR_FILESYSTEM, "path '%s' is a directory", file->path_original); | |
372 | 5 | 53 | error = GIT_EDIRECTORY; | |
373 | 5 | 53 | goto cleanup; | |
374 | - | } | ||
375 | - | |||
376 | - | /* open the file for locking */ | ||
377 | 10356 | 54,55 | if ((error = lock_file(file, flags, mode)) < 0) | |
378 | 33 | 56 | goto cleanup; | |
379 | - | |||
380 | 10323 | 57 | file->created_lock = true; | |
381 | - | } | ||
382 | - | |||
383 | 14106 | 60 | return 0; | |
384 | - | |||
385 | - | cleanup: | ||
386 | 39 | 62 | git_filebuf_cleanup(file); | |
387 | 39 | 63 | return error; | |
388 | - | } | ||
389 | - | |||
390 | 1381 | 2 | int git_filebuf_hash(git_oid *oid, git_filebuf *file) | |
391 | - | { | ||
392 | 1381 | 2-5 | assert(oid && file && file->compute_digest); | |
393 | - | |||
394 | 1381 | 6 | flush_buffer(file); | |
395 | - | |||
396 | 1381 | 7,8 | if (verify_last_error(file) < 0) | |
397 | ##### | 9 | return -1; | |
398 | - | |||
399 | 1381 | 10 | git_hash_final(oid, &file->digest); | |
400 | 1381 | 11 | git_hash_ctx_cleanup(&file->digest); | |
401 | 1381 | 12 | file->compute_digest = 0; | |
402 | - | |||
403 | 1381 | 12 | return 0; | |
404 | - | } | ||
405 | - | |||
406 | 1733 | 2 | int git_filebuf_commit_at(git_filebuf *file, const char *path) | |
407 | - | { | ||
408 | 1733 | 2 | git__free(file->path_original); | |
409 | 1733 | 3 | file->path_original = git__strdup(path); | |
410 | 1733 | 4,5 | GIT_ERROR_CHECK_ALLOC(file->path_original); | |
411 | - | |||
412 | 1733 | 6 | return git_filebuf_commit(file); | |
413 | - | } | ||
414 | - | |||
415 | 11202 | 2 | int git_filebuf_commit(git_filebuf *file) | |
416 | - | { | ||
417 | - | /* temporary files cannot be committed */ | ||
418 | 11202 | 2-4 | assert(file && file->path_original); | |
419 | - | |||
420 | 11202 | 5 | file->flush_mode = Z_FINISH; | |
421 | 11202 | 5 | flush_buffer(file); | |
422 | - | |||
423 | 11202 | 6,7 | if (verify_last_error(file) < 0) | |
424 | ##### | 8 | goto on_error; | |
425 | - | |||
426 | 11202 | 9 | file->fd_is_open = false; | |
427 | - | |||
428 | 11202 | 9-11 | if (file->do_fsync && p_fsync(file->fd) < 0) { | |
429 | ##### | 12 | git_error_set(GIT_ERROR_OS, "failed to fsync '%s'", file->path_lock); | |
430 | ##### | 27 | goto on_error; | |
431 | - | } | ||
432 | - | |||
433 | 11202 | 13,14 | if (p_close(file->fd) < 0) { | |
434 | ##### | 15 | git_error_set(GIT_ERROR_OS, "failed to close file at '%s'", file->path_lock); | |
435 | ##### | 16 | goto on_error; | |
436 | - | } | ||
437 | - | |||
438 | 11202 | 17 | file->fd = -1; | |
439 | - | |||
440 | 11202 | 17,18 | if (p_rename(file->path_lock, file->path_original) < 0) { | |
441 | ##### | 19 | git_error_set(GIT_ERROR_OS, "failed to rename lockfile to '%s'", file->path_original); | |
442 | ##### | 20 | goto on_error; | |
443 | - | } | ||
444 | - | |||
445 | 11202 | 21-23 | if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0) | |
446 | ##### | 24 | goto on_error; | |
447 | - | |||
448 | 11202 | 25 | file->did_rename = true; | |
449 | - | |||
450 | 11202 | 25 | git_filebuf_cleanup(file); | |
451 | 11202 | 26 | return 0; | |
452 | - | |||
453 | - | on_error: | ||
454 | ##### | 28 | git_filebuf_cleanup(file); | |
455 | ##### | 29 | return -1; | |
456 | - | } | ||
457 | - | |||
458 | 62305 | 2 | GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len) | |
459 | - | { | ||
460 | 62305 | 2 | memcpy(file->buffer + file->buf_pos, buf, len); | |
461 | 62305 | 2 | file->buf_pos += len; | |
462 | 62305 | 2 | } | |
463 | - | |||
464 | 56004 | 2 | int git_filebuf_write(git_filebuf *file, const void *buff, size_t len) | |
465 | - | { | ||
466 | 56004 | 2 | const unsigned char *buf = buff; | |
467 | - | |||
468 | 56004 | 2,3 | ENSURE_BUF_OK(file); | |
469 | - | |||
470 | 56004 | 4 | if (file->do_not_buffer) | |
471 | 113 | 5 | return file->write(file, (void *)buff, len); | |
472 | - | |||
473 | - | for (;;) { | ||
474 | 62305 | 6 | size_t space_left = file->buf_size - file->buf_pos; | |
475 | - | |||
476 | - | /* cache if it's small */ | ||
477 | 62305 | 6 | if (space_left > len) { | |
478 | 55891 | 7 | add_to_cache(file, buf, len); | |
479 | 55891 | 8 | return 0; | |
480 | - | } | ||
481 | - | |||
482 | 6414 | 9 | add_to_cache(file, buf, space_left); | |
483 | 6414 | 10,11 | if (flush_buffer(file) < 0) | |
484 | ##### | 12 | return -1; | |
485 | - | |||
486 | 6414 | 13 | len -= space_left; | |
487 | 6414 | 13 | buf += space_left; | |
488 | 6414 | 13 | } | |
489 | - | } | ||
490 | - | |||
491 | 22629 | 2 | int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len) | |
492 | - | { | ||
493 | 22629 | 2 | size_t space_left = file->buf_size - file->buf_pos; | |
494 | - | |||
495 | 22629 | 2 | *buffer = NULL; | |
496 | - | |||
497 | 22629 | 2,3 | ENSURE_BUF_OK(file); | |
498 | - | |||
499 | 22629 | 4 | if (len > file->buf_size) { | |
500 | ##### | 5 | file->last_error = BUFERR_MEM; | |
501 | ##### | 5 | return -1; | |
502 | - | } | ||
503 | - | |||
504 | 22629 | 6 | if (space_left <= len) { | |
505 | 137 | 7,8 | if (flush_buffer(file) < 0) | |
506 | ##### | 9 | return -1; | |
507 | - | } | ||
508 | - | |||
509 | 22629 | 10 | *buffer = (file->buffer + file->buf_pos); | |
510 | 22629 | 10 | file->buf_pos += len; | |
511 | - | |||
512 | 22629 | 10 | return 0; | |
513 | - | } | ||
514 | - | |||
515 | 6326 | 2 | int git_filebuf_printf(git_filebuf *file, const char *format, ...) | |
516 | - | { | ||
517 | - | va_list arglist; | ||
518 | - | size_t space_left, len, alloclen; | ||
519 | - | int written, res; | ||
520 | - | char *tmp_buffer; | ||
521 | - | |||
522 | 6326 | 2,3 | ENSURE_BUF_OK(file); | |
523 | - | |||
524 | 6326 | 4 | space_left = file->buf_size - file->buf_pos; | |
525 | - | |||
526 | - | do { | ||
527 | 6326 | 5 | va_start(arglist, format); | |
528 | 6326 | 5 | written = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist); | |
529 | 6326 | 5 | va_end(arglist); | |
530 | - | |||
531 | 6326 | 5 | if (written < 0) { | |
532 | ##### | 6 | file->last_error = BUFERR_MEM; | |
533 | ##### | 6 | return -1; | |
534 | - | } | ||
535 | - | |||
536 | 6326 | 7 | len = written; | |
537 | 6326 | 7 | if (len + 1 <= space_left) { | |
538 | 6326 | 8 | file->buf_pos += len; | |
539 | 6326 | 8 | return 0; | |
540 | - | } | ||
541 | - | |||
542 | ##### | 9,10 | if (flush_buffer(file) < 0) | |
543 | ##### | 11 | return -1; | |
544 | - | |||
545 | ##### | 12 | space_left = file->buf_size - file->buf_pos; | |
546 | - | |||
547 | ##### | 12 | } while (len + 1 <= space_left); | |
548 | - | |||
549 | ##### | 13-17 | if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, 1) || | |
550 | ##### | 16 | !(tmp_buffer = git__malloc(alloclen))) { | |
551 | ##### | 18 | file->last_error = BUFERR_MEM; | |
552 | ##### | 18 | return -1; | |
553 | - | } | ||
554 | - | |||
555 | ##### | 19 | va_start(arglist, format); | |
556 | ##### | 19 | written = p_vsnprintf(tmp_buffer, len + 1, format, arglist); | |
557 | ##### | 19 | va_end(arglist); | |
558 | - | |||
559 | ##### | 19 | if (written < 0) { | |
560 | ##### | 20 | git__free(tmp_buffer); | |
561 | ##### | 21 | file->last_error = BUFERR_MEM; | |
562 | ##### | 21 | return -1; | |
563 | - | } | ||
564 | - | |||
565 | ##### | 22 | res = git_filebuf_write(file, tmp_buffer, len); | |
566 | ##### | 23 | git__free(tmp_buffer); | |
567 | - | |||
568 | ##### | 24 | return res; | |
569 | - | } | ||
570 | - | |||
571 | ##### | 2 | int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file) | |
572 | - | { | ||
573 | - | int res; | ||
574 | - | struct stat st; | ||
575 | - | |||
576 | ##### | 2 | if (file->fd_is_open) | |
577 | ##### | 3 | res = p_fstat(file->fd, &st); | |
578 | - | else | ||
579 | ##### | 4 | res = p_stat(file->path_original, &st); | |
580 | - | |||
581 | ##### | 5 | if (res < 0) { | |
582 | ##### | 6 | git_error_set(GIT_ERROR_OS, "could not get stat info for '%s'", | |
583 | - | file->path_original); | ||
584 | ##### | 7 | return res; | |
585 | - | } | ||
586 | - | |||
587 | ##### | 8 | if (mtime) | |
588 | ##### | 9 | *mtime = st.st_mtime; | |
589 | ##### | 10 | if (size) | |
590 | ##### | 11 | *size = (size_t)st.st_size; | |
591 | - | |||
592 | ##### | 12 | return 0; | |
593 | - | } |