diff options
Diffstat (limited to 'src/logic')
-rw-r--r-- | src/logic/tpax_archive_append.c | 709 | ||||
-rw-r--r-- | src/logic/tpax_archive_enqueue.c | 582 | ||||
-rw-r--r-- | src/logic/tpax_archive_reset.c | 59 | ||||
-rw-r--r-- | src/logic/tpax_archive_write.c | 368 | ||||
-rw-r--r-- | src/logic/tpax_file_create_memory_snapshot.c | 98 | ||||
-rw-r--r-- | src/logic/tpax_file_create_tmpfs_snapshot.c | 127 | ||||
-rw-r--r-- | src/logic/tpax_init_ustar_header.c | 218 | ||||
-rw-r--r-- | src/logic/tpax_queue_vector.c | 101 |
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; +} |