|
|
fb29e1 |
/**************************************************************/
|
|
|
fb29e1 |
/* tpax: a topological pax implementation */
|
|
|
fb29e1 |
/* Copyright (C) 2020--2024 SysDeer Technologies, LLC */
|
|
|
fb29e1 |
/* Released under GPLv2 and GPLv3; see COPYING.TPAX. */
|
|
|
fb29e1 |
/**************************************************************/
|
|
|
fb29e1 |
|
|
|
fb29e1 |
#include <tpax/tpax.h>
|
|
|
fb29e1 |
#include "tpax_driver_impl.h"
|
|
|
fb29e1 |
#include "tpax_errinfo_impl.h"
|
|
|
fb29e1 |
#include "tpax_visibility_impl.h"
|
|
|
fb29e1 |
|
|
|
5ce6ff |
#ifndef ssizeof
|
|
|
5ce6ff |
#define ssizeof(x) (ssize_t)(sizeof(x))
|
|
|
5ce6ff |
#endif
|
|
|
5ce6ff |
|
|
|
fb29e1 |
static int tpax_archive_append_memory_data(
|
|
|
fb29e1 |
int fdout,
|
|
|
fb29e1 |
void * buf,
|
|
|
fb29e1 |
ssize_t nbytes)
|
|
|
fb29e1 |
{
|
|
|
fb29e1 |
ssize_t ret;
|
|
|
fb29e1 |
char * ch;
|
|
|
fb29e1 |
|
|
|
fb29e1 |
for (ch=buf; nbytes; ch+=ret) {
|
|
|
fb29e1 |
ret = write(fdout,ch,nbytes);
|
|
|
fb29e1 |
|
|
|
fb29e1 |
while ((ret < 0) && (errno == EINTR))
|
|
|
fb29e1 |
ret = write(fdout,ch,nbytes);
|
|
|
fb29e1 |
|
|
|
fb29e1 |
if (ret < 0)
|
|
|
fb29e1 |
return ret;
|
|
|
fb29e1 |
|
|
|
fb29e1 |
nbytes -= ret;
|
|
|
fb29e1 |
}
|
|
|
fb29e1 |
|
|
|
fb29e1 |
return 0;
|
|
|
fb29e1 |
}
|
|
|
fb29e1 |
|
|
|
fb29e1 |
static int tpax_archive_append_pad(
|
|
|
fb29e1 |
const struct tpax_driver_ctx * dctx,
|
|
|
fb29e1 |
int fdout,
|
|
|
fb29e1 |
const struct stat * st)
|
|
|
fb29e1 |
{
|
|
|
fb29e1 |
int ret;
|
|
|
fb29e1 |
off_t cpos;
|
|
|
fb29e1 |
ssize_t nbytes;
|
|
|
fb29e1 |
char buf[512];
|
|
|
fb29e1 |
|
|
|
fb29e1 |
nbytes = st->st_size;
|
|
|
fb29e1 |
nbytes += 0x1ff;
|
|
|
fb29e1 |
nbytes |= 0x1ff;
|
|
|
fb29e1 |
nbytes ^= 0x1ff;
|
|
|
fb29e1 |
nbytes -= st->st_size;
|
|
|
fb29e1 |
|
|
|
fb29e1 |
memset(buf,0,nbytes);
|
|
|
fb29e1 |
|
|
|
fb29e1 |
cpos = tpax_get_driver_cpos(dctx);
|
|
|
fb29e1 |
cpos += st->st_size + nbytes;
|
|
|
fb29e1 |
|
|
|
fb29e1 |
if (!(ret = tpax_archive_append_memory_data(fdout,buf,nbytes)))
|
|
|
fb29e1 |
tpax_set_driver_cpos(dctx,cpos);
|
|
|
fb29e1 |
|
|
|
fb29e1 |
return ret;
|
|
|
fb29e1 |
}
|
|
|
fb29e1 |
|
|
|
5ce6ff |
static int tpax_archive_write_ret(
|
|
|
e99de3 |
int ret,
|
|
|
e99de3 |
const struct tpax_driver_ctx * dctx,
|
|
|
e99de3 |
struct tpax_unit_ctx * uctx)
|
|
|
5ce6ff |
{
|
|
|
e99de3 |
if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
|
|
|
e99de3 |
tpax_dprintf(tpax_driver_fderr(dctx),"\n",0);
|
|
|
e99de3 |
|
|
|
c9eeca |
tpax_lib_free_unit_ctx(uctx);
|
|
|
5ce6ff |
return ret;
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ea1d7 |
static int tpax_apply_string_replacement(
|
|
|
5ea1d7 |
const struct tpax_driver_ctx * dctx,
|
|
|
5ea1d7 |
const char * path,
|
|
|
5ea1d7 |
char * replbuf,
|
|
|
5ea1d7 |
size_t buflen)
|
|
|
5ea1d7 |
{
|
|
|
5ea1d7 |
int ret;
|
|
|
5ea1d7 |
struct tpax_driver_ctx_impl * ictx;
|
|
|
5ea1d7 |
struct tpax_replstr * replstrv;
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
ictx = tpax_get_driver_ictx(dctx);
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
if (!(replstrv = ictx->replstrv))
|
|
|
5ea1d7 |
return 0;
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
for (ret=0; !ret && replstrv->regexp; replstrv++) {
|
|
|
5ea1d7 |
ret = tpax_util_path_replstr(
|
|
|
5ea1d7 |
replbuf,path,
|
|
|
5ea1d7 |
replstrv->replstr,
|
|
|
5ea1d7 |
&replstrv->regex,
|
|
|
5ea1d7 |
buflen,replstrv->flags);
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
if ((ret > 0) && (replstrv->flags & TPAX_REPL_PRINT))
|
|
|
5ea1d7 |
tpax_dprintf(tpax_driver_fderr(dctx),"%s >> %s\n",path,replbuf);
|
|
|
5ea1d7 |
}
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
return ret;
|
|
|
5ea1d7 |
}
|
|
|
5ea1d7 |
|
|
|
5ce6ff |
static int tpax_archive_write_impl(
|
|
|
5ce6ff |
const struct tpax_driver_ctx * dctx,
|
|
|
5ce6ff |
const struct tpax_dirent * cdent,
|
|
|
5ce6ff |
int fdcwd,
|
|
|
5ce6ff |
int fdout)
|
|
|
5ce6ff |
{
|
|
|
5ce6ff |
struct tpax_unit_ctx * uctx;
|
|
|
5ce6ff |
struct tpax_ustar_header uhdr;
|
|
|
b8874b |
const struct stat * st;
|
|
|
37f513 |
struct stat stbuf;
|
|
|
5ea1d7 |
const char * apath;
|
|
|
5ce6ff |
const char * path;
|
|
|
b8874b |
const char * slnk;
|
|
|
37f513 |
const char * mlnk;
|
|
|
5ce6ff |
off_t hpos;
|
|
|
5ce6ff |
off_t dpos;
|
|
|
5ce6ff |
int fdtmp;
|
|
|
5ea1d7 |
int slen;
|
|
|
5ce6ff |
ssize_t nread;
|
|
|
5ce6ff |
ssize_t nbytes;
|
|
|
5ce6ff |
void * buf;
|
|
|
5ce6ff |
size_t buflen;
|
|
|
5ce6ff |
size_t cmplen;
|
|
|
5ce6ff |
void * membuf;
|
|
|
37f513 |
char * ch;
|
|
|
5ea1d7 |
char replbuf[PATH_MAX];
|
|
|
37f513 |
char pathbuf[PATH_MAX];
|
|
|
37f513 |
|
|
|
37f513 |
/* followed symlink? */
|
|
|
37f513 |
if (cdent->flags & TPAX_ITEM_NAMEREF)
|
|
|
37f513 |
return 0;
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* full path */
|
|
|
5ce6ff |
if (!(path = tpax_queue_item_full_path(dctx,cdent)))
|
|
|
5ce6ff |
return TPAX_CUSTOM_ERROR(
|
|
|
5ce6ff |
dctx,
|
|
|
5ce6ff |
TPAX_ERR_FLOW_ERROR);
|
|
|
5ce6ff |
|
|
|
5ea1d7 |
/* regex matching and patter substitution */
|
|
|
5ea1d7 |
if ((slen = tpax_apply_string_replacement(dctx,path,replbuf,PATH_MAX)) < 0)
|
|
|
5ea1d7 |
return TPAX_CUSTOM_ERROR(
|
|
|
5ea1d7 |
dctx,
|
|
|
5ea1d7 |
TPAX_ERR_FLOW_ERROR);
|
|
|
5ea1d7 |
|
|
|
5ea1d7 |
apath = slen ? replbuf : path;
|
|
|
5ea1d7 |
|
|
|
e99de3 |
/* verbose mode */
|
|
|
e99de3 |
if (dctx->cctx->drvflags & TPAX_DRIVER_VERBOSE)
|
|
|
5ea1d7 |
tpax_dprintf(tpax_driver_fderr(dctx),"%s",apath);
|
|
|
e99de3 |
|
|
|
5ce6ff |
/* uctx */
|
|
|
c9eeca |
if (tpax_lib_get_unit_ctx(dctx,fdcwd,path,&uctx) < 0)
|
|
|
c1438f |
return tpax_archive_write_ret(
|
|
|
e99de3 |
TPAX_NESTED_ERROR(dctx),
|
|
|
e99de3 |
dctx,0);
|
|
|
5ce6ff |
|
|
|
b8874b |
st = uctx->st;
|
|
|
b8874b |
slnk = uctx->link[0];
|
|
|
37f513 |
mlnk = 0;
|
|
|
37f513 |
|
|
|
37f513 |
if (cdent->flags & TPAX_ITEM_SYMLINK) {
|
|
|
37f513 |
st = &stbuf;
|
|
|
37f513 |
mlnk = slnk;
|
|
|
37f513 |
slnk = 0;
|
|
|
37f513 |
ch = 0;
|
|
|
37f513 |
|
|
|
37f513 |
if (mlnk[0] != '/') {
|
|
|
37f513 |
if (strlen(path) >= PATH_MAX)
|
|
|
37f513 |
return tpax_archive_write_ret(
|
|
|
37f513 |
TPAX_CUSTOM_ERROR(
|
|
|
37f513 |
dctx,
|
|
|
37f513 |
TPAX_ERR_FLOW_ERROR),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
37f513 |
|
|
|
37f513 |
strcpy(pathbuf,path);
|
|
|
37f513 |
|
|
|
37f513 |
ch = strrchr(pathbuf,'/');
|
|
|
37f513 |
}
|
|
|
37f513 |
|
|
|
37f513 |
if (ch && (++ch - pathbuf >= PATH_MAX))
|
|
|
37f513 |
return tpax_archive_write_ret(
|
|
|
37f513 |
TPAX_CUSTOM_ERROR(
|
|
|
37f513 |
dctx,
|
|
|
37f513 |
TPAX_ERR_FLOW_ERROR),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
37f513 |
|
|
|
37f513 |
if (ch) {
|
|
|
37f513 |
strcpy(ch,mlnk);
|
|
|
37f513 |
mlnk = pathbuf;
|
|
|
37f513 |
}
|
|
|
37f513 |
|
|
|
37f513 |
if (fstatat(fdcwd,mlnk,&stbuf,0) < 0)
|
|
|
37f513 |
return tpax_archive_write_ret(
|
|
|
37f513 |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
37f513 |
}
|
|
|
b8874b |
|
|
|
5ce6ff |
/* record errors */
|
|
|
5ce6ff |
tpax_driver_set_ectx(
|
|
|
5ce6ff |
dctx,0,path);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* header and data offsets: todo pax and cpio */
|
|
|
5ce6ff |
hpos = tpax_get_driver_cpos(dctx);
|
|
|
5ce6ff |
dpos = hpos + sizeof(uhdr);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* header */
|
|
|
4cb6ae |
if (tpax_meta_init_ustar_header(
|
|
|
5ea1d7 |
dctx,apath,st,
|
|
|
b8874b |
slnk,&uhdr) < 0)
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_NESTED_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* buffer */
|
|
|
5ce6ff |
membuf = 0;
|
|
|
5ce6ff |
fdtmp = -1;
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* associated data? */
|
|
|
b8874b |
if S_ISREG(st->st_mode) {
|
|
|
5ce6ff |
buf = tpax_get_driver_anon_map_addr(
|
|
|
5ce6ff |
dctx,&buflen);
|
|
|
5ce6ff |
|
|
|
b8874b |
if (buflen >= (cmplen = st->st_size))
|
|
|
5ce6ff |
membuf = buf;
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* snapshot */
|
|
|
5ce6ff |
if (membuf) {
|
|
|
0ee4a8 |
if (tpax_io_create_memory_snapshot(
|
|
|
37f513 |
dctx,fdcwd,
|
|
|
37f513 |
mlnk ? mlnk : path,
|
|
|
b8874b |
st,membuf) < 0)
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_NESTED_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
} else {
|
|
|
0ee4a8 |
if ((fdtmp = tpax_io_create_tmpfs_snapshot(
|
|
|
37f513 |
dctx,fdcwd,
|
|
|
37f513 |
mlnk ? mlnk : path,
|
|
|
b8874b |
st)) < 0)
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_NESTED_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
if (lseek(fdtmp,0,SEEK_SET) < 0)
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* append header */
|
|
|
5ce6ff |
if (tpax_archive_append_memory_data(fdout,&uhdr,ssizeof(uhdr)) < 0) {
|
|
|
5ce6ff |
if (fdtmp >= 0)
|
|
|
5ce6ff |
close(fdtmp);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
tpax_set_driver_cpos(dctx,dpos);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* all done? */
|
|
|
e99de3 |
if (!(S_ISREG(st->st_mode)))
|
|
|
e99de3 |
return tpax_archive_write_ret(
|
|
|
e99de3 |
0,dctx,uctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* append data from snapshot */
|
|
|
5ce6ff |
if (fdtmp >= 0) {
|
|
|
5ce6ff |
buf = tpax_get_driver_anon_map_addr(
|
|
|
5ce6ff |
dctx,&buflen);
|
|
|
5ce6ff |
|
|
|
b8874b |
for (nread=0; nread<st->st_size; ) {
|
|
|
5ce6ff |
nbytes = read(fdtmp,buf,buflen);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
while ((nbytes < 0) && (errno == EINTR))
|
|
|
5ce6ff |
nbytes = read(fdtmp,buf,buflen);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
if (nbytes < 0) {
|
|
|
5ce6ff |
close(fdtmp);
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
} else if (nbytes == 0) {
|
|
|
5ce6ff |
close(fdtmp);
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
} else {
|
|
|
5ce6ff |
nread += nbytes;
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
if (tpax_archive_append_memory_data(fdout,buf,nbytes) < 0) {
|
|
|
5ce6ff |
close(fdtmp);
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
close(fdtmp);
|
|
|
5ce6ff |
} else {
|
|
|
5ce6ff |
if (tpax_archive_append_memory_data(
|
|
|
5ce6ff |
fdout,membuf,
|
|
|
b8874b |
st->st_size) < 0)
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
5ce6ff |
TPAX_SYSTEM_ERROR(dctx),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
return tpax_archive_write_ret(
|
|
|
b8874b |
tpax_archive_append_pad(dctx,fdout,st),
|
|
|
e99de3 |
dctx,uctx);
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
5ce6ff |
int tpax_archive_write(const struct tpax_driver_ctx * dctx)
|
|
|
5ce6ff |
{
|
|
|
5ce6ff |
struct tpax_driver_ctx_impl * ictx;
|
|
|
5ce6ff |
struct tpax_dirent ** direntv;
|
|
|
5ce6ff |
int fdcwd;
|
|
|
5ce6ff |
int fdout;
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* driver */
|
|
|
5ce6ff |
ictx = tpax_get_driver_ictx(dctx);
|
|
|
5ce6ff |
fdcwd = tpax_driver_fdcwd(dctx);
|
|
|
5ce6ff |
fdout = tpax_driver_fdout(dctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
/* quote to archive */
|
|
|
5ce6ff |
if (tpax_update_queue_vector(dctx) < 0)
|
|
|
5ce6ff |
return TPAX_NESTED_ERROR(dctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
for (direntv=ictx->direntv; *direntv; direntv++)
|
|
|
5ce6ff |
if (tpax_archive_write_impl(dctx,*direntv,fdcwd,fdout) < 0)
|
|
|
5ce6ff |
return TPAX_NESTED_ERROR(dctx);
|
|
|
5ce6ff |
|
|
|
5ce6ff |
return 0;
|
|
|
5ce6ff |
}
|
|
|
5ce6ff |
|
|
|
fa0b03 |
static int tpax_archive_seal_cpio(const struct tpax_driver_ctx * dctx)
|
|
|
fa0b03 |
{
|
|
|
fa0b03 |
(void)dctx;
|
|
|
fa0b03 |
return -1;
|
|
|
fa0b03 |
}
|
|
|
fa0b03 |
|
|
|
fb29e1 |
int tpax_archive_seal(const struct tpax_driver_ctx * dctx)
|
|
|
fb29e1 |
{
|
|
|
fb29e1 |
int fdout;
|
|
|
fb29e1 |
off_t cpos;
|
|
|
fb29e1 |
ssize_t nbytes;
|
|
|
fb29e1 |
ssize_t nwritten;
|
|
|
fb29e1 |
ssize_t blksize;
|
|
|
fa0b03 |
char buf[1024];
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* archive block size, current position */
|
|
|
333924 |
blksize = dctx->cctx->blksize;
|
|
|
fb29e1 |
cpos = tpax_get_driver_cpos(dctx);
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* internal error? */
|
|
|
fb29e1 |
if (cpos % 512)
|
|
|
fb29e1 |
return TPAX_CUSTOM_ERROR(dctx,TPAX_ERR_FLOW_ERROR);
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* cpio trailer? */
|
|
|
fa0b03 |
if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_CPIO)
|
|
|
fa0b03 |
return tpax_archive_seal_cpio(dctx);
|
|
|
fa0b03 |
|
|
|
fa0b03 |
/* ustar, pax */
|
|
|
fb29e1 |
fdout = tpax_driver_fdout(dctx);
|
|
|
fb29e1 |
memset(buf,0,sizeof(buf));
|
|
|
fb29e1 |
|
|
|
fa0b03 |
if (tpax_archive_append_memory_data(fdout,buf,2*512) < 0)
|
|
|
fa0b03 |
return TPAX_SYSTEM_ERROR(dctx);
|
|
|
fb29e1 |
|
|
|
fa0b03 |
cpos += 2*512;
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* pax? */
|
|
|
fa0b03 |
if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_PAX) {
|
|
|
fa0b03 |
tpax_set_driver_cpos(dctx,cpos);
|
|
|
fa0b03 |
return 0;
|
|
|
fb29e1 |
}
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* already at block boundary? */
|
|
|
fa0b03 |
if ((cpos % blksize) == 0) {
|
|
|
fa0b03 |
tpax_set_driver_cpos(dctx,cpos);
|
|
|
fa0b03 |
return 0;
|
|
|
fa0b03 |
}
|
|
|
fa0b03 |
|
|
|
fa0b03 |
/* zero-fill the remainder of the block */
|
|
|
fa0b03 |
nbytes = cpos / blksize;
|
|
|
fa0b03 |
nbytes *= blksize;
|
|
|
fa0b03 |
nbytes += blksize;
|
|
|
fa0b03 |
|
|
|
fa0b03 |
for (nwritten=cpos; nwritten
|
|
|
fb29e1 |
if (tpax_archive_append_memory_data(fdout,buf,512) < 0)
|
|
|
fb29e1 |
return TPAX_SYSTEM_ERROR(dctx);
|
|
|
fb29e1 |
|
|
|
fa0b03 |
/* all done */
|
|
|
fa0b03 |
tpax_set_driver_cpos(dctx,nwritten);
|
|
|
fb29e1 |
|
|
|
fb29e1 |
return 0;
|
|
|
fb29e1 |
}
|