]> git.proxmox.com Git - libgit2.git/blob - src/blob.c
Oh yeah, bugs from my rebase
[libgit2.git] / src / blob.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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 "git2/common.h"
9 #include "git2/object.h"
10 #include "git2/repository.h"
11
12 #include "common.h"
13 #include "blob.h"
14 #include "filter.h"
15
16 const void *git_blob_rawcontent(git_blob *blob)
17 {
18 assert(blob);
19 return blob->odb_object->raw.data;
20 }
21
22 git_off_t git_blob_rawsize(git_blob *blob)
23 {
24 assert(blob);
25 return (git_off_t)blob->odb_object->raw.len;
26 }
27
28 int git_blob__getbuf(git_buf *buffer, git_blob *blob)
29 {
30 return git_buf_set(
31 buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
32 }
33
34 void git_blob__free(git_blob *blob)
35 {
36 git_odb_object_free(blob->odb_object);
37 git__free(blob);
38 }
39
40 int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
41 {
42 assert(blob);
43 git_cached_obj_incref((git_cached_obj *)odb_obj);
44 blob->odb_object = odb_obj;
45 return 0;
46 }
47
48 int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
49 {
50 int error;
51 git_odb *odb;
52 git_odb_stream *stream;
53
54 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
55 (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
56 return error;
57
58 if ((error = stream->write(stream, buffer, len)) == 0)
59 error = stream->finalize_write(oid, stream);
60
61 stream->free(stream);
62 return error;
63 }
64
65 static int write_file_stream(
66 git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
67 {
68 int fd, error;
69 char buffer[4096];
70 git_odb_stream *stream = NULL;
71 ssize_t read_len = -1, written = 0;
72
73 if ((error = git_odb_open_wstream(
74 &stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
75 return error;
76
77 if ((fd = git_futils_open_ro(path)) < 0) {
78 stream->free(stream);
79 return -1;
80 }
81
82 while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
83 error = stream->write(stream, buffer, read_len);
84 written += read_len;
85 }
86
87 p_close(fd);
88
89 if (written != file_size || read_len < 0) {
90 giterr_set(GITERR_OS, "Failed to read file into stream");
91 error = -1;
92 }
93
94 if (!error)
95 error = stream->finalize_write(oid, stream);
96
97 stream->free(stream);
98 return error;
99 }
100
101 static int write_file_filtered(
102 git_oid *oid,
103 git_odb *odb,
104 const char *full_path,
105 git_vector *filters)
106 {
107 int error;
108 git_buf source = GIT_BUF_INIT;
109 git_buf dest = GIT_BUF_INIT;
110
111 if ((error = git_futils_readbuffer(&source, full_path)) < 0)
112 return error;
113
114 error = git_filters_apply(&dest, &source, filters);
115
116 /* Free the source as soon as possible. This can be big in memory,
117 * and we don't want to ODB write to choke */
118 git_buf_free(&source);
119
120 /* Write the file to disk if it was properly filtered */
121 if (!error)
122 error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
123
124 git_buf_free(&dest);
125 return error;
126 }
127
128 static int write_symlink(
129 git_oid *oid, git_odb *odb, const char *path, size_t link_size)
130 {
131 char *link_data;
132 ssize_t read_len;
133 int error;
134
135 link_data = git__malloc(link_size);
136 GITERR_CHECK_ALLOC(link_data);
137
138 read_len = p_readlink(path, link_data, link_size);
139 if (read_len != (ssize_t)link_size) {
140 giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
141 git__free(link_data);
142 return -1;
143 }
144
145 error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
146 git__free(link_data);
147 return error;
148 }
149
150 static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
151 {
152 int error;
153 struct stat st;
154 git_odb *odb = NULL;
155 git_off_t size;
156
157 assert(hint_path || !try_load_filters);
158
159 if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
160 return error;
161
162 size = st.st_size;
163
164 if (S_ISLNK(st.st_mode)) {
165 error = write_symlink(oid, odb, content_path, (size_t)size);
166 } else {
167 git_vector write_filters = GIT_VECTOR_INIT;
168 int filter_count = 0;
169
170 if (try_load_filters) {
171 /* Load the filters for writing this file to the ODB */
172 filter_count = git_filters_load(
173 &write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
174 }
175
176 if (filter_count < 0) {
177 /* Negative value means there was a critical error */
178 error = filter_count;
179 } else if (filter_count == 0) {
180 /* No filters need to be applied to the document: we can stream
181 * directly from disk */
182 error = write_file_stream(oid, odb, content_path, size);
183 } else {
184 /* We need to apply one or more filters */
185 error = write_file_filtered(oid, odb, content_path, &write_filters);
186 }
187
188 git_filters_free(&write_filters);
189
190 /*
191 * TODO: eventually support streaming filtered files, for files
192 * which are bigger than a given threshold. This is not a priority
193 * because applying a filter in streaming mode changes the final
194 * size of the blob, and without knowing its final size, the blob
195 * cannot be written in stream mode to the ODB.
196 *
197 * The plan is to do streaming writes to a tempfile on disk and then
198 * opening streaming that file to the ODB, using
199 * `write_file_stream`.
200 *
201 * CAREFULLY DESIGNED APIS YO
202 */
203 }
204
205 return error;
206 }
207
208 int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
209 {
210 git_buf full_path = GIT_BUF_INIT;
211 const char *workdir;
212 int error;
213
214 if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
215 return error;
216
217 workdir = git_repository_workdir(repo);
218
219 if (git_buf_joinpath(&full_path, workdir, path) < 0) {
220 git_buf_free(&full_path);
221 return -1;
222 }
223
224 error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
225
226 git_buf_free(&full_path);
227 return error;
228 }
229
230 int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
231 {
232 int error;
233 git_buf full_path = GIT_BUF_INIT;
234
235 if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
236 git_buf_free(&full_path);
237 return error;
238 }
239
240 error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
241
242 git_buf_free(&full_path);
243 return error;
244 }
245
246 #define BUFFER_SIZE 4096
247
248 int git_blob_create_fromchunks(
249 git_oid *oid,
250 git_repository *repo,
251 const char *hintpath,
252 int (*source_cb)(char *content, size_t max_length, void *payload),
253 void *payload)
254 {
255 int error = -1, read_bytes;
256 char *content = NULL;
257 git_filebuf file = GIT_FILEBUF_INIT;
258 git_buf path = GIT_BUF_INIT;
259
260 if (git_buf_join_n(
261 &path, '/', 3,
262 git_repository_path(repo),
263 GIT_OBJECTS_DIR,
264 "streamed") < 0)
265 goto cleanup;
266
267 content = git__malloc(BUFFER_SIZE);
268 GITERR_CHECK_ALLOC(content);
269
270 if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY) < 0)
271 goto cleanup;
272
273 while (1) {
274 read_bytes = source_cb(content, BUFFER_SIZE, payload);
275
276 assert(read_bytes <= BUFFER_SIZE);
277
278 if (read_bytes <= 0)
279 break;
280
281 if (git_filebuf_write(&file, content, read_bytes) < 0)
282 goto cleanup;
283 }
284
285 if (read_bytes < 0)
286 goto cleanup;
287
288 if (git_filebuf_flush(&file) < 0)
289 goto cleanup;
290
291 error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
292
293 cleanup:
294 git_buf_free(&path);
295 git_filebuf_cleanup(&file);
296 git__free(content);
297 return error;
298 }
299
300 int git_blob_is_binary(git_blob *blob)
301 {
302 git_buf content;
303
304 assert(blob);
305
306 content.ptr = blob->odb_object->raw.data;
307 content.size = min(blob->odb_object->raw.len, 4000);
308
309 return git_buf_text_is_binary(&content);
310 }