commit - 33147df7bc894e4498cffb3b04fd04080132c5ce
commit + ca6e02acaa175cf833a3424ff1c4842445c0109f
blob - 29c3af82ae5c984d7253f7b989561cb43f705e69
blob + 29a4d2fb492f107dfe0f7c24c53f0e54de0dda9c
--- lib/commit_graph.c
+++ lib/commit_graph.c
struct got_commit_graph_node {
struct got_object_id id;
- time_t timestamp;
- /* Used during graph iteration. */
+ /* Used only during iteration. */
+ time_t timestamp;
TAILQ_ENTRY(got_commit_graph_node) entry;
};
static void
add_node_to_iter_list(struct got_commit_graph *graph,
- struct got_commit_graph_node *node)
+ struct got_commit_graph_node *node, time_t committer_time)
{
struct got_commit_graph_node *n, *next;
+ node->timestamp = committer_time;
+
n = TAILQ_FIRST(&graph->iter_list);
while (n) {
next = TAILQ_NEXT(n, entry);
}
static const struct got_error *
+add_node(struct got_commit_graph_node **new_node,
+ struct got_commit_graph *graph, struct got_object_id *commit_id,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_commit_graph_node *node;
+
+ *new_node = NULL;
+
+ node = calloc(1, sizeof(*node));
+ if (node == NULL)
+ return got_error_from_errno("calloc");
+
+ memcpy(&node->id, commit_id, sizeof(node->id));
+ err = got_object_idset_add(graph->node_ids, &node->id, NULL);
+ if (err)
+ free(node);
+ else
+ *new_node = node;
+ return err;
+}
+
+/*
+ * Ask got-read-pack to traverse first-parent history until a commit is
+ * encountered which modified graph->path, or until the pack file runs
+ * out of relevant commits. This is faster than sending an individual
+ * request for each commit stored in the pack file.
+ */
+static const struct got_error *
+packed_first_parent_traversal(int *ncommits_traversed,
+ struct got_commit_graph *graph, struct got_object_id *commit_id,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id_queue traversed_commits;
+ struct got_object_qid *qid;
+
+ SIMPLEQ_INIT(&traversed_commits);
+ *ncommits_traversed = 0;
+
+ err = got_traverse_packed_commits(&traversed_commits,
+ commit_id, graph->path, repo);
+ if (err)
+ return err;
+
+ /* Add all traversed commits to the graph... */
+ SIMPLEQ_FOREACH(qid, &traversed_commits, entry) {
+ struct got_commit_graph_node *node;
+
+ if (got_object_idset_contains(graph->open_branches, qid->id))
+ continue;
+ if (got_object_idset_contains(graph->node_ids, qid->id))
+ continue;
+
+ (*ncommits_traversed)++;
+
+ /* ... except the last commit is the new branch tip. */
+ if (SIMPLEQ_NEXT(qid, entry) == NULL) {
+ err = got_object_idset_add(graph->open_branches,
+ qid->id, NULL);
+ break;
+ }
+
+ err = add_node(&node, graph, qid->id, repo);
+ if (err)
+ break;
+ }
+
+ got_object_id_queue_free(&traversed_commits);
+ return err;
+}
+
+static const struct got_error *
close_branch(struct got_commit_graph *graph, struct got_object_id *commit_id)
{
const struct got_error *err;
if (qid == NULL ||
got_object_idset_contains(graph->open_branches, qid->id))
return NULL;
+ /*
+ * The root directory always changes by definition, and when
+ * logging the root we want to traverse consecutive commits
+ * even if they point at the same tree.
+ * But if we are looking for a specific path then we can avoid
+ * fetching packed commits which did not modify the path and
+ * only fetch their IDs. This speeds up 'got blame'.
+ */
+ if (!got_path_is_root_dir(graph->path) &&
+ (commit->flags & GOT_COMMIT_FLAG_PACKED)) {
+ int ncommits = 0;
+ err = packed_first_parent_traversal(&ncommits,
+ graph, qid->id, repo);
+ if (err || ncommits > 0)
+ return err;
+ }
return got_object_idset_add(graph->open_branches,
qid->id, NULL);
}
}
return NULL;
-}
-
-static const struct got_error *
-add_node(struct got_commit_graph_node **new_node,
- struct got_commit_graph *graph,
- struct got_object_id *commit_id,
- struct got_commit_object *commit,
- struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_commit_graph_node *node;
-
- *new_node = NULL;
-
- node = calloc(1, sizeof(*node));
- if (node == NULL)
- return got_error_from_errno("calloc");
-
- memcpy(&node->id, commit_id, sizeof(node->id));
- node->timestamp = commit->committer_time;
-
- err = got_object_idset_add(graph->node_ids, &node->id, NULL);
- if (err)
- free(node);
- else
- *new_node = node;
- return err;
}
const struct got_error *
if (err)
return err;
- err = add_node(&new_node, a->graph, commit_id, commit, a->repo);
+ err = add_node(&new_node, a->graph, commit_id, a->repo);
if (err)
return err;
continue;
}
if (changed)
- add_node_to_iter_list(graph, new_node);
+ add_node_to_iter_list(graph, new_node,
+ got_object_commit_get_committer_time(commit));
err = advance_branch(graph, commit_id, commit, repo);
if (err)
break;
blob - 2f3281ac854ba937676647e70436adc7c7b38175
blob + 989af7a5c391e8d6a066b2235aecd6c3246aa0d3
--- lib/got_lib_object.h
+++ lib/got_lib_object.h
time_t committer_gmtoff;
char *logmsg;
int refcnt; /* > 0 if open and/or cached */
+
+ int flags;
+#define GOT_COMMIT_FLAG_PACKED 0x01
};
struct got_tree_entry {
struct got_repository *, struct got_object *);
const struct got_error *got_object_tree_entry_dup(struct got_tree_entry **,
struct got_tree_entry *);
+
+const struct got_error *got_traverse_packed_commits(
+ struct got_object_id_queue *, struct got_object_id *, const char *,
+ struct got_repository *);
blob - aa15a4c961b9caec70632114eb8f2f50b71d5df7
blob + 363157f9fa862d3d8847f8e39601526a53ece92d
--- lib/got_lib_pack.h
+++ lib/got_lib_pack.h
const struct got_error *got_packidx_open(struct got_packidx **,
const char *, int);
const struct got_error *got_packidx_close(struct got_packidx *);
+int got_packidx_get_object_idx_sha1(struct got_packidx *, uint8_t *);
int got_packidx_get_object_idx(struct got_packidx *, struct got_object_id *);
const struct got_error *got_packidx_match_id_str_prefix(
struct got_object_id_queue *, struct got_packidx *, const char *);
blob - b27325da498aa72413f8377303ba76d4012c7813
blob + 08d4c9dc4e0493c2c7ecc0d939c2320f04ae787f
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
GOT_IMSG_PACKIDX,
GOT_IMSG_PACK,
GOT_IMSG_PACKED_OBJECT_REQUEST,
+ GOT_IMSG_COMMIT_TRAVERSAL_REQUEST,
+ GOT_IMSG_TRAVERSED_COMMITS,
+ GOT_IMSG_COMMIT_TRAVERSAL_DONE,
/* Message sending file descriptor to a temporary file. */
GOT_IMSG_TMPFD,
int idx;
} __attribute__((__packed__));
+/* Structure for GOT_IMSG_COMMIT_TRAVERSAL_REQUEST */
+struct got_imsg_commit_traversal_request {
+ uint8_t id[SHA1_DIGEST_LENGTH];
+ int idx;
+ size_t path_len;
+ /* Followed by path_len bytes of path data */
+} __attribute__((__packed__));
+
+/* Structure for GOT_IMSG_TRAVERSED_COMMITS */
+struct got_imsg_traversed_commits {
+ size_t ncommits;
+ /* Followed by ncommit IDs of SHA1_DIGEST_LENGTH each */
+} __attribute__((__packed__));
+
/*
* Structure for GOT_IMSG_GITCONFIG_REMOTE data.
*/
const struct got_error *got_privsep_recv_gitconfig_remotes(
struct got_remote_repo **, int *, struct imsgbuf *);
+const struct got_error *got_privsep_send_commit_traversal_request(
+ struct imsgbuf *, struct got_object_id *, int, const char *);
+const struct got_error *got_privsep_recv_traversed_commits(
+ struct got_commit_object **, struct got_object_id **,
+ struct got_object_id_queue *, struct imsgbuf *);
+const struct got_error *got_privsep_send_traversed_commits(
+ struct got_object_id *, size_t, struct imsgbuf *);
+const struct got_error *got_privsep_send_commit_traversal_done(
+ struct imsgbuf *);
+
void got_privsep_exec_child(int[2], const char *, const char *);
blob - 4aaeb8a3199435ce3b95f7218a552f6375558752
blob + 4f98167915dd8661414656d1b92c2624248f1519
--- lib/object.c
+++ lib/object.c
#ifndef MIN
#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
#endif
-
-struct got_object_id *
-got_object_id_dup(struct got_object_id *id1)
-{
- struct got_object_id *id2;
-
- id2 = malloc(sizeof(*id2));
- if (id2 == NULL)
- return NULL;
- memcpy(id2, id1, sizeof(*id2));
- return id2;
-}
struct got_object_id *
got_object_get_id(struct got_object *obj)
if (err)
return err;
- return got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
+ err = got_privsep_recv_commit(commit, pack->privsep_child->ibuf);
+ if (err)
+ return err;
+
+ (*commit)->flags |= GOT_COMMIT_FLAG_PACKED;
+ return NULL;
}
static const struct got_error *
got_object_tree_entry_is_submodule(struct got_tree_entry *te)
{
return (te->mode & S_IFMT) == (S_IFDIR | S_IFLNK);
+}
+
+const struct got_error *
+got_traverse_packed_commits(struct got_object_id_queue *traversed_commits,
+ struct got_object_id *commit_id, const char *path,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_pack *pack = NULL;
+ struct got_packidx *packidx = NULL;
+ char *path_packfile = NULL;
+ struct got_commit_object *changed_commit = NULL;
+ struct got_object_id *changed_commit_id = NULL;
+ int idx;
+
+ err = got_repo_search_packidx(&packidx, &idx, repo, commit_id);
+ if (err) {
+ if (err->code != GOT_ERR_NO_OBJ)
+ return err;
+ return NULL;
+ }
+
+ err = get_packfile_path(&path_packfile, packidx);
+ if (err)
+ return err;
+
+ pack = got_repo_get_cached_pack(repo, path_packfile);
+ if (pack == NULL) {
+ err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
+ if (err)
+ goto done;
+ }
+
+ if (pack->privsep_child == NULL) {
+ err = start_pack_privsep_child(pack, packidx);
+ if (err)
+ goto done;
+ }
+
+ err = got_privsep_send_commit_traversal_request(
+ pack->privsep_child->ibuf, commit_id, idx, path);
+ if (err)
+ goto done;
+
+ err = got_privsep_recv_traversed_commits(&changed_commit,
+ &changed_commit_id, traversed_commits, pack->privsep_child->ibuf);
+ if (err)
+ goto done;
+
+ if (changed_commit) {
+ /*
+ * Cache the commit in which the path was changed.
+ * This commit might be opened again soon.
+ */
+ changed_commit->refcnt++;
+ err = got_repo_cache_commit(repo, changed_commit_id,
+ changed_commit);
+ got_object_commit_close(changed_commit);
+ }
+done:
+ free(path_packfile);
+ free(changed_commit_id);
+ return err;
}
blob - 32d44fbf7ceea77198e8a62d8d0c0cf604e2ed8c
blob + d3584125c591dde528f7c8eb8d1eb44dd9728c86
--- lib/object_parse.c
+++ lib/object_parse.c
#ifndef nitems
#define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
#endif
+
+struct got_object_id *
+got_object_id_dup(struct got_object_id *id1)
+{
+ struct got_object_id *id2;
+
+ id2 = malloc(sizeof(*id2));
+ if (id2 == NULL)
+ return NULL;
+ memcpy(id2, id1, sizeof(*id2));
+ return id2;
+}
int
got_object_id_cmp(const struct got_object_id *id1,
blob - ea83c44496699701bf12591fb8784e500ac8b797
blob + 938d4cf659ed6d1195e4bea48877dd45cc539555
--- lib/pack.c
+++ lib/pack.c
}
int
-got_packidx_get_object_idx(struct got_packidx *packidx, struct got_object_id *id)
+got_packidx_get_object_idx_sha1(struct got_packidx *packidx, uint8_t *sha1)
{
- u_int8_t id0 = id->sha1[0];
+ u_int8_t id0 = sha1[0];
uint32_t totobj = betoh32(packidx->hdr.fanout_table[0xff]);
int left = 0, right = totobj - 1;
i = ((left + right) / 2);
oid = &packidx->hdr.sorted_ids[i];
- cmp = memcmp(id->sha1, oid->sha1, SHA1_DIGEST_LENGTH);
+ cmp = memcmp(sha1, oid->sha1, SHA1_DIGEST_LENGTH);
if (cmp == 0)
return i;
else if (cmp > 0)
}
return -1;
+}
+
+int
+got_packidx_get_object_idx(struct got_packidx *packidx, struct got_object_id *id)
+{
+ return got_packidx_get_object_idx_sha1(packidx, id->sha1);
}
const struct got_error *
blob - 0e15115f6c894362a4e3046ee857ca73f6637802
blob + a2566dac4aee1eadf39748a400c7710ebdd34828
--- lib/privsep.c
+++ lib/privsep.c
return err;
}
-const struct got_error *
-got_privsep_recv_commit(struct got_commit_object **commit, struct imsgbuf *ibuf)
+static const struct got_error *
+get_commit_from_imsg(struct got_commit_object **commit,
+ struct imsg *imsg, size_t datalen, struct imsgbuf *ibuf)
{
const struct got_error *err = NULL;
- struct imsg imsg;
struct got_imsg_commit_object *icommit;
- size_t len, datalen;
+ size_t len = 0;
int i;
- const size_t min_datalen =
- MIN(sizeof(struct got_imsg_error),
- sizeof(struct got_imsg_commit_object));
- *commit = NULL;
-
- err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen);
- if (err)
- return err;
+ if (datalen < sizeof(*icommit))
+ return got_error(GOT_ERR_PRIVSEP_LEN);
- datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
- len = 0;
+ icommit = imsg->data;
+ if (datalen != sizeof(*icommit) + icommit->author_len +
+ icommit->committer_len +
+ icommit->nparents * SHA1_DIGEST_LENGTH)
+ return got_error(GOT_ERR_PRIVSEP_LEN);
- switch (imsg.hdr.type) {
- case GOT_IMSG_COMMIT:
- if (datalen < sizeof(*icommit)) {
- err = got_error(GOT_ERR_PRIVSEP_LEN);
- break;
- }
- icommit = imsg.data;
- if (datalen != sizeof(*icommit) + icommit->author_len +
- icommit->committer_len +
- icommit->nparents * SHA1_DIGEST_LENGTH) {
- err = got_error(GOT_ERR_PRIVSEP_LEN);
- break;
- }
- if (icommit->nparents < 0) {
- err = got_error(GOT_ERR_PRIVSEP_LEN);
- break;
- }
- len += sizeof(*icommit);
+ if (icommit->nparents < 0)
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+
+ len += sizeof(*icommit);
- *commit = got_object_commit_alloc_partial();
- if (*commit == NULL) {
- err = got_error_from_errno(
- "got_object_commit_alloc_partial");
- break;
+ *commit = got_object_commit_alloc_partial();
+ if (*commit == NULL)
+ return got_error_from_errno(
+ "got_object_commit_alloc_partial");
+
+ memcpy((*commit)->tree_id->sha1, icommit->tree_id,
+ SHA1_DIGEST_LENGTH);
+ (*commit)->author_time = icommit->author_time;
+ (*commit)->author_gmtoff = icommit->author_gmtoff;
+ (*commit)->committer_time = icommit->committer_time;
+ (*commit)->committer_gmtoff = icommit->committer_gmtoff;
+
+ if (icommit->author_len == 0) {
+ (*commit)->author = strdup("");
+ if ((*commit)->author == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
}
+ } else {
+ (*commit)->author = malloc(icommit->author_len + 1);
+ if ((*commit)->author == NULL) {
+ err = got_error_from_errno("malloc");
+ goto done;
+ }
+ memcpy((*commit)->author, imsg->data + len,
+ icommit->author_len);
+ (*commit)->author[icommit->author_len] = '\0';
+ }
+ len += icommit->author_len;
- memcpy((*commit)->tree_id->sha1, icommit->tree_id,
- SHA1_DIGEST_LENGTH);
- (*commit)->author_time = icommit->author_time;
- (*commit)->author_gmtoff = icommit->author_gmtoff;
- (*commit)->committer_time = icommit->committer_time;
- (*commit)->committer_gmtoff = icommit->committer_gmtoff;
-
- if (icommit->author_len == 0) {
- (*commit)->author = strdup("");
- if ((*commit)->author == NULL) {
- err = got_error_from_errno("strdup");
- break;
- }
- } else {
- (*commit)->author = malloc(icommit->author_len + 1);
- if ((*commit)->author == NULL) {
- err = got_error_from_errno("malloc");
- break;
- }
- memcpy((*commit)->author, imsg.data + len,
- icommit->author_len);
- (*commit)->author[icommit->author_len] = '\0';
+ if (icommit->committer_len == 0) {
+ (*commit)->committer = strdup("");
+ if ((*commit)->committer == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+ } else {
+ (*commit)->committer =
+ malloc(icommit->committer_len + 1);
+ if ((*commit)->committer == NULL) {
+ err = got_error_from_errno("malloc");
+ goto done;
+ }
+ memcpy((*commit)->committer, imsg->data + len,
+ icommit->committer_len);
+ (*commit)->committer[icommit->committer_len] = '\0';
+ }
+ len += icommit->committer_len;
+
+ if (icommit->logmsg_len == 0) {
+ (*commit)->logmsg = strdup("");
+ if ((*commit)->logmsg == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+ } else {
+ size_t offset = 0, remain = icommit->logmsg_len;
+
+ (*commit)->logmsg = malloc(icommit->logmsg_len + 1);
+ if ((*commit)->logmsg == NULL) {
+ err = got_error_from_errno("malloc");
+ goto done;
}
- len += icommit->author_len;
+ while (remain > 0) {
+ struct imsg imsg_log;
+ size_t n = MIN(MAX_IMSGSIZE - IMSG_HEADER_SIZE,
+ remain);
- if (icommit->committer_len == 0) {
- (*commit)->committer = strdup("");
- if ((*commit)->committer == NULL) {
- err = got_error_from_errno("strdup");
- break;
- }
- } else {
- (*commit)->committer =
- malloc(icommit->committer_len + 1);
- if ((*commit)->committer == NULL) {
- err = got_error_from_errno("malloc");
- break;
- }
- memcpy((*commit)->committer, imsg.data + len,
- icommit->committer_len);
- (*commit)->committer[icommit->committer_len] = '\0';
- }
- len += icommit->committer_len;
+ err = got_privsep_recv_imsg(&imsg_log, ibuf, n);
+ if (err)
+ goto done;
- if (icommit->logmsg_len == 0) {
- (*commit)->logmsg = strdup("");
- if ((*commit)->logmsg == NULL) {
- err = got_error_from_errno("strdup");
- break;
+ if (imsg_log.hdr.type != GOT_IMSG_COMMIT_LOGMSG) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ goto done;
}
- } else {
- size_t offset = 0, remain = icommit->logmsg_len;
- (*commit)->logmsg = malloc(icommit->logmsg_len + 1);
- if ((*commit)->logmsg == NULL) {
- err = got_error_from_errno("malloc");
- break;
- }
- while (remain > 0) {
- struct imsg imsg_log;
- size_t n = MIN(MAX_IMSGSIZE - IMSG_HEADER_SIZE,
- remain);
-
- err = got_privsep_recv_imsg(&imsg_log, ibuf, n);
- if (err)
- return err;
-
- if (imsg_log.hdr.type != GOT_IMSG_COMMIT_LOGMSG)
- return got_error(GOT_ERR_PRIVSEP_MSG);
-
- memcpy((*commit)->logmsg + offset,
- imsg_log.data, n);
- imsg_free(&imsg_log);
- offset += n;
- remain -= n;
- }
- (*commit)->logmsg[icommit->logmsg_len] = '\0';
+ memcpy((*commit)->logmsg + offset,
+ imsg_log.data, n);
+ imsg_free(&imsg_log);
+ offset += n;
+ remain -= n;
}
+ (*commit)->logmsg[icommit->logmsg_len] = '\0';
+ }
- for (i = 0; i < icommit->nparents; i++) {
- struct got_object_qid *qid;
+ for (i = 0; i < icommit->nparents; i++) {
+ struct got_object_qid *qid;
- err = got_object_qid_alloc_partial(&qid);
- if (err)
- break;
- memcpy(qid->id, imsg.data + len +
- i * SHA1_DIGEST_LENGTH, sizeof(*qid->id));
- SIMPLEQ_INSERT_TAIL(&(*commit)->parent_ids, qid, entry);
- (*commit)->nparents++;
- }
+ err = got_object_qid_alloc_partial(&qid);
+ if (err)
+ break;
+ memcpy(qid->id, imsg->data + len +
+ i * SHA1_DIGEST_LENGTH, sizeof(*qid->id));
+ SIMPLEQ_INSERT_TAIL(&(*commit)->parent_ids, qid, entry);
+ (*commit)->nparents++;
+ }
+done:
+ if (err) {
+ got_object_commit_close(*commit);
+ *commit = NULL;
+ }
+ return err;
+}
+
+const struct got_error *
+got_privsep_recv_commit(struct got_commit_object **commit, struct imsgbuf *ibuf)
+{
+ const struct got_error *err = NULL;
+ struct imsg imsg;
+ size_t datalen;
+ const size_t min_datalen =
+ MIN(sizeof(struct got_imsg_error),
+ sizeof(struct got_imsg_commit_object));
+
+ *commit = NULL;
+
+ err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen);
+ if (err)
+ return err;
+
+ datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+ switch (imsg.hdr.type) {
+ case GOT_IMSG_COMMIT:
+ err = get_commit_from_imsg(commit, &imsg, datalen, ibuf);
break;
default:
err = got_error(GOT_ERR_PRIVSEP_MSG);
*nremotes = 0;
}
return err;
+}
+
+const struct got_error *
+got_privsep_send_commit_traversal_request(struct imsgbuf *ibuf,
+ struct got_object_id *id, int idx, const char *path)
+{
+ const struct got_error *err = NULL;
+ struct ibuf *wbuf;
+ size_t path_len = strlen(path) + 1;
+
+ wbuf = imsg_create(ibuf, GOT_IMSG_COMMIT_TRAVERSAL_REQUEST, 0, 0,
+ sizeof(struct got_imsg_commit_traversal_request) + path_len);
+ if (wbuf == NULL)
+ return got_error_from_errno(
+ "imsg_create COMMIT_TRAVERSAL_REQUEST");
+ if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1) {
+ err = got_error_from_errno("imsg_add COMMIT_TRAVERSAL_REQUEST");
+ ibuf_free(wbuf);
+ return err;
+ }
+ if (imsg_add(wbuf, &idx, sizeof(idx)) == -1) {
+ err = got_error_from_errno("imsg_add COMMIT_TRAVERSAL_REQUEST");
+ ibuf_free(wbuf);
+ return err;
+ }
+ if (imsg_add(wbuf, path, path_len) == -1) {
+ err = got_error_from_errno("imsg_add COMMIT_TRAVERSAL_REQUEST");
+ ibuf_free(wbuf);
+ return err;
+ }
+
+ wbuf->fd = -1;
+ imsg_close(ibuf, wbuf);
+
+ return flush_imsg(ibuf);
}
const struct got_error *
+got_privsep_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 flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_recv_traversed_commits(struct got_commit_object **changed_commit,
+ struct got_object_id **changed_commit_id,
+ struct got_object_id_queue *commit_ids, struct imsgbuf *ibuf)
+{
+ const struct got_error *err = NULL;
+ struct imsg imsg;
+ struct got_imsg_traversed_commits *icommits;
+ size_t datalen;
+ int i, done = 0;
+
+ *changed_commit = NULL;
+ *changed_commit_id = NULL;
+
+ while (!done) {
+ err = got_privsep_recv_imsg(&imsg, ibuf, 0);
+ if (err)
+ return err;
+
+ datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+ switch (imsg.hdr.type) {
+ case GOT_IMSG_TRAVERSED_COMMITS:
+ icommits = imsg.data;
+ if (datalen != sizeof(*icommits) +
+ icommits->ncommits * SHA1_DIGEST_LENGTH) {
+ err = got_error(GOT_ERR_PRIVSEP_LEN);
+ break;
+ }
+ for (i = 0; i < icommits->ncommits; i++) {
+ struct got_object_qid *qid;
+ uint8_t *sha1 = (uint8_t *)imsg.data +
+ sizeof(*icommits) + i * SHA1_DIGEST_LENGTH;
+ err = got_object_qid_alloc_partial(&qid);
+ if (err)
+ break;
+ memcpy(qid->id->sha1, sha1, SHA1_DIGEST_LENGTH);
+ SIMPLEQ_INSERT_TAIL(commit_ids, qid, entry);
+
+ /* The last commit may contain a change. */
+ if (i == icommits->ncommits - 1) {
+ *changed_commit_id =
+ got_object_id_dup(qid->id);
+ if (*changed_commit_id == NULL) {
+ err = got_error_from_errno(
+ "got_object_id_dup");
+ break;
+ }
+ }
+ }
+ break;
+ case GOT_IMSG_COMMIT:
+ if (*changed_commit_id == NULL) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = get_commit_from_imsg(changed_commit, &imsg,
+ datalen, ibuf);
+ break;
+ case GOT_IMSG_COMMIT_TRAVERSAL_DONE:
+ done = 1;
+ break;
+ default:
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+
+ imsg_free(&imsg);
+ if (err)
+ break;
+ }
+
+ if (err)
+ got_object_id_queue_free(commit_ids);
+ return err;
+}
+
+const struct got_error *
+got_privsep_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 flush_imsg(ibuf);
+}
+
+const struct got_error *
got_privsep_unveil_exec_helpers(void)
{
const char *helpers[] = {
blob - b10c699ebc8f5fe4fa7e3e5e9e256811a3a11687
blob + 49133597718c2ff09eff39bb48313bb803aea2e4
--- libexec/got-read-pack/got-read-pack.c
+++ libexec/got-read-pack/got-read-pack.c
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 *
+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_imsg_packed_object iobj;
struct got_object *obj = NULL;
- struct got_commit_object *commit = 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);
+ *commit = NULL;
- obj = got_object_cache_get(objcache, &id);
+ obj = got_object_cache_get(objcache, id);
if (obj) {
obj->refcnt++;
} else {
- err = open_object(&obj, pack, packidx, iobj.idx, &id,
+ err = open_object(&obj, pack, packidx, obj_idx, id,
objcache);
if (err)
return err;
goto done;
obj->size = len;
- err = got_object_parse_commit(&commit, buf, 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:
- free(buf);
- got_object_close(obj);
if (commit)
got_object_commit_close(commit);
if (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_object *obj = NULL;
struct got_pathlist_head entries;
int nentries = 0;
uint8_t *buf = NULL;
- size_t len;
struct got_object_id id;
size_t datalen;
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);
+ err = open_tree(&buf, &entries, &nentries, pack, packidx, iobj.idx,
+ &id, objcache);
if (err)
- goto done;
+ return err;
- obj->size = len;
- err = got_object_parse_tree(&entries, &nentries, buf, len);
- if (err)
- goto done;
-
err = got_privsep_send_tree(ibuf, &entries, nentries);
-done:
got_object_parsed_tree_entries_free(&entries);
free(buf);
- got_object_close(obj);
if (err) {
if (err->code == GOT_ERR_PRIVSEP_PIPE)
err = NULL;
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 are expecting an absolute in-repository path. */
+ if (path[0] != '/')
+ return got_error(GOT_ERR_NOT_ABSPATH);
+
+ /* We not do support comparing the root path. */
+ if (path[1] == '\0')
+ return got_error(GOT_ERR_BAD_PATH);
+
+ s = path;
+ s++; /* skip leading '/' */
+ 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;
+
+ idx = got_packidx_get_object_idx_sha1(packidx,
+ pte1->id);
+ if (idx == -1) {
+ err = got_error(GOT_ERR_NO_OBJ);
+ break;
+ }
+ memcpy(id1.sha1, pte1->id, SHA1_DIGEST_LENGTH);
+ 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;
+
+ idx = got_packidx_get_object_idx_sha1(packidx,
+ pte2->id);
+ if (idx == -1) {
+ err = got_error(GOT_ERR_NO_OBJ);
+ break;
+ }
+ memcpy(id2.sha1, pte2->id, SHA1_DIGEST_LENGTH);
+ 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 *
+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 = got_privsep_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 = got_privsep_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 = got_privsep_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;
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;