]>
Commit | Line | Data |
---|---|---|
68535125 VM |
1 | /* |
2 | * This file is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License, version 2, | |
4 | * as published by the Free Software Foundation. | |
5 | * | |
6 | * In addition to the permissions in the GNU General Public License, | |
7 | * the authors give you unlimited permission to link the compiled | |
8 | * version of this file into combinations with other programs, | |
9 | * and to distribute those combinations without any restriction | |
10 | * coming from the use of this file. (The General Public License | |
11 | * restrictions do apply in other respects; for example, they cover | |
12 | * modification of the file, and distribution when not linked into | |
13 | * a combined executable.) | |
14 | * | |
15 | * This file is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; see the file COPYING. If not, write to | |
22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, | |
23 | * Boston, MA 02110-1301, USA. | |
24 | */ | |
25 | ||
26 | #include <stddef.h> | |
27 | ||
28 | #include "common.h" | |
c3a20d5c | 29 | #include "repository.h" |
68535125 VM |
30 | #include "index.h" |
31 | #include "hash.h" | |
44908fe7 VM |
32 | #include "git2/odb.h" |
33 | #include "git2/blob.h" | |
68535125 | 34 | |
68535125 VM |
35 | #define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) |
36 | #define short_entry_size(len) entry_size(struct entry_short, len) | |
37 | #define long_entry_size(len) entry_size(struct entry_long, len) | |
38 | ||
39 | #define minimal_entry_size (offsetof(struct entry_short, path)) | |
40 | ||
68535125 VM |
41 | static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ; |
42 | static const size_t INDEX_HEADER_SIZE = 12; | |
43 | ||
44 | static const unsigned int INDEX_VERSION_NUMBER = 2; | |
348c7335 VM |
45 | static const unsigned int INDEX_VERSION_NUMBER_EXT = 3; |
46 | ||
47 | static const unsigned int INDEX_HEADER_SIG = 0x44495243; | |
48 | static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; | |
4c0b6a6d | 49 | static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; |
68535125 VM |
50 | |
51 | struct index_header { | |
52 | uint32_t signature; | |
53 | uint32_t version; | |
54 | uint32_t entry_count; | |
55 | }; | |
56 | ||
57 | struct index_extension { | |
58 | char signature[4]; | |
59 | uint32_t extension_size; | |
60 | }; | |
61 | ||
a44fc1d4 VM |
62 | struct entry_time { |
63 | uint32_t seconds; | |
64 | uint32_t nanoseconds; | |
65 | }; | |
66 | ||
68535125 | 67 | struct entry_short { |
a44fc1d4 VM |
68 | struct entry_time ctime; |
69 | struct entry_time mtime; | |
68535125 VM |
70 | uint32_t dev; |
71 | uint32_t ino; | |
72 | uint32_t mode; | |
73 | uint32_t uid; | |
74 | uint32_t gid; | |
75 | uint32_t file_size; | |
76 | git_oid oid; | |
77 | uint16_t flags; | |
683581a3 | 78 | char path[1]; /* arbitrary length */ |
68535125 VM |
79 | }; |
80 | ||
81 | struct entry_long { | |
a44fc1d4 VM |
82 | struct entry_time ctime; |
83 | struct entry_time mtime; | |
68535125 VM |
84 | uint32_t dev; |
85 | uint32_t ino; | |
86 | uint32_t mode; | |
87 | uint32_t uid; | |
88 | uint32_t gid; | |
89 | uint32_t file_size; | |
90 | git_oid oid; | |
91 | uint16_t flags; | |
92 | uint16_t flags_extended; | |
683581a3 | 93 | char path[1]; /* arbitrary length */ |
68535125 VM |
94 | }; |
95 | ||
96 | /* local declarations */ | |
97 | static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size); | |
98 | static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size); | |
99 | static int read_header(struct index_header *dest, const void *buffer); | |
100 | ||
101 | static int read_tree(git_index *index, const char *buffer, size_t buffer_size); | |
4c0b6a6d | 102 | static int read_unmerged_internal(git_index *, const char **, size_t buffer_size); |
68535125 | 103 | static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *); |
4c0b6a6d | 104 | static int read_unmerged_internal(git_index *, const char **, size_t buffer_size); |
68535125 | 105 | |
348c7335 VM |
106 | static int parse_index(git_index *index, const char *buffer, size_t buffer_size); |
107 | static void sort_index(git_index *index); | |
817c2820 | 108 | static int write_index(git_index *index, git_filebuf *file); |
68535125 | 109 | |
c4034e63 VM |
110 | int index_srch(const void *key, const void *array_member) |
111 | { | |
112 | const char *filename = (const char *)key; | |
113 | const git_index_entry *entry = *(const git_index_entry **)(array_member); | |
114 | ||
115 | return strcmp(filename, entry->path); | |
116 | } | |
117 | ||
118 | int index_cmp(const void *a, const void *b) | |
119 | { | |
120 | const git_index_entry *entry_a = *(const git_index_entry **)(a); | |
121 | const git_index_entry *entry_b = *(const git_index_entry **)(b); | |
122 | ||
123 | return strcmp(entry_a->path, entry_b->path); | |
124 | } | |
125 | ||
4c0b6a6d JP |
126 | int unmerged_srch(const void *key, const void *array_member) |
127 | { | |
128 | const char *path = (const char *) key; | |
129 | const git_index_entry_unmerged *entry = *(const git_index_entry_unmerged **) (array_member); | |
130 | ||
131 | return strcmp(path, entry->path); | |
132 | } | |
133 | ||
134 | int unmerged_cmp(const void *a, const void *b) | |
135 | { | |
136 | const git_index_entry_unmerged *info_a = *(const git_index_entry_unmerged **)(a); | |
137 | const git_index_entry_unmerged *info_b = *(const git_index_entry_unmerged **)(b); | |
138 | ||
139 | return strcmp(info_a->path, info_b->path); | |
140 | } | |
c4034e63 | 141 | |
c3a20d5c | 142 | static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path) |
68535125 VM |
143 | { |
144 | git_index *index; | |
145 | ||
1795f879 | 146 | assert(index_out && index_path); |
68535125 VM |
147 | |
148 | index = git__malloc(sizeof(git_index)); | |
149 | if (index == NULL) | |
1795f879 | 150 | return GIT_ENOMEM; |
68535125 VM |
151 | |
152 | memset(index, 0x0, sizeof(git_index)); | |
153 | ||
154 | index->index_file_path = git__strdup(index_path); | |
155 | if (index->index_file_path == NULL) { | |
156 | free(index); | |
1795f879 | 157 | return GIT_ENOMEM; |
68535125 VM |
158 | } |
159 | ||
c3a20d5c | 160 | index->repository = owner; |
6fd195d7 | 161 | |
86d7e1ca | 162 | git_vector_init(&index->entries, 32, index_cmp); |
4c0b6a6d | 163 | git_vector_init(&index->unmerged, 32, unmerged_cmp); |
348c7335 | 164 | |
68535125 VM |
165 | /* Check if index file is stored on disk already */ |
166 | if (gitfo_exists(index->index_file_path) == 0) | |
167 | index->on_disk = 1; | |
168 | ||
1795f879 | 169 | *index_out = index; |
3bdc0d4c | 170 | return git_index_read(index); |
68535125 VM |
171 | } |
172 | ||
c3a20d5c VM |
173 | int git_index_open_bare(git_index **index_out, const char *index_path) |
174 | { | |
175 | return index_initialize(index_out, NULL, index_path); | |
176 | } | |
177 | ||
178 | int git_index_open_inrepo(git_index **index_out, git_repository *repo) | |
179 | { | |
c3dd69a9 VM |
180 | if (repo->is_bare) |
181 | return GIT_EBAREINDEX; | |
182 | ||
c3a20d5c VM |
183 | return index_initialize(index_out, repo, repo->path_index); |
184 | } | |
185 | ||
348c7335 VM |
186 | void git_index_free(git_index *index) |
187 | { | |
971c90be | 188 | if (index == NULL || index->repository != NULL) |
348c7335 VM |
189 | return; |
190 | ||
191 | git_index_clear(index); | |
192 | git_vector_free(&index->entries); | |
193 | ||
194 | free(index->index_file_path); | |
195 | free(index); | |
196 | } | |
197 | ||
198 | static void free_tree(git_index_tree *tree) | |
199 | { | |
200 | unsigned int i; | |
201 | ||
202 | if (tree == NULL) | |
203 | return; | |
204 | ||
205 | for (i = 0; i < tree->children_count; ++i) | |
206 | free_tree(tree->children[i]); | |
207 | ||
208 | free(tree->name); | |
209 | free(tree->children); | |
210 | free(tree); | |
211 | } | |
212 | ||
68535125 VM |
213 | void git_index_clear(git_index *index) |
214 | { | |
215 | unsigned int i; | |
6fd195d7 VM |
216 | |
217 | assert(index); | |
218 | ||
c4034e63 VM |
219 | for (i = 0; i < index->entries.length; ++i) { |
220 | git_index_entry *e; | |
221 | e = git_vector_get(&index->entries, i); | |
222 | free(e->path); | |
223 | free(e); | |
224 | } | |
68535125 | 225 | |
c4034e63 | 226 | git_vector_clear(&index->entries); |
68535125 | 227 | index->last_modified = 0; |
68535125 | 228 | |
348c7335 | 229 | free_tree(index->tree); |
68535125 VM |
230 | index->tree = NULL; |
231 | } | |
232 | ||
68535125 VM |
233 | int git_index_read(git_index *index) |
234 | { | |
235 | struct stat indexst; | |
6f02c3ba | 236 | int error = GIT_SUCCESS; |
68535125 | 237 | |
1795f879 | 238 | assert(index->index_file_path); |
68535125 VM |
239 | |
240 | if (!index->on_disk || gitfo_exists(index->index_file_path) < 0) { | |
241 | git_index_clear(index); | |
242 | index->on_disk = 0; | |
6f02c3ba | 243 | return GIT_SUCCESS; |
68535125 VM |
244 | } |
245 | ||
246 | if (gitfo_stat(index->index_file_path, &indexst) < 0) | |
247 | return GIT_EOSERR; | |
248 | ||
c3dd69a9 VM |
249 | if (!S_ISREG(indexst.st_mode)) |
250 | return GIT_ENOTFOUND; | |
251 | ||
68535125 VM |
252 | if (indexst.st_mtime != index->last_modified) { |
253 | ||
254 | gitfo_buf buffer; | |
255 | ||
6f02c3ba | 256 | if (gitfo_read_file(&buffer, index->index_file_path) < GIT_SUCCESS) |
c3dd69a9 | 257 | return GIT_EOSERR; |
68535125 VM |
258 | |
259 | git_index_clear(index); | |
348c7335 | 260 | error = parse_index(index, buffer.data, buffer.len); |
68535125 | 261 | |
6f02c3ba | 262 | if (error == GIT_SUCCESS) |
68535125 VM |
263 | index->last_modified = indexst.st_mtime; |
264 | ||
265 | gitfo_free_buf(&buffer); | |
266 | } | |
267 | ||
268 | return error; | |
269 | } | |
270 | ||
271 | int git_index_write(git_index *index) | |
272 | { | |
817c2820 | 273 | git_filebuf file; |
68535125 | 274 | struct stat indexst; |
348c7335 | 275 | int error; |
68535125 | 276 | |
86d7e1ca | 277 | sort_index(index); |
68535125 | 278 | |
817c2820 | 279 | if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS) |
348c7335 | 280 | return error; |
68535125 | 281 | |
348c7335 | 282 | if ((error = write_index(index, &file)) < GIT_SUCCESS) { |
817c2820 | 283 | git_filebuf_cleanup(&file); |
348c7335 | 284 | return error; |
68535125 VM |
285 | } |
286 | ||
817c2820 | 287 | if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS) |
348c7335 | 288 | return error; |
68535125 VM |
289 | |
290 | if (gitfo_stat(index->index_file_path, &indexst) == 0) { | |
291 | index->last_modified = indexst.st_mtime; | |
292 | index->on_disk = 1; | |
293 | } | |
294 | ||
6f02c3ba | 295 | return GIT_SUCCESS; |
68535125 VM |
296 | } |
297 | ||
0be42199 SC |
298 | unsigned int git_index_entrycount(git_index *index) |
299 | { | |
300 | assert(index); | |
c4034e63 | 301 | return index->entries.length; |
0be42199 SC |
302 | } |
303 | ||
4c0b6a6d JP |
304 | unsigned int git_index_unmerged_entrycount(git_index *index) |
305 | { | |
306 | assert(index); | |
307 | ||
308 | if (!&index->unmerged) | |
309 | return 0; | |
310 | ||
311 | return index->unmerged.length; | |
312 | } | |
313 | ||
6fd195d7 VM |
314 | git_index_entry *git_index_get(git_index *index, int n) |
315 | { | |
3f43678e | 316 | assert(index); |
348c7335 | 317 | sort_index(index); |
c4034e63 | 318 | return git_vector_get(&index->entries, (unsigned int)n); |
6fd195d7 VM |
319 | } |
320 | ||
c3a20d5c | 321 | int git_index_add(git_index *index, const char *rel_path, int stage) |
68535125 VM |
322 | { |
323 | git_index_entry entry; | |
c3a20d5c VM |
324 | char full_path[GIT_PATH_MAX]; |
325 | struct stat st; | |
326 | int error; | |
68535125 | 327 | |
c3a20d5c VM |
328 | if (index->repository == NULL) |
329 | return GIT_EBAREINDEX; | |
68535125 | 330 | |
995f9c34 | 331 | git__joinpath(full_path, index->repository->path_workdir, rel_path); |
68535125 | 332 | |
c3a20d5c VM |
333 | if (gitfo_exists(full_path) < 0) |
334 | return GIT_ENOTFOUND; | |
335 | ||
336 | if (gitfo_stat(full_path, &st) < 0) | |
337 | return GIT_EOSERR; | |
68535125 VM |
338 | |
339 | if (stage < 0 || stage > 3) | |
340 | return GIT_ERROR; | |
341 | ||
c3a20d5c | 342 | memset(&entry, 0x0, sizeof(git_index_entry)); |
68535125 | 343 | |
56d8ca26 | 344 | entry.ctime.seconds = (git_time_t)st.st_ctime; |
345 | entry.mtime.seconds = (git_time_t)st.st_mtime; | |
c3a20d5c VM |
346 | /* entry.mtime.nanoseconds = st.st_mtimensec; */ |
347 | /* entry.ctime.nanoseconds = st.st_ctimensec; */ | |
348 | entry.dev= st.st_rdev; | |
349 | entry.ino = st.st_ino; | |
350 | entry.mode = st.st_mode; | |
351 | entry.uid = st.st_uid; | |
352 | entry.gid = st.st_gid; | |
f0bde7fa | 353 | entry.file_size = st.st_size; |
c3a20d5c VM |
354 | |
355 | /* write the blob to disk and get the oid */ | |
72a3fe42 | 356 | if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS) |
c3a20d5c | 357 | return error; |
68535125 | 358 | |
c3a20d5c VM |
359 | entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT); |
360 | entry.path = (char *)rel_path; /* do not duplicate; index_insert already does this */ | |
361 | ||
362 | return git_index_insert(index, &entry); | |
68535125 VM |
363 | } |
364 | ||
348c7335 | 365 | void sort_index(git_index *index) |
68535125 | 366 | { |
86d7e1ca | 367 | git_vector_sort(&index->entries); |
68535125 VM |
368 | } |
369 | ||
c3a20d5c | 370 | int git_index_insert(git_index *index, const git_index_entry *source_entry) |
68535125 | 371 | { |
c4034e63 | 372 | git_index_entry *entry; |
c3a20d5c VM |
373 | size_t path_length; |
374 | int position; | |
68535125 | 375 | |
c3a20d5c | 376 | assert(index && source_entry); |
68535125 | 377 | |
c3a20d5c VM |
378 | if (source_entry->path == NULL) |
379 | return GIT_EMISSINGOBJDATA; | |
68535125 | 380 | |
c4034e63 VM |
381 | entry = git__malloc(sizeof(git_index_entry)); |
382 | if (entry == NULL) | |
383 | return GIT_ENOMEM; | |
68535125 | 384 | |
c4034e63 | 385 | memcpy(entry, source_entry, sizeof(git_index_entry)); |
c3a20d5c VM |
386 | |
387 | /* duplicate the path string so we own it */ | |
c4034e63 VM |
388 | entry->path = git__strdup(entry->path); |
389 | if (entry->path == NULL) | |
c3a20d5c VM |
390 | return GIT_ENOMEM; |
391 | ||
392 | /* make sure that the path length flag is correct */ | |
c4034e63 | 393 | path_length = strlen(entry->path); |
c3a20d5c | 394 | |
c4034e63 | 395 | entry->flags &= ~GIT_IDXENTRY_NAMEMASK; |
c3a20d5c VM |
396 | |
397 | if (path_length < GIT_IDXENTRY_NAMEMASK) | |
c4034e63 | 398 | entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK; |
c3a20d5c | 399 | else |
c4034e63 | 400 | entry->flags |= GIT_IDXENTRY_NAMEMASK;; |
c3a20d5c | 401 | |
68535125 | 402 | |
c4034e63 VM |
403 | /* look if an entry with this path already exists */ |
404 | position = git_index_find(index, source_entry->path); | |
68535125 | 405 | |
c4034e63 VM |
406 | /* if no entry exists, add the entry at the end; |
407 | * the index is no longer sorted */ | |
408 | if (position == GIT_ENOTFOUND) { | |
6f02c3ba | 409 | if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) |
c4034e63 | 410 | return GIT_ENOMEM; |
68535125 | 411 | |
c4034e63 VM |
412 | /* if a previous entry exists, replace it */ |
413 | } else { | |
414 | git_index_entry **entry_array = (git_index_entry **)index->entries.contents; | |
3f43678e | 415 | |
c4034e63 VM |
416 | free(entry_array[position]->path); |
417 | free(entry_array[position]); | |
68535125 | 418 | |
c4034e63 VM |
419 | entry_array[position] = entry; |
420 | } | |
68535125 | 421 | |
3f43678e | 422 | return GIT_SUCCESS; |
68535125 VM |
423 | } |
424 | ||
c4034e63 | 425 | int git_index_remove(git_index *index, int position) |
68535125 | 426 | { |
c4034e63 | 427 | assert(index); |
348c7335 | 428 | sort_index(index); |
c4034e63 VM |
429 | return git_vector_remove(&index->entries, (unsigned int)position); |
430 | } | |
68535125 | 431 | |
c4034e63 VM |
432 | int git_index_find(git_index *index, const char *path) |
433 | { | |
348c7335 | 434 | sort_index(index); |
86d7e1ca | 435 | return git_vector_bsearch2(&index->entries, index_srch, path); |
68535125 VM |
436 | } |
437 | ||
4c0b6a6d JP |
438 | int git_index_get_unmerged(git_index_entry_unmerged **entry, git_index *index, const char *path) |
439 | { | |
440 | int pos; | |
441 | assert(index); | |
442 | ||
443 | if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS) | |
444 | return pos; | |
445 | ||
446 | if ((*entry = git_vector_get(&index->unmerged, pos)) == NULL) { | |
447 | return GIT_ENOTFOUND; | |
448 | } | |
449 | ||
450 | return GIT_SUCCESS; | |
451 | } | |
452 | ||
453 | ||
68535125 VM |
454 | static git_index_tree *read_tree_internal( |
455 | const char **buffer_in, const char *buffer_end, git_index_tree *parent) | |
456 | { | |
457 | git_index_tree *tree; | |
458 | const char *name_start, *buffer; | |
c6e65aca | 459 | long count; |
68535125 VM |
460 | |
461 | if ((tree = git__malloc(sizeof(git_index_tree))) == NULL) | |
462 | return NULL; | |
463 | ||
464 | memset(tree, 0x0, sizeof(git_index_tree)); | |
465 | tree->parent = parent; | |
466 | ||
467 | buffer = name_start = *buffer_in; | |
468 | ||
469 | if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) | |
470 | goto error_cleanup; | |
471 | ||
472 | /* NUL-terminated tree name */ | |
473 | tree->name = git__strdup(name_start); | |
474 | if (++buffer >= buffer_end) | |
475 | goto error_cleanup; | |
476 | ||
477 | /* Blank-terminated ASCII decimal number of entries in this tree */ | |
c6e65aca VM |
478 | if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || |
479 | count < 0) | |
480 | goto error_cleanup; | |
481 | ||
482 | tree->entries = (size_t)count; | |
483 | ||
68535125 VM |
484 | if (*buffer != ' ' || ++buffer >= buffer_end) |
485 | goto error_cleanup; | |
486 | ||
487 | /* Number of children of the tree, newline-terminated */ | |
c6e65aca VM |
488 | if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || |
489 | count < 0) | |
490 | goto error_cleanup; | |
491 | ||
492 | tree->children_count = (size_t)count; | |
493 | ||
68535125 VM |
494 | if (*buffer != '\n' || ++buffer >= buffer_end) |
495 | goto error_cleanup; | |
496 | ||
497 | /* 160-bit SHA-1 for this tree and it's children */ | |
498 | if (buffer + GIT_OID_RAWSZ > buffer_end) | |
499 | goto error_cleanup; | |
500 | ||
501 | git_oid_mkraw(&tree->oid, (const unsigned char *)buffer); | |
502 | buffer += GIT_OID_RAWSZ; | |
503 | ||
504 | /* Parse children: */ | |
505 | if (tree->children_count > 0) { | |
506 | unsigned int i; | |
507 | ||
508 | tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *)); | |
509 | if (tree->children == NULL) | |
510 | goto error_cleanup; | |
511 | ||
512 | for (i = 0; i < tree->children_count; ++i) { | |
513 | tree->children[i] = read_tree_internal(&buffer, buffer_end, tree); | |
514 | ||
515 | if (tree->children[i] == NULL) | |
516 | goto error_cleanup; | |
517 | } | |
518 | } | |
519 | ||
520 | *buffer_in = buffer; | |
521 | return tree; | |
522 | ||
523 | error_cleanup: | |
348c7335 | 524 | free_tree(tree); |
68535125 VM |
525 | return NULL; |
526 | } | |
527 | ||
528 | static int read_tree(git_index *index, const char *buffer, size_t buffer_size) | |
529 | { | |
530 | const char *buffer_end = buffer + buffer_size; | |
531 | ||
532 | index->tree = read_tree_internal(&buffer, buffer_end, NULL); | |
6f02c3ba | 533 | return (index->tree != NULL && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED; |
68535125 VM |
534 | } |
535 | ||
4c0b6a6d JP |
536 | static int read_unmerged_internal( |
537 | git_index *index, const char **buffer_in, size_t buffer_size) | |
538 | { | |
539 | const char *buffer, *endptr; | |
540 | size_t size, len; | |
541 | int i; | |
542 | ||
543 | size = buffer_size; | |
544 | ||
545 | while (size) { | |
546 | git_index_entry_unmerged *lost; | |
547 | ||
548 | buffer = *buffer_in; | |
549 | ||
550 | len = strlen(buffer) + 1; | |
551 | if (size <= len) | |
552 | return GIT_ERROR; | |
553 | ||
554 | if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL) | |
555 | return GIT_ERROR; | |
556 | ||
557 | if ((lost->path = git__malloc(strlen(buffer))) == NULL) | |
558 | return GIT_ERROR; | |
559 | strcpy(lost->path, buffer); | |
560 | ||
561 | if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS) | |
562 | return GIT_ERROR; | |
563 | ||
564 | size -= len; | |
565 | buffer += len; | |
566 | ||
567 | for (i = 0; i < 3; i++) { | |
568 | if (git__strtol32((long int *) &lost->mode[i], buffer, &endptr, 8) < GIT_SUCCESS || !endptr || endptr == buffer || *endptr) | |
569 | return GIT_ERROR; | |
570 | len = (endptr + 1) - (char *) buffer; | |
571 | if (size <= len) | |
572 | return GIT_ERROR; | |
573 | size -= len; | |
574 | buffer += len; | |
575 | } | |
576 | ||
577 | for (i = 0; i < 3; i++) { | |
578 | if (!lost->mode[i]) | |
579 | continue; | |
580 | if (size < 20) | |
581 | return GIT_ERROR; | |
582 | git_oid_mkraw(&lost->oid[i], (unsigned char *) buffer); | |
583 | size -= 20; | |
584 | buffer += 20; | |
585 | } | |
586 | } | |
587 | ||
588 | *buffer_in = buffer; | |
589 | return GIT_SUCCESS; | |
590 | } | |
591 | ||
592 | static int read_unmerged(git_index *index, const char *buffer, size_t buffer_size) | |
593 | { | |
594 | read_unmerged_internal(index, &buffer, buffer_size); | |
595 | return (&index->unmerged != NULL) ? GIT_SUCCESS : GIT_EOBJCORRUPTED; | |
596 | } | |
597 | ||
68535125 VM |
598 | static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) |
599 | { | |
600 | size_t path_length, entry_size; | |
601 | uint16_t flags_raw; | |
602 | const char *path_ptr; | |
603 | const struct entry_short *source; | |
604 | ||
605 | if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) | |
606 | return 0; | |
607 | ||
91e88941 VM |
608 | memset(dest, 0x0, sizeof(git_index_entry)); |
609 | ||
68535125 VM |
610 | source = (const struct entry_short *)(buffer); |
611 | ||
56d8ca26 | 612 | dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); |
613 | dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds); | |
614 | dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); | |
615 | dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds); | |
68535125 VM |
616 | dest->dev = ntohl(source->dev); |
617 | dest->ino = ntohl(source->ino); | |
618 | dest->mode = ntohl(source->mode); | |
619 | dest->uid = ntohl(source->uid); | |
620 | dest->gid = ntohl(source->gid); | |
621 | dest->file_size = ntohl(source->file_size); | |
622 | git_oid_cpy(&dest->oid, &source->oid); | |
623 | dest->flags = ntohs(source->flags); | |
624 | ||
625 | if (dest->flags & GIT_IDXENTRY_EXTENDED) { | |
626 | struct entry_long *source_l = (struct entry_long *)source; | |
627 | path_ptr = source_l->path; | |
628 | ||
629 | flags_raw = ntohs(source_l->flags_extended); | |
630 | memcpy(&dest->flags_extended, &flags_raw, 2); | |
631 | } else | |
632 | path_ptr = source->path; | |
633 | ||
634 | path_length = dest->flags & GIT_IDXENTRY_NAMEMASK; | |
635 | ||
636 | /* if this is a very long string, we must find its | |
637 | * real length without overflowing */ | |
638 | if (path_length == 0xFFF) { | |
639 | const char *path_end; | |
640 | ||
641 | path_end = memchr(path_ptr, '\0', buffer_size); | |
642 | if (path_end == NULL) | |
643 | return 0; | |
644 | ||
645 | path_length = path_end - path_ptr; | |
646 | } | |
647 | ||
648 | if (dest->flags & GIT_IDXENTRY_EXTENDED) | |
649 | entry_size = long_entry_size(path_length); | |
650 | else | |
651 | entry_size = short_entry_size(path_length); | |
652 | ||
653 | if (INDEX_FOOTER_SIZE + entry_size > buffer_size) | |
654 | return 0; | |
655 | ||
656 | dest->path = git__strdup(path_ptr); | |
c3a20d5c | 657 | assert(dest->path); |
68535125 VM |
658 | |
659 | return entry_size; | |
660 | } | |
661 | ||
662 | static int read_header(struct index_header *dest, const void *buffer) | |
663 | { | |
664 | const struct index_header *source; | |
665 | source = (const struct index_header *)(buffer); | |
666 | ||
348c7335 VM |
667 | dest->signature = ntohl(source->signature); |
668 | if (dest->signature != INDEX_HEADER_SIG) | |
68535125 VM |
669 | return GIT_EOBJCORRUPTED; |
670 | ||
671 | dest->version = ntohl(source->version); | |
348c7335 VM |
672 | if (dest->version != INDEX_VERSION_NUMBER_EXT && |
673 | dest->version != INDEX_VERSION_NUMBER) | |
68535125 VM |
674 | return GIT_EOBJCORRUPTED; |
675 | ||
676 | dest->entry_count = ntohl(source->entry_count); | |
6f02c3ba | 677 | return GIT_SUCCESS; |
68535125 VM |
678 | } |
679 | ||
680 | static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size) | |
681 | { | |
682 | const struct index_extension *source; | |
683 | struct index_extension dest; | |
684 | size_t total_size; | |
685 | ||
686 | source = (const struct index_extension *)(buffer); | |
687 | ||
688 | memcpy(dest.signature, source->signature, 4); | |
689 | dest.extension_size = ntohl(source->extension_size); | |
690 | ||
691 | total_size = dest.extension_size + sizeof(struct index_extension); | |
692 | ||
693 | if (buffer_size - total_size < INDEX_FOOTER_SIZE) | |
694 | return 0; | |
695 | ||
696 | /* optional extension */ | |
697 | if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') { | |
698 | /* tree cache */ | |
699 | if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) { | |
700 | ||
6f02c3ba | 701 | if (read_tree(index, buffer + 8, dest.extension_size) < GIT_SUCCESS) |
68535125 | 702 | return 0; |
4c0b6a6d JP |
703 | |
704 | } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { | |
705 | ||
706 | if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS) | |
707 | return 0; | |
708 | } else { | |
709 | ; | |
68535125 VM |
710 | } |
711 | } else { | |
712 | /* we cannot handle non-ignorable extensions; | |
713 | * in fact they aren't even defined in the standard */ | |
714 | return 0; | |
715 | } | |
716 | ||
717 | return total_size; | |
718 | } | |
719 | ||
348c7335 | 720 | static int parse_index(git_index *index, const char *buffer, size_t buffer_size) |
68535125 VM |
721 | { |
722 | unsigned int i; | |
723 | struct index_header header; | |
724 | git_oid checksum_calculated, checksum_expected; | |
725 | ||
726 | #define seek_forward(_increase) { \ | |
727 | if (_increase >= buffer_size) \ | |
728 | return GIT_EOBJCORRUPTED; \ | |
729 | buffer += _increase; \ | |
730 | buffer_size -= _increase;\ | |
731 | } | |
732 | ||
733 | if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE) | |
734 | return GIT_EOBJCORRUPTED; | |
735 | ||
736 | /* Precalculate the SHA1 of the files's contents -- we'll match it to | |
737 | * the provided SHA1 in the footer */ | |
738 | git_hash_buf(&checksum_calculated, (const void *)buffer, buffer_size - INDEX_FOOTER_SIZE); | |
739 | ||
740 | /* Parse header */ | |
6f02c3ba | 741 | if (read_header(&header, buffer) < GIT_SUCCESS) |
68535125 VM |
742 | return GIT_EOBJCORRUPTED; |
743 | ||
744 | seek_forward(INDEX_HEADER_SIZE); | |
745 | ||
c4034e63 | 746 | git_vector_clear(&index->entries); |
68535125 VM |
747 | |
748 | /* Parse all the entries */ | |
c4034e63 | 749 | for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { |
68535125 | 750 | size_t entry_size; |
c4034e63 VM |
751 | git_index_entry *entry; |
752 | ||
753 | entry = git__malloc(sizeof(git_index_entry)); | |
754 | if (entry == NULL) | |
755 | return GIT_ENOMEM; | |
756 | ||
757 | entry_size = read_entry(entry, buffer, buffer_size); | |
68535125 VM |
758 | |
759 | /* 0 bytes read means an object corruption */ | |
760 | if (entry_size == 0) | |
761 | return GIT_EOBJCORRUPTED; | |
762 | ||
6f02c3ba | 763 | if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) |
c4034e63 VM |
764 | return GIT_ENOMEM; |
765 | ||
68535125 VM |
766 | seek_forward(entry_size); |
767 | } | |
768 | ||
c4034e63 | 769 | if (i != header.entry_count) |
c3a20d5c VM |
770 | return GIT_EOBJCORRUPTED; |
771 | ||
68535125 VM |
772 | /* There's still space for some extensions! */ |
773 | while (buffer_size > INDEX_FOOTER_SIZE) { | |
774 | size_t extension_size; | |
775 | ||
776 | extension_size = read_extension(index, buffer, buffer_size); | |
777 | ||
778 | /* see if we have read any bytes from the extension */ | |
779 | if (extension_size == 0) | |
780 | return GIT_EOBJCORRUPTED; | |
781 | ||
782 | seek_forward(extension_size); | |
783 | } | |
784 | ||
785 | if (buffer_size != INDEX_FOOTER_SIZE) | |
786 | return GIT_EOBJCORRUPTED; | |
787 | ||
788 | /* 160-bit SHA-1 over the content of the index file before this checksum. */ | |
789 | git_oid_mkraw(&checksum_expected, (const unsigned char *)buffer); | |
790 | ||
791 | if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0) | |
792 | return GIT_EOBJCORRUPTED; | |
793 | ||
794 | #undef seek_forward | |
795 | ||
86d7e1ca VM |
796 | /* force sorting in the vector: the entries are |
797 | * assured to be sorted on the index */ | |
798 | index->entries.sorted = 1; | |
6f02c3ba | 799 | return GIT_SUCCESS; |
68535125 VM |
800 | } |
801 | ||
817c2820 | 802 | static int write_disk_entry(git_filebuf *file, git_index_entry *entry) |
68535125 | 803 | { |
348c7335 | 804 | struct entry_short *ondisk; |
817c2820 | 805 | size_t path_len, disk_size; |
348c7335 | 806 | char *path; |
68535125 | 807 | |
348c7335 | 808 | path_len = strlen(entry->path); |
68535125 | 809 | |
348c7335 | 810 | if (entry->flags & GIT_IDXENTRY_EXTENDED) |
817c2820 | 811 | disk_size = long_entry_size(path_len); |
348c7335 | 812 | else |
817c2820 | 813 | disk_size = short_entry_size(path_len); |
68535125 | 814 | |
817c2820 VM |
815 | if (git_filebuf_reserve(file, (void **)&ondisk, disk_size) < GIT_SUCCESS) |
816 | return GIT_ENOMEM; | |
817 | ||
818 | memset(ondisk, 0x0, disk_size); | |
68535125 | 819 | |
084c1935 VM |
820 | ondisk->ctime.seconds = htonl((unsigned long)entry->ctime.seconds); |
821 | ondisk->mtime.seconds = htonl((unsigned long)entry->mtime.seconds); | |
348c7335 VM |
822 | ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds); |
823 | ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds); | |
824 | ondisk->dev = htonl(entry->dev); | |
825 | ondisk->ino = htonl(entry->ino); | |
826 | ondisk->mode = htonl(entry->mode); | |
827 | ondisk->uid = htonl(entry->uid); | |
828 | ondisk->gid = htonl(entry->gid); | |
084c1935 | 829 | ondisk->file_size = htonl((unsigned long)entry->file_size); |
348c7335 VM |
830 | |
831 | git_oid_cpy(&ondisk->oid, &entry->oid); | |
832 | ||
833 | ondisk->flags = htons(entry->flags); | |
834 | ||
835 | if (entry->flags & GIT_IDXENTRY_EXTENDED) { | |
836 | struct entry_long *ondisk_ext; | |
837 | ondisk_ext = (struct entry_long *)ondisk; | |
838 | ondisk_ext->flags_extended = htons(entry->flags_extended); | |
839 | path = ondisk_ext->path; | |
840 | } | |
841 | else | |
842 | path = ondisk->path; | |
843 | ||
844 | memcpy(path, entry->path, path_len); | |
68535125 | 845 | |
e822508a | 846 | return GIT_SUCCESS; |
68535125 VM |
847 | } |
848 | ||
817c2820 | 849 | static int write_entries(git_index *index, git_filebuf *file) |
348c7335 VM |
850 | { |
851 | unsigned int i; | |
68535125 | 852 | |
c4034e63 | 853 | for (i = 0; i < index->entries.length; ++i) { |
68535125 | 854 | git_index_entry *entry; |
c4034e63 | 855 | entry = git_vector_get(&index->entries, i); |
817c2820 | 856 | if (write_disk_entry(file, entry) < GIT_SUCCESS) |
348c7335 | 857 | return GIT_ENOMEM; |
68535125 VM |
858 | } |
859 | ||
348c7335 VM |
860 | return GIT_SUCCESS; |
861 | } | |
862 | ||
817c2820 | 863 | static int write_index(git_index *index, git_filebuf *file) |
348c7335 VM |
864 | { |
865 | int error = GIT_SUCCESS; | |
348c7335 VM |
866 | git_oid hash_final; |
867 | ||
868 | struct index_header header; | |
869 | ||
870 | int is_extended = 1; | |
871 | ||
817c2820 | 872 | assert(index && file); |
348c7335 VM |
873 | |
874 | header.signature = htonl(INDEX_HEADER_SIG); | |
875 | header.version = htonl(is_extended ? INDEX_VERSION_NUMBER : INDEX_VERSION_NUMBER_EXT); | |
876 | header.entry_count = htonl(index->entries.length); | |
877 | ||
817c2820 | 878 | git_filebuf_write(file, &header, sizeof(struct index_header)); |
348c7335 | 879 | |
817c2820 | 880 | error = write_entries(index, file); |
348c7335 | 881 | if (error < GIT_SUCCESS) |
817c2820 | 882 | return error; |
68535125 VM |
883 | |
884 | /* TODO: write extensions (tree cache) */ | |
885 | ||
817c2820 VM |
886 | /* get out the hash for all the contents we've appended to the file */ |
887 | git_filebuf_hash(&hash_final, file); | |
888 | ||
889 | /* write it at the end of the file */ | |
890 | git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ); | |
68535125 VM |
891 | |
892 | return error; | |
893 | } |