mirror of
https://github.com/openbsd/src.git
synced 2025-01-04 15:25:38 -08:00
Add basic write support for 'pax' format archives
Keep writing archives in ustar format by default. People can test the posix 'pax' format using pax(1) -w -x pax ... or cpio -o -H pax ...; tar(1) can't exercise this code yet. Only long names file and link names are supported for now. With input and tests from caspar@, ok millert@
This commit is contained in:
parent
2da541025c
commit
013e174a37
@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: cpio.1,v 1.36 2020/01/16 16:46:46 schwarze Exp $
|
||||
.\" $OpenBSD: cpio.1,v 1.37 2023/12/09 23:00:11 jca Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1997 SigmaSoft, Th. Lockert
|
||||
.\" All rights reserved.
|
||||
@ -23,7 +23,7 @@
|
||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd $Mdocdate: January 16 2020 $
|
||||
.Dd $Mdocdate: December 9 2023 $
|
||||
.Dt CPIO 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -98,6 +98,8 @@ format.
|
||||
Old octal character
|
||||
.Nm
|
||||
format.
|
||||
.It Ar pax
|
||||
POSIX pax format.
|
||||
.It Ar sv4cpio
|
||||
SVR4 hex
|
||||
.Nm
|
||||
@ -173,6 +175,8 @@ format.
|
||||
Old octal character
|
||||
.Nm
|
||||
format.
|
||||
.It Ar pax
|
||||
POSIX pax format.
|
||||
.It Ar sv4cpio
|
||||
SVR4 hex
|
||||
.Nm
|
||||
@ -298,6 +302,8 @@ be used for larger files.
|
||||
.It bcpio Ta "4 Gigabytes"
|
||||
.It sv4cpio Ta "4 Gigabytes"
|
||||
.It cpio Ta "8 Gigabytes"
|
||||
.\" XXX should be "unlimited"
|
||||
.It pax Ta "8 Gigabytes"
|
||||
.It tar Ta "8 Gigabytes"
|
||||
.It ustar Ta "8 Gigabytes"
|
||||
.El
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: extern.h,v 1.61 2023/11/26 16:04:17 espie Exp $ */
|
||||
/* $OpenBSD: extern.h,v 1.62 2023/12/09 23:00:11 jca Exp $ */
|
||||
/* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */
|
||||
|
||||
/*-
|
||||
@ -284,6 +284,7 @@ int tar_wr(ARCHD *);
|
||||
int ustar_id(char *, int);
|
||||
int ustar_rd(ARCHD *, char *);
|
||||
int ustar_wr(ARCHD *);
|
||||
int pax_wr(ARCHD *);
|
||||
|
||||
/*
|
||||
* tty_subs.c
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: options.c,v 1.106 2023/11/26 16:04:17 espie Exp $ */
|
||||
/* $OpenBSD: options.c,v 1.107 2023/12/09 23:00:11 jca Exp $ */
|
||||
/* $NetBSD: options.c,v 1.6 1996/03/26 23:54:18 mrg Exp $ */
|
||||
|
||||
/*-
|
||||
@ -216,6 +216,8 @@ FSUB fsub[] = {
|
||||
{ },
|
||||
/* 9: gzip, to detect failure to use -z */
|
||||
{ },
|
||||
/* 10: POSIX PAX */
|
||||
{ },
|
||||
#else
|
||||
/* 6: compress, to detect failure to use -Z */
|
||||
{NULL, 0, 4, 0, 0, 0, 0, compress_id},
|
||||
@ -225,6 +227,10 @@ FSUB fsub[] = {
|
||||
{NULL, 0, 4, 0, 0, 0, 0, bzip2_id},
|
||||
/* 9: gzip, to detect failure to use -z */
|
||||
{NULL, 0, 4, 0, 0, 0, 0, gzip_id},
|
||||
/* 10: POSIX PAX */
|
||||
{"pax", 5120, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, no_op,
|
||||
ustar_rd, tar_endrd, no_op, pax_wr, tar_endwr, tar_trail,
|
||||
tar_opt},
|
||||
#endif
|
||||
};
|
||||
#define F_OCPIO 0 /* format when called as cpio -6 */
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: pax.1,v 1.76 2022/03/31 17:27:14 naddy Exp $
|
||||
.\" $OpenBSD: pax.1,v 1.77 2023/12/09 23:00:11 jca Exp $
|
||||
.\" $NetBSD: pax.1,v 1.3 1995/03/21 09:07:37 cgd Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1992 Keith Muller.
|
||||
@ -34,7 +34,7 @@
|
||||
.\"
|
||||
.\" @(#)pax.1 8.4 (Berkeley) 4/18/94
|
||||
.\"
|
||||
.Dd $Mdocdate: March 31 2022 $
|
||||
.Dd $Mdocdate: December 9 2023 $
|
||||
.Dt PAX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -868,6 +868,11 @@ standard.
|
||||
The default blocksize for this format is 10240 bytes.
|
||||
Filenames stored by this format must be 100 characters or less in length;
|
||||
the total pathname must be 256 characters or less.
|
||||
.It Cm pax
|
||||
The pax interchange format specified in the
|
||||
.St -p1003.1-2001
|
||||
standard.
|
||||
The default blocksize for this format is 5120 bytes.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
@ -1081,9 +1086,10 @@ utility is compliant with the
|
||||
specification,
|
||||
except that the
|
||||
.Cm pax
|
||||
archive format and the
|
||||
archive format is only partially supported,
|
||||
and the
|
||||
.Cm listopt
|
||||
keyword are unsupported.
|
||||
keyword is unsupported.
|
||||
.Pp
|
||||
The flags
|
||||
.Op Fl 0BDEGjOPTUYZz ,
|
||||
|
232
bin/pax/tar.c
232
bin/pax/tar.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tar.c,v 1.73 2023/09/04 17:05:34 jca Exp $ */
|
||||
/* $OpenBSD: tar.c,v 1.74 2023/12/09 23:00:11 jca Exp $ */
|
||||
/* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */
|
||||
|
||||
/*-
|
||||
@ -35,10 +35,12 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
@ -50,6 +52,19 @@
|
||||
#include "extern.h"
|
||||
#include "tar.h"
|
||||
|
||||
SLIST_HEAD(xheader, xheader_record);
|
||||
struct xheader_record {
|
||||
SLIST_ENTRY(xheader_record) entry;
|
||||
size_t reclen;
|
||||
char *record;
|
||||
};
|
||||
|
||||
/* shortest possible extended record: "5 a=\n" */
|
||||
#define MINXHDRSZ 5
|
||||
|
||||
/* longest record we'll accept */
|
||||
#define MAXXHDRSZ BLKMULT
|
||||
|
||||
/*
|
||||
* Routines for reading, writing and header identify of various versions of tar
|
||||
*/
|
||||
@ -60,6 +75,9 @@ static char *name_split(char *, int);
|
||||
static int ul_oct(u_long, char *, int, int);
|
||||
static int ull_oct(unsigned long long, char *, int, int);
|
||||
static int rd_xheader(ARCHD *arcn, int, off_t);
|
||||
#ifndef SMALL
|
||||
static int wr_xheader(ARCHD *, struct xheader *);
|
||||
#endif
|
||||
|
||||
static uid_t uid_nobody;
|
||||
static uid_t uid_warn;
|
||||
@ -891,24 +909,121 @@ reset:
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ustar_wr()
|
||||
* write a ustar header for the file specified in the ARCHD to the archive
|
||||
* Have to check for file types that cannot be stored and file names that
|
||||
* are too long. Be careful of the term (last arg) to ul_oct, we only use
|
||||
* '\0' for the termination character (this is different than picky tar)
|
||||
* ASSUMED: space after header in header block is zero filled
|
||||
* Return:
|
||||
* 0 if file has data to be written after the header, 1 if file has NO
|
||||
* data to write after the header, -1 if archive write failed
|
||||
*/
|
||||
#ifndef SMALL
|
||||
static int
|
||||
xheader_add(struct xheader *xhdr, const char *keyword,
|
||||
const char *value)
|
||||
{
|
||||
struct xheader_record *rec;
|
||||
int reclen, tmplen;
|
||||
char *s;
|
||||
|
||||
int
|
||||
ustar_wr(ARCHD *arcn)
|
||||
tmplen = MINXHDRSZ;
|
||||
do {
|
||||
reclen = tmplen;
|
||||
tmplen = snprintf(NULL, 0, "%d %s=%s\n", reclen, keyword,
|
||||
value);
|
||||
} while (tmplen >= 0 && tmplen != reclen);
|
||||
if (tmplen < 0)
|
||||
return -1;
|
||||
|
||||
rec = calloc(1, sizeof(*rec));
|
||||
if (rec == NULL)
|
||||
return -1;
|
||||
rec->reclen = reclen;
|
||||
if (asprintf(&s, "%d %s=%s\n", reclen, keyword, value) < 0) {
|
||||
free(rec);
|
||||
return -1;
|
||||
}
|
||||
rec->record = s;
|
||||
|
||||
SLIST_INSERT_HEAD(xhdr, rec, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xheader_free(struct xheader *xhdr)
|
||||
{
|
||||
struct xheader_record *rec;
|
||||
|
||||
while (!SLIST_EMPTY(xhdr)) {
|
||||
rec = SLIST_FIRST(xhdr);
|
||||
SLIST_REMOVE_HEAD(xhdr, entry);
|
||||
free(rec->record);
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
wr_xheader(ARCHD *arcn, struct xheader *xhdr)
|
||||
{
|
||||
char hdblk[sizeof(HD_USTAR)];
|
||||
HD_USTAR *hd;
|
||||
char buf[sizeof(hd->name) + 1];
|
||||
struct xheader_record *rec;
|
||||
size_t size;
|
||||
|
||||
size = 0;
|
||||
SLIST_FOREACH(rec, xhdr, entry)
|
||||
size += rec->reclen;
|
||||
|
||||
memset(hdblk, 0, sizeof(hdblk));
|
||||
hd = (HD_USTAR *)hdblk;
|
||||
hd->typeflag = XHDRTYPE;
|
||||
strncpy(hd->magic, TMAGIC, TMAGLEN);
|
||||
strncpy(hd->version, TVERSION, TVERSLEN);
|
||||
if (ul_oct(size, hd->size, sizeof(hd->size), 3))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Best effort attempt at providing a useful file name for
|
||||
* implementations that don't support pax format. Don't bother
|
||||
* with truncation if the resulting file name doesn't fit.
|
||||
* XXX dirname/basename portability (check return value?)
|
||||
*/
|
||||
(void)snprintf(buf, sizeof(buf), "%s/PaxHeaders.%ld/%s",
|
||||
dirname(arcn->name), (long)getpid(), basename(arcn->name));
|
||||
fieldcpy(hd->name, sizeof(hd->name), buf, sizeof(buf));
|
||||
|
||||
if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
|
||||
ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime, hd->mtime,
|
||||
sizeof(hd->mtime), 1) ||
|
||||
ul_oct(arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
|
||||
ul_oct(arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0))
|
||||
return -1;
|
||||
|
||||
if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
|
||||
sizeof(hd->chksum), 3))
|
||||
return -1;
|
||||
|
||||
/* write out extended header */
|
||||
if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
|
||||
return -1;
|
||||
if (wr_skip(BLKMULT - sizeof(HD_USTAR)) < 0)
|
||||
return -1;
|
||||
|
||||
/* write out extended header records */
|
||||
SLIST_FOREACH(rec, xhdr, entry)
|
||||
if (wr_rdbuf(rec->record, rec->reclen) < 0)
|
||||
return -1;
|
||||
|
||||
if (wr_skip(TAR_PAD(size)) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
wr_ustar_or_pax(ARCHD *arcn, int ustar)
|
||||
{
|
||||
HD_USTAR *hd;
|
||||
const char *name;
|
||||
char *pt, hdblk[sizeof(HD_USTAR)];
|
||||
#ifndef SMALL
|
||||
struct xheader xhdr = SLIST_HEAD_INITIALIZER(xhdr);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* check for those file system types ustar cannot store
|
||||
@ -929,8 +1044,19 @@ ustar_wr(ARCHD *arcn)
|
||||
*/
|
||||
if (PAX_IS_LINK(arcn->type) &&
|
||||
((size_t)arcn->ln_nlen > sizeof(hd->linkname))) {
|
||||
paxwarn(1, "Link name too long for ustar %s", arcn->ln_name);
|
||||
return(1);
|
||||
if (ustar) {
|
||||
paxwarn(1, "Link name too long for ustar %s",
|
||||
arcn->ln_name);
|
||||
return(1);
|
||||
}
|
||||
#ifndef SMALL
|
||||
else if (xheader_add(&xhdr, "linkpath", arcn->name) == -1) {
|
||||
paxwarn(1, "Link name too long for pax %s",
|
||||
arcn->ln_name);
|
||||
xheader_free(&xhdr);
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -938,8 +1064,21 @@ ustar_wr(ARCHD *arcn)
|
||||
* pt != arcn->name, the name has to be split
|
||||
*/
|
||||
if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
|
||||
paxwarn(1, "File name too long for ustar %s", arcn->name);
|
||||
return(1);
|
||||
if (ustar) {
|
||||
paxwarn(1, "File name too long for ustar %s",
|
||||
arcn->name);
|
||||
return(1);
|
||||
}
|
||||
#ifndef SMALL
|
||||
else if (xheader_add(&xhdr, "path", arcn->name) == -1) {
|
||||
paxwarn(1, "File name too long for pax %s",
|
||||
arcn->ln_name);
|
||||
xheader_free(&xhdr);
|
||||
return(1);
|
||||
}
|
||||
/* PAX format, we don't need to split the path */
|
||||
pt = arcn->name;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1074,6 +1213,18 @@ ustar_wr(ARCHD *arcn)
|
||||
strncpy(hd->gname, name, sizeof(hd->gname));
|
||||
}
|
||||
|
||||
#ifndef SMALL
|
||||
/* write out a pax extended header if needed */
|
||||
if (!SLIST_EMPTY(&xhdr)) {
|
||||
int ret;
|
||||
|
||||
ret = wr_xheader(arcn, &xhdr);
|
||||
xheader_free(&xhdr);
|
||||
if (ret == -1)
|
||||
return(-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* calculate and store the checksum write the header to the archive
|
||||
* return 0 tells the caller to now write the file data, 1 says no data
|
||||
@ -1091,6 +1242,9 @@ ustar_wr(ARCHD *arcn)
|
||||
return(1);
|
||||
|
||||
out:
|
||||
#ifndef SMALL
|
||||
xheader_free(&xhdr);
|
||||
#endif
|
||||
/*
|
||||
* header field is out of range
|
||||
*/
|
||||
@ -1098,6 +1252,42 @@ ustar_wr(ARCHD *arcn)
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* ustar_wr()
|
||||
* Write out a ustar format archive.
|
||||
* Have to check for file types that cannot be stored and file names that
|
||||
* are too long. Be careful of the term (last arg) to ul_oct, we only use
|
||||
* '\0' for the termination character (this is different than picky tar).
|
||||
* ASSUMED: space after header in header block is zero filled
|
||||
* Return:
|
||||
* 0 if file has data to be written after the header, 1 if file has NO
|
||||
* data to write after the header, -1 if archive write failed
|
||||
*/
|
||||
int
|
||||
ustar_wr(ARCHD *arcn)
|
||||
{
|
||||
return wr_ustar_or_pax(arcn, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* pax_wr()
|
||||
* Write out a pax format archive.
|
||||
* Have to check for file types that cannot be stored. Be careful of the
|
||||
* term (last arg) to ul_oct, we only use '\0' for the termination
|
||||
* character (this is different than picky tar).
|
||||
* ASSUMED: space after header in header block is zero filled
|
||||
* Return:
|
||||
* 0 if file has data to be written after the header, 1 if file has NO
|
||||
* data to write after the header, -1 if archive write failed
|
||||
*/
|
||||
#ifndef SMALL
|
||||
int
|
||||
pax_wr(ARCHD *arcn)
|
||||
{
|
||||
return wr_ustar_or_pax(arcn, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* name_split()
|
||||
* see if the name has to be split for storage in a ustar header. We try
|
||||
@ -1184,12 +1374,6 @@ expandname(char *buf, size_t len, char **gnu_name, const char *name,
|
||||
return(nlen);
|
||||
}
|
||||
|
||||
/* shortest possible extended record: "5 a=\n" */
|
||||
#define MINXHDRSZ 5
|
||||
|
||||
/* longest record we'll accept */
|
||||
#define MAXXHDRSZ BLKMULT
|
||||
|
||||
static int
|
||||
rd_time(struct timespec *ts, const char *keyword, char *p)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user