Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
29601ccaf75ffa6a0cdc2e6bcaedaa5a573f59a2
Author
Runxi Yu <me@runxiyu.org>
Author date
Sat, 05 Apr 2025 11:58:21 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sat, 05 Apr 2025 12:34:01 +0800
Actions
git2d: Add a basic command for tree
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

int
cmd2(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
{
	/* Path */
	char path[4096] = {0};
	int err = bare_get_data(reader, (uint8_t *)path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		bare_put_uint(writer, 10);
		return -1;
	}
	path[sizeof(path) - 1] = '\0';

	/* HEAD^{tree} */
	git_object *head_obj = NULL;
	err = git_revparse_single(&head_obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(writer, 11);
		return -1;
	}
	git_tree *tree = (git_tree *)head_obj;

	/* Path in tree */
	git_tree_entry *entry = NULL;
	git_otype objtype;
	if (strlen(path) == 0) {
		entry = NULL;
		objtype = GIT_OBJECT_TREE;
	} else {
		err = git_tree_entry_bypath(&entry, tree, path);
		if (err != 0) {
			bare_put_uint(writer, 3);
			git_tree_free(tree);
			return 0;
		}
		objtype = git_tree_entry_type(entry);
	}

	if (objtype == GIT_OBJECT_TREE) {
		/* Tree */
		git_object *tree_obj = NULL;
		if (entry == NULL) {
			tree_obj = (git_object *)tree;
		} else {
			err = git_tree_entry_to_object(&tree_obj, repo, entry);
			if (err != 0) {
				bare_put_uint(writer, 3);
				goto cleanup;
			}
		}
		git_tree *subtree = (git_tree *)tree_obj;

		size_t count = git_tree_entrycount(subtree);
		bare_put_uint(writer, 0);
		bare_put_uint(writer, 1);
		bare_put_uint(writer, count);
		for (size_t i = 0; i < count; i++) {
			const git_tree_entry *subentry = git_tree_entry_byindex(subtree, i);
			const char *name = git_tree_entry_name(subentry);
			git_otype type = git_tree_entry_type(subentry);
			uint32_t mode = git_tree_entry_filemode(subentry);

			uint8_t entry_type = 0;
			uint64_t size = 0;

			if (type == GIT_OBJECT_TREE) {
				entry_type = 1;
			} else if (type == GIT_OBJECT_BLOB) {
				entry_type = 2;

				git_object *subobj = NULL;
				if (git_tree_entry_to_object(&subobj, repo, subentry) == 0) {
					git_blob *b = (git_blob *)subobj;
					size = git_blob_rawsize(b);
					git_blob_free(b);
				}
			}

			bare_put_uint(writer, entry_type);
			bare_put_uint(writer, mode);
			bare_put_uint(writer, size);
			bare_put_data(writer, (const uint8_t *)name, strlen(name));
		}
		if (entry != NULL) {
			git_tree_free(subtree);
		}
	} else if (objtype == GIT_OBJECT_BLOB) {
		/* Blob */
		git_object *blob_obj = NULL;
		err = git_tree_entry_to_object(&blob_obj, repo, entry);
		if (err != 0) {
			bare_put_uint(writer, 3);
			goto cleanup;
		}
		git_blob *blob = (git_blob *)blob_obj;
		const void *content = git_blob_rawcontent(blob);
		if (content == NULL) {
			bare_put_uint(writer, 3);
			git_blob_free(blob);
			goto cleanup;
		}
		bare_put_uint(writer, 0);
		bare_put_uint(writer, 2);
		bare_put_data(writer, content, git_blob_rawsize(blob));
		git_blob_free(blob);
	} else {
		/* Unknown */
		bare_put_uint(writer, 3);
	}

cleanup:
	if (entry != NULL)
		git_tree_entry_free(entry);
	git_tree_free(tree);
	return 0;
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

void *
session(void *_conn)
{
	int conn = *(int *)_conn;
	free((int *)_conn);

	int err;

	conn_io_t io = {.fd = conn };
	struct bare_reader reader = {
		.buffer = &io,
		.read = conn_read,
	};
	struct bare_writer writer = {
		.buffer = &io,
		.write = conn_write,
	};

	/* Repo path */
	char path[4096] = {0};
	err = bare_get_data(&reader, (uint8_t *) path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		goto close;
	}
	path[sizeof(path) - 1] = '\0';

	/* Open repo */
	git_repository *repo = NULL;
	err = git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
	if (err != 0) {
		bare_put_uint(&writer, 1);
		goto close;
	}

	/* Command */
	uint64_t cmd = 0;
	err = bare_get_uint(&reader, &cmd);
	if (err != BARE_ERROR_NONE) {
		bare_put_uint(&writer, 2);
		goto free_repo;
	}
	switch (cmd) {
	case 1:
		err = cmd1(repo, &writer);
		if (err != 0)
			goto free_repo;
		break;
	case 2:
		err = cmd2(repo, &reader, &writer);
		if (err != 0)
			goto free_repo;
		break;
	case 0:
		bare_put_uint(&writer, 3);
		goto free_repo;
	default:
		bare_put_uint(&writer, 3);
		goto free_repo;
	}

free_repo:
	git_repository_free(repo);

close:
	close(conn);

	return NULL;
}

/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include <err.h>
#include <errno.h>
#include <git2.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "bare.h"

#ifndef X_H
#define X_H

typedef struct {
	int fd;
} conn_io_t;


bare_error conn_read(void *buffer, void *dst, uint64_t sz);
bare_error conn_write(void *buffer, const void *src, uint64_t sz);

void * session(void *_conn);

int cmd1(git_repository *repo, struct bare_writer *writer);
int cmd2(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer);

#endif // X_H