summaryrefslogtreecommitdiff
path: root/src/logic
diff options
context:
space:
mode:
Diffstat (limited to 'src/logic')
-rw-r--r--src/logic/tpax_archive_append.c709
-rw-r--r--src/logic/tpax_archive_enqueue.c582
-rw-r--r--src/logic/tpax_archive_reset.c59
-rw-r--r--src/logic/tpax_archive_write.c368
-rw-r--r--src/logic/tpax_file_create_memory_snapshot.c98
-rw-r--r--src/logic/tpax_file_create_tmpfs_snapshot.c127
-rw-r--r--src/logic/tpax_init_ustar_header.c218
-rw-r--r--src/logic/tpax_queue_vector.c101
8 files changed, 1110 insertions, 1152 deletions
diff --git a/src/logic/tpax_archive_append.c b/src/logic/tpax_archive_append.c
deleted file mode 100644
index 76ac436..0000000
--- a/src/logic/tpax_archive_append.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/**************************************************************/
-/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
-/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
-/**************************************************************/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include <tpax/tpax.h>
-#include <tpax/tpax_specs.h>
-#include "tpax_driver_impl.h"
-#include "tpax_getdents_impl.h"
-#include "tpax_tmpfile_impl.h"
-#include "tpax_errinfo_impl.h"
-
-#ifndef ssizeof
-#define ssizeof(x) (ssize_t)(sizeof(x))
-#endif
-
-static int tpax_archive_append_memory_data(
- int fdout,
- void * buf,
- ssize_t nbytes)
-{
- ssize_t ret;
- char * ch;
-
- for (ch=buf; nbytes; ch+=ret) {
- ret = write(fdout,ch,nbytes);
-
- while ((ret < 0) && (errno == EINTR))
- ret = write(fdout,ch,nbytes);
-
- if (ret < 0)
- return ret;
-
- nbytes -= ret;
- }
-
- return 0;
-}
-
-static int tpax_archive_append_pad(
- const struct tpax_driver_ctx * dctx,
- int fdout,
- const struct stat * st)
-{
- int ret;
- off_t cpos;
- ssize_t nbytes;
- char buf[512];
-
- nbytes = st->st_size;
- nbytes += 0x1ff;
- nbytes |= 0x1ff;
- nbytes ^= 0x1ff;
- nbytes -= st->st_size;
-
- memset(buf,0,nbytes);
-
- cpos = tpax_get_driver_cpos(dctx);
- cpos += st->st_size + nbytes;
-
- if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
- tpax_set_driver_cpos(dctx,cpos);
-
- return ret;
-}
-
-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_append_ret(
- int ret,
- struct tpax_unit_ctx * unit)
-{
- if (unit)
- tpax_free_unit_ctx(unit);
-
- return ret;
-}
-
-static int tpax_archive_append_one(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- const struct dirent * dent,
- int fdat,
- const char * prefix,
- const struct tpax_dirent * parent,
- const char * pdir)
-{
- struct tpax_unit_ctx * unit;
- struct tpax_ustar_header uhdr;
- off_t hpos;
- off_t dpos;
- int fdout;
- int fdtmp;
- ssize_t nread;
- ssize_t nbytes;
- void * buf;
- size_t buflen;
- size_t cmplen;
- void * membuf;
- size_t nlen;
- const char * path;
- const char * src;
- char * dst;
- char pbuf[1024];
-
- /* fake uctx for recursion items */
- unit = 0;
-
- if (dent && tpax_get_unit_ctx(
- dctx,fdat,dent->d_name,
- &unit) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- uctx = dent ? unit : uctx;
-
- /* prefixed path */
- if (!prefix && !parent && !pdir) {
- path = *uctx->path;
-
- } else {
- nlen = strlen(*uctx->path);
- nlen += prefix ? strlen(prefix) + 1 : 0;
- nlen += parent ? strlen(parent->dirent.d_name) + 1 : 0;
-
- if (nlen >= sizeof(pbuf))
- return TPAX_BUFFER_ERROR(dctx);
-
- dst = pbuf;
-
- if (prefix) {
- src = prefix;
-
- for (; *src; )
- *dst++ = *src++;
-
- if (dst[-1] != '/')
- *dst++ = '/';
- }
-
- if (parent) {
- src = parent->dirent.d_name;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- if (pdir) {
- src = pdir;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- src = *uctx->path;
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst = 0;
- path = pbuf;
- }
-
- /* record errors */
- tpax_driver_set_ectx(
- dctx,0,path);
-
- /* driver */
- fdout = tpax_driver_fdout(dctx);
-
- /* header and data offsets: todo pax and cpio */
- hpos = tpax_get_driver_cpos(dctx);
- dpos = hpos + sizeof(uhdr);
-
- /* header */
- if (tpax_init_ustar_header(
- dctx,path,uctx->st,
- *uctx->link,&uhdr) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- /* buffer */
- membuf = 0;
- fdtmp = -1;
-
- /* associated data? */
- if S_ISREG(uctx->st->st_mode) {
- buf = tpax_get_driver_anon_map_addr(
- dctx,&buflen);
-
- if (buflen >= (cmplen = uctx->st->st_size))
- membuf = buf;
-
- /* snapshot */
- if (membuf) {
- if (tpax_file_create_memory_snapshot(
- dctx,fdat,*uctx->path,
- uctx->st,membuf) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
- } else {
- if ((fdtmp = tpax_file_create_tmpfs_snapshot(
- dctx,fdat,*uctx->path,
- uctx->st)) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- if (lseek(fdtmp,0,SEEK_SET) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
- }
-
- /* append header */
- if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
- if (fdtmp >= 0)
- close(fdtmp);
-
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
-
- tpax_set_driver_cpos(dctx,dpos);
-
- /* all done? */
- if (!(S_ISREG(uctx->st->st_mode))) {
- tpax_archive_append_ret(0,unit);
- return 0;
- }
-
- /* append data from snapshot */
- if (fdtmp >= 0) {
- buf = tpax_get_driver_anon_map_addr(
- dctx,&buflen);
-
- for (nread=0; nread<uctx->st->st_size; ) {
- nbytes = read(fdtmp,buf,buflen);
-
- while ((nbytes < 0) && (errno == EINTR))
- nbytes = read(fdtmp,buf,buflen);
-
- if (nbytes < 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- } else if (nbytes == 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
- unit);
-
- } else {
- nread += nbytes;
- }
-
- if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
- close(fdtmp);
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
- }
-
- close(fdtmp);
- } else {
- if (tpax_archive_append_memory_data(
- fdout,membuf,
- uctx->st->st_size) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
- }
-
- return tpax_archive_append_ret(
- tpax_archive_append_pad(dctx,fdout,uctx->st),
- unit);
-}
-
-static int tpax_archive_append_dir(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- struct tpax_dirent * dent,
- int fdat,
- int depth,
- const char * prefix,
- const struct tpax_dirent * parent)
-{
- int fd;
- bool fkeep;
- long nbytes;
- size_t needed;
- struct dirent * dirent;
- struct dirent * dirents;
- struct tpax_dirent_buffer * dentbuf;
- struct tpax_dirent * cdent;
- struct tpax_unit_ctx * unit;
- struct stat st;
- uintptr_t addr;
- char * src;
- char * dst;
- char * cap;
-
- /* fake uctx for recursion items */
- unit = 0;
-
- if (dent && tpax_get_unit_ctx(
- dctx,dent->fdat,dent->dirent.d_name,
- &unit) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- uctx = dent ? unit : uctx;
-
- /* verify that recursion item is still a directory */
- if (unit && !S_ISDIR(unit->st->st_mode))
- return tpax_archive_append_ret(
- TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
- unit);
-
- /* append directory entry to archive */
- if (tpax_archive_append_one(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- prefix,0,0) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
-
- /* 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,*uctx->path,O_RDONLY|O_DIRECTORY|O_CLOEXEC,0)) < 0)
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- 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_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- /* 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_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- if (S_ISDIR(st.st_mode)) {
- dirent->d_type = DT_DIR;
- }
- }
-
- if (dirent->d_type == DT_DIR) {
- if (!(dentbuf = tpax_get_driver_dirents(dctx)))
- if (!(dentbuf = tpax_dirent_buf_first_alloc(dctx)))
- return tpax_archive_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- 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_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- fkeep = true;
- cdent = dentbuf->cdent;
-
- cdent->fdat = fd;
- cdent->depth = depth;
- cdent->nsize = needed;
- cdent->parent = parent;
-
- memset(&cdent->dirent,0,offsetof(struct dirent,d_name));
-
- 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 (; dst<cap; )
- *dst++ = 0;
-
- dentbuf->cdent = (struct tpax_dirent *)cap;
- dentbuf->nfree -= needed;
- } else {
- if (tpax_archive_append_one(
- dctx,0,dirent,fd,prefix,
- 0,*uctx->path) < 0)
- return tpax_archive_append_ret(
- TPAX_NESTED_ERROR(dctx),
- unit);
- }
- }
-
- 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_append_ret(
- TPAX_SYSTEM_ERROR(dctx),
- unit);
-
- dirent = dirents;
- }
- }
-
- /* all done */
- return tpax_archive_append_ret(
- fkeep ? 0 : close(fd),
- unit);
-}
-
-static int tpax_archive_append_impl(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- int depth,
- const char * prefix,
- struct tpax_dirent * parent)
-{
- if (S_ISDIR(uctx->st->st_mode))
- if (dctx->cctx->drvflags & TPAX_DRIVER_DIR_MEMBER_RECURSE)
- return tpax_archive_append_dir(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- depth,prefix,parent);
-
- return tpax_archive_append_one(
- dctx,uctx,0,
- tpax_driver_fdcwd(dctx),
- prefix,0,0);
-}
-
-int tpax_archive_append(
- const struct tpax_driver_ctx * dctx,
- const struct tpax_unit_ctx * uctx,
- const char * prefix)
-{
- struct tpax_dirent_buffer * dentbuf;
- struct tpax_dirent * cdent;
- struct tpax_dirent * cnext;
- const struct tpax_dirent * parent;
- uintptr_t addr;
- const char * rdir;
- const char * src;
- char * dst;
- char * cap;
- char * mark;
- int idx;
- const char * dirv[256];
- char pbuf[2048];
-
- /* normalized prefix */
- if (prefix && (prefix[0] == '.') && (!prefix[1]))
- prefix = 0;
-
- if (prefix && (prefix[0] == '.') && (prefix[1] == '/'))
- for (++prefix; *prefix=='/'; prefix++)
- (void)0;
-
- /* append explicit item */
- tpax_archive_append_impl(dctx,uctx,0,prefix,0);
-
- /* iterate through queued items */
- dentbuf = tpax_get_driver_dirents(dctx);
- cdent = dentbuf ? dentbuf->dbuf : 0;
-
- if (cdent) {
- dst = pbuf;
- cap = &pbuf[sizeof(pbuf)];
-
- if (prefix && prefix[0]) {
- for (; *src && dst<cap; )
- *dst++ = *src++;
-
- if (dst == cap)
- return TPAX_BUFFER_ERROR(dctx);
-
- if (dst[-1] != '/')
- *dst++ = '/';
- }
-
- src = *uctx->path;
-
- for (; *src && dst<cap; )
- *dst++ = *src++;
-
- if (dst == cap)
- return TPAX_BUFFER_ERROR(dctx);
-
- if (dst[-1] != '/')
- *dst++ = '/';
-
- *dst = 0;
- mark = dst;
- rdir = pbuf;
-
- (void)mark;
- }
-
- for (; cdent; ) {
- switch (cdent->dirent.d_type) {
- case DT_DIR:
- if (tpax_archive_append_dir(
- dctx,0,cdent,cdent->fdat,
- cdent->depth,rdir,cdent) < 0)
- return TPAX_NESTED_ERROR(dctx);
-
- break;
-
- default:
- if (tpax_archive_append_one(
- dctx,0,&cdent->dirent,
- cdent->fdat,rdir,cdent,0) < 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;
- }
-
- if (cnext && (cnext->parent != cdent->parent)) {
- if (cnext->depth > 256)
- return TPAX_BUFFER_ERROR(dctx);
-
- for (parent=cnext->parent; parent; parent=parent->parent)
- dirv[parent->depth - 1] = parent->dirent.d_name;
-
- for (idx=0,dst=mark; idx<cnext->parent->depth; idx++) {
- src = dirv[idx];
-
- for (; *src; )
- *dst++ = *src++;
-
- *dst++ = '/';
- }
-
- *--dst = 0;
- }
-
- cdent = cnext;
- }
-
- return 0;
-}
-
-int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
-{
- int fdout;
- off_t cpos;
- ssize_t nbytes;
- ssize_t nwritten;
- ssize_t blksize;
- char buf[512];
-
- blksize = tpax_get_archive_block_size(dctx);
- cpos = tpax_get_driver_cpos(dctx);
-
- if (cpos % 512)
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
-
- fdout = tpax_driver_fdout(dctx);
- memset(buf,0,sizeof(buf));
-
- switch (cpos % blksize) {
- case 0:
- nbytes = cpos + blksize;
- break;
-
- default:
- nbytes = cpos / blksize;
- nbytes *= blksize;
- nbytes += blksize;
-
- if (nbytes-cpos == 512)
- nbytes += blksize;
- }
-
- for (nwritten=cpos; nwritten<nbytes; nwritten+=512) {
- if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
- return TPAX_SYSTEM_ERROR(dctx);
-
- tpax_set_driver_cpos(dctx,nwritten);
- }
-
- return 0;
-}
diff --git a/src/logic/tpax_archive_enqueue.c b/src/logic/tpax_archive_enqueue.c
new file mode 100644
index 0000000..8685cad
--- /dev/null
+++ b/src/logic/tpax_archive_enqueue.c
@@ -0,0 +1,582 @@
+/**************************************************************/
+/* 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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <tpax/tpax.h>
+#include <tpax/tpax_specs.h>
+#include "tpax_driver_impl.h"
+#include "tpax_getdents_impl.h"
+#include "tpax_readlink_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 int tpax_dirent_init_from_stat(
+ 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;
+}
+
+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,
+ dev_t stdev,
+ 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->stdev = stdev;
+ 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 (; dst<cap; )
+ *dst++ = 0;
+
+ dentbuf->cdent = (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 fdlnk;
+ int depth;
+ bool fkeep;
+ bool flinks;
+ bool fstdev;
+ long nbytes;
+ struct dirent * lnkent;
+ struct dirent * dirent;
+ struct dirent * dirents;
+ struct tpax_dirent * cdent;
+ struct tpax_unit_ctx * uctx;
+ struct stat st;
+ struct stat lnkst;
+ uintptr_t addr;
+ char lnktgt[PATH_MAX];
+ char lnkbuf[PATH_MAX + sizeof(struct dirent)];
+
+ /* 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);
+
+ /* ensure physical device identity as needed */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_STRICT_DEVICE_ID)
+ if (dent->parent && (uctx->st->st_dev != dent->parent->stdev))
+ return 0;
+
+ /* 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);
+
+ lnkent = (struct dirent *)lnkbuf;
+ flinks = (dctx->cctx->drvflags & TPAX_DRIVER_PAX_SYMLINK_ITEMS);
+ fstdev = (dctx->cctx->drvflags & TPAX_DRIVER_STRICT_DEVICE_ID);
+ nbytes = tpax_getdents(fd,dirents,TPAX_DIRENT_BUFLEN);
+
+ /* debugging, struct stat initialization when fstdev is false */
+ memset(&st,0,sizeof(st));
+
+ 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; ) {
+ 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;
+ }
+ } else if (fstdev) {
+ if (fstatat(fd,dirent->d_name,&st,AT_SYMLINK_NOFOLLOW))
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ uctx);
+ }
+
+ if (tpax_archive_add_queue_item(
+ dctx,dirent,dent,0,
+ st.st_dev,depth,
+ TPAX_ITEM_IMPLICIT,
+ fd,&fkeep) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_NESTED_ERROR(dctx),
+ uctx);
+
+ /* follow encountered symlink arguments as needed */
+ fdlnk = (flinks && (dirent->d_type == DT_LNK))
+ ? openat(fd,dirent->d_name,O_RDONLY|O_CLOEXEC)
+ : (-1);
+
+ if (fdlnk >= 0) {
+ if (fstat(fdlnk,&lnkst) <0) {
+ close(fdlnk);
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ uctx);
+ }
+
+ if (tpax_readlinkat(fd,dirent->d_name,lnktgt,PATH_MAX) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ uctx);
+
+ close(fdlnk);
+
+ if (tpax_dirent_init_from_stat(&lnkst,lnktgt,lnkent) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ 0);
+
+ cdent = tpax_get_driver_dirmark(dctx);
+ cdent->flags |= TPAX_ITEM_NAMEREF;
+
+ if (tpax_archive_add_queue_item(
+ dctx,lnkent,cdent,0,
+ lnkst.st_dev,depth+1,
+ TPAX_ITEM_IMPLICIT|TPAX_ITEM_SYMLINK,
+ fd,&fkeep) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_NESTED_ERROR(dctx),
+ 0);
+ }
+ }
+
+ 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];
+}
+
+int tpax_archive_enqueue(
+ const struct tpax_driver_ctx * dctx,
+ const struct tpax_unit_ctx * uctx)
+{
+ int fdat;
+ int fdlnk;
+ 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;
+ struct dirent * lnkent;
+ struct stat lnkst;
+ char entbuf[PATH_MAX + sizeof(struct dirent)];
+ char lnkbuf[PATH_MAX + sizeof(struct dirent)];
+
+ /* init */
+ fdat = tpax_driver_fdcwd(dctx);
+ dirent = (struct dirent *)entbuf;
+ lnkent = (struct dirent *)lnkbuf;
+ 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_stat(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,
+ uctx->st->st_dev,0,
+ TPAX_ITEM_EXPLICIT,
+ fdat,&fkeep) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_NESTED_ERROR(dctx),
+ 0);
+
+ /* follow command-line symlink arguments as needed */
+ fdlnk = (uctx->link[0] && (dctx->cctx->drvflags & TPAX_DRIVER_PAX_SYMLINK_ARGS))
+ ? openat(fdat,uctx->link[0],O_RDONLY|O_CLOEXEC) : (-1);
+
+ if (fdlnk >= 0) {
+ if (fstat(fdlnk,&lnkst) <0) {
+ close(fdlnk);
+ return tpax_archive_enqueue_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ 0);
+ }
+
+ close(fdlnk);
+
+ if (tpax_dirent_init_from_stat(&lnkst,uctx->link[0],lnkent) < 0)
+ return tpax_archive_enqueue_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ 0);
+
+ cdent = tpax_get_driver_dirmark(dctx);
+ cdent->flags |= TPAX_ITEM_NAMEREF;
+
+ if (tpax_archive_add_queue_item(
+ dctx,lnkent,cdent,0,
+ lnkst.st_dev,1,
+ TPAX_ITEM_EXPLICIT|TPAX_ITEM_SYMLINK,
+ 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;
+}
diff --git a/src/logic/tpax_archive_reset.c b/src/logic/tpax_archive_reset.c
new file mode 100644
index 0000000..52a9008
--- /dev/null
+++ b/src/logic/tpax_archive_reset.c
@@ -0,0 +1,59 @@
+/**************************************************************/
+/* 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 <sys/mman.h>
+
+#include <tpax/tpax.h>
+#include "tpax_driver_impl.h"
+
+/**********************************************/
+/* release and reset the following objects: */
+/* */
+/* - item queue */
+/* - queue vector */
+/* - cached prefixes */
+/* - prefix vector */
+/* */
+/**********************************************/
+
+int tpax_archive_reset(const struct tpax_driver_ctx * dctx)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ void * next;
+ size_t size;
+ char ** ppref;
+
+ ictx = tpax_get_driver_ictx(dctx);
+
+ for (; ictx->dirents; ) {
+ next = ictx->dirents->next;
+ size = ictx->dirents->size;
+
+ munmap(ictx->dirents,size);
+ ictx->dirents = (struct tpax_dirent_buffer *)next;
+ }
+
+ for (ppref=ictx->prefixv; *ppref; ppref++)
+ free(*ppref);
+
+ for (ppref=ictx->prefptr; ppref<ictx->prefcap; ppref++)
+ *ppref = 0;
+
+ if (ictx->prefixv != ictx->prefptr)
+ free(ictx->prefixv);
+
+ if (ictx->direntv)
+ free(ictx->direntv);
+
+ ictx->nqueued = 0;
+ ictx->dirents = 0;
+ ictx->direntv = 0;
+ ictx->prefixv = ictx->prefptr;
+
+ return 0;
+}
diff --git a/src/logic/tpax_archive_write.c b/src/logic/tpax_archive_write.c
new file mode 100644
index 0000000..7ce6cca
--- /dev/null
+++ b/src/logic/tpax_archive_write.c
@@ -0,0 +1,368 @@
+/**************************************************************/
+/* tpax: a topological pax implementation */
+/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
+/**************************************************************/
+
+#include <tpax/tpax.h>
+#include "tpax_driver_impl.h"
+#include "tpax_errinfo_impl.h"
+#include "tpax_visibility_impl.h"
+
+#ifndef ssizeof
+#define ssizeof(x) (ssize_t)(sizeof(x))
+#endif
+
+static int tpax_archive_append_memory_data(
+ int fdout,
+ void * buf,
+ ssize_t nbytes)
+{
+ ssize_t ret;
+ char * ch;
+
+ for (ch=buf; nbytes; ch+=ret) {
+ ret = write(fdout,ch,nbytes);
+
+ while ((ret < 0) && (errno == EINTR))
+ ret = write(fdout,ch,nbytes);
+
+ if (ret < 0)
+ return ret;
+
+ nbytes -= ret;
+ }
+
+ return 0;
+}
+
+static int tpax_archive_append_pad(
+ const struct tpax_driver_ctx * dctx,
+ int fdout,
+ const struct stat * st)
+{
+ int ret;
+ off_t cpos;
+ ssize_t nbytes;
+ char buf[512];
+
+ nbytes = st->st_size;
+ nbytes += 0x1ff;
+ nbytes |= 0x1ff;
+ nbytes ^= 0x1ff;
+ nbytes -= st->st_size;
+
+ memset(buf,0,nbytes);
+
+ cpos = tpax_get_driver_cpos(dctx);
+ cpos += st->st_size + nbytes;
+
+ if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
+ tpax_set_driver_cpos(dctx,cpos);
+
+ return ret;
+}
+
+static int tpax_archive_write_ret(
+ int ret,
+ const struct tpax_driver_ctx * dctx,
+ struct tpax_unit_ctx * uctx)
+{
+ if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
+ tpax_dprintf(tpax_driver_fderr(dctx),"\n",0);
+
+ tpax_lib_free_unit_ctx(uctx);
+ return ret;
+}
+
+static int tpax_archive_write_impl(
+ const struct tpax_driver_ctx * dctx,
+ const struct tpax_dirent * cdent,
+ int fdcwd,
+ int fdout)
+{
+ struct tpax_unit_ctx * uctx;
+ struct tpax_ustar_header uhdr;
+ const struct stat * st;
+ struct stat stbuf;
+ const char * path;
+ const char * slnk;
+ const char * mlnk;
+ off_t hpos;
+ off_t dpos;
+ int fdtmp;
+ ssize_t nread;
+ ssize_t nbytes;
+ void * buf;
+ size_t buflen;
+ size_t cmplen;
+ void * membuf;
+ char * ch;
+ char pathbuf[PATH_MAX];
+
+ /* followed symlink? */
+ if (cdent->flags & TPAX_ITEM_NAMEREF)
+ return 0;
+
+ /* full path */
+ if (!(path = tpax_queue_item_full_path(dctx,cdent)))
+ return TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR);
+
+ /* verbose mode */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
+ tpax_dprintf(tpax_driver_fderr(dctx),"%s",path);
+
+ /* uctx */
+ if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
+ tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,0);
+
+ st = uctx->st;
+ slnk = uctx->link[0];
+ mlnk = 0;
+
+ if (cdent->flags & TPAX_ITEM_SYMLINK) {
+ st = &stbuf;
+ mlnk = slnk;
+ slnk = 0;
+ ch = 0;
+
+ if (mlnk[0] != '/') {
+ if (strlen(path) >= PATH_MAX)
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ strcpy(pathbuf,path);
+
+ ch = strrchr(pathbuf,'/');
+ }
+
+ if (ch && (++ch - pathbuf >= PATH_MAX))
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(
+ dctx,
+ TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ if (ch) {
+ strcpy(ch,mlnk);
+ mlnk = pathbuf;
+ }
+
+ if (fstatat(fdcwd,mlnk,&stbuf,0) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ /* record errors */
+ tpax_driver_set_ectx(
+ dctx,0,path);
+
+ /* header and data offsets: todo pax and cpio */
+ hpos = tpax_get_driver_cpos(dctx);
+ dpos = hpos + sizeof(uhdr);
+
+ /* header */
+ if (tpax_meta_init_ustar_header(
+ dctx,path,st,
+ slnk,&uhdr) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ /* buffer */
+ membuf = 0;
+ fdtmp = -1;
+
+ /* associated data? */
+ if S_ISREG(st->st_mode) {
+ buf = tpax_get_driver_anon_map_addr(
+ dctx,&buflen);
+
+ if (buflen >= (cmplen = st->st_size))
+ membuf = buf;
+
+ /* snapshot */
+ if (membuf) {
+ if (tpax_io_create_memory_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st,membuf) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+ } else {
+ if ((fdtmp = tpax_io_create_tmpfs_snapshot(
+ dctx,fdcwd,
+ mlnk ? mlnk : path,
+ st)) < 0)
+ return tpax_archive_write_ret(
+ TPAX_NESTED_ERROR(dctx),
+ dctx,uctx);
+
+ if (lseek(fdtmp,0,SEEK_SET) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+ }
+
+ /* append header */
+ if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
+ if (fdtmp >= 0)
+ close(fdtmp);
+
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ tpax_set_driver_cpos(dctx,dpos);
+
+ /* all done? */
+ if (!(S_ISREG(st->st_mode)))
+ return tpax_archive_write_ret(
+ 0,dctx,uctx);
+
+ /* append data from snapshot */
+ if (fdtmp >= 0) {
+ buf = tpax_get_driver_anon_map_addr(
+ dctx,&buflen);
+
+ for (nread=0; nread<st->st_size; ) {
+ nbytes = read(fdtmp,buf,buflen);
+
+ while ((nbytes < 0) && (errno == EINTR))
+ nbytes = read(fdtmp,buf,buflen);
+
+ if (nbytes < 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+
+ } else if (nbytes == 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
+ dctx,uctx);
+
+ } else {
+ nread += nbytes;
+ }
+
+ if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
+ close(fdtmp);
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+ }
+
+ close(fdtmp);
+ } else {
+ if (tpax_archive_append_memory_data(
+ fdout,membuf,
+ st->st_size) < 0)
+ return tpax_archive_write_ret(
+ TPAX_SYSTEM_ERROR(dctx),
+ dctx,uctx);
+ }
+
+ return tpax_archive_write_ret(
+ tpax_archive_append_pad(dctx,fdout,st),
+ dctx,uctx);
+}
+
+int tpax_archive_write(const struct tpax_driver_ctx * dctx)
+{
+ struct tpax_driver_ctx_impl * ictx;
+ struct tpax_dirent ** direntv;
+ int fdcwd;
+ int fdout;
+
+ /* driver */
+ ictx = tpax_get_driver_ictx(dctx);
+ fdcwd = tpax_driver_fdcwd(dctx);
+ fdout = tpax_driver_fdout(dctx);
+
+ /* quote to archive */
+ if (tpax_update_queue_vector(dctx) < 0)
+ return TPAX_NESTED_ERROR(dctx);
+
+ for (direntv=ictx->direntv; *direntv; direntv++)
+ if (tpax_archive_write_impl(dctx,*direntv,fdcwd,fdout) < 0)
+ return TPAX_NESTED_ERROR(dctx);
+
+ return 0;
+}
+
+static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx)
+{
+ (void)dctx;
+ return -1;
+}
+
+int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
+{
+ int fdout;
+ off_t cpos;
+ ssize_t nbytes;
+ ssize_t nwritten;
+ ssize_t blksize;
+ char buf[1024];
+
+ /* archive block size, current position */
+ blksize = dctx->cctx->blksize;
+ cpos = tpax_get_driver_cpos(dctx);
+
+ /* internal error? */
+ if (cpos % 512)
+ return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
+
+ /* cpio trailer? */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
+ return tpax_archive_seal_cpio(dctx);
+
+ /* ustar, pax */
+ fdout = tpax_driver_fdout(dctx);
+ memset(buf,0,sizeof(buf));
+
+ if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ cpos += 2*512;
+
+ /* pax? */
+ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* already at block boundary? */
+ if ((cpos % blksize) == 0) {
+ tpax_set_driver_cpos(dctx,cpos);
+ return 0;
+ }
+
+ /* zero-fill the remainder of the block */
+ nbytes = cpos / blksize;
+ nbytes *= blksize;
+ nbytes += blksize;
+
+ for (nwritten=cpos; nwritten<nbytes; nwritten+=512)
+ if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ /* all done */
+ tpax_set_driver_cpos(dctx,nwritten);
+
+ return 0;
+}
diff --git a/src/logic/tpax_file_create_memory_snapshot.c b/src/logic/tpax_file_create_memory_snapshot.c
deleted file mode 100644
index 7c1f90e..0000000
--- a/src/logic/tpax_file_create_memory_snapshot.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/**************************************************************/
-/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
-/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
-/**************************************************************/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/stat.h>
-
-#include <tpax/tpax.h>
-#include <tpax/tpax_specs.h>
-#include "tpax_driver_impl.h"
-#include "tpax_errinfo_impl.h"
-
-#ifndef ssizeof
-#define ssizeof(x) (ssize_t)(sizeof(x))
-#endif
-
-int tpax_file_create_memory_snapshot(
- const struct tpax_driver_ctx * dctx,
- int fdat,
- const char * path,
- const struct stat * srcst,
- void * addr)
-{
- int fd;
- char * ch;
- char * cap;
- ssize_t nread;
- struct stat dstst;
-
- /* record errors */
- tpax_driver_set_ectx(
- dctx,0,path);
-
- /* memory snapshot internal limit */
- if (srcst->st_size >= 0x80000000)
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_REGION_SIZE);
-
- /* open */
- if ((fd = openat(fdat,path,O_CLOEXEC|O_NOCTTY|O_NOFOLLOW,0)) < 0)
- return TPAX_SYSTEM_ERROR(dctx);
-
- /* stat compare */
- if ((fstat(fd,&dstst)) < 0) {
- close(fd);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (tpax_stat_compare(srcst,&dstst)) {
- close(fd);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
- }
-
- /* read loop */
- ch = addr;
- cap = &ch[srcst->st_size];
-
- while (ch < cap) {
- nread = read(fd,ch,cap-ch);
-
- while ((nread < 0) && (errno == EINTR))
- nread = read(fd,ch,cap-ch);
-
- if (nread < 0) {
- close(fd);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (nread == 0) {
- close(fd);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
-
- } else {
- ch += nread;
- }
- }
-
- /* stat compare */
- if ((fstat(fd,&dstst)) < 0) {
- close(fd);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (tpax_stat_compare(srcst,&dstst)) {
- close(fd);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
- }
-
- /* yay */
- close(fd);
-
- return 0;
-}
diff --git a/src/logic/tpax_file_create_tmpfs_snapshot.c b/src/logic/tpax_file_create_tmpfs_snapshot.c
deleted file mode 100644
index 5620238..0000000
--- a/src/logic/tpax_file_create_tmpfs_snapshot.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/**************************************************************/
-/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
-/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
-/**************************************************************/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/stat.h>
-
-#include <tpax/tpax.h>
-#include <tpax/tpax_specs.h>
-#include "tpax_driver_impl.h"
-#include "tpax_tmpfile_impl.h"
-#include "tpax_errinfo_impl.h"
-
-#ifndef ssizeof
-#define ssizeof(x) (ssize_t)(sizeof(x))
-#endif
-
-int tpax_file_create_tmpfs_snapshot(
- const struct tpax_driver_ctx * dctx,
- int fdat,
- const char * path,
- const struct stat * srcst)
-{
- int fdsrc;
- int fdtmp;
- char * ch;
- ssize_t nread;
- ssize_t nbytes;
- ssize_t ret;
- void * buf;
- size_t buflen;
- struct stat dstst;
-
- /* record errors */
- tpax_driver_set_ectx(
- dctx,0,path);
-
- /* tmpfile */
- if ((fdtmp = tpax_tmpfile()) < 0)
- return TPAX_SYSTEM_ERROR(dctx);
-
- /* buffer */
- buf = tpax_get_driver_anon_map_addr(
- dctx,&buflen);
-
- /* open */
- if ((fdsrc = openat(fdat,path,O_CLOEXEC|O_NOCTTY|O_NOFOLLOW,0)) < 0) {
- close(fdtmp);
- return TPAX_SYSTEM_ERROR(dctx);
- }
-
- /* stat compare */
- if ((fstat(fdsrc,&dstst)) < 0) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (tpax_stat_compare(srcst,&dstst)) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
- }
-
- /* read/write loop */
- for (nread=0; nread<srcst->st_size; ) {
- nbytes = read(fdsrc,buf,buflen);
-
- while ((nbytes < 0) && (errno == EINTR))
- nbytes = read(fdsrc,buf,buflen);
-
- if (nbytes < 0) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (nbytes == 0) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
-
- } else {
- nread += nbytes;
- }
-
- for (ch=buf; nbytes; ch+=ret) {
- ret = write(fdtmp,ch,nbytes);
-
- while ((ret < 0) && (errno == EINTR))
- ret = write(fdtmp,ch,nbytes);
-
- if (ret < 0) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else {
- nbytes -= ret;
- }
- }
- }
-
- /* stat compare */
- if ((fstat(fdsrc,&dstst)) < 0) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_SYSTEM_ERROR(dctx);
-
- } else if (tpax_stat_compare(srcst,&dstst)) {
- close(fdsrc);
- close(fdtmp);
- return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FILE_CHANGED);
- }
-
- /* yay */
- close(fdsrc);
-
- return fdtmp;
-}
diff --git a/src/logic/tpax_init_ustar_header.c b/src/logic/tpax_init_ustar_header.c
deleted file mode 100644
index fb83ba4..0000000
--- a/src/logic/tpax_init_ustar_header.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/**************************************************************/
-/* tpax: a topological pax implementation */
-/* Copyright (C) 2020--2021 SysDeer Technologies, LLC */
-/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
-/**************************************************************/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/stat.h>
-
-#include <tpax/tpax.h>
-#include <tpax/tpax_specs.h>
-#include "tpax_driver_impl.h"
-
-#ifndef ssizeof
-#define ssizeof(x) (ssize_t)(sizeof(x))
-#endif
-
-static void tpax_octal_write(char * ch, ssize_t len, uint64_t val)
-{
- for (--len,--len; len>=0 ; len--) {
- ch[len] = val % 8 + '0';
- val /= 8;
- }
-}
-
-int tpax_init_ustar_header(
- const struct tpax_driver_ctx * dctx,
- const char * path,
- const struct stat * st,
- const char * linkname,
- struct tpax_ustar_header * uhdr)
-{
- size_t len;
- const char * cap;
- const char * mark;
- unsigned char * uch;
- unsigned char * uchcap;
- size_t lnklen;
- size_t stsize;
- int64_t stmtim;
- uint32_t chksum;
- char typeflag;
- struct group grp;
- struct group * grpres;
- struct passwd pwd;
- struct passwd * pwdres;
- char pwdbuf[2048];
-
- /* size & mtime validation */
- stsize = S_ISREG(st->st_mode) ? st->st_size : 0;
- stmtim = st->st_mtim.tv_sec;
-
- if (stsize > 077777777777)
- return -1;
-
- if ((stmtim < 0) || (stmtim > 077777777777))
- return -1;
-
- /* linkname validation */
- if (S_ISLNK(st->st_mode) && !linkname)
- return -1;
-
- lnklen = S_ISLNK(st->st_mode)
- ? strnlen(linkname,sizeof(uhdr->u_linkname) + 1)
- : 0;
-
- if (lnklen > sizeof(uhdr->u_linkname))
- return -1;
-
- /* typeflag validation */
- if (S_ISREG(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_REGFILE;
- else if (S_ISLNK(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_SYMLINK;
- else if (S_ISDIR(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_DIRFILE;
- else if (S_ISCHR(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_CHARDEV;
- else if (S_ISBLK(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_BLKDEV;
- else if (S_ISFIFO(st->st_mode))
- typeflag = TPAX_USTAR_TYPEFLAG_FIFODEV;
- else
- return -1;
-
- /* cap (without the trailing slash) */
- mark = &path[strlen(path)];
- cap = mark;
-
- for (--cap; (*cap == '/') && (cap > path); cap--)
- (void)0;
-
- cap = ((cap == path) && (path[0] == '/'))
- ? mark : &cap[1];
-
- /* path solely consists of slash symbols? */
- if ((cap == mark) && (path[0] =='/')) {
- if ((cap - path) > (ssizeof(uhdr->u_prefix) + 1 + ssizeof(uhdr->u_name)))
- return -1;
-
- else if ((cap - path) <= ssizeof(uhdr->u_name))
- mark = 0;
-
- else
- mark -= sizeof(uhdr->u_name);
-
- /* path entirely fits in u_name? */
- } else if ((cap - path) <= ssizeof(uhdr->u_name)) {
- mark = 0;
-
- /* split between u_prefix and u_name as needed */
- } else {
- mark = cap;
-
- do {
- for (--mark; (mark > path) && (*mark != '/'); mark--)
- (void)0;
- } while ((mark - path) > ssizeof(uhdr->u_prefix));
- }
-
- /* one shot */
- memset(uhdr,0,sizeof(*uhdr));
-
- /* u_name, u_prefix */
- if (mark) {
- memcpy(uhdr->u_prefix,path,mark-path);
- memcpy(uhdr->u_name,&mark[1],cap - mark);
- } else {
- memcpy(uhdr->u_name,path,cap - path);
- }
-
- /* u_mode */
- tpax_octal_write(uhdr->u_mode,ssizeof(uhdr->u_mode),st->st_mode & TPAX_USTAR_MODE_MASK);
-
- /* u_uid, u_gid, u_uname, u_gname */
- if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) {
- tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0);
- tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0);
- } else {
- if ((uint64_t)st->st_uid <= 07777777)
- tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),st->st_uid);
- else
- tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0);
-
- if ((uint64_t)st->st_gid <= 07777777)
- tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),st->st_gid);
- else
- tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0);
-
- pwdres = 0;
- grpres = 0;
-
- getpwuid_r(
- st->st_uid,&pwd,
- pwdbuf,sizeof(pwdbuf),
- &pwdres);
-
- if (pwdres && pwd.pw_name)
- if ((len = strlen(pwd.pw_name)) < sizeof(uhdr->u_uname))
- memcpy(uhdr->u_uname,pwd.pw_name,len);
-
- getgrgid_r(
- st->st_gid,&grp,
- pwdbuf,sizeof(pwdbuf),
- &grpres);
-
- if (grpres && grp.gr_name)
- if ((len = strlen(grp.gr_name)) < sizeof(uhdr->u_gname))
- memcpy(uhdr->u_gname,grp.gr_name,len);
- }
-
- /* u_size, u_mtime */
- tpax_octal_write(uhdr->u_size,ssizeof(uhdr->u_size),stsize);
- tpax_octal_write(uhdr->u_mtime,ssizeof(uhdr->u_mtime),stmtim);
-
- /* u_typeflag */
- uhdr->u_typeflag[0] = typeflag;
-
- /* u_linkname */
- if (lnklen)
- memcpy(uhdr->u_linkname,linkname,lnklen);
-
- /* u_magic */
- uhdr->u_magic[0] = 'u';
- uhdr->u_magic[1] = 's';
- uhdr->u_magic[2] = 't';
- uhdr->u_magic[3] = 'a';
- uhdr->u_magic[4] = 'r';
-
- /* u_version */
- uhdr->u_version[0] = '0';
- uhdr->u_version[1] = '0';
-
- /* u_devmajor, u_devminor */
- tpax_octal_write(uhdr->u_devmajor,ssizeof(uhdr->u_devmajor),0);
- tpax_octal_write(uhdr->u_devminor,ssizeof(uhdr->u_devminor),0);
-
- /* u_chksum */
- uch = (unsigned char *)uhdr->u_name;
- uchcap = (unsigned char *)uhdr->u_pad;
-
- for (chksum=0; uch<uchcap; uch++)
- chksum += *uch;
-
- chksum += sizeof(uhdr->u_chksum) * ' ';
-
- tpax_octal_write(uhdr->u_chksum,ssizeof(uhdr->u_chksum),chksum);
-
- /* all done; caller may now change REGFILE to HARDLINK */
- return 0;
-}
diff --git a/src/logic/tpax_queue_vector.c b/src/logic/tpax_queue_vector.c
new file mode 100644
index 0000000..cae4da0
--- /dev/null
+++ b/src/logic/tpax_queue_vector.c
@@ -0,0 +1,101 @@
+/**************************************************************/
+/* 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;
+}
+
+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);
+
+ arrsize = ictx->nqueued + 1;
+
+ if (!(ictx->direntv = calloc(arrsize,sizeof(struct tpax_dirent *))))
+ return TPAX_SYSTEM_ERROR(dctx);
+
+ if (ictx->nqueued == 0)
+ return 0;
+
+ /* queue vector */
+ dentbuf = tpax_get_driver_dirents(dctx);
+ cdent = dentbuf->dbuf;
+
+ 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;
+ }
+
+ return 0;
+}