/* * Copyright (c) 2018, 2019, 2020 Stefan Sperling * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "got_error.h" #include "got_object.h" #include "got_path.h" #include "got_lib_delta.h" #include "got_lib_delta_cache.h" #include "got_lib_object.h" #include "got_lib_object_cache.h" #include "got_lib_object_parse.h" #include "got_lib_privsep.h" #include "got_lib_pack.h" static volatile sig_atomic_t sigint_received; static void catch_sigint(int signo) { sigint_received = 1; } static const struct got_error * open_object(struct got_object **obj, struct got_pack *pack, struct got_packidx *packidx, int idx, struct got_object_id *id, struct got_object_cache *objcache) { const struct got_error *err; err = got_packfile_open_object(obj, pack, packidx, idx, id); if (err) return err; (*obj)->refcnt++; err = got_object_cache_add(objcache, id, *obj); if (err) { if (err->code == GOT_ERR_OBJ_EXISTS || err->code == GOT_ERR_OBJ_TOO_LARGE) err = NULL; return err; } (*obj)->refcnt++; return NULL; } static const struct got_error * object_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_object *obj; struct got_object_id id; size_t datalen; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); obj = got_object_cache_get(objcache, &id); if (obj) { obj->refcnt++; } else { err = open_object(&obj, pack, packidx, iobj.idx, &id, objcache); if (err) goto done; } err = got_privsep_send_obj(ibuf, obj); done: got_object_close(obj); return err; } const struct got_error * open_commit(struct got_commit_object **commit, struct got_pack *pack, struct got_packidx *packidx, int obj_idx, struct got_object_id *id, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_object *obj = NULL; uint8_t *buf = NULL; size_t len; *commit = NULL; obj = got_object_cache_get(objcache, id); if (obj) { obj->refcnt++; } else { err = open_object(&obj, pack, packidx, obj_idx, id, objcache); if (err) return err; } err = got_packfile_extract_object_to_mem(&buf, &len, obj, pack); if (err) goto done; obj->size = len; err = got_object_parse_commit(commit, buf, len); done: got_object_close(obj); free(buf); return err; } static const struct got_error * commit_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_commit_object *commit = NULL; struct got_object_id id; size_t datalen; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); err = open_commit(&commit, pack, packidx, iobj.idx, &id, objcache); if (err) goto done; err = got_privsep_send_commit(ibuf, commit); done: if (commit) got_object_commit_close(commit); if (err) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; else got_privsep_send_error(ibuf, err); } return err; } const struct got_error * open_tree(uint8_t **buf, struct got_pathlist_head *entries, int *nentries, struct got_pack *pack, struct got_packidx *packidx, int obj_idx, struct got_object_id *id, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_object *obj = NULL; size_t len; *buf = NULL; *nentries = 0; obj = got_object_cache_get(objcache, id); if (obj) { obj->refcnt++; } else { err = open_object(&obj, pack, packidx, obj_idx, id, objcache); if (err) return err; } err = got_packfile_extract_object_to_mem(buf, &len, obj, pack); if (err) goto done; obj->size = len; err = got_object_parse_tree(entries, nentries, *buf, len); done: got_object_close(obj); if (err) { free(*buf); *buf = NULL; } return err; } static const struct got_error * tree_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_pathlist_head entries; int nentries = 0; uint8_t *buf = NULL; struct got_object_id id; size_t datalen; TAILQ_INIT(&entries); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); err = open_tree(&buf, &entries, &nentries, pack, packidx, iobj.idx, &id, objcache); if (err) return err; err = got_privsep_send_tree(ibuf, &entries, nentries); got_object_parsed_tree_entries_free(&entries); free(buf); if (err) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; else got_privsep_send_error(ibuf, err); } return err; } static const struct got_error * receive_file(FILE **f, struct imsgbuf *ibuf, int imsg_code) { const struct got_error *err; struct imsg imsg; size_t datalen; err = got_privsep_recv_imsg(&imsg, ibuf, 0); if (err) return err; if (imsg.hdr.type != imsg_code) { err = got_error(GOT_ERR_PRIVSEP_MSG); goto done; } datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (datalen != 0) { err = got_error(GOT_ERR_PRIVSEP_LEN); goto done; } if (imsg.fd == -1) { err = got_error(GOT_ERR_PRIVSEP_NO_FD); goto done; } *f = fdopen(imsg.fd, "w+"); if (*f == NULL) { err = got_error_from_errno("fdopen"); close(imsg.fd); goto done; } done: imsg_free(&imsg); return err; } static const struct got_error * blob_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_object *obj = NULL; FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL; struct got_object_id id; size_t datalen; uint64_t blob_size; uint8_t *buf = NULL; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); obj = got_object_cache_get(objcache, &id); if (obj) { obj->refcnt++; } else { err = open_object(&obj, pack, packidx, iobj.idx, &id, objcache); if (err) return err; } err = receive_file(&outfile, ibuf, GOT_IMSG_BLOB_OUTFD); if (err) goto done; err = receive_file(&basefile, ibuf, GOT_IMSG_TMPFD); if (err) goto done; err = receive_file(&accumfile, ibuf, GOT_IMSG_TMPFD); if (err) goto done; if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) { err = got_pack_get_max_delta_object_size(&blob_size, obj, pack); if (err) goto done; } else blob_size = obj->size; if (blob_size <= GOT_PRIVSEP_INLINE_BLOB_DATA_MAX) err = got_packfile_extract_object_to_mem(&buf, &obj->size, obj, pack); else err = got_packfile_extract_object(pack, obj, outfile, basefile, accumfile); if (err) goto done; err = got_privsep_send_blob(ibuf, obj->size, obj->hdrlen, buf); done: free(buf); if (outfile && fclose(outfile) != 0 && err == NULL) err = got_error_from_errno("fclose"); if (basefile && fclose(basefile) != 0 && err == NULL) err = got_error_from_errno("fclose"); if (accumfile && fclose(accumfile) != 0 && err == NULL) err = got_error_from_errno("fclose"); got_object_close(obj); if (err && err->code != GOT_ERR_PRIVSEP_PIPE) got_privsep_send_error(ibuf, err); return err; } static const struct got_error * tag_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_object *obj = NULL; struct got_tag_object *tag = NULL; uint8_t *buf = NULL; size_t len; struct got_object_id id; size_t datalen; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); obj = got_object_cache_get(objcache, &id); if (obj) { obj->refcnt++; } else { err = open_object(&obj, pack, packidx, iobj.idx, &id, objcache); if (err) return err; } err = got_packfile_extract_object_to_mem(&buf, &len, obj, pack); if (err) goto done; obj->size = len; err = got_object_parse_tag(&tag, buf, len); if (err) goto done; err = got_privsep_send_tag(ibuf, tag); done: free(buf); got_object_close(obj); if (tag) got_object_tag_close(tag); if (err) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; else got_privsep_send_error(ibuf, err); } return err; } static struct got_parsed_tree_entry * find_entry_by_name(struct got_pathlist_head *entries, int nentries, const char *name, size_t len) { struct got_pathlist_entry *pe; /* Note that tree entries are sorted in strncmp() order. */ TAILQ_FOREACH(pe, entries, entry) { int cmp = strncmp(pe->path, name, len); if (cmp < 0) continue; if (cmp > 0) break; if (pe->path[len] == '\0') return (struct got_parsed_tree_entry *)pe->data; } return NULL; } const struct got_error * tree_path_changed(int *changed, uint8_t **buf1, uint8_t **buf2, struct got_pathlist_head *entries1, int *nentries1, struct got_pathlist_head *entries2, int *nentries2, const char *path, struct got_pack *pack, struct got_packidx *packidx, struct imsgbuf *ibuf, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_parsed_tree_entry *pte1 = NULL, *pte2 = NULL; const char *seg, *s; size_t seglen; *changed = 0; /* We not do support comparing the root path. */ if (got_path_is_root_dir(path)) return got_error_path(path, GOT_ERR_BAD_PATH); s = path; while (*s == '/') s++; seg = s; seglen = 0; while (*s) { if (*s != '/') { s++; seglen++; if (*s) continue; } pte1 = find_entry_by_name(entries1, *nentries1, seg, seglen); if (pte1 == NULL) { err = got_error(GOT_ERR_NO_OBJ); break; } pte2 = find_entry_by_name(entries2, *nentries2, seg, seglen); if (pte2 == NULL) { *changed = 1; break; } if (pte1->mode != pte2->mode) { *changed = 1; break; } if (memcmp(pte1->id, pte2->id, SHA1_DIGEST_LENGTH) == 0) { *changed = 0; break; } if (*s == '\0') { /* final path element */ *changed = 1; break; } seg = s + 1; s++; seglen = 0; if (*s) { struct got_object_id id1, id2; int idx; memcpy(id1.sha1, pte1->id, SHA1_DIGEST_LENGTH); idx = got_packidx_get_object_idx(packidx, &id1); if (idx == -1) { err = got_error_no_obj(&id1); break; } got_object_parsed_tree_entries_free(entries1); *nentries1 = 0; free(*buf1); *buf1 = NULL; err = open_tree(buf1, entries1, nentries1, pack, packidx, idx, &id1, objcache); pte1 = NULL; if (err) break; memcpy(id2.sha1, pte2->id, SHA1_DIGEST_LENGTH); idx = got_packidx_get_object_idx(packidx, &id2); if (idx == -1) { err = got_error_no_obj(&id2); break; } got_object_parsed_tree_entries_free(entries2); *nentries2 = 0; free(*buf2); *buf2 = NULL; err = open_tree(buf2, entries2, nentries2, pack, packidx, idx, &id2, objcache); pte2 = NULL; if (err) break; } } return err; } static const struct got_error * send_traversed_commits(struct got_object_id *commit_ids, size_t ncommits, struct imsgbuf *ibuf) { const struct got_error *err; struct ibuf *wbuf; int i; wbuf = imsg_create(ibuf, GOT_IMSG_TRAVERSED_COMMITS, 0, 0, sizeof(struct got_imsg_traversed_commits) + ncommits * SHA1_DIGEST_LENGTH); if (wbuf == NULL) return got_error_from_errno("imsg_create TRAVERSED_COMMITS"); if (imsg_add(wbuf, &ncommits, sizeof(ncommits)) == -1) { err = got_error_from_errno("imsg_add TRAVERSED_COMMITS"); ibuf_free(wbuf); return err; } for (i = 0; i < ncommits; i++) { struct got_object_id *id = &commit_ids[i]; if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1) { err = got_error_from_errno( "imsg_add TRAVERSED_COMMITS"); ibuf_free(wbuf); return err; } } wbuf->fd = -1; imsg_close(ibuf, wbuf); return got_privsep_flush_imsg(ibuf); } static const struct got_error * send_commit_traversal_done(struct imsgbuf *ibuf) { if (imsg_compose(ibuf, GOT_IMSG_COMMIT_TRAVERSAL_DONE, 0, 0, -1, NULL, 0) == -1) return got_error_from_errno("imsg_compose TRAVERSAL_DONE"); return got_privsep_flush_imsg(ibuf); } static const struct got_error * commit_traversal_request(struct imsg *imsg, struct imsgbuf *ibuf, struct got_pack *pack, struct got_packidx *packidx, struct got_object_cache *objcache) { const struct got_error *err = NULL; struct got_imsg_packed_object iobj; struct got_object_qid *pid; struct got_commit_object *commit = NULL, *pcommit = NULL; struct got_pathlist_head entries, pentries; int nentries = 0, pnentries = 0; struct got_object_id id; size_t datalen, path_len; char *path = NULL; const int min_alloc = 64; int changed = 0, ncommits = 0, nallocated = 0; struct got_object_id *commit_ids = NULL; TAILQ_INIT(&entries); TAILQ_INIT(&pentries); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; if (datalen < sizeof(iobj)) return got_error(GOT_ERR_PRIVSEP_LEN); memcpy(&iobj, imsg->data, sizeof(iobj)); memcpy(id.sha1, iobj.id, SHA1_DIGEST_LENGTH); path_len = datalen - sizeof(iobj) - 1; if (path_len < 0) return got_error(GOT_ERR_PRIVSEP_LEN); if (path_len > 0) { path = imsg->data + sizeof(iobj); if (path[path_len] != '\0') return got_error(GOT_ERR_PRIVSEP_LEN); } nallocated = min_alloc; commit_ids = reallocarray(NULL, nallocated, sizeof(*commit_ids)); if (commit_ids == NULL) return got_error_from_errno("reallocarray"); do { const size_t max_datalen = MAX_IMSGSIZE - IMSG_HEADER_SIZE; int idx; if (sigint_received) { err = got_error(GOT_ERR_CANCELLED); goto done; } if (commit == NULL) { idx = got_packidx_get_object_idx(packidx, &id); if (idx == -1) break; err = open_commit(&commit, pack, packidx, idx, &id, objcache); if (err) { if (err->code != GOT_ERR_NO_OBJ) goto done; err = NULL; break; } } if (sizeof(struct got_imsg_traversed_commits) + ncommits * SHA1_DIGEST_LENGTH >= max_datalen) { err = send_traversed_commits(commit_ids, ncommits, ibuf); if (err) goto done; ncommits = 0; } ncommits++; if (ncommits > nallocated) { struct got_object_id *new; nallocated += min_alloc; new = reallocarray(commit_ids, nallocated, sizeof(*commit_ids)); if (new == NULL) { err = got_error_from_errno("reallocarray"); goto done; } commit_ids = new; } memcpy(commit_ids[ncommits - 1].sha1, id.sha1, SHA1_DIGEST_LENGTH); pid = SIMPLEQ_FIRST(&commit->parent_ids); if (pid == NULL) break; idx = got_packidx_get_object_idx(packidx, pid->id); if (idx == -1) break; err = open_commit(&pcommit, pack, packidx, idx, pid->id, objcache); if (err) { if (err->code != GOT_ERR_NO_OBJ) goto done; err = NULL; break; } if (path[0] == '/' && path[1] == '\0') { if (got_object_id_cmp(pcommit->tree_id, commit->tree_id) != 0) { changed = 1; break; } } else { int pidx; uint8_t *buf = NULL, *pbuf = NULL; idx = got_packidx_get_object_idx(packidx, commit->tree_id); if (idx == -1) break; pidx = got_packidx_get_object_idx(packidx, pcommit->tree_id); if (pidx == -1) break; err = open_tree(&buf, &entries, &nentries, pack, packidx, idx, commit->tree_id, objcache); if (err) goto done; err = open_tree(&pbuf, &pentries, &pnentries, pack, packidx, pidx, pcommit->tree_id, objcache); if (err) { free(buf); goto done; } err = tree_path_changed(&changed, &buf, &pbuf, &entries, &nentries, &pentries, &pnentries, path, pack, packidx, ibuf, objcache); got_object_parsed_tree_entries_free(&entries); nentries = 0; free(buf); got_object_parsed_tree_entries_free(&pentries); pnentries = 0; free(pbuf); if (err) { if (err->code != GOT_ERR_NO_OBJ) goto done; err = NULL; break; } } if (!changed) { memcpy(id.sha1, pid->id->sha1, SHA1_DIGEST_LENGTH); got_object_commit_close(commit); commit = pcommit; pcommit = NULL; } } while (!changed); if (ncommits > 0) { err = send_traversed_commits(commit_ids, ncommits, ibuf); if (err) goto done; if (changed) { err = got_privsep_send_commit(ibuf, commit); if (err) goto done; } } err = send_commit_traversal_done(ibuf); done: free(commit_ids); if (commit) got_object_commit_close(commit); if (pcommit) got_object_commit_close(pcommit); if (nentries != 0) got_object_parsed_tree_entries_free(&entries); if (pnentries != 0) got_object_parsed_tree_entries_free(&pentries); if (err) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; else got_privsep_send_error(ibuf, err); } return err; } static const struct got_error * receive_packidx(struct got_packidx **packidx, struct imsgbuf *ibuf) { const struct got_error *err = NULL; struct imsg imsg; struct got_imsg_packidx ipackidx; size_t datalen; struct got_packidx *p; *packidx = NULL; err = got_privsep_recv_imsg(&imsg, ibuf, 0); if (err) return err; p = calloc(1, sizeof(*p)); if (p == NULL) { err = got_error_from_errno("calloc"); goto done; } if (imsg.hdr.type != GOT_IMSG_PACKIDX) { err = got_error(GOT_ERR_PRIVSEP_MSG); goto done; } if (imsg.fd == -1) { err = got_error(GOT_ERR_PRIVSEP_NO_FD); goto done; } datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(ipackidx)) { err = got_error(GOT_ERR_PRIVSEP_LEN); goto done; } memcpy(&ipackidx, imsg.data, sizeof(ipackidx)); p->len = ipackidx.len; p->fd = dup(imsg.fd); if (p->fd == -1) { err = got_error_from_errno("dup"); goto done; } if (lseek(p->fd, 0, SEEK_SET) == -1) { err = got_error_from_errno("lseek"); goto done; } #ifndef GOT_PACK_NO_MMAP p->map = mmap(NULL, p->len, PROT_READ, MAP_PRIVATE, p->fd, 0); if (p->map == MAP_FAILED) p->map = NULL; /* fall back to read(2) */ #endif err = got_packidx_init_hdr(p, 1); done: if (err) { if (imsg.fd != -1) close(imsg.fd); got_packidx_close(p); } else *packidx = p; imsg_free(&imsg); return err; } static const struct got_error * receive_pack(struct got_pack **packp, struct imsgbuf *ibuf) { const struct got_error *err = NULL; struct imsg imsg; struct got_imsg_pack ipack; size_t datalen; struct got_pack *pack; *packp = NULL; err = got_privsep_recv_imsg(&imsg, ibuf, 0); if (err) return err; pack = calloc(1, sizeof(*pack)); if (pack == NULL) { err = got_error_from_errno("calloc"); goto done; } if (imsg.hdr.type != GOT_IMSG_PACK) { err = got_error(GOT_ERR_PRIVSEP_MSG); goto done; } if (imsg.fd == -1) { err = got_error(GOT_ERR_PRIVSEP_NO_FD); goto done; } datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (datalen != sizeof(ipack)) { err = got_error(GOT_ERR_PRIVSEP_LEN); goto done; } memcpy(&ipack, imsg.data, sizeof(ipack)); pack->filesize = ipack.filesize; pack->fd = dup(imsg.fd); if (pack->fd == -1) { err = got_error_from_errno("dup"); goto done; } if (lseek(pack->fd, 0, SEEK_SET) == -1) { err = got_error_from_errno("lseek"); goto done; } pack->path_packfile = strdup(ipack.path_packfile); if (pack->path_packfile == NULL) { err = got_error_from_errno("strdup"); goto done; } pack->delta_cache = got_delta_cache_alloc(100, GOT_DELTA_RESULT_SIZE_CACHED_MAX); if (pack->delta_cache == NULL) { err = got_error_from_errno("got_delta_cache_alloc"); goto done; } #ifndef GOT_PACK_NO_MMAP pack->map = mmap(NULL, pack->filesize, PROT_READ, MAP_PRIVATE, pack->fd, 0); if (pack->map == MAP_FAILED) pack->map = NULL; /* fall back to read(2) */ #endif done: if (err) { if (imsg.fd != -1) close(imsg.fd); free(pack); } else *packp = pack; imsg_free(&imsg); return err; } int main(int argc, char *argv[]) { const struct got_error *err = NULL; struct imsgbuf ibuf; struct imsg imsg; struct got_packidx *packidx = NULL; struct got_pack *pack = NULL; struct got_object_cache objcache; //static int attached; //while (!attached) sleep(1); signal(SIGINT, catch_sigint); imsg_init(&ibuf, GOT_IMSG_FD_CHILD); err = got_object_cache_init(&objcache, GOT_OBJECT_CACHE_TYPE_OBJ); if (err) { err = got_error_from_errno("got_object_cache_init"); got_privsep_send_error(&ibuf, err); return 1; } #ifndef PROFILE /* revoke access to most system calls */ if (pledge("stdio recvfd", NULL) == -1) { err = got_error_from_errno("pledge"); got_privsep_send_error(&ibuf, err); return 1; } #endif err = receive_packidx(&packidx, &ibuf); if (err) { got_privsep_send_error(&ibuf, err); return 1; } err = receive_pack(&pack, &ibuf); if (err) { got_privsep_send_error(&ibuf, err); return 1; } for (;;) { imsg.fd = -1; if (sigint_received) { err = got_error(GOT_ERR_CANCELLED); break; } err = got_privsep_recv_imsg(&imsg, &ibuf, 0); if (err) { if (err->code == GOT_ERR_PRIVSEP_PIPE) err = NULL; break; } if (imsg.hdr.type == GOT_IMSG_STOP) break; switch (imsg.hdr.type) { case GOT_IMSG_PACKED_OBJECT_REQUEST: err = object_request(&imsg, &ibuf, pack, packidx, &objcache); break; case GOT_IMSG_COMMIT_REQUEST: err = commit_request(&imsg, &ibuf, pack, packidx, &objcache); break; case GOT_IMSG_TREE_REQUEST: err = tree_request(&imsg, &ibuf, pack, packidx, &objcache); break; case GOT_IMSG_BLOB_REQUEST: err = blob_request(&imsg, &ibuf, pack, packidx, &objcache); break; case GOT_IMSG_TAG_REQUEST: err = tag_request(&imsg, &ibuf, pack, packidx, &objcache); break; case GOT_IMSG_COMMIT_TRAVERSAL_REQUEST: err = commit_traversal_request(&imsg, &ibuf, pack, packidx, &objcache); break; default: err = got_error(GOT_ERR_PRIVSEP_MSG); break; } if (imsg.fd != -1 && close(imsg.fd) != 0 && err == NULL) err = got_error_from_errno("close"); imsg_free(&imsg); if (err) break; } if (packidx) got_packidx_close(packidx); if (pack) got_pack_close(pack); got_object_cache_close(&objcache); imsg_clear(&ibuf); if (err) { if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) { fprintf(stderr, "%s: %s\n", getprogname(), err->msg); got_privsep_send_error(&ibuf, err); } } if (close(GOT_IMSG_FD_CHILD) != 0 && err == NULL) err = got_error_from_errno("close"); return err ? 1 : 0; }