File: | tog/../lib/object_create.c |
Warning: | line 442, column 10 Potential leak of memory pointed to by 'msg' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org> | |||
3 | * | |||
4 | * Permission to use, copy, modify, and distribute this software for any | |||
5 | * purpose with or without fee is hereby granted, provided that the above | |||
6 | * copyright notice and this permission notice appear in all copies. | |||
7 | * | |||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
15 | */ | |||
16 | ||||
17 | #include <sys/types.h> | |||
18 | #include <sys/stat.h> | |||
19 | #include <sys/queue.h> | |||
20 | ||||
21 | #include <ctype.h> | |||
22 | #include <errno(*__errno()).h> | |||
23 | #include <fcntl.h> | |||
24 | #include <limits.h> | |||
25 | #include <stdio.h> | |||
26 | #include <stdlib.h> | |||
27 | #include <string.h> | |||
28 | #include <stdint.h> | |||
29 | #include <sha1.h> | |||
30 | #include <unistd.h> | |||
31 | #include <zlib.h> | |||
32 | ||||
33 | #include "got_error.h" | |||
34 | #include "got_object.h" | |||
35 | #include "got_repository.h" | |||
36 | #include "got_opentemp.h" | |||
37 | #include "got_path.h" | |||
38 | ||||
39 | #include "got_lib_sha1.h" | |||
40 | #include "got_lib_deflate.h" | |||
41 | #include "got_lib_delta.h" | |||
42 | #include "got_lib_object.h" | |||
43 | #include "got_lib_object_parse.h" | |||
44 | #include "got_lib_lockfile.h" | |||
45 | ||||
46 | #ifndef nitems | |||
47 | #define nitems(_a)(sizeof(_a) / sizeof((_a)[0])) (sizeof(_a) / sizeof((_a)[0])) | |||
48 | #endif | |||
49 | ||||
50 | static const struct got_error * | |||
51 | create_object_file(struct got_object_id *id, FILE *content, | |||
52 | struct got_repository *repo) | |||
53 | { | |||
54 | const struct got_error *err = NULL((void *)0), *unlock_err = NULL((void *)0); | |||
55 | char *objpath = NULL((void *)0), *tmppath = NULL((void *)0); | |||
56 | FILE *tmpfile = NULL((void *)0); | |||
57 | struct got_lockfile *lf = NULL((void *)0); | |||
58 | size_t tmplen = 0; | |||
59 | ||||
60 | err = got_object_get_path(&objpath, id, repo); | |||
61 | if (err) | |||
62 | return err; | |||
63 | ||||
64 | err = got_opentemp_named(&tmppath, &tmpfile, objpath); | |||
65 | if (err) { | |||
66 | char *parent_path; | |||
67 | if (!(err->code == GOT_ERR_ERRNO1 && errno(*__errno()) == ENOENT2)) | |||
68 | goto done; | |||
69 | err = got_path_dirname(&parent_path, objpath); | |||
70 | if (err) | |||
71 | goto done; | |||
72 | err = got_path_mkdir(parent_path); | |||
73 | free(parent_path); | |||
74 | if (err) | |||
75 | goto done; | |||
76 | err = got_opentemp_named(&tmppath, &tmpfile, objpath); | |||
77 | if (err) | |||
78 | goto done; | |||
79 | } | |||
80 | ||||
81 | if (fchmod(fileno(tmpfile)(!__isthreaded ? ((tmpfile)->_file) : (fileno)(tmpfile)), GOT_DEFAULT_FILE_MODE(0100000 | 0000400|0000200 | 0000040 | 0000004)) != 0) { | |||
82 | err = got_error_from_errno2("fchmod", tmppath); | |||
83 | goto done; | |||
84 | } | |||
85 | ||||
86 | err = got_deflate_to_file(&tmplen, content, tmpfile, NULL((void *)0)); | |||
87 | if (err) | |||
88 | goto done; | |||
89 | ||||
90 | err = got_lockfile_lock(&lf, objpath); | |||
91 | if (err) | |||
92 | goto done; | |||
93 | ||||
94 | if (rename(tmppath, objpath) != 0) { | |||
95 | err = got_error_from_errno3("rename", tmppath, objpath); | |||
96 | goto done; | |||
97 | } | |||
98 | free(tmppath); | |||
99 | tmppath = NULL((void *)0); | |||
100 | done: | |||
101 | free(objpath); | |||
102 | if (tmppath) { | |||
103 | if (unlink(tmppath) != 0 && err == NULL((void *)0)) | |||
104 | err = got_error_from_errno2("unlink", tmppath); | |||
105 | free(tmppath); | |||
106 | } | |||
107 | if (tmpfile && fclose(tmpfile) == EOF(-1) && err == NULL((void *)0)) | |||
108 | err = got_error_from_errno("fclose"); | |||
109 | if (lf) | |||
110 | unlock_err = got_lockfile_unlock(lf); | |||
111 | return err ? err : unlock_err; | |||
112 | } | |||
113 | ||||
114 | const struct got_error * | |||
115 | got_object_blob_file_create(struct got_object_id **id, FILE **blobfile, | |||
116 | const char *ondisk_path) | |||
117 | { | |||
118 | const struct got_error *err = NULL((void *)0); | |||
119 | char *header = NULL((void *)0); | |||
120 | int fd = -1; | |||
121 | struct stat sb; | |||
122 | SHA1_CTX sha1_ctx; | |||
123 | size_t headerlen = 0, n; | |||
124 | ||||
125 | *id = NULL((void *)0); | |||
126 | *blobfile = NULL((void *)0); | |||
127 | ||||
128 | SHA1Init(&sha1_ctx); | |||
129 | ||||
130 | fd = open(ondisk_path, O_RDONLY0x0000 | O_NOFOLLOW0x0100); | |||
131 | if (fd == -1) { | |||
132 | if (errno(*__errno()) != ELOOP62) | |||
133 | return got_error_from_errno2("open", ondisk_path); | |||
134 | ||||
135 | if (lstat(ondisk_path, &sb) == -1) { | |||
136 | err = got_error_from_errno2("lstat", ondisk_path); | |||
137 | goto done; | |||
138 | } | |||
139 | } else if (fstat(fd, &sb) == -1) { | |||
140 | err = got_error_from_errno2("fstat", ondisk_path); | |||
141 | goto done; | |||
142 | } | |||
143 | ||||
144 | if (asprintf(&header, "%s %lld", GOT_OBJ_LABEL_BLOB"blob", | |||
145 | (long long)sb.st_size) == -1) { | |||
146 | err = got_error_from_errno("asprintf"); | |||
147 | goto done; | |||
148 | } | |||
149 | headerlen = strlen(header) + 1; | |||
150 | SHA1Update(&sha1_ctx, header, headerlen); | |||
151 | ||||
152 | *blobfile = got_opentemp(); | |||
153 | if (*blobfile == NULL((void *)0)) { | |||
154 | err = got_error_from_errno("got_opentemp"); | |||
155 | goto done; | |||
156 | } | |||
157 | ||||
158 | n = fwrite(header, 1, headerlen, *blobfile); | |||
159 | if (n != headerlen) { | |||
160 | err = got_ferror(*blobfile, GOT_ERR_IO6); | |||
161 | goto done; | |||
162 | } | |||
163 | for (;;) { | |||
164 | char buf[PATH_MAX1024 * 8]; | |||
165 | ssize_t inlen; | |||
166 | ||||
167 | if (S_ISLNK(sb.st_mode)((sb.st_mode & 0170000) == 0120000)) { | |||
168 | inlen = readlink(ondisk_path, buf, sizeof(buf)); | |||
169 | if (inlen == -1) { | |||
170 | err = got_error_from_errno("readlink"); | |||
171 | goto done; | |||
172 | } | |||
173 | } else { | |||
174 | inlen = read(fd, buf, sizeof(buf)); | |||
175 | if (inlen == -1) { | |||
176 | err = got_error_from_errno("read"); | |||
177 | goto done; | |||
178 | } | |||
179 | } | |||
180 | if (inlen == 0) | |||
181 | break; /* EOF */ | |||
182 | SHA1Update(&sha1_ctx, buf, inlen); | |||
183 | n = fwrite(buf, 1, inlen, *blobfile); | |||
184 | if (n != inlen) { | |||
185 | err = got_ferror(*blobfile, GOT_ERR_IO6); | |||
186 | goto done; | |||
187 | } | |||
188 | if (S_ISLNK(sb.st_mode)((sb.st_mode & 0170000) == 0120000)) | |||
189 | break; | |||
190 | } | |||
191 | ||||
192 | *id = malloc(sizeof(**id)); | |||
193 | if (*id == NULL((void *)0)) { | |||
194 | err = got_error_from_errno("malloc"); | |||
195 | goto done; | |||
196 | } | |||
197 | SHA1Final((*id)->sha1, &sha1_ctx); | |||
198 | ||||
199 | if (fflush(*blobfile) != 0) { | |||
200 | err = got_error_from_errno("fflush"); | |||
201 | goto done; | |||
202 | } | |||
203 | rewind(*blobfile); | |||
204 | done: | |||
205 | free(header); | |||
206 | if (fd != -1 && close(fd) == -1 && err == NULL((void *)0)) | |||
207 | err = got_error_from_errno("close"); | |||
208 | if (err) { | |||
209 | free(*id); | |||
210 | *id = NULL((void *)0); | |||
211 | if (*blobfile) { | |||
212 | fclose(*blobfile); | |||
213 | *blobfile = NULL((void *)0); | |||
214 | } | |||
215 | } | |||
216 | return err; | |||
217 | } | |||
218 | ||||
219 | const struct got_error * | |||
220 | got_object_blob_create(struct got_object_id **id, const char *ondisk_path, | |||
221 | struct got_repository *repo) | |||
222 | { | |||
223 | const struct got_error *err = NULL((void *)0); | |||
224 | FILE *blobfile = NULL((void *)0); | |||
225 | ||||
226 | err = got_object_blob_file_create(id, &blobfile, ondisk_path); | |||
227 | if (err) | |||
228 | return err; | |||
229 | ||||
230 | err = create_object_file(*id, blobfile, repo); | |||
231 | if (fclose(blobfile) == EOF(-1) && err == NULL((void *)0)) | |||
232 | err = got_error_from_errno("fclose"); | |||
233 | if (err) { | |||
234 | free(*id); | |||
235 | *id = NULL((void *)0); | |||
236 | } | |||
237 | return err; | |||
238 | } | |||
239 | ||||
240 | static const struct got_error * | |||
241 | te_mode2str(char *buf, size_t len, struct got_tree_entry *te) | |||
242 | { | |||
243 | int ret; | |||
244 | mode_t mode; | |||
245 | ||||
246 | /* | |||
247 | * Some Git implementations are picky about modes seen in tree entries. | |||
248 | * For best compatibility we normalize the file/directory mode here. | |||
249 | * Note that we do not support committing symlinks. | |||
250 | */ | |||
251 | if (S_ISREG(te->mode)((te->mode & 0170000) == 0100000)) { | |||
252 | mode = GOT_DEFAULT_FILE_MODE(0100000 | 0000400|0000200 | 0000040 | 0000004); | |||
253 | if (te->mode & (S_IXUSR0000100 | S_IXGRP0000010 | S_IXOTH0000001)) | |||
254 | mode |= S_IXUSR0000100 | S_IXGRP0000010 | S_IXOTH0000001; | |||
255 | } else if (got_object_tree_entry_is_submodule(te)) | |||
256 | mode = S_IFDIR0040000 | S_IFLNK0120000; | |||
257 | else if (S_ISLNK(te->mode)((te->mode & 0170000) == 0120000)) | |||
258 | mode = S_IFLNK0120000; /* Git leaves all the other bits unset. */ | |||
259 | else if (S_ISDIR(te->mode)((te->mode & 0170000) == 0040000)) | |||
260 | mode = S_IFDIR0040000; /* Git leaves all the other bits unset. */ | |||
261 | else | |||
262 | return got_error(GOT_ERR_BAD_FILETYPE3); | |||
263 | ||||
264 | ret = snprintf(buf, len, "%o ", mode); | |||
265 | if (ret == -1 || ret >= len) | |||
266 | return got_error(GOT_ERR_NO_SPACE9); | |||
267 | return NULL((void *)0); | |||
268 | } | |||
269 | ||||
270 | /* | |||
271 | * Git expects directory tree entries to be sorted with an imaginary slash | |||
272 | * appended to their name, and will break otherwise. Let's be nice. | |||
273 | * This function is intended to be used with mergesort(3) to sort an | |||
274 | * array of pointers to struct got_tree_entry objects. | |||
275 | */ | |||
276 | static int | |||
277 | sort_tree_entries_the_way_git_likes_it(const void *arg1, const void *arg2) | |||
278 | { | |||
279 | struct got_tree_entry * const *te1 = arg1; | |||
280 | struct got_tree_entry * const *te2 = arg2; | |||
281 | char name1[NAME_MAX255 + 2]; | |||
282 | char name2[NAME_MAX255 + 2]; | |||
283 | ||||
284 | strlcpy(name1, (*te1)->name, sizeof(name1)); | |||
285 | strlcpy(name2, (*te2)->name, sizeof(name2)); | |||
286 | if (S_ISDIR((*te1)->mode)(((*te1)->mode & 0170000) == 0040000)) | |||
287 | strlcat(name1, "/", sizeof(name1)); | |||
288 | if (S_ISDIR((*te2)->mode)(((*te2)->mode & 0170000) == 0040000)) | |||
289 | strlcat(name2, "/", sizeof(name2)); | |||
290 | return strcmp(name1, name2); | |||
291 | } | |||
292 | ||||
293 | const struct got_error * | |||
294 | got_object_tree_create(struct got_object_id **id, | |||
295 | struct got_pathlist_head *paths, int nentries, struct got_repository *repo) | |||
296 | { | |||
297 | const struct got_error *err = NULL((void *)0); | |||
298 | char modebuf[sizeof("100644 ")]; | |||
299 | SHA1_CTX sha1_ctx; | |||
300 | char *header = NULL((void *)0); | |||
301 | size_t headerlen, len = 0, n; | |||
302 | FILE *treefile = NULL((void *)0); | |||
303 | struct got_pathlist_entry *pe; | |||
304 | struct got_tree_entry **sorted_entries; | |||
305 | struct got_tree_entry *te; | |||
306 | int i; | |||
307 | ||||
308 | *id = NULL((void *)0); | |||
309 | ||||
310 | SHA1Init(&sha1_ctx); | |||
311 | ||||
312 | sorted_entries = calloc(nentries, sizeof(struct got_tree_entry *)); | |||
313 | if (sorted_entries == NULL((void *)0)) | |||
314 | return got_error_from_errno("calloc"); | |||
315 | ||||
316 | i = 0; | |||
317 | TAILQ_FOREACH(pe, paths, entry)for((pe) = ((paths)->tqh_first); (pe) != ((void *)0); (pe) = ((pe)->entry.tqe_next)) | |||
318 | sorted_entries[i++] = pe->data; | |||
319 | mergesort(sorted_entries, nentries, sizeof(struct got_tree_entry *), | |||
320 | sort_tree_entries_the_way_git_likes_it); | |||
321 | ||||
322 | for (i = 0; i < nentries; i++) { | |||
323 | te = sorted_entries[i]; | |||
324 | err = te_mode2str(modebuf, sizeof(modebuf), te); | |||
325 | if (err) | |||
326 | goto done; | |||
327 | len += strlen(modebuf) + strlen(te->name) + 1 + | |||
328 | SHA1_DIGEST_LENGTH20; | |||
329 | } | |||
330 | ||||
331 | if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_TREE"tree", len) == -1) { | |||
332 | err = got_error_from_errno("asprintf"); | |||
333 | goto done; | |||
334 | } | |||
335 | headerlen = strlen(header) + 1; | |||
336 | SHA1Update(&sha1_ctx, header, headerlen); | |||
337 | ||||
338 | treefile = got_opentemp(); | |||
339 | if (treefile == NULL((void *)0)) { | |||
340 | err = got_error_from_errno("got_opentemp"); | |||
341 | goto done; | |||
342 | } | |||
343 | ||||
344 | n = fwrite(header, 1, headerlen, treefile); | |||
345 | if (n != headerlen) { | |||
346 | err = got_ferror(treefile, GOT_ERR_IO6); | |||
347 | goto done; | |||
348 | } | |||
349 | ||||
350 | for (i = 0; i < nentries; i++) { | |||
351 | te = sorted_entries[i]; | |||
352 | err = te_mode2str(modebuf, sizeof(modebuf), te); | |||
353 | if (err) | |||
354 | goto done; | |||
355 | len = strlen(modebuf); | |||
356 | n = fwrite(modebuf, 1, len, treefile); | |||
357 | if (n != len) { | |||
358 | err = got_ferror(treefile, GOT_ERR_IO6); | |||
359 | goto done; | |||
360 | } | |||
361 | SHA1Update(&sha1_ctx, modebuf, len); | |||
362 | ||||
363 | len = strlen(te->name) + 1; /* must include NUL */ | |||
364 | n = fwrite(te->name, 1, len, treefile); | |||
365 | if (n != len) { | |||
366 | err = got_ferror(treefile, GOT_ERR_IO6); | |||
367 | goto done; | |||
368 | } | |||
369 | SHA1Update(&sha1_ctx, te->name, len); | |||
370 | ||||
371 | len = SHA1_DIGEST_LENGTH20; | |||
372 | n = fwrite(te->id.sha1, 1, len, treefile); | |||
373 | if (n != len) { | |||
374 | err = got_ferror(treefile, GOT_ERR_IO6); | |||
375 | goto done; | |||
376 | } | |||
377 | SHA1Update(&sha1_ctx, te->id.sha1, len); | |||
378 | } | |||
379 | ||||
380 | *id = malloc(sizeof(**id)); | |||
381 | if (*id == NULL((void *)0)) { | |||
382 | err = got_error_from_errno("malloc"); | |||
383 | goto done; | |||
384 | } | |||
385 | SHA1Final((*id)->sha1, &sha1_ctx); | |||
386 | ||||
387 | if (fflush(treefile) != 0) { | |||
388 | err = got_error_from_errno("fflush"); | |||
389 | goto done; | |||
390 | } | |||
391 | rewind(treefile); | |||
392 | ||||
393 | err = create_object_file(*id, treefile, repo); | |||
394 | done: | |||
395 | free(header); | |||
396 | free(sorted_entries); | |||
397 | if (treefile && fclose(treefile) == EOF(-1) && err == NULL((void *)0)) | |||
398 | err = got_error_from_errno("fclose"); | |||
399 | if (err) { | |||
400 | free(*id); | |||
401 | *id = NULL((void *)0); | |||
402 | } | |||
403 | return err; | |||
404 | } | |||
405 | ||||
406 | const struct got_error * | |||
407 | got_object_commit_create(struct got_object_id **id, | |||
408 | struct got_object_id *tree_id, struct got_object_id_queue *parent_ids, | |||
409 | int nparents, const char *author, time_t author_time, | |||
410 | const char *committer, time_t committer_time, | |||
411 | const char *logmsg, struct got_repository *repo) | |||
412 | { | |||
413 | const struct got_error *err = NULL((void *)0); | |||
414 | SHA1_CTX sha1_ctx; | |||
415 | char *header = NULL((void *)0), *tree_str = NULL((void *)0); | |||
416 | char *author_str = NULL((void *)0), *committer_str = NULL((void *)0); | |||
417 | char *id_str = NULL((void *)0); | |||
418 | size_t headerlen, len = 0, n; | |||
419 | FILE *commitfile = NULL((void *)0); | |||
420 | struct got_object_qid *qid; | |||
421 | char *msg0, *msg; | |||
422 | ||||
423 | *id = NULL((void *)0); | |||
424 | ||||
425 | SHA1Init(&sha1_ctx); | |||
426 | ||||
427 | msg0 = strdup(logmsg); | |||
| ||||
428 | if (msg0 == NULL((void *)0)) | |||
429 | return got_error_from_errno("strdup"); | |||
430 | msg = msg0; | |||
431 | ||||
432 | while (isspace((unsigned char)msg[0])) | |||
433 | msg++; | |||
434 | len = strlen(msg); | |||
435 | while (len > 0 && isspace((unsigned char)msg[len - 1])) { | |||
436 | msg[len - 1] = '\0'; | |||
437 | len--; | |||
438 | } | |||
439 | ||||
440 | if (asprintf(&author_str, "%s%s %lld +0000\n", | |||
441 | GOT_COMMIT_LABEL_AUTHOR"author ", author, (long long)author_time) == -1) | |||
442 | return got_error_from_errno("asprintf"); | |||
| ||||
443 | ||||
444 | if (asprintf(&committer_str, "%s%s %lld +0000\n", | |||
445 | GOT_COMMIT_LABEL_COMMITTER"committer ", committer ? committer : author, | |||
446 | (long long)(committer ? committer_time : author_time)) | |||
447 | == -1) { | |||
448 | err = got_error_from_errno("asprintf"); | |||
449 | goto done; | |||
450 | } | |||
451 | ||||
452 | len = strlen(GOT_COMMIT_LABEL_TREE"tree ") + SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1) + | |||
453 | nparents * | |||
454 | (strlen(GOT_COMMIT_LABEL_PARENT"parent ") + SHA1_DIGEST_STRING_LENGTH(20 * 2 + 1)) + | |||
455 | + strlen(author_str) + strlen(committer_str) + 2 + strlen(msg); | |||
456 | ||||
457 | if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_COMMIT"commit", len) == -1) { | |||
458 | err = got_error_from_errno("asprintf"); | |||
459 | goto done; | |||
460 | } | |||
461 | headerlen = strlen(header) + 1; | |||
462 | SHA1Update(&sha1_ctx, header, headerlen); | |||
463 | ||||
464 | commitfile = got_opentemp(); | |||
465 | if (commitfile == NULL((void *)0)) { | |||
466 | err = got_error_from_errno("got_opentemp"); | |||
467 | goto done; | |||
468 | } | |||
469 | ||||
470 | n = fwrite(header, 1, headerlen, commitfile); | |||
471 | if (n != headerlen) { | |||
472 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
473 | goto done; | |||
474 | } | |||
475 | ||||
476 | err = got_object_id_str(&id_str, tree_id); | |||
477 | if (err) | |||
478 | goto done; | |||
479 | if (asprintf(&tree_str, "%s%s\n", GOT_COMMIT_LABEL_TREE"tree ", id_str) | |||
480 | == -1) { | |||
481 | err = got_error_from_errno("asprintf"); | |||
482 | goto done; | |||
483 | } | |||
484 | len = strlen(tree_str); | |||
485 | SHA1Update(&sha1_ctx, tree_str, len); | |||
486 | n = fwrite(tree_str, 1, len, commitfile); | |||
487 | if (n != len) { | |||
488 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
489 | goto done; | |||
490 | } | |||
491 | ||||
492 | if (parent_ids) { | |||
493 | SIMPLEQ_FOREACH(qid, parent_ids, entry)for((qid) = ((parent_ids)->sqh_first); (qid) != ((void *)0 ); (qid) = ((qid)->entry.sqe_next)) { | |||
494 | char *parent_str = NULL((void *)0); | |||
495 | ||||
496 | free(id_str); | |||
497 | ||||
498 | err = got_object_id_str(&id_str, qid->id); | |||
499 | if (err) | |||
500 | goto done; | |||
501 | if (asprintf(&parent_str, "%s%s\n", | |||
502 | GOT_COMMIT_LABEL_PARENT"parent ", id_str) == -1) { | |||
503 | err = got_error_from_errno("asprintf"); | |||
504 | goto done; | |||
505 | } | |||
506 | len = strlen(parent_str); | |||
507 | SHA1Update(&sha1_ctx, parent_str, len); | |||
508 | n = fwrite(parent_str, 1, len, commitfile); | |||
509 | if (n != len) { | |||
510 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
511 | free(parent_str); | |||
512 | goto done; | |||
513 | } | |||
514 | free(parent_str); | |||
515 | } | |||
516 | } | |||
517 | ||||
518 | len = strlen(author_str); | |||
519 | SHA1Update(&sha1_ctx, author_str, len); | |||
520 | n = fwrite(author_str, 1, len, commitfile); | |||
521 | if (n != len) { | |||
522 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
523 | goto done; | |||
524 | } | |||
525 | ||||
526 | len = strlen(committer_str); | |||
527 | SHA1Update(&sha1_ctx, committer_str, len); | |||
528 | n = fwrite(committer_str, 1, len, commitfile); | |||
529 | if (n != len) { | |||
530 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
531 | goto done; | |||
532 | } | |||
533 | ||||
534 | SHA1Update(&sha1_ctx, "\n", 1); | |||
535 | n = fwrite("\n", 1, 1, commitfile); | |||
536 | if (n != 1) { | |||
537 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
538 | goto done; | |||
539 | } | |||
540 | ||||
541 | len = strlen(msg); | |||
542 | SHA1Update(&sha1_ctx, msg, len); | |||
543 | n = fwrite(msg, 1, len, commitfile); | |||
544 | if (n != len) { | |||
545 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
546 | goto done; | |||
547 | } | |||
548 | ||||
549 | SHA1Update(&sha1_ctx, "\n", 1); | |||
550 | n = fwrite("\n", 1, 1, commitfile); | |||
551 | if (n != 1) { | |||
552 | err = got_ferror(commitfile, GOT_ERR_IO6); | |||
553 | goto done; | |||
554 | } | |||
555 | ||||
556 | *id = malloc(sizeof(**id)); | |||
557 | if (*id == NULL((void *)0)) { | |||
558 | err = got_error_from_errno("malloc"); | |||
559 | goto done; | |||
560 | } | |||
561 | SHA1Final((*id)->sha1, &sha1_ctx); | |||
562 | ||||
563 | if (fflush(commitfile) != 0) { | |||
564 | err = got_error_from_errno("fflush"); | |||
565 | goto done; | |||
566 | } | |||
567 | rewind(commitfile); | |||
568 | ||||
569 | err = create_object_file(*id, commitfile, repo); | |||
570 | done: | |||
571 | free(msg0); | |||
572 | free(header); | |||
573 | free(tree_str); | |||
574 | free(author_str); | |||
575 | free(committer_str); | |||
576 | if (commitfile && fclose(commitfile) == EOF(-1) && err == NULL((void *)0)) | |||
577 | err = got_error_from_errno("fclose"); | |||
578 | if (err) { | |||
579 | free(*id); | |||
580 | *id = NULL((void *)0); | |||
581 | } | |||
582 | return err; | |||
583 | } | |||
584 | ||||
585 | const struct got_error * | |||
586 | got_object_tag_create(struct got_object_id **id, | |||
587 | const char *tag_name, struct got_object_id *object_id, const char *tagger, | |||
588 | time_t tagger_time, const char *tagmsg, struct got_repository *repo) | |||
589 | { | |||
590 | const struct got_error *err = NULL((void *)0); | |||
591 | SHA1_CTX sha1_ctx; | |||
592 | char *header = NULL((void *)0); | |||
593 | char *tag_str = NULL((void *)0), *tagger_str = NULL((void *)0); | |||
594 | char *id_str = NULL((void *)0), *obj_str = NULL((void *)0), *type_str = NULL((void *)0); | |||
595 | size_t headerlen, len = 0, n; | |||
596 | FILE *tagfile = NULL((void *)0); | |||
597 | char *msg0 = NULL((void *)0), *msg; | |||
598 | const char *obj_type_str; | |||
599 | int obj_type; | |||
600 | ||||
601 | *id = NULL((void *)0); | |||
602 | ||||
603 | SHA1Init(&sha1_ctx); | |||
604 | ||||
605 | err = got_object_id_str(&id_str, object_id); | |||
606 | if (err) | |||
607 | goto done; | |||
608 | if (asprintf(&obj_str, "%s%s\n", GOT_TAG_LABEL_OBJECT"object ", id_str) == -1) { | |||
609 | err = got_error_from_errno("asprintf"); | |||
610 | goto done; | |||
611 | } | |||
612 | ||||
613 | err = got_object_get_type(&obj_type, repo, object_id); | |||
614 | if (err) | |||
615 | goto done; | |||
616 | ||||
617 | switch (obj_type) { | |||
618 | case GOT_OBJ_TYPE_BLOB3: | |||
619 | obj_type_str = GOT_OBJ_LABEL_BLOB"blob"; | |||
620 | break; | |||
621 | case GOT_OBJ_TYPE_TREE2: | |||
622 | obj_type_str = GOT_OBJ_LABEL_TREE"tree"; | |||
623 | break; | |||
624 | case GOT_OBJ_TYPE_COMMIT1: | |||
625 | obj_type_str = GOT_OBJ_LABEL_COMMIT"commit"; | |||
626 | break; | |||
627 | case GOT_OBJ_TYPE_TAG4: | |||
628 | obj_type_str = GOT_OBJ_LABEL_TAG"tag"; | |||
629 | break; | |||
630 | default: | |||
631 | err = got_error(GOT_ERR_OBJ_TYPE11); | |||
632 | goto done; | |||
633 | } | |||
634 | ||||
635 | if (asprintf(&type_str, "%s%s\n", GOT_TAG_LABEL_TYPE"type ", | |||
636 | obj_type_str) == -1) { | |||
637 | err = got_error_from_errno("asprintf"); | |||
638 | goto done; | |||
639 | } | |||
640 | ||||
641 | if (asprintf(&tag_str, "%s%s\n", GOT_TAG_LABEL_TAG"tag ", tag_name) == -1) { | |||
642 | err = got_error_from_errno("asprintf"); | |||
643 | goto done; | |||
644 | } | |||
645 | ||||
646 | if (asprintf(&tagger_str, "%s%s %lld +0000\n", | |||
647 | GOT_TAG_LABEL_TAGGER"tagger ", tagger, (long long)tagger_time) == -1) | |||
648 | return got_error_from_errno("asprintf"); | |||
649 | ||||
650 | msg0 = strdup(tagmsg); | |||
651 | if (msg0 == NULL((void *)0)) { | |||
652 | err = got_error_from_errno("strdup"); | |||
653 | goto done; | |||
654 | } | |||
655 | msg = msg0; | |||
656 | ||||
657 | while (isspace((unsigned char)msg[0])) | |||
658 | msg++; | |||
659 | ||||
660 | len = strlen(obj_str) + strlen(type_str) + strlen(tag_str) + | |||
661 | strlen(tagger_str) + 1 + strlen(msg) + 1; | |||
662 | ||||
663 | if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_TAG"tag", len) == -1) { | |||
664 | err = got_error_from_errno("asprintf"); | |||
665 | goto done; | |||
666 | } | |||
667 | ||||
668 | headerlen = strlen(header) + 1; | |||
669 | SHA1Update(&sha1_ctx, header, headerlen); | |||
670 | ||||
671 | tagfile = got_opentemp(); | |||
672 | if (tagfile == NULL((void *)0)) { | |||
673 | err = got_error_from_errno("got_opentemp"); | |||
674 | goto done; | |||
675 | } | |||
676 | ||||
677 | n = fwrite(header, 1, headerlen, tagfile); | |||
678 | if (n != headerlen) { | |||
679 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
680 | goto done; | |||
681 | } | |||
682 | len = strlen(obj_str); | |||
683 | SHA1Update(&sha1_ctx, obj_str, len); | |||
684 | n = fwrite(obj_str, 1, len, tagfile); | |||
685 | if (n != len) { | |||
686 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
687 | goto done; | |||
688 | } | |||
689 | len = strlen(type_str); | |||
690 | SHA1Update(&sha1_ctx, type_str, len); | |||
691 | n = fwrite(type_str, 1, len, tagfile); | |||
692 | if (n != len) { | |||
693 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
694 | goto done; | |||
695 | } | |||
696 | ||||
697 | len = strlen(tag_str); | |||
698 | SHA1Update(&sha1_ctx, tag_str, len); | |||
699 | n = fwrite(tag_str, 1, len, tagfile); | |||
700 | if (n != len) { | |||
701 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
702 | goto done; | |||
703 | } | |||
704 | ||||
705 | len = strlen(tagger_str); | |||
706 | SHA1Update(&sha1_ctx, tagger_str, len); | |||
707 | n = fwrite(tagger_str, 1, len, tagfile); | |||
708 | if (n != len) { | |||
709 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
710 | goto done; | |||
711 | } | |||
712 | ||||
713 | SHA1Update(&sha1_ctx, "\n", 1); | |||
714 | n = fwrite("\n", 1, 1, tagfile); | |||
715 | if (n != 1) { | |||
716 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
717 | goto done; | |||
718 | } | |||
719 | ||||
720 | len = strlen(msg); | |||
721 | SHA1Update(&sha1_ctx, msg, len); | |||
722 | n = fwrite(msg, 1, len, tagfile); | |||
723 | if (n != len) { | |||
724 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
725 | goto done; | |||
726 | } | |||
727 | ||||
728 | SHA1Update(&sha1_ctx, "\n", 1); | |||
729 | n = fwrite("\n", 1, 1, tagfile); | |||
730 | if (n != 1) { | |||
731 | err = got_ferror(tagfile, GOT_ERR_IO6); | |||
732 | goto done; | |||
733 | } | |||
734 | ||||
735 | *id = malloc(sizeof(**id)); | |||
736 | if (*id == NULL((void *)0)) { | |||
737 | err = got_error_from_errno("malloc"); | |||
738 | goto done; | |||
739 | } | |||
740 | SHA1Final((*id)->sha1, &sha1_ctx); | |||
741 | ||||
742 | if (fflush(tagfile) != 0) { | |||
743 | err = got_error_from_errno("fflush"); | |||
744 | goto done; | |||
745 | } | |||
746 | rewind(tagfile); | |||
747 | ||||
748 | err = create_object_file(*id, tagfile, repo); | |||
749 | done: | |||
750 | free(msg0); | |||
751 | free(header); | |||
752 | free(obj_str); | |||
753 | free(tagger_str); | |||
754 | if (tagfile && fclose(tagfile) == EOF(-1) && err == NULL((void *)0)) | |||
755 | err = got_error_from_errno("fclose"); | |||
756 | if (err) { | |||
757 | free(*id); | |||
758 | *id = NULL((void *)0); | |||
759 | } | |||
760 | return err; | |||
761 | } |