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 "reader.h"
9 
10 #include "futils.h"
11 #include "blob.h"
12 
13 #include "git2/tree.h"
14 #include "git2/blob.h"
15 #include "git2/index.h"
16 #include "git2/repository.h"
17 
18 /* tree reader */
19 
20 typedef struct {
21 	git_reader reader;
22 	git_tree *tree;
23 } tree_reader;
24 
tree_reader_read(git_buf * out,git_oid * out_id,git_filemode_t * out_filemode,git_reader * _reader,const char * filename)25 static int tree_reader_read(
26 	git_buf *out,
27 	git_oid *out_id,
28 	git_filemode_t *out_filemode,
29 	git_reader *_reader,
30 	const char *filename)
31 {
32 	tree_reader *reader = (tree_reader *)_reader;
33 	git_tree_entry *tree_entry = NULL;
34 	git_blob *blob = NULL;
35 	git_object_size_t blobsize;
36 	int error;
37 
38 	if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 ||
39 	    (error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0)
40 		goto done;
41 
42 	blobsize = git_blob_rawsize(blob);
43 	GIT_ERROR_CHECK_BLOBSIZE(blobsize);
44 
45 	if ((error = git_buf_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
46 		goto done;
47 
48 	if (out_id)
49 		git_oid_cpy(out_id, git_tree_entry_id(tree_entry));
50 
51 	if (out_filemode)
52 		*out_filemode = git_tree_entry_filemode(tree_entry);
53 
54 done:
55 	git_blob_free(blob);
56 	git_tree_entry_free(tree_entry);
57 	return error;
58 }
59 
git_reader_for_tree(git_reader ** out,git_tree * tree)60 int git_reader_for_tree(git_reader **out, git_tree *tree)
61 {
62 	tree_reader *reader;
63 
64 	GIT_ASSERT_ARG(out);
65 	GIT_ASSERT_ARG(tree);
66 
67 	reader = git__calloc(1, sizeof(tree_reader));
68 	GIT_ERROR_CHECK_ALLOC(reader);
69 
70 	reader->reader.read = tree_reader_read;
71 	reader->tree = tree;
72 
73 	*out = (git_reader *)reader;
74 	return 0;
75 }
76 
77 /* workdir reader */
78 
79 typedef struct {
80 	git_reader reader;
81 	git_repository *repo;
82 	git_index *index;
83 } workdir_reader;
84 
workdir_reader_read(git_buf * out,git_oid * out_id,git_filemode_t * out_filemode,git_reader * _reader,const char * filename)85 static int workdir_reader_read(
86 	git_buf *out,
87 	git_oid *out_id,
88 	git_filemode_t *out_filemode,
89 	git_reader *_reader,
90 	const char *filename)
91 {
92 	workdir_reader *reader = (workdir_reader *)_reader;
93 	git_buf path = GIT_BUF_INIT;
94 	struct stat st;
95 	git_filemode_t filemode;
96 	git_filter_list *filters = NULL;
97 	const git_index_entry *idx_entry;
98 	git_oid id;
99 	int error;
100 
101 	if ((error = git_repository_workdir_path(&path, reader->repo, filename)) < 0)
102 		goto done;
103 
104 	if ((error = p_lstat(path.ptr, &st)) < 0) {
105 		if (error == -1 && errno == ENOENT)
106 			error = GIT_ENOTFOUND;
107 
108 		git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr);
109 		goto done;
110 	}
111 
112 	filemode = git_futils_canonical_mode(st.st_mode);
113 
114 	/*
115 	 * Patch application - for example - uses the filtered version of
116 	 * the working directory data to match git.  So we will run the
117 	 * workdir -> ODB filter on the contents in this workdir reader.
118 	 */
119 	if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename,
120 		GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0)
121 		goto done;
122 
123 	if ((error = git_filter_list_apply_to_file(out,
124 	    filters, reader->repo, path.ptr)) < 0)
125 		goto done;
126 
127 	if (out_id || reader->index) {
128 		if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB)) < 0)
129 			goto done;
130 	}
131 
132 	if (reader->index) {
133 		if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
134 		    filemode != idx_entry->mode ||
135 		    !git_oid_equal(&id, &idx_entry->id)) {
136 			error = GIT_READER_MISMATCH;
137 			goto done;
138 		}
139 	}
140 
141 	if (out_id)
142 		git_oid_cpy(out_id, &id);
143 
144 	if (out_filemode)
145 		*out_filemode = filemode;
146 
147 done:
148 	git_filter_list_free(filters);
149 	git_buf_dispose(&path);
150 	return error;
151 }
152 
git_reader_for_workdir(git_reader ** out,git_repository * repo,bool validate_index)153 int git_reader_for_workdir(
154 	git_reader **out,
155 	git_repository *repo,
156 	bool validate_index)
157 {
158 	workdir_reader *reader;
159 	int error;
160 
161 	GIT_ASSERT_ARG(out);
162 	GIT_ASSERT_ARG(repo);
163 
164 	reader = git__calloc(1, sizeof(workdir_reader));
165 	GIT_ERROR_CHECK_ALLOC(reader);
166 
167 	reader->reader.read = workdir_reader_read;
168 	reader->repo = repo;
169 
170 	if (validate_index &&
171 	    (error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
172 		git__free(reader);
173 		return error;
174 	}
175 
176 	*out = (git_reader *)reader;
177 	return 0;
178 }
179 
180 /* index reader */
181 
182 typedef struct {
183 	git_reader reader;
184 	git_repository *repo;
185 	git_index *index;
186 } index_reader;
187 
index_reader_read(git_buf * out,git_oid * out_id,git_filemode_t * out_filemode,git_reader * _reader,const char * filename)188 static int index_reader_read(
189 	git_buf *out,
190 	git_oid *out_id,
191 	git_filemode_t *out_filemode,
192 	git_reader *_reader,
193 	const char *filename)
194 {
195 	index_reader *reader = (index_reader *)_reader;
196 	const git_index_entry *entry;
197 	git_blob *blob;
198 	int error;
199 
200 	if ((entry = git_index_get_bypath(reader->index, filename, 0)) == NULL)
201 		return GIT_ENOTFOUND;
202 
203 	if ((error = git_blob_lookup(&blob, reader->repo, &entry->id)) < 0)
204 		goto done;
205 
206 	if (out_id)
207 		git_oid_cpy(out_id, &entry->id);
208 
209 	if (out_filemode)
210 		*out_filemode = entry->mode;
211 
212 	error = git_blob__getbuf(out, blob);
213 
214 done:
215 	git_blob_free(blob);
216 	return error;
217 }
218 
git_reader_for_index(git_reader ** out,git_repository * repo,git_index * index)219 int git_reader_for_index(
220 	git_reader **out,
221 	git_repository *repo,
222 	git_index *index)
223 {
224 	index_reader *reader;
225 	int error;
226 
227 	GIT_ASSERT_ARG(out);
228 	GIT_ASSERT_ARG(repo);
229 
230 	reader = git__calloc(1, sizeof(index_reader));
231 	GIT_ERROR_CHECK_ALLOC(reader);
232 
233 	reader->reader.read = index_reader_read;
234 	reader->repo = repo;
235 
236 	if (index) {
237 		reader->index = index;
238 	} else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
239 		git__free(reader);
240 		return error;
241 	}
242 
243 	*out = (git_reader *)reader;
244 	return 0;
245 }
246 
247 /* generic */
248 
git_reader_read(git_buf * out,git_oid * out_id,git_filemode_t * out_filemode,git_reader * reader,const char * filename)249 int git_reader_read(
250 	git_buf *out,
251 	git_oid *out_id,
252 	git_filemode_t *out_filemode,
253 	git_reader *reader,
254 	const char *filename)
255 {
256 	GIT_ASSERT_ARG(out);
257 	GIT_ASSERT_ARG(reader);
258 	GIT_ASSERT_ARG(filename);
259 
260 	return reader->read(out, out_id, out_filemode, reader, filename);
261 }
262 
git_reader_free(git_reader * reader)263 void git_reader_free(git_reader *reader)
264 {
265 	if (!reader)
266 		return;
267 
268 	git__free(reader);
269 }
270