Blob Blame History Raw
/**************************************************************/
/*  tpax: a topological pax implementation                    */
/*  Copyright (C) 2020--2024  SysDeer Technologies, LLC       */
/*  Released under GPLv2 and GPLv3; see COPYING.TPAX.         */
/**************************************************************/

#include <stdint.h>
#include <stdlib.h>

#include <tpax/tpax.h>
#include "tpax_driver_impl.h"
#include "tpax_errinfo_impl.h"
#include "tpax_visibility_impl.h"

#define TPAX_MAX_DEPTH 512

tpax_hidden const char * tpax_queue_item_full_path(
	const struct tpax_driver_ctx *  dctx,
	const struct tpax_dirent *      cdent)
{
	char *                          ch;
	char *                          pathbuf;
	const struct tpax_dirent *      pparent;
	const struct tpax_dirent **     pdirent;
	const struct tpax_dirent *      dirstck[TPAX_MAX_DEPTH];

	if (cdent->depth >= TPAX_MAX_DEPTH)
		return 0;

	ch = pathbuf = (tpax_get_driver_ictx(dctx))->dirbuff;

	for (pparent=cdent,pdirent=dirstck; pparent; pparent=pparent->parent)
		*pdirent++ = pparent;

	*pdirent-- = 0;

	if (pdirent[0]->prefix)
		ch += sprintf(ch,"%s",pdirent[0]->prefix);

	for (; pdirent > dirstck; ) {
		if (!(pdirent[0]->flags & TPAX_ITEM_SYMLINK))
			ch += sprintf(ch,"%s/",pdirent[0]->dirent.d_name);

		pdirent--;
	}

	if (pdirent[0]->flags & TPAX_ITEM_SYMLINK) {
		*--ch = '\0';
	} else {
		sprintf(ch,"%s",pdirent[0]->dirent.d_name);
	}

	return pathbuf;
}

static int tpax_cpio_dirent_compare(const void * a, const void * b)
{
	const struct tpax_dirent * direnta;
	const struct tpax_dirent * direntb;

	direnta = *(const struct tpax_dirent **)a;
	direntb = *(const struct tpax_dirent **)b;

	return (direnta->stdev == direntb->stdev)
		? direnta->stino - direntb->stino
		: direnta->stdev - direntb->stdev;
}

static int tpax_update_cpio_queue_vector(const struct tpax_driver_ctx * dctx)
{
	struct tpax_driver_ctx_impl *   ictx;
	struct tpax_dirent **           cpiov;
	struct tpax_dirent **           direntv;
	struct tpax_dirent *            cdent;
	dev_t                           stdev;
	ino_t                           stino;
	int                             cpdev;
	int                             cpino;
	int                             nlink;
	int                             idx;

	/* driver */
	ictx = tpax_get_driver_ictx(dctx);

	/* not needed? */
	if (ictx->nqueued == 0)
		return 0;

	/* vector copy */
	direntv = ictx->direntv;
	cpiov   = ictx->cpiov;

	for (; *direntv; )
		*cpiov++ = *direntv++;

	/* sort by real st_dev, st_ino */
	qsort(ictx->cpiov,ictx->nqueued,sizeof(*cpiov),tpax_cpio_dirent_compare);

	/* set the cpio internal c_dev, c_ino, and c_nlink fields (U+1F620) */
	cpiov = ictx->cpiov;
	cdent = cpiov[0];
	cpiov++;

	cpdev = 1;
	cpino = 1;
	nlink = 1;

	stdev = cdent->stdev;
	stino = cdent->stino;

	cdent->cpdev = cpdev;
	cdent->cpino = cpino;
	cdent->nlink = nlink;

	for (; *cpiov; ) {
		cdent = *cpiov;

		if (cdent->srdev > 0777777)
			return TPAX_CUSTOM_ERROR(
				dctx,
				TPAX_ERR_FLOW_ERROR);

		if ((cdent->stdev == stdev) && (cdent->stino == stino)) {
			nlink++;

		} else if (cdent->stdev > stdev) {
			if (nlink > 1)
				for (idx=2; idx<=nlink; idx++)
					cpiov[-idx]->nlink = nlink;

			cpdev++;
			cpino = 1;
			nlink = 1;

			stdev = cdent->stdev;
			stino = cdent->stino;

			if (cpdev > 0777777)
				return TPAX_CUSTOM_ERROR(
					dctx,
					TPAX_ERR_FLOW_ERROR);

		} else if (cdent->stino > stino) {
			if (nlink > 1)
				for (idx=2; idx<=nlink; idx++)
					cpiov[-idx]->nlink = nlink;

			cpino++;
			stino = cdent->stino;
			nlink = 1;

			if (cpino > 0777777)
				return TPAX_CUSTOM_ERROR(
					dctx,
					TPAX_ERR_FLOW_ERROR);
		}

		cdent->cpdev = cpdev;
		cdent->cpino = cpino;
		cdent->nlink = nlink;

		cpiov++;
	}

	if (nlink > 1)
		for (idx=2; idx<=nlink; idx++)
			cpiov[-idx]->nlink = nlink;

	return 0;
}

tpax_hidden int tpax_update_queue_vector(const struct tpax_driver_ctx * dctx)
{
	uintptr_t                       addr;
	struct tpax_driver_ctx_impl *   ictx;
	struct tpax_dirent_buffer *     dentbuf;
	struct tpax_dirent **           direntv;
	struct tpax_dirent *            cdent;
	struct tpax_dirent *            cnext;
	size_t                          arrsize;

	/* driver */
	ictx = tpax_get_driver_ictx(dctx);

	/* vector alloc */
	if (ictx->direntv) {
		free(ictx->direntv);
		free(ictx->cpiov);

		ictx->direntv = 0;
		ictx->cpiov   = 0;
	}

	arrsize = ictx->nqueued + 1;

	if (!(ictx->direntv = calloc(arrsize,sizeof(struct tpax_dirent *))))
		return TPAX_SYSTEM_ERROR(dctx);

	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
		if (!(ictx->cpiov = calloc(arrsize,sizeof(struct tpax_dirent *))))
			return TPAX_SYSTEM_ERROR(dctx);

	/* queue vector */
	dentbuf = tpax_get_driver_dirents(dctx);
	cdent   = ictx->nqueued ? dentbuf->dbuf : 0;

	for (direntv=ictx->direntv; cdent; direntv++) {
		*direntv = cdent;

		addr  = (uintptr_t)cdent;
		addr += cdent->nsize;
		cnext = (struct tpax_dirent *)addr;

		if (cnext == dentbuf->cdent) {
			dentbuf = dentbuf->next;
			cnext   = dentbuf ? dentbuf->dbuf : 0;
		}

		cdent = cnext;
	}

	if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
		if (tpax_update_cpio_queue_vector(dctx) < 0)
			return TPAX_NESTED_ERROR(dctx);

	return 0;
}