Blob


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 */
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/queue.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <sha1.h>
29 #include <unistd.h>
30 #include <zlib.h>
32 #include "got_error.h"
33 #include "got_object.h"
34 #include "got_repository.h"
35 #include "got_opentemp.h"
36 #include "got_path.h"
38 #include "got_lib_sha1.h"
39 #include "got_lib_deflate.h"
40 #include "got_lib_delta.h"
41 #include "got_lib_object.h"
42 #include "got_lib_lockfile.h"
44 #ifndef nitems
45 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
46 #endif
48 static const struct got_error *
49 create_object_file(struct got_object_id *id, FILE *content,
50 struct got_repository *repo)
51 {
52 const struct got_error *err = NULL, *unlock_err = NULL;
53 char *objpath = NULL, *tmppath = NULL;
54 FILE *tmpfile = NULL;
55 struct got_lockfile *lf = NULL;
56 size_t tmplen = 0;
58 err = got_object_get_path(&objpath, id, repo);
59 if (err)
60 return err;
62 err = got_opentemp_named(&tmppath, &tmpfile, objpath);
63 if (err) {
64 char *parent_path;
65 if (!(err->code == GOT_ERR_ERRNO && errno == ENOENT))
66 goto done;
67 err = got_path_dirname(&parent_path, objpath);
68 if (err)
69 goto done;
70 err = got_path_mkdir(parent_path);
71 free(parent_path);
72 if (err)
73 goto done;
74 err = got_opentemp_named(&tmppath, &tmpfile, objpath);
75 if (err)
76 goto done;
77 }
79 err = got_deflate_to_file(&tmplen, content, tmpfile);
80 if (err)
81 goto done;
83 err = got_lockfile_lock(&lf, objpath);
84 if (err)
85 goto done;
87 if (rename(tmppath, objpath) != 0) {
88 err = got_error_from_errno3("rename", tmppath, objpath);
89 goto done;
90 }
91 free(tmppath);
92 tmppath = NULL;
94 if (chmod(objpath, GOT_DEFAULT_FILE_MODE) != 0) {
95 err = got_error_from_errno2("chmod", objpath);
96 goto done;
97 }
98 done:
99 free(objpath);
100 if (tmppath) {
101 if (unlink(tmppath) != 0 && err == NULL)
102 err = got_error_from_errno2("unlink", tmppath);
103 free(tmppath);
105 if (tmpfile && fclose(tmpfile) != 0 && err == NULL)
106 err = got_error_from_errno("fclose");
107 if (lf)
108 unlock_err = got_lockfile_unlock(lf);
109 return err ? err : unlock_err;
112 const struct got_error *
113 got_object_blob_create(struct got_object_id **id, const char *ondisk_path,
114 struct got_repository *repo)
116 const struct got_error *err = NULL;
117 char *header = NULL;
118 FILE *blobfile = NULL;
119 int fd = -1;
120 struct stat sb;
121 SHA1_CTX sha1_ctx;
122 size_t headerlen = 0, n;
124 *id = NULL;
126 SHA1Init(&sha1_ctx);
128 fd = open(ondisk_path, O_RDONLY | O_NOFOLLOW);
129 if (fd == -1)
130 return got_error_from_errno2("open", ondisk_path);
132 if (fstat(fd, &sb) == -1) {
133 err = got_error_from_errno2("fstat", ondisk_path);
134 goto done;
137 if (asprintf(&header, "%s %lld", GOT_OBJ_LABEL_BLOB,
138 sb.st_size) == -1) {
139 err = got_error_from_errno("asprintf");
140 goto done;
142 headerlen = strlen(header) + 1;
143 SHA1Update(&sha1_ctx, header, headerlen);
145 blobfile = got_opentemp();
146 if (blobfile == NULL) {
147 err = got_error_from_errno("got_opentemp");
148 goto done;
151 n = fwrite(header, 1, headerlen, blobfile);
152 if (n != headerlen) {
153 err = got_ferror(blobfile, GOT_ERR_IO);
154 goto done;
156 for (;;) {
157 char buf[8192];
158 ssize_t inlen;
160 inlen = read(fd, buf, sizeof(buf));
161 if (inlen == -1) {
162 err = got_error_from_errno("read");
163 goto done;
165 if (inlen == 0)
166 break; /* EOF */
167 SHA1Update(&sha1_ctx, buf, inlen);
168 n = fwrite(buf, 1, inlen, blobfile);
169 if (n != inlen) {
170 err = got_ferror(blobfile, GOT_ERR_IO);
171 goto done;
175 *id = malloc(sizeof(**id));
176 if (*id == NULL) {
177 err = got_error_from_errno("malloc");
178 goto done;
180 SHA1Final((*id)->sha1, &sha1_ctx);
182 if (fflush(blobfile) != 0) {
183 err = got_error_from_errno("fflush");
184 goto done;
186 rewind(blobfile);
188 err = create_object_file(*id, blobfile, repo);
189 done:
190 free(header);
191 if (fd != -1 && close(fd) != 0 && err == NULL)
192 err = got_error_from_errno("close");
193 if (blobfile && fclose(blobfile) != 0 && err == NULL)
194 err = got_error_from_errno("fclose");
195 if (err) {
196 free(*id);
197 *id = NULL;
199 return err;
202 static const struct got_error *
203 mode2str(char *buf, size_t len, mode_t mode)
205 int ret;
206 ret = snprintf(buf, len, "%o ", mode);
207 if (ret == -1 || ret >= len)
208 return got_error(GOT_ERR_NO_SPACE);
209 return NULL;
212 const struct got_error *
213 got_object_tree_create(struct got_object_id **id,
214 struct got_tree_entries *entries, struct got_repository *repo)
216 const struct got_error *err = NULL;
217 char modebuf[sizeof("100644 ")];
218 SHA1_CTX sha1_ctx;
219 char *header = NULL;
220 size_t headerlen, len = 0, n;
221 FILE *treefile = NULL;
222 struct got_tree_entry *te;
224 *id = NULL;
226 SHA1Init(&sha1_ctx);
228 SIMPLEQ_FOREACH(te, &entries->head, entry) {
229 err = mode2str(modebuf, sizeof(modebuf), te->mode);
230 if (err)
231 return err;
232 len += strlen(modebuf) + strlen(te->name) + 1 +
233 SHA1_DIGEST_LENGTH;
236 if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_TREE, len) == -1) {
237 err = got_error_from_errno("asprintf");
238 goto done;
240 headerlen = strlen(header) + 1;
241 SHA1Update(&sha1_ctx, header, headerlen);
243 treefile = got_opentemp();
244 if (treefile == NULL) {
245 err = got_error_from_errno("got_opentemp");
246 goto done;
249 n = fwrite(header, 1, headerlen, treefile);
250 if (n != headerlen) {
251 err = got_ferror(treefile, GOT_ERR_IO);
252 goto done;
255 SIMPLEQ_FOREACH(te, &entries->head, entry) {
256 err = mode2str(modebuf, sizeof(modebuf), te->mode);
257 if (err)
258 goto done;
259 len = strlen(modebuf);
260 n = fwrite(modebuf, 1, len, treefile);
261 if (n != len) {
262 err = got_ferror(treefile, GOT_ERR_IO);
263 goto done;
265 SHA1Update(&sha1_ctx, modebuf, len);
267 len = strlen(te->name) + 1; /* must include NUL */
268 n = fwrite(te->name, 1, len, treefile);
269 if (n != len) {
270 err = got_ferror(treefile, GOT_ERR_IO);
271 goto done;
273 SHA1Update(&sha1_ctx, te->name, len);
275 len = SHA1_DIGEST_LENGTH;
276 n = fwrite(te->id->sha1, 1, len, treefile);
277 if (n != len) {
278 err = got_ferror(treefile, GOT_ERR_IO);
279 goto done;
281 SHA1Update(&sha1_ctx, te->id->sha1, len);
284 *id = malloc(sizeof(**id));
285 if (*id == NULL) {
286 err = got_error_from_errno("malloc");
287 goto done;
289 SHA1Final((*id)->sha1, &sha1_ctx);
291 if (fflush(treefile) != 0) {
292 err = got_error_from_errno("fflush");
293 goto done;
295 rewind(treefile);
297 err = create_object_file(*id, treefile, repo);
298 done:
299 free(header);
300 if (treefile && fclose(treefile) != 0 && err == NULL)
301 err = got_error_from_errno("fclose");
302 if (err) {
303 free(*id);
304 *id = NULL;
306 return err;
309 const struct got_error *
310 got_object_commit_create(struct got_object_id **id,
311 struct got_object_id *tree_id, struct got_object_id_queue *parent_ids,
312 int nparents, const char *author, time_t author_time,
313 const char *committer, time_t committer_time,
314 const char *logmsg, struct got_repository *repo)
316 const struct got_error *err = NULL;
317 SHA1_CTX sha1_ctx;
318 char *header = NULL, *tree_str = NULL;
319 char *author_str = NULL, *committer_str = NULL;
320 char *id_str = NULL;
321 size_t headerlen, len = 0, n;
322 FILE *commitfile = NULL;
323 struct got_object_qid *qid;
324 char *msg0, *msg;
326 *id = NULL;
328 SHA1Init(&sha1_ctx);
330 msg0 = strdup(logmsg);
331 if (msg0 == NULL)
332 return got_error_from_errno("strdup");
333 msg = msg0;
335 while (isspace((unsigned char)msg[0]))
336 msg++;
337 len = strlen(msg);
338 while (len > 0 && isspace((unsigned char)msg[len - 1])) {
339 msg[len - 1] = '\0';
340 len--;
343 if (asprintf(&author_str, "%s%s %lld +0000\n",
344 GOT_COMMIT_LABEL_AUTHOR, author, author_time) == -1)
345 return got_error_from_errno("asprintf");
347 if (asprintf(&committer_str, "%s%s %lld +0000\n",
348 GOT_COMMIT_LABEL_COMMITTER, committer ? committer : author,
349 committer ? committer_time : author_time)
350 == -1) {
351 err = got_error_from_errno("asprintf");
352 goto done;
355 len = strlen(GOT_COMMIT_LABEL_TREE) + SHA1_DIGEST_STRING_LENGTH +
356 nparents *
357 (strlen(GOT_COMMIT_LABEL_PARENT) + SHA1_DIGEST_STRING_LENGTH) +
358 + strlen(author_str) + strlen(committer_str) + 2 + strlen(msg);
360 if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_COMMIT, len) == -1) {
361 err = got_error_from_errno("asprintf");
362 goto done;
364 headerlen = strlen(header) + 1;
365 SHA1Update(&sha1_ctx, header, headerlen);
367 commitfile = got_opentemp();
368 if (commitfile == NULL) {
369 err = got_error_from_errno("got_opentemp");
370 goto done;
373 n = fwrite(header, 1, headerlen, commitfile);
374 if (n != headerlen) {
375 err = got_ferror(commitfile, GOT_ERR_IO);
376 goto done;
379 err = got_object_id_str(&id_str, tree_id);
380 if (err)
381 goto done;
382 if (asprintf(&tree_str, "%s%s\n", GOT_COMMIT_LABEL_TREE, id_str)
383 == -1) {
384 err = got_error_from_errno("asprintf");
385 goto done;
387 len = strlen(tree_str);
388 SHA1Update(&sha1_ctx, tree_str, len);
389 n = fwrite(tree_str, 1, len, commitfile);
390 if (n != len) {
391 err = got_ferror(commitfile, GOT_ERR_IO);
392 goto done;
395 if (parent_ids) {
396 SIMPLEQ_FOREACH(qid, parent_ids, entry) {
397 char *parent_str = NULL;
399 free(id_str);
401 err = got_object_id_str(&id_str, qid->id);
402 if (err)
403 goto done;
404 if (asprintf(&parent_str, "%s%s\n",
405 GOT_COMMIT_LABEL_PARENT, id_str) == -1) {
406 err = got_error_from_errno("asprintf");
407 goto done;
409 len = strlen(parent_str);
410 SHA1Update(&sha1_ctx, parent_str, len);
411 n = fwrite(parent_str, 1, len, commitfile);
412 if (n != len) {
413 err = got_ferror(commitfile, GOT_ERR_IO);
414 free(parent_str);
415 goto done;
417 free(parent_str);
421 len = strlen(author_str);
422 SHA1Update(&sha1_ctx, author_str, len);
423 n = fwrite(author_str, 1, len, commitfile);
424 if (n != len) {
425 err = got_ferror(commitfile, GOT_ERR_IO);
426 goto done;
429 len = strlen(committer_str);
430 SHA1Update(&sha1_ctx, committer_str, len);
431 n = fwrite(committer_str, 1, len, commitfile);
432 if (n != len) {
433 err = got_ferror(commitfile, GOT_ERR_IO);
434 goto done;
437 SHA1Update(&sha1_ctx, "\n", 1);
438 n = fwrite("\n", 1, 1, commitfile);
439 if (n != 1) {
440 err = got_ferror(commitfile, GOT_ERR_IO);
441 goto done;
444 len = strlen(msg);
445 SHA1Update(&sha1_ctx, msg, len);
446 n = fwrite(msg, 1, len, commitfile);
447 if (n != len) {
448 err = got_ferror(commitfile, GOT_ERR_IO);
449 goto done;
452 SHA1Update(&sha1_ctx, "\n", 1);
453 n = fwrite("\n", 1, 1, commitfile);
454 if (n != 1) {
455 err = got_ferror(commitfile, GOT_ERR_IO);
456 goto done;
459 *id = malloc(sizeof(**id));
460 if (*id == NULL) {
461 err = got_error_from_errno("malloc");
462 goto done;
464 SHA1Final((*id)->sha1, &sha1_ctx);
466 if (fflush(commitfile) != 0) {
467 err = got_error_from_errno("fflush");
468 goto done;
470 rewind(commitfile);
472 err = create_object_file(*id, commitfile, repo);
473 done:
474 free(msg0);
475 free(header);
476 free(tree_str);
477 free(author_str);
478 free(committer_str);
479 if (commitfile && fclose(commitfile) != 0 && err == NULL)
480 err = got_error_from_errno("fclose");
481 if (err) {
482 free(*id);
483 *id = NULL;
485 return err;