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 | - | } |