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 "blob.h"
9 -
10 - #include "git2/common.h"
11 - #include "git2/object.h"
12 - #include "git2/repository.h"
13 - #include "git2/odb_backend.h"
14 -
15 - #include "filebuf.h"
16 - #include "filter.h"
17 - #include "buf_text.h"
18 -
19 10256 2 const void *git_blob_rawcontent(const git_blob *blob)
20 - {
21 10256 2,3 assert(blob);
22 10256 4 if (blob->raw)
23 ##### 5 return blob->data.raw.data;
24 - else
25 10256 6 return git_odb_object_data(blob->data.odb);
26 - }
27 -
28 8655 2 git_object_size_t git_blob_rawsize(const git_blob *blob)
29 - {
30 8655 2,3 assert(blob);
31 8655 4 if (blob->raw)
32 ##### 5 return blob->data.raw.size;
33 - else
34 8655 6 return (git_object_size_t)git_odb_object_size(blob->data.odb);
35 - }
36 -
37 102 2 int git_blob__getbuf(git_buf *buffer, git_blob *blob)
38 - {
39 102 2 git_object_size_t size = git_blob_rawsize(blob);
40 -
41 102 3-6 GIT_ERROR_CHECK_BLOBSIZE(size);
42 102 7 return git_buf_set(buffer, git_blob_rawcontent(blob), (size_t)size);
43 - }
44 -
45 23861 2 void git_blob__free(void *_blob)
46 - {
47 23861 2 git_blob *blob = (git_blob *) _blob;
48 23861 2 if (!blob->raw)
49 23851 3 git_odb_object_free(blob->data.odb);
50 23908 4 git__free(blob);
51 23920 5 }
52 -
53 4 2 int git_blob__parse_raw(void *_blob, const char *data, size_t size)
54 - {
55 4 2 git_blob *blob = (git_blob *) _blob;
56 4 2,3 assert(blob);
57 4 4 blob->raw = 1;
58 4 4 blob->data.raw.data = data;
59 4 4 blob->data.raw.size = size;
60 4 4 return 0;
61 - }
62 -
63 23795 2 int git_blob__parse(void *_blob, git_odb_object *odb_obj)
64 - {
65 23795 2 git_blob *blob = (git_blob *) _blob;
66 23795 2,3 assert(blob);
67 23795 4 git_cached_obj_incref((git_cached_obj *)odb_obj);
68 23909 5 blob->raw = 0;
69 23909 5 blob->data.odb = odb_obj;
70 23909 5 return 0;
71 - }
72 -
73 837 2 int git_blob_create_from_buffer(
74 - git_oid *id, git_repository *repo, const void *buffer, size_t len)
75 - {
76 - int error;
77 - git_odb *odb;
78 - git_odb_stream *stream;
79 -
80 837 2-4 assert(id && repo);
81 -
82 837 5-8 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
83 837 7 (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJECT_BLOB)) < 0)
84 ##### 9 return error;
85 -
86 837 10,11 if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
87 837 12 error = git_odb_stream_finalize_write(id, stream);
88 -
89 837 13 git_odb_stream_free(stream);
90 837 14 return error;
91 - }
92 -
93 2043 2 static int write_file_stream(
94 - git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
95 - {
96 - int fd, error;
97 - char buffer[FILEIO_BUFSIZE];
98 2043 2 git_odb_stream *stream = NULL;
99 2043 2 ssize_t read_len = -1;
100 2043 2 git_object_size_t written = 0;
101 -
102 2043 2,3 if ((error = git_odb_open_wstream(
103 - &stream, odb, file_size, GIT_OBJECT_BLOB)) < 0)
104 ##### 4 return error;
105 -
106 2043 5,6 if ((fd = git_futils_open_ro(path)) < 0) {
107 ##### 7 git_odb_stream_free(stream);
108 ##### 8 return -1;
109 - }
110 -
111 4054 9,12-14 while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
112 2011 10 error = git_odb_stream_write(stream, buffer, read_len);
113 2011 11 written += read_len;
114 - }
115 -
116 2043 15 p_close(fd);
117 -
118 2043 16,17 if (written != file_size || read_len < 0) {
119 ##### 18 git_error_set(GIT_ERROR_OS, "failed to read file into stream");
120 ##### 19 error = -1;
121 - }
122 -
123 2043 20 if (!error)
124 2043 21 error = git_odb_stream_finalize_write(id, stream);
125 -
126 2043 22 git_odb_stream_free(stream);
127 2043 23 return error;
128 - }
129 -
130 1514 2 static int write_file_filtered(
131 - git_oid *id,
132 - git_object_size_t *size,
133 - git_odb *odb,
134 - const char *full_path,
135 - git_filter_list *fl)
136 - {
137 - int error;
138 1514 2 git_buf tgt = GIT_BUF_INIT;
139 -
140 1514 2 error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
141 -
142 - /* Write the file to disk if it was properly filtered */
143 1514 3 if (!error) {
144 1242 4 *size = tgt.size;
145 -
146 1242 4 error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJECT_BLOB);
147 - }
148 -
149 1514 5 git_buf_dispose(&tgt);
150 1514 6 return error;
151 - }
152 -
153 ##### 2 static int write_symlink(
154 - git_oid *id, git_odb *odb, const char *path, size_t link_size)
155 - {
156 - char *link_data;
157 - ssize_t read_len;
158 - int error;
159 -
160 ##### 2 link_data = git__malloc(link_size);
161 ##### 3,4 GIT_ERROR_CHECK_ALLOC(link_data);
162 -
163 ##### 5 read_len = p_readlink(path, link_data, link_size);
164 ##### 6 if (read_len != (ssize_t)link_size) {
165 ##### 7 git_error_set(GIT_ERROR_OS, "failed to create blob: cannot read symlink '%s'", path);
166 ##### 8 git__free(link_data);
167 ##### 9 return -1;
168 - }
169 -
170 ##### 10 error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJECT_BLOB);
171 ##### 11 git__free(link_data);
172 ##### 12 return error;
173 - }
174 -
175 3560 2 int git_blob__create_from_paths(
176 - git_oid *id,
177 - struct stat *out_st,
178 - git_repository *repo,
179 - const char *content_path,
180 - const char *hint_path,
181 - mode_t hint_mode,
182 - bool try_load_filters)
183 - {
184 - int error;
185 - struct stat st;
186 3560 2 git_odb *odb = NULL;
187 - git_object_size_t size;
188 - mode_t mode;
189 3560 2 git_buf path = GIT_BUF_INIT;
190 -
191 3560 2-4 assert(hint_path || !try_load_filters);
192 -
193 3560 5 if (!content_path) {
194 3554 6,7 if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
195 ##### 8 return GIT_EBAREREPO;
196 -
197 3554 9-11 if (git_buf_joinpath(
198 - &path, git_repository_workdir(repo), hint_path) < 0)
199 ##### 12 return -1;
200 -
201 3554 13 content_path = path.ptr;
202 - }
203 -
204 3560 14-17 if ((error = git_path_lstat(content_path, &st)) < 0 ||
205 - (error = git_repository_odb(&odb, repo)) < 0)
206 - goto done;
207 -
208 3560 18 if (S_ISDIR(st.st_mode)) {
209 3 19 git_error_set(GIT_ERROR_ODB, "cannot create blob from '%s': it is a directory", content_path);
210 3 20 error = GIT_EDIRECTORY;
211 3 20 goto done;
212 - }
213 -
214 3557 21 if (out_st)
215 3436 22 memcpy(out_st, &st, sizeof(st));
216 -
217 3557 23 size = st.st_size;
218 3557 23-25 mode = hint_mode ? hint_mode : st.st_mode;
219 -
220 3557 26 if (S_ISLNK(mode)) {
221 ##### 27 error = write_symlink(id, odb, content_path, (size_t)size);
222 - } else {
223 3557 28 git_filter_list *fl = NULL;
224 -
225 3557 28 if (try_load_filters)
226 - /* Load the filters for writing this file to the ODB */
227 3556 29 error = git_filter_list_load(
228 - &fl, repo, NULL, hint_path,
229 - GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
230 -
231 3557 30 if (error < 0)
232 - /* well, that didn't work */;
233 3557 31 else if (fl == NULL)
234 - /* No filters need to be applied to the document: we can stream
235 - * directly from disk */
236 2043 32 error = write_file_stream(id, odb, content_path, size);
237 - else {
238 - /* We need to apply one or more filters */
239 1514 33 error = write_file_filtered(id, &size, odb, content_path, fl);
240 -
241 3557 34,35 git_filter_list_free(fl);
242 - }
243 -
244 - /*
245 - * TODO: eventually support streaming filtered files, for files
246 - * which are bigger than a given threshold. This is not a priority
247 - * because applying a filter in streaming mode changes the final
248 - * size of the blob, and without knowing its final size, the blob
249 - * cannot be written in stream mode to the ODB.
250 - *
251 - * The plan is to do streaming writes to a tempfile on disk and then
252 - * opening streaming that file to the ODB, using
253 - * `write_file_stream`.
254 - *
255 - * CAREFULLY DESIGNED APIS YO
256 - */
257 - }
258 -
259 - done:
260 3560 36 git_odb_free(odb);
261 3560 37 git_buf_dispose(&path);
262 -
263 3560 38 return error;
264 - }
265 -
266 115 2 int git_blob_create_from_workdir(
267 - git_oid *id, git_repository *repo, const char *path)
268 - {
269 115 2 return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true);
270 - }
271 -
272 2 2 int git_blob_create_from_disk(
273 - git_oid *id, git_repository *repo, const char *path)
274 - {
275 - int error;
276 2 2 git_buf full_path = GIT_BUF_INIT;
277 - const char *workdir, *hintpath;
278 -
279 2 2,3 if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
280 ##### 4 git_buf_dispose(&full_path);
281 ##### 5 return error;
282 - }
283 -
284 2 6 hintpath = git_buf_cstr(&full_path);
285 2 7 workdir = git_repository_workdir(repo);
286 -
287 2 8-10 if (workdir && !git__prefixcmp(hintpath, workdir))
288 ##### 11 hintpath += strlen(workdir);
289 -
290 2 12,13 error = git_blob__create_from_paths(
291 - id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
292 -
293 2 14 git_buf_dispose(&full_path);
294 2 15 return error;
295 - }
296 -
297 - typedef struct {
298 - git_writestream parent;
299 - git_filebuf fbuf;
300 - git_repository *repo;
301 - char *hintpath;
302 - } blob_writestream;
303 -
304 ##### 2 static int blob_writestream_close(git_writestream *_stream)
305 - {
306 ##### 2 blob_writestream *stream = (blob_writestream *) _stream;
307 -
308 ##### 2 git_filebuf_cleanup(&stream->fbuf);
309 ##### 3 return 0;
310 - }
311 -
312 4 2 static void blob_writestream_free(git_writestream *_stream)
313 - {
314 4 2 blob_writestream *stream = (blob_writestream *) _stream;
315 -
316 4 2 git_filebuf_cleanup(&stream->fbuf);
317 4 3 git__free(stream->hintpath);
318 4 4 git__free(stream);
319 4 5 }
320 -
321 24 2 static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len)
322 - {
323 24 2 blob_writestream *stream = (blob_writestream *) _stream;
324 -
325 24 2 return git_filebuf_write(&stream->fbuf, buffer, len);
326 - }
327 -
328 4 2 int git_blob_create_from_stream(git_writestream **out, git_repository *repo, const char *hintpath)
329 - {
330 - int error;
331 4 2 git_buf path = GIT_BUF_INIT;
332 - blob_writestream *stream;
333 -
334 4 2-4 assert(out && repo);
335 -
336 4 5 stream = git__calloc(1, sizeof(blob_writestream));
337 4 6,7 GIT_ERROR_CHECK_ALLOC(stream);
338 -
339 4 8 if (hintpath) {
340 3 9 stream->hintpath = git__strdup(hintpath);
341 3 10,11 GIT_ERROR_CHECK_ALLOC(stream->hintpath);
342 - }
343 -
344 4 12 stream->repo = repo;
345 4 12 stream->parent.write = blob_writestream_write;
346 4 12 stream->parent.close = blob_writestream_close;
347 4 12 stream->parent.free = blob_writestream_free;
348 -
349 4 12,13 if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
350 4 14,15 || (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0)
351 - goto cleanup;
352 -
353 4 16-18 if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY,
354 - 0666, 2 * 1024 * 1024)) < 0)
355 ##### 19 goto cleanup;
356 -
357 4 20 *out = (git_writestream *) stream;
358 -
359 - cleanup:
360 4 21 if (error < 0)
361 ##### 22 blob_writestream_free((git_writestream *) stream);
362 -
363 4 23 git_buf_dispose(&path);
364 4 24 return error;
365 - }
366 -
367 4 2 int git_blob_create_from_stream_commit(git_oid *out, git_writestream *_stream)
368 - {
369 - int error;
370 4 2 blob_writestream *stream = (blob_writestream *) _stream;
371 -
372 - /*
373 - * We can make this more officient by avoiding writing to
374 - * disk, but for now let's re-use the helper functions we
375 - * have.
376 - */
377 4 2,3 if ((error = git_filebuf_flush(&stream->fbuf)) < 0)
378 ##### 4 goto cleanup;
379 -
380 4 5,5 error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock,
381 4 5,5 stream->hintpath, 0, !!stream->hintpath);
382 -
383 - cleanup:
384 4 6 blob_writestream_free(_stream);
385 4 7 return error;
386 -
387 - }
388 -
389 566 2 int git_blob_is_binary(const git_blob *blob)
390 - {
391 566 2 git_buf content = GIT_BUF_INIT;
392 - git_object_size_t size;
393 -
394 566 2,3 assert(blob);
395 -
396 566 4 size = git_blob_rawsize(blob);
397 -
398 566 5,6 git_buf_attach_notowned(&content, git_blob_rawcontent(blob),
399 - (size_t)min(size, GIT_FILTER_BYTES_TO_CHECK_NUL));
400 566 7 return git_buf_text_is_binary(&content);
401 - }
402 -
403 33 2 int git_blob_filter(
404 - git_buf *out,
405 - git_blob *blob,
406 - const char *path,
407 - git_blob_filter_options *given_opts)
408 - {
409 33 2 int error = 0;
410 33 2 git_filter_list *fl = NULL;
411 33 2 git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
412 33 2 git_filter_flag_t flags = GIT_FILTER_DEFAULT;
413 -
414 33 2-5 assert(blob && path && out);
415 -
416 33 6 git_buf_sanitize(out);
417 -
418 33 7-9 GIT_ERROR_CHECK_VERSION(
419 - given_opts, GIT_BLOB_FILTER_OPTIONS_VERSION, "git_blob_filter_options");
420 -
421 33 10 if (given_opts != NULL)
422 16 11 memcpy(&opts, given_opts, sizeof(git_blob_filter_options));
423 -
424 33 12,14 if ((opts.flags & GIT_BLOB_FILTER_CHECK_FOR_BINARY) != 0 &&
425 33 13 git_blob_is_binary(blob))
426 ##### 15 return 0;
427 -
428 33 16 if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
429 16 17 flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
430 -
431 33 18 if ((opts.flags & GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD) != 0)
432 13 19 flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
433 -
434 33 20-22 if (!(error = git_filter_list_load(
435 - &fl, git_blob_owner(blob), blob, path,
436 - GIT_FILTER_TO_WORKTREE, flags))) {
437 -
438 33 23 error = git_filter_list_apply_to_blob(out, fl, blob);
439 -
440 33 24 git_filter_list_free(fl);
441 - }
442 -
443 33 25 return error;
444 - }
445 -
446 - /* Deprecated functions */
447 -
448 - #ifndef GIT_DEPRECATE_HARD
449 ##### 2 int git_blob_create_frombuffer(
450 - git_oid *id, git_repository *repo, const void *buffer, size_t len)
451 - {
452 ##### 2 return git_blob_create_from_buffer(id, repo, buffer, len);
453 - }
454 -
455 ##### 2 int git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path)
456 - {
457 ##### 2 return git_blob_create_from_workdir(id, repo, relative_path);
458 - }
459 -
460 ##### 2 int git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path)
461 - {
462 ##### 2 return git_blob_create_from_disk(id, repo, path);
463 - }
464 -
465 ##### 2 int git_blob_create_fromstream(
466 - git_writestream **out,
467 - git_repository *repo,
468 - const char *hintpath)
469 - {
470 ##### 2 return git_blob_create_from_stream(out, repo, hintpath);
471 - }
472 -
473 ##### 2 int git_blob_create_fromstream_commit(
474 - git_oid *out,
475 - git_writestream *stream)
476 - {
477 ##### 2 return git_blob_create_from_stream_commit(out, stream);
478 - }
479 -
480 ##### 2 int git_blob_filtered_content(
481 - git_buf *out,
482 - git_blob *blob,
483 - const char *path,
484 - int check_for_binary_data)
485 - {
486 ##### 2 git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
487 -
488 ##### 2 if (check_for_binary_data)
489 ##### 3 opts.flags |= GIT_BLOB_FILTER_CHECK_FOR_BINARY;
490 - else
491 ##### 4 opts.flags &= ~GIT_BLOB_FILTER_CHECK_FOR_BINARY;
492 -
493 ##### 5 return git_blob_filter(out, blob, path, &opts);
494 - }
495 - #endif