From 43c39bb6b44cc96ac012c259e30c1466cfb23b48 Mon Sep 17 00:00:00 2001 From: midipix Date: Thu, 30 May 2024 03:52:14 +0000 Subject: core api: renamed tpax_archive_append() as tpax_archive_enqueue(). --- src/logic/tpax_archive_enqueue.c | 475 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 475 insertions(+) create mode 100644 src/logic/tpax_archive_enqueue.c (limited to 'src/logic/tpax_archive_enqueue.c') diff --git a/src/logic/tpax_archive_enqueue.c b/src/logic/tpax_archive_enqueue.c new file mode 100644 index 0000000..222b0ad --- /dev/null +++ b/src/logic/tpax_archive_enqueue.c @@ -0,0 +1,475 @@ +/**************************************************************/ +/* tpax: a topological pax implementation */ +/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ +/**************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tpax_driver_impl.h" +#include "tpax_getdents_impl.h" +#include "tpax_tmpfile_impl.h" +#include "tpax_errinfo_impl.h" + +static char * tpax_add_prefix_item( + const struct tpax_driver_ctx * dctx, + const char * prefix) +{ + struct tpax_driver_ctx_impl * ictx; + char ** prefv; + char ** psrc; + char ** pdst; + off_t elements; + char * pitem; + + ictx = tpax_get_driver_ictx(dctx); + + for (psrc=ictx->prefixv; *psrc; psrc++) + if (!strcmp(*psrc,prefix)) + return *psrc; + + if (ictx->prefixp == ictx->prefcap) { + elements = ictx->prefcap - ictx->prefixv; + elements += 256; + + if (!(prefv = calloc(elements,sizeof(char *)))) + return 0; + + for (psrc=ictx->prefixv,pdst=prefv; *psrc; psrc++,pdst++) + *pdst = *psrc; + + if (ictx->prefixv != ictx->prefptr) + free(ictx->prefixv); + + ictx->prefixv = prefv; + ictx->prefixp = pdst; + ictx->prefcap = &prefv[--elements]; + } + + if (!(pitem = strdup(prefix))) + return 0; + + *ictx->prefixp++ = pitem; + + return pitem; +} + +static char * tpax_add_prefix_item_from_path( + const struct tpax_driver_ctx * dctx, + const char * path, + const char * mark) +{ + off_t nbytes; + char pathbuf[PATH_MAX]; + + if ((nbytes = (mark - path)) >= (PATH_MAX - 1)) + return 0; + + memcpy(pathbuf,path,nbytes); + pathbuf[nbytes++] = '/'; + pathbuf[nbytes] = '\0'; + + return tpax_add_prefix_item(dctx,pathbuf); +} + +static struct tpax_dirent_buffer * tpax_dirent_buf_first_alloc( + const struct tpax_driver_ctx * dctx) +{ + void * addr; + struct tpax_driver_ctx_impl * ictx; + + addr = (struct tpax_dirent_buffer *)mmap( + 0,TPAX_DIRENT_BUFLEN, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, + -1,0); + + if (addr == MAP_FAILED) + return 0; + + ictx = tpax_get_driver_ictx(dctx); + ictx->dirents = (struct tpax_dirent_buffer *)addr; + ictx->dirents->cdent = ictx->dirents->dbuf; + + ictx->dirents->size = TPAX_DIRENT_BUFLEN; + ictx->dirents->next = 0; + + ictx->dirents->nfree = TPAX_DIRENT_BUFLEN; + ictx->dirents->nfree -= offsetof(struct tpax_dirent_buffer,dbuf); + + return ictx->dirents; +} + +static struct tpax_dirent_buffer * tpax_dirent_buf_next_alloc( + struct tpax_dirent_buffer * current) +{ + void * addr; + + addr = (struct tpax_dirent_buffer *)mmap( + 0,TPAX_DIRENT_BUFLEN, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, + -1,0); + + if (addr == MAP_FAILED) + return 0; + + current->next = (struct tpax_dirent_buffer *)addr; + current->next->cdent = current->next->dbuf; + + current->next->size = TPAX_DIRENT_BUFLEN; + current->next->next = 0; + + current->next->nfree = TPAX_DIRENT_BUFLEN; + current->next->nfree -= offsetof(struct tpax_dirent_buffer,dbuf); + + return current->next; +} + +static int tpax_archive_enqueue_ret( + int ret, + struct tpax_unit_ctx * unit) +{ + if (unit) + tpax_lib_free_unit_ctx(unit); + + return ret; +} + +static int tpax_archive_add_queue_item( + const struct tpax_driver_ctx * dctx, + const struct dirent * dirent, + const struct tpax_dirent * parent, + const char * prefix, + int depth, + int flags, + int fdat, + bool * fkeep) +{ + struct tpax_dirent_buffer * dentbuf; + struct tpax_dirent * cdent; + const char * src; + char * dst; + char * cap; + size_t needed; + + if (!(dentbuf = tpax_get_driver_dirents(dctx))) + if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx))) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + 0); + + needed = dirent->d_reclen; + needed += offsetof(struct tpax_dirent,dirent); + needed += 0x7; + needed |= 0x7; + needed ^= 0x7; + + for (; dentbuf->next && (dentbuf->nfree < needed); ) + dentbuf = dentbuf->next; + + if (dentbuf->nfree < needed) + if (!(dentbuf = tpax_dirent_buf_next_alloc(dentbuf))) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + 0); + + *fkeep = true; + cdent = dentbuf->cdent; + + cdent->fdat = fdat; + cdent->depth = depth; + cdent->flags = flags; + cdent->nsize = needed; + cdent->parent = parent; + cdent->prefix = prefix; + + memset(&cdent->dirent,0,offsetof(struct dirent,d_name)); + + cdent->dirent.d_ino = dirent->d_ino; + cdent->dirent.d_type = dirent->d_type; + cdent->dirent.d_reclen = dirent->d_reclen; + + src = dirent->d_name; + dst = cdent->dirent.d_name; + + cap = dst - offsetof(struct dirent,d_name); + cap -= offsetof(struct tpax_dirent,dirent); + cap += needed; + + for (; *src; ) + *dst++ = *src++; + + for (; dstcdent = (struct tpax_dirent *)cap; + dentbuf->nfree -= needed; + + tpax_set_driver_dirmark(dctx,cdent); + + return 0; +} + +static int tpax_archive_enqueue_dir_entries( + const struct tpax_driver_ctx * dctx, + struct tpax_dirent * dent) +{ + int fd; + int fdat; + int depth; + bool fkeep; + long nbytes; + struct dirent * dirent; + struct dirent * dirents; + struct tpax_unit_ctx * uctx; + struct stat st; + uintptr_t addr; + + /* init */ + fdat = dent->fdat; + depth = dent->depth; + + /* uctx on the fly */ + if (tpax_lib_get_unit_ctx( + dctx,fdat, + dent->dirent.d_name, + &uctx) < 0) + return TPAX_NESTED_ERROR(dctx); + + /* verify that recursion item is still a directory */ + if (!S_ISDIR(uctx->st->st_mode)) + return tpax_archive_enqueue_ret( + TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR), + uctx); + + /* obtain buffer for file-system directory entries */ + dirents = tpax_get_driver_getdents_buffer(dctx); + dirent = dirents; + fkeep = false; + nbytes = 0; + depth++; + + /* open directory and obtain first directory entries */ + if ((fd = openat(fdat,dent->dirent.d_name,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); + + while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR))) + nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); + + if (nbytes < 0) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + /* iterate */ + for (; nbytes>0; ) { + if (!strcmp(dirent->d_name,".")) { + (void)0; + + } else if (!strcmp(dirent->d_name,"..")) { + (void)0; + + } else { + if (dirent->d_type == DT_UNKNOWN) { + if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW)) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + if (S_ISDIR(st.st_mode)) { + dirent->d_type = DT_DIR; + } + } + + if (tpax_archive_add_queue_item( + dctx,dirent, + dent,0,depth, + TPAX_ITEM_IMPLICIT, + fd,&fkeep) < 0) + return tpax_archive_enqueue_ret( + TPAX_NESTED_ERROR(dctx), + uctx); + } + + addr = (uintptr_t)dirent; + addr += dirent->d_reclen; + nbytes -= dirent->d_reclen; + dirent = (struct dirent *)addr; + + if (nbytes == 0) { + nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); + + while ((nbytes == -EINTR) || ((nbytes < 0) && (errno == EINTR))) + nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN); + + if (nbytes < 0) + tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + uctx); + + dirent = dirents; + } + } + + /* all done */ + return tpax_archive_enqueue_ret( + fkeep ? 0 : close(fd), + uctx); +} + +static const char * tpax_path_prefix_mark(const char * path) +{ + const char * src; + char * mark; + char pathbuf[PATH_MAX]; + + src = path; + mark = pathbuf; + + for (; *src; ) + *mark++ = *src++; + + for (--mark; (*mark == '/'); mark--) + (void)0; + + *++mark = '\0'; + + for (; (mark > pathbuf) && (mark[-1] != '/'); ) + mark--; + + return (mark <= pathbuf) ? 0 : &path[--mark-pathbuf]; +} + +static int tpax_dirent_init_from_uctx( + const struct stat * st, + const char * basename, + struct dirent * dirent) +{ + /* st_mode to d_type translation */ + if (S_ISREG(st->st_mode)) + dirent->d_type = DT_REG; + else if (S_ISLNK(st->st_mode)) + dirent->d_type = DT_LNK; + else if (S_ISDIR(st->st_mode)) + dirent->d_type = DT_DIR; + else if (S_ISCHR(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISBLK(st->st_mode)) + dirent->d_type = DT_CHR; + else if (S_ISFIFO(st->st_mode)) + dirent->d_type = DT_CHR; + else + return -1; + + /* d_off, d_ino */ + dirent->d_off = 0; + dirent->d_ino = st->st_ino; + + /* d_reclen */ + dirent->d_reclen = offsetof(struct dirent,d_name); + dirent->d_reclen += strlen(basename) + 1; + + dirent->d_reclen += 0x1; + dirent->d_reclen |= 0x1; + dirent->d_reclen ^= 0x1; + + /* d_name */ + strcpy(dirent->d_name,basename); + + return 0; +} + +int tpax_archive_enqueue( + const struct tpax_driver_ctx * dctx, + const struct tpax_unit_ctx * uctx) +{ + int fdat; + uintptr_t addr; + const char * name; + const char * mark; + const char * prefix; + bool fkeep; + struct tpax_dirent_buffer * dentbuf; + struct tpax_dirent * cdent; + struct tpax_dirent * cnext; + struct dirent * dirent; + char entbuf[PATH_MAX + sizeof(struct dirent)]; + + /* init */ + fdat = tpax_driver_fdcwd(dctx); + dirent = (struct dirent *)entbuf; + prefix = 0; + + /* split path to prefix + basename */ + if ((mark = tpax_path_prefix_mark(*uctx->path))) + if (!(prefix = tpax_add_prefix_item_from_path( + dctx,*uctx->path,mark))) + return tpax_archive_enqueue_ret( + TPAX_BUFFER_ERROR(dctx), + 0); + + name = mark ? ++mark : *uctx->path; + + if (prefix) + if ((fdat = openat(fdat,prefix,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0) + return tpax_archive_enqueue_ret( + TPAX_SYSTEM_ERROR(dctx), + 0); + + /* explicit item directory entry */ + if (tpax_dirent_init_from_uctx(uctx->st,name,dirent) < 0) + return tpax_archive_enqueue_ret( + TPAX_CUSTOM_ERROR( + dctx, + TPAX_ERR_FLOW_ERROR), + 0); + + /* add to queue */ + if (tpax_archive_add_queue_item( + dctx,dirent,0,prefix,0, + TPAX_ITEM_EXPLICIT, + fdat,&fkeep) < 0) + return tpax_archive_enqueue_ret( + TPAX_NESTED_ERROR(dctx), + 0); + + /* queue directory child items */ + dentbuf = tpax_get_driver_dirents(dctx); + cdent = tpax_get_driver_dirmark(dctx); + + for (; cdent; ) { + if (cdent->dirent.d_type == DT_DIR) + if (dctx->cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE) + if (tpax_archive_enqueue_dir_entries(dctx,cdent) < 0) + return TPAX_NESTED_ERROR(dctx); + + 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; + } + + return 0; +} -- cgit v1.2.3