Initial commit of bsdcpio 0.9.11b.

A new implementation of cpio that uses libarchive as it's back-end
archiving/dearchiving infrastructure.  Includes test harness;
"make check" in the bsdcpio directory to build and run the test
harness.
This commit is contained in:
Tim Kientzle 2008-05-26 17:15:35 +00:00
parent eb36031906
commit 1e38350b0a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=179323
44 changed files with 5986 additions and 0 deletions

19
usr.bin/cpio/Makefile Normal file
View File

@ -0,0 +1,19 @@
# $FreeBSD$
PROG= bsdcpio
BSDCPIO_VERSION_STRING=0.9.11b
SRCS= cpio.c cmdline.c err.c matching.c pathmatch.c
WARNS?= 6
DPADD= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2}
CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" -O0 -g
LDADD+= -larchive -lz -lbz2
SYMLINKS=bsdcpio ${BINDIR}/cpio
MLINKS= bsdcpio.1 cpio.1
.PHONY: check test
check test: $(PROG) bsdcpio.1.gz
cd ${.CURDIR}/test && make clean test
.include <bsd.prog.mk>

367
usr.bin/cpio/bsdcpio.1 Normal file
View File

@ -0,0 +1,367 @@
.\" Copyright (c) 2003-2007 Tim Kientzle
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd December 21, 2007
.Dt BSDCPIO 1
.Os
.Sh NAME
.Nm cpio
.Nd copy files to and from archives
.Sh SYNOPSIS
.Nm
.Brq Fl i
.Op Ar options
.Op Ar pattern ...
.Op Ar < archive
.Nm
.Brq Fl o
.Op Ar options
.Ar < name-list
.Op Ar > archive
.Nm
.Brq Fl p
.Op Ar options
.Ar dest-dir
.Ar < name-list
.Sh DESCRIPTION
.Nm
copies files between archives and directories.
This implementation can extract from tar, pax, cpio, zip, jar, ar,
and ISO 9660 cdrom images and can create tar, pax, cpio, ar,
and shar archives.
.Pp
The first option to
.Nm
is a mode indicator from the following list:
.Bl -tag -compact -width indent
.It Fl i
Input.
Read an archive from standard input (unless overriden) and extract the
contents to disk or (if the
.Fl t
option is specified)
list the contents to standard output.
If one or more file patterns are specified, only files matching
one of the patterns will be extracted.
.It Fl o
Output.
Read a list of filenames from standard input and produce a new archive
on standard output (unless overriden) containing the specified items.
.It Fl p
Pass-through.
Read a list of filenames from standard input and copy the files to the
specified directory.
.El
.Pp
.Sh OPTIONS
Unless specifically stated otherwise, options are applicable in
all operating modes.
.Bl -tag -width indent
.It Fl A
(o mode only)
Append to the specified archive.
(Not yet implemented.)
.It Fl a
(o and p modes)
Reset access times on files after they are read.
.It Fl B
(o mode only)
Block output to records of 5120 bytes.
.It Fl C Ar size
(o mode only)
Block output to records of
.Ar size
bytes.
.It Fl c
(o mode only)
Use the old POSIX portable character format.
Equivalent to
.Fl -format Ar odc .
.It Fl d
(i and p modes)
Create directories as necessary.
.It Fl E Ar file
(i mode only)
Read list of file name patterns from
.Ar file
to list and extract.
.It Fl F Ar file
Read archive from or write archive to
.Ar file .
.It Fl f Ar pattern
(i mode only)
Ignore files that match
.Ar pattern .
.It Fl -format Ar format
(o mode only)
Produce the output archive in the specified format.
Supported formats include:
.Pp
.Bl -tag -width "iso9660" -compact
.It Ar cpio
Synonym for
.Ar odc .
.It Ar newc
The SVR4 portable cpio format.
.It Ar odc
The old POSIX.1 portable octet-oriented cpio format.
.It Ar pax
The POSIX.1 pax format, an extension of the ustar format.
.It Ar ustar
The POSIX.1 tar format.
.El
.Pp
The default format is
.Ar odc .
See
.Xr libarchive_formats 5
for more complete information about the
formats currently supported by the underlying
.Xr libarchive 3
library.
.It Fl I Ar file
Read archive from
.Ar file .
.It Fl i
Input mode.
See above for description.
.It Fl -insecure
(i and p mode only)
Disable security checks during extraction or copying.
This allows extraction via symbolic links and path names containing
.Sq ..
in the name.
.It Fl L
(o and p modes)
All symbolic links will be followed.
Normally, symbolic links are archived and copied as symbolic links.
With this option, the target of the link will be archived or copied instead.
.It Fl l
(p mode only)
Create links from the target directory to the original files,
instead of copying.
.It Fl m
(i and p modes)
Set file modification time on created files to match
those in the source.
.It Fl O Ar file
Write archive to
.Ar file .
.It Fl o
Output mode.
See above for description.
.It Fl p
Pass-through mode.
See above for description.
.It Fl -quiet
Suppress unnecessary messages.
.It Fl R Oo user Oc Ns Oo : Oc Ns Oo group Oc
Set the owner and/or group on files in the output.
If group is specified with no user
(for example,
.Fl R Ar :wheel )
then the group will be set but not the user.
If the user is specified with a trailing colon and no group
(for example,
.Fl R Ar root: )
then the group will be set to the user's default group.
If the user is specified with no trailing colon, then
the user will be set but not the group.
In
.Fl i
and
.Fl p
modes, this option can only be used by the super-user.
(For compatibility, a period can be used in place of the colon.)
.It Fl r
(All modes.)
Rename files interactively.
For each file, a prompt is written to
.Pa /dev/tty
containing the name of the file and a line is read from
.Pa /dev/tty .
If the line read is blank, the file is skipped.
If the line contains a single period, the file is processed normally.
Otherwise, the line is taken to be the new name of the file.
.It Fl t
(i mode only)
List the contents of the archive to stdout;
do not restore the contents to disk.
.It Fl u
(i and p modes)
Unconditionally overwrite existing files.
Ordinarily, an older file will not overwrite a newer file on disk.
.It Fl v
Print the name of each file to stderr as it is processed.
With
.Fl t ,
provide a detailed listing of each file.
.It Fl -version
Print the program version information and exit.
.It Fl y
(o mode only)
Compress the archive with bzip2-compatible compression before writing it.
In input mode, this option is ignored;
bzip2 compression is recognized automatically on input.
.It Fl Z
(o mode only)
Compress the archive with compress-compatible compression before writing it.
In input mode, this option is ignored;
compression is recognized automatically on input.
.It Fl z
(o mode only)
Compress the archive with gzip-compatible compression before writing it.
In input mode, this option is ignored;
gzip compression is recognized automatically on input.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
.Nm :
.Bl -tag -width ".Ev BLOCKSIZE"
.It Ev LANG
The locale to use.
See
.Xr environ 7
for more information.
.It Ev TZ
The timezone to use when displaying dates.
See
.Xr environ 7
for more information.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
The
.Nm
command is traditionally used to copy file heirarchies in conjunction
with the
.Xr find 1
command.
The first example here simply copies all files from
.Pa src
to
.Pa dest :
.Dl Nm find Pa src | Nm Fl pmud Pa dest
.Pp
By carefully selecting options to the
.Xr find 1
command and combining it with other standard utilities,
it is possible to exercise very fine control over which files are copied.
This next example copies files from
.Pa src
to
.Pa dest
that are more than 2 days old and whose names match a particular pattern:
.Dl Nm find Pa src Fl mtime Ar +2 | Nm grep foo[bar] | Nm Fl pdmu Pa dest
.Pp
This example copies files from
.Pa src
to
.Pa dest
that are more than 2 days old and which contain the word
.Do foobar Dc :
.Dl Nm find Pa src Fl mtime Ar +2 | Nm xargs Nm grep -l foobar | Nm Fl pdmu Pa dest
.Sh COMPATIBILITY
The mode options i, o, and p and the options
a, B, c, d, f, l, m, r, t, u, and v comply with SUSv2.
.Pp
The old POSIX.1 standard specified that only
.Fl i ,
.Fl o ,
and
.Fl p
were interpreted as command-line options.
Each took a single argument of a list of modifier
characters.
For example, the standard syntax allows
.Fl imu
but does not support
.Fl miu
or
.Fl i Fl m Fl u ,
since
.Ar m
and
.Ar u
are only modifiers to
.Fl i ,
they are not command-line options in their own right.
The syntax supported by this implementation is backwards-compatible
with the standard.
For best compatibility, scripts should limit themselves to the
standard syntax.
.Sh SEE ALSO
.Xr bzip2 1 ,
.Xr tar 1 ,
.Xr gzip 1 ,
.Xr mt 1 ,
.Xr pax 1 ,
.Xr libarchive 3 ,
.Xr cpio 5 ,
.Xr libarchive-formats 5 ,
.Xr tar 5
.Sh STANDARDS
There is no current POSIX standard for the cpio command; it appeared
in
.St -p1003.1-96
but was dropped from
.St -p1003.1-2001 .
.Pp
The cpio, ustar, and pax interchange file formats are defined by
.St -p1003.1-2001
for the pax command.
.Sh HISTORY
The original
.Nm cpio
and
.Nm find
utilities were written by Dick Haight
while working in AT&T's Unix Support Group.
They first appeared in 1977 in PWB/UNIX 1.0, the
.Dq Programmer's Work Bench
system developed for use within AT&T.
They were first released outside of AT&T as part of System III Unix in 1981.
As a result,
.Nm cpio
actually predates
.Nm tar ,
even though it was not well-known outside of AT&T until some time later.
.Pp
This is a complete re-implementation based on the
.Xr libarchive 3
library.
.Sh BUGS
The cpio archive format has several basic limitations:
It does not store user and group names, only numbers.
As a result, it cannot be reliably used to transfer
files between systems with dissimilar user and group numbering.
Older cpio formats limit the user and group numbers to
16 or 18 bits, which is insufficient for modern systems.
The cpio archive formats cannot support files over 4 gigabytes,
except for the
.Dq odc
variant, which can support files up to 8 gigabytes.

247
usr.bin/cpio/cmdline.c Normal file
View File

@ -0,0 +1,247 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cpio_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
#define no_argument 0
#define required_argument 1
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "cpio.h"
/*
*
* Option parsing routines for bsdcpio.
*
*/
static const char *cpio_opts = "AaBC:F:O:cdE:f:H:hijLlmopR:rtuvW:yZz";
/*
* On systems that lack getopt_long, long options can be specified
* using -W longopt and -W longopt=value, e.g. "-W version" is the
* same as "--version" and "-W format=ustar" is the same as "--format
* ustar". This does not rely the GNU getopt() "W;" extension, so
* should work correctly on any system with a POSIX-compliant
* getopt().
*/
/*
* If you add anything, be very careful to keep this list properly
* sorted, as the -W logic below relies on it.
*/
static const struct option cpio_longopts[] = {
{ "file", required_argument, NULL, 'F' },
{ "format", required_argument, NULL, 'H' },
{ "help", no_argument, NULL, 'h' },
{ "insecure", no_argument, NULL, OPTION_INSECURE },
{ "owner", required_argument, NULL, 'R' },
{ "quiet", no_argument, NULL, OPTION_QUIET },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, OPTION_VERSION },
{ NULL, 0, NULL, 0 }
};
/*
* Parse command-line options using system-provided getopt() or getopt_long().
* If option is -W, then parse argument as a long option.
*/
int
cpio_getopt(struct cpio *cpio)
{
char *p, *q;
const struct option *option, *option2;
int opt;
int option_index;
size_t option_length;
option_index = -1;
#ifdef HAVE_GETOPT_LONG
opt = getopt_long(cpio->argc, cpio->argv, cpio_opts,
cpio_longopts, &option_index);
#else
opt = getopt(cpio->argc, cpio->argv, cpio_opts);
#endif
/* Support long options through -W longopt=value */
if (opt == 'W') {
p = optarg;
q = strchr(optarg, '=');
if (q != NULL) {
option_length = (size_t)(q - p);
optarg = q + 1;
} else {
option_length = strlen(p);
optarg = NULL;
}
option = cpio_longopts;
while (option->name != NULL &&
(strlen(option->name) < option_length ||
strncmp(p, option->name, option_length) != 0 )) {
option++;
}
if (option->name != NULL) {
option2 = option;
opt = option->val;
/* If the first match was exact, we're done. */
if (strncmp(p, option->name, strlen(option->name)) == 0) {
while (option->name != NULL)
option++;
} else {
/* Check if there's another match. */
option++;
while (option->name != NULL &&
(strlen(option->name) < option_length ||
strncmp(p, option->name, option_length) != 0)) {
option++;
}
}
if (option->name != NULL)
cpio_errc(1, 0,
"Ambiguous option %s "
"(matches both %s and %s)",
p, option2->name, option->name);
if (option2->has_arg == required_argument
&& optarg == NULL)
cpio_errc(1, 0,
"Option \"%s\" requires argument", p);
} else {
opt = '?';
}
}
return (opt);
}
/*
* Parse the argument to the -R or --owner flag.
*
* The format is one of the following:
* <user> - Override user but not group
* <user>: - Override both, group is user's default group
* <user>:<group> - Override both
* :<group> - Override group but not user
*
* A period can be used instead of the colon.
*
* Sets uid/gid as appropriate, -1 indicates uid/gid not specified.
*
*/
int
owner_parse(const char *spec, int *uid, int *gid)
{
const char *u, *ue, *g;
*uid = -1;
*gid = -1;
/*
* Split spec into [user][:.][group]
* u -> first char of username, NULL if no username
* ue -> first char after username (colon, period, or \0)
* g -> first char of group name
*/
if (*spec == ':' || *spec == '.') {
/* If spec starts with ':' or '.', then just group. */
ue = u = NULL;
g = spec + 1;
} else {
/* Otherwise, [user] or [user][:] or [user][:][group] */
ue = u = spec;
while (*ue != ':' && *ue != '.' && *ue != '\0')
++ue;
g = ue;
if (*g != '\0') /* Skip : or . to find first char of group. */
++g;
}
if (u != NULL) {
/* Look up user: ue is first char after end of user. */
char *user;
struct passwd *pwent;
user = (char *)malloc(ue - u + 1);
if (user == NULL) {
cpio_warnc(errno, "Couldn't allocate memory");
return (1);
}
memcpy(user, u, ue - u);
user[ue - u] = '\0';
pwent = getpwnam(user);
if (pwent == NULL) {
cpio_warnc(errno, "Couldn't lookup user ``%s''", user);
return (1);
}
free(user);
*uid = pwent->pw_uid;
if (*ue != '\0' && *g == '\0')
*gid = pwent->pw_gid;
}
if (*g != '\0') {
struct group *grp;
grp = getgrnam(g);
if (grp != NULL)
*gid = grp->gr_gid;
else {
cpio_warnc(errno, "Couldn't look up group ``%s''", g);
return (1);
}
}
return (0);
}

View File

@ -0,0 +1,109 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
/* A default configuration for FreeBSD, used if there is no config.h. */
#include <sys/param.h> /* __FreeBSD_version */
#if __FreeBSD__ > 4
#define HAVE_ACL_GET_PERM 0
#define HAVE_ACL_GET_PERM_NP 1
#define HAVE_ACL_PERMSET_T 1
#define HAVE_ACL_USER 1
#endif
#undef HAVE_ATTR_XATTR_H
#define HAVE_BZLIB_H 1
#define HAVE_CHFLAGS 1
#define HAVE_DECL_OPTARG 1
#define HAVE_DECL_OPTIND 1
#define HAVE_DIRENT_D_NAMLEN 1
#define HAVE_DIRENT_H 1
#define HAVE_D_MD_ORDER 1
#define HAVE_ERRNO_H 1
#undef HAVE_EXT2FS_EXT2_FS_H
#define HAVE_FCHDIR 1
#define HAVE_FCNTL_H 1
#define HAVE_FNMATCH 1
#define HAVE_FNMATCH_H 1
#define HAVE_FNM_LEADING_DIR 1
#define HAVE_FTRUNCATE 1
#define HAVE_FUTIMES 1
#define HAVE_GETOPT_LONG 1
#undef HAVE_GETXATTR
#define HAVE_GRP_H 1
#define HAVE_INTTYPES_H 1
#define HAVE_LANGINFO_H 1
#undef HAVE_LGETXATTR
#undef HAVE_LIBACL
#define HAVE_LIBARCHIVE 1
#define HAVE_LIBBZ2 1
#define HAVE_LIBZ 1
#define HAVE_LIMITS_H 1
#undef HAVE_LINUX_EXT2_FS_H
#undef HAVE_LINUX_FS_H
#undef HAVE_LISTXATTR
#undef HAVE_LLISTXATTR
#define HAVE_LOCALE_H 1
#define HAVE_LUTIMES 1
#define HAVE_MALLOC 1
#define HAVE_MEMMOVE 1
#define HAVE_MEMORY_H 1
#define HAVE_MEMSET 1
#if __FreeBSD_version >= 450002 /* nl_langinfo introduced */
#define HAVE_NL_LANGINFO 1
#endif
#define HAVE_PATHS_H 1
#define HAVE_PWD_H 1
#define HAVE_SETLOCALE 1
#define HAVE_STDARG_H 1
#define HAVE_STDINT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRCHR 1
#define HAVE_STRDUP 1
#define HAVE_STRERROR 1
#define HAVE_STRFTIME 1
#define HAVE_STRINGS_H 1
#define HAVE_STRING_H 1
#define HAVE_STRRCHR 1
#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
#define HAVE_STRUCT_STAT_ST_RDEV 1
#define HAVE_SYS_ACL_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_TIME_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_UINTMAX_T 1
#define HAVE_UNISTD_H 1
#define HAVE_UNSIGNED_LONG_LONG 1
#define HAVE_UTIMES 1
#define HAVE_VPRINTF 1
#define HAVE_ZLIB_H 1
#undef MAJOR_IN_MKDEV
#define STDC_HEADERS 1

946
usr.bin/cpio/cpio.c Normal file
View File

@ -0,0 +1,946 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cpio_platform.h"
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <archive.h>
#include <archive_entry.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "cpio.h"
#include "matching.h"
static int copy_data(struct archive *, struct archive *);
static const char *cpio_rename(const char *name);
static int entry_to_archive(struct cpio *, struct archive_entry *);
static int file_to_archive(struct cpio *, const char *);
static void long_help(void);
static void mode_in(struct cpio *);
static void mode_list(struct cpio *);
static void mode_out(struct cpio *);
static void mode_pass(struct cpio *, const char *);
static void restore_time(struct cpio *, struct archive_entry *,
const char *, int fd);
static void usage(void);
static void version(void);
int
main(int argc, char *argv[])
{
static char buff[16384];
struct cpio _cpio; /* Allocated on stack. */
struct cpio *cpio;
int uid, gid;
int opt;
cpio = &_cpio;
memset(cpio, 0, sizeof(*cpio));
cpio->buff = buff;
cpio->buff_size = sizeof(buff);
/* Need cpio_progname before calling cpio_warnc. */
if (*argv == NULL)
cpio_progname = "bsdcpio";
else {
cpio_progname = strrchr(*argv, '/');
if (cpio_progname != NULL)
cpio_progname++;
else
cpio_progname = *argv;
}
cpio->uid_override = -1;
cpio->gid_override = -1;
cpio->argv = argv;
cpio->argc = argc;
cpio->mode = '\0';
cpio->verbose = 0;
cpio->compress = '\0';
/* TODO: Implement old binary format in libarchive, use that here. */
cpio->format = "odc"; /* Default format */
cpio->extract_flags = ARCHIVE_EXTRACT_NO_AUTODIR;
cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS;
cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;
/* TODO: If run by root, set owner as well. */
cpio->bytes_per_block = 512;
cpio->filename = NULL;
while ((opt = cpio_getopt(cpio)) != -1) {
switch (opt) {
case 'A': /* NetBSD/OpenBSD */
cpio->option_append = 1;
break;
case 'a': /* POSIX 1997 */
cpio->option_atime_restore = 1;
break;
case 'B': /* POSIX 1997 */
cpio->bytes_per_block = 5120;
break;
case 'C': /* NetBSD/OpenBSD */
cpio->bytes_per_block = atoi(optarg);
if (cpio->bytes_per_block <= 0)
cpio_errc(1, 0, "Invalid blocksize %s", optarg);
break;
case 'c': /* POSIX 1997 */
cpio->format = "odc";
break;
case 'd': /* POSIX 1997 */
cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR;
break;
case 'E': /* NetBSD/OpenBSD */
include_from_file(cpio, optarg);
break;
case 'F': /* NetBSD/OpenBSD/GNU cpio */
cpio->filename = optarg;
break;
case 'f': /* POSIX 1997 */
exclude(cpio, optarg);
break;
case 'H': /* GNU cpio (also --format) */
cpio->format = optarg;
break;
case 'h':
long_help();
break;
case 'I': /* NetBSD/OpenBSD */
cpio->filename = optarg;
break;
case 'i': /* POSIX 1997 */
cpio->mode = opt;
break;
case OPTION_INSECURE:
cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS;
cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT;
break;
case 'L': /* GNU cpio */
cpio->option_follow_links = 1;
break;
case 'l': /* POSIX 1997 */
cpio->option_link = 1;
break;
case 'm': /* POSIX 1997 */
cpio->extract_flags |= ARCHIVE_EXTRACT_TIME;
break;
case 'O': /* GNU cpio */
cpio->filename = optarg;
break;
case 'o': /* POSIX 1997 */
cpio->mode = opt;
break;
case 'p': /* POSIX 1997 */
cpio->mode = opt;
break;
case OPTION_QUIET: /* GNU cpio */
cpio->quiet = 1;
break;
case 'R': /* GNU cpio, also --owner */
if (owner_parse(optarg, &uid, &gid))
usage();
if (uid != -1)
cpio->uid_override = uid;
if (gid != -1)
cpio->gid_override = gid;
break;
case 'r': /* POSIX 1997 */
cpio->option_rename = 1;
break;
case 't': /* POSIX 1997 */
cpio->option_list = 1;
break;
case 'u': /* POSIX 1997 */
cpio->extract_flags
&= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
break;
case 'v': /* POSIX 1997 */
cpio->verbose++;
break;
case OPTION_VERSION: /* GNU convention */
version();
break;
#if 0
/*
* cpio_getopt() handles -W specially, so it's not
* available here.
*/
case 'W': /* Obscure, but useful GNU convention. */
break;
#endif
case 'y': /* tar convention */
cpio->compress = opt;
break;
case 'Z': /* tar convention */
cpio->compress = opt;
break;
case 'z': /* tar convention */
cpio->compress = opt;
break;
default:
usage();
}
}
/* TODO: Sanity-check args, error out on nonsensical combinations. */
cpio->argc -= optind;
cpio->argv += optind;
switch (cpio->mode) {
case 'o':
mode_out(cpio);
break;
case 'i':
while (*cpio->argv != NULL) {
include(cpio, *cpio->argv);
--cpio->argc;
++cpio->argv;
}
if (cpio->option_list)
mode_list(cpio);
else
mode_in(cpio);
break;
case 'p':
if (*cpio->argv == NULL || **cpio->argv == '\0')
cpio_errc(1, 0,
"-p mode requires a target directory");
mode_pass(cpio, *cpio->argv);
break;
default:
cpio_errc(1, 0,
"Must specify at least one of -i, -o, or -p");
}
return (0);
}
void
usage(void)
{
const char *p;
p = cpio_progname;
fprintf(stderr, "Brief Usage:\n");
fprintf(stderr, " List: %s -it < archive\n", p);
fprintf(stderr, " Extract: %s -i < archive\n", p);
fprintf(stderr, " Create: %s -o < filenames > archive\n", p);
#ifdef HAVE_GETOPT_LONG
fprintf(stderr, " Help: %s --help\n", p);
#else
fprintf(stderr, " Help: %s -h\n", p);
#endif
exit(1);
}
static const char *long_help_msg =
"First option must be a mode specifier:\n"
" -i Input -o Output -p Pass\n"
"Common Options:\n"
" -v Verbose\n"
"Create: %p -o [options] < [list of files] > [archive]\n"
" -z, -y Compress archive with gzip/bzip2\n"
" --format {odc|newc|ustar} Select archive format\n"
"List: %p -it < [archive]\n"
"Extract: %p -i [options] < [archive]\n";
/*
* Note that the word 'bsdcpio' will always appear in the first line
* of output.
*
* In particular, /bin/sh scripts that need to test for the presence
* of bsdcpio can use the following template:
*
* if (cpio --help 2>&1 | grep bsdcpio >/dev/null 2>&1 ) then \
* echo bsdcpio; else echo not bsdcpio; fi
*/
static void
long_help(void)
{
const char *prog;
const char *p;
prog = cpio_progname;
fflush(stderr);
p = (strcmp(prog,"bsdcpio") != 0) ? "(bsdcpio)" : "";
printf("%s%s: manipulate archive files\n", prog, p);
for (p = long_help_msg; *p != '\0'; p++) {
if (*p == '%') {
if (p[1] == 'p') {
fputs(prog, stdout);
p++;
} else
putchar('%');
} else
putchar(*p);
}
version();
}
static void
version(void)
{
fprintf(stdout,"bsdcpio %s -- %s\n",
BSDCPIO_VERSION_STRING,
archive_version());
exit(0);
}
static void
mode_out(struct cpio *cpio)
{
struct archive_entry *entry, *spare;
unsigned long blocks;
int r;
if (cpio->option_append)
cpio_errc(1, 0, "Append mode not yet supported.");
cpio->archive = archive_write_new();
if (cpio->archive == NULL)
cpio_errc(1, 0, "Failed to allocate archive object");
switch (cpio->compress) {
case 'j': case 'y':
archive_write_set_compression_bzip2(cpio->archive);
break;
case 'z':
archive_write_set_compression_gzip(cpio->archive);
break;
case 'Z':
archive_write_set_compression_compress(cpio->archive);
break;
default:
archive_write_set_compression_none(cpio->archive);
break;
}
r = archive_write_set_format_by_name(cpio->archive, cpio->format);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(cpio->archive));
archive_write_set_bytes_per_block(cpio->archive, cpio->bytes_per_block);
cpio->linkresolver = archive_entry_linkresolver_new();
archive_entry_linkresolver_set_strategy(cpio->linkresolver,
archive_format(cpio->archive));
r = archive_write_open_file(cpio->archive, cpio->filename);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(cpio->archive));
process_lines(cpio, "-", file_to_archive);
/*
* The hardlink detection may have queued up a couple of entries
* that can now be flushed.
*/
entry = NULL;
archive_entry_linkify(cpio->linkresolver, &entry, &spare);
while (entry != NULL) {
entry_to_archive(cpio, entry);
archive_entry_free(entry);
entry = NULL;
archive_entry_linkify(cpio->linkresolver, &entry, &spare);
}
r = archive_write_close(cpio->archive);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(cpio->archive));
if (!cpio->quiet) {
blocks = (archive_position_uncompressed(cpio->archive) + 511)
/ 512;
fprintf(stderr, "%lu %s\n", blocks,
blocks == 1 ? "block" : "blocks");
}
archive_write_finish(cpio->archive);
}
/*
* This is used by both out mode (to copy objects from disk into
* an archive) and pass mode (to copy objects from disk to
* an archive_write_disk "archive").
*/
static int
file_to_archive(struct cpio *cpio, const char *srcpath)
{
struct stat st;
const char *destpath;
struct archive_entry *entry, *spare;
size_t len;
const char *p;
int lnklen;
int r;
/*
* Create an archive_entry describing the source file.
*/
entry = archive_entry_new();
if (entry == NULL)
cpio_errc(1, 0, "Couldn't allocate entry");
archive_entry_copy_sourcepath(entry, srcpath);
/* Get stat information. */
if (cpio->option_follow_links)
r = stat(srcpath, &st);
else
r = lstat(srcpath, &st);
if (r != 0) {
cpio_warnc(errno, "Couldn't stat \"%s\"", srcpath);
archive_entry_free(entry);
return (0);
}
if (cpio->uid_override >= 0)
st.st_uid = cpio->uid_override;
if (cpio->gid_override >= 0)
st.st_gid = cpio->uid_override;
archive_entry_copy_stat(entry, &st);
/* If its a symlink, pull the target. */
if (S_ISLNK(st.st_mode)) {
lnklen = readlink(srcpath, cpio->buff, cpio->buff_size);
if (lnklen < 0) {
cpio_warnc(errno,
"%s: Couldn't read symbolic link", srcpath);
archive_entry_free(entry);
return (0);
}
cpio->buff[lnklen] = 0;
archive_entry_set_symlink(entry, cpio->buff);
}
/*
* Generate a destination path for this entry.
* "destination path" is the name to which it will be copied in
* pass mode or the name that will go into the archive in
* output mode.
*/
destpath = srcpath;
if (cpio->destdir) {
len = strlen(cpio->destdir) + strlen(srcpath) + 8;
if (len >= cpio->pass_destpath_alloc) {
while (len >= cpio->pass_destpath_alloc) {
cpio->pass_destpath_alloc += 512;
cpio->pass_destpath_alloc *= 2;
}
free(cpio->pass_destpath);
cpio->pass_destpath = malloc(cpio->pass_destpath_alloc);
if (cpio->pass_destpath == NULL)
cpio_errc(1, ENOMEM,
"Can't allocate path buffer");
}
strcpy(cpio->pass_destpath, cpio->destdir);
p = srcpath;
while (p[0] == '/')
++p;
strcat(cpio->pass_destpath, p);
destpath = cpio->pass_destpath;
}
if (cpio->option_rename)
destpath = cpio_rename(destpath);
if (destpath == NULL)
return (0);
archive_entry_copy_pathname(entry, destpath);
/*
* If we're trying to preserve hardlinks, match them here.
*/
spare = NULL;
if (cpio->linkresolver != NULL
&& !S_ISDIR(st.st_mode)) {
archive_entry_linkify(cpio->linkresolver, &entry, &spare);
}
if (entry != NULL) {
r = entry_to_archive(cpio, entry);
archive_entry_free(entry);
}
if (spare != NULL) {
if (r == 0)
r = entry_to_archive(cpio, spare);
archive_entry_free(spare);
}
return (r);
}
static int
entry_to_archive(struct cpio *cpio, struct archive_entry *entry)
{
const char *destpath = archive_entry_pathname(entry);
const char *srcpath = archive_entry_sourcepath(entry);
int fd = -1;
ssize_t bytes_read;
int r;
/* Print out the destination name to the user. */
if (cpio->verbose)
fprintf(stderr,"%s", destpath);
/*
* Obviously, this only gets invoked in pass mode.
*/
if (cpio->option_link) {
/* Note: link(2) doesn't create parent directories. */
archive_entry_set_hardlink(entry, srcpath);
r = archive_write_header(cpio->archive, entry);
if (r != ARCHIVE_OK)
cpio_warnc(archive_errno(cpio->archive),
archive_error_string(cpio->archive));
if (r == ARCHIVE_FATAL)
exit(1);
return (0);
}
/*
* Make sure we can open the file (if necessary) before
* trying to write the header.
*/
if (archive_entry_filetype(entry) == AE_IFREG) {
if (archive_entry_size(entry) > 0) {
fd = open(srcpath, O_RDONLY);
if (fd < 0) {
cpio_warnc(errno,
"%s: could not open file", srcpath);
goto cleanup;
}
}
} else {
archive_entry_set_size(entry, 0);
}
r = archive_write_header(cpio->archive, entry);
if (r != ARCHIVE_OK)
cpio_warnc(archive_errno(cpio->archive),
"%s: %s",
destpath,
archive_error_string(cpio->archive));
if (r == ARCHIVE_FATAL)
exit(1);
if (r >= ARCHIVE_WARN && fd >= 0) {
bytes_read = read(fd, cpio->buff, cpio->buff_size);
while (bytes_read > 0) {
r = archive_write_data(cpio->archive,
cpio->buff, bytes_read);
if (r < 0)
cpio_errc(1, archive_errno(cpio->archive),
archive_error_string(cpio->archive));
if (r < bytes_read) {
cpio_warnc(0,
"Truncated write; file may have grown while being archived.");
}
bytes_read = read(fd, cpio->buff, cpio->buff_size);
}
}
restore_time(cpio, entry, srcpath, fd);
cleanup:
if (cpio->verbose)
fprintf(stderr,"\n");
if (fd >= 0)
close(fd);
return (0);
}
static void
restore_time(struct cpio *cpio, struct archive_entry *entry,
const char *name, int fd)
{
#ifndef HAVE_UTIMES
static int warned = 0;
(void)cpio; /* UNUSED */
(void)entry; /* UNUSED */
(void)name; /* UNUSED */
(void)fd; /* UNUSED */
if (!warned)
cpio_warnc(0, "Can't restore access times on this platform");
warned = 1;
return;
#else
struct timeval times[2];
if (!cpio->option_atime_restore)
return;
times[1].tv_sec = archive_entry_mtime(entry);
times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000;
times[0].tv_sec = archive_entry_atime(entry);
times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000;
#ifdef HAVE_FUTIMES
if (fd >= 0 && futimes(fd, times) == 0)
return;
#endif
#ifdef HAVE_LUTIMES
if (lutimes(name, times) != 0)
#else
if (!S_ISLNK(archive_entry_mode(entry)) && utimes(name, times) != 0)
#endif
cpio_warnc(errno, "Can't update time for %s", name);
#endif
}
static void
mode_in(struct cpio *cpio)
{
struct archive *a;
struct archive_entry *entry;
struct archive *ext;
const char *destpath;
unsigned long blocks;
int r;
ext = archive_write_disk_new();
if (ext == NULL)
cpio_errc(1, 0, "Couldn't allocate restore object");
r = archive_write_disk_set_options(ext, cpio->extract_flags);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(ext));
a = archive_read_new();
if (a == NULL)
cpio_errc(1, 0, "Couldn't allocate archive object");
archive_read_support_compression_all(a);
archive_read_support_format_all(a);
if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
cpio_errc(1, archive_errno(a),
archive_error_string(a));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK) {
cpio_errc(1, archive_errno(a),
archive_error_string(a));
}
if (excluded(cpio, archive_entry_pathname(entry)))
continue;
if (cpio->option_rename) {
destpath = cpio_rename(archive_entry_pathname(entry));
archive_entry_set_pathname(entry, destpath);
} else
destpath = archive_entry_pathname(entry);
if (destpath == NULL)
continue;
if (cpio->verbose)
fprintf(stdout, "%s\n", destpath);
if (cpio->uid_override >= 0)
archive_entry_set_uid(entry, cpio->uid_override);
if (cpio->gid_override >= 0)
archive_entry_set_gid(entry, cpio->gid_override);
r = archive_write_header(ext, entry);
if (r != ARCHIVE_OK) {
fprintf(stderr, "%s: %s\n",
archive_entry_pathname(entry),
archive_error_string(ext));
} else if (archive_entry_size(entry) > 0) {
r = copy_data(a, ext);
}
}
r = archive_read_close(a);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(a));
r = archive_write_close(ext);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(ext));
if (!cpio->quiet) {
blocks = (archive_position_uncompressed(a) + 511)
/ 512;
fprintf(stderr, "%lu %s\n", blocks,
blocks == 1 ? "block" : "blocks");
}
archive_read_finish(a);
archive_write_finish(ext);
exit(0);
}
static int
copy_data(struct archive *ar, struct archive *aw)
{
int r;
size_t size;
const void *block;
off_t offset;
for (;;) {
r = archive_read_data_block(ar, &block, &size, &offset);
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r != ARCHIVE_OK) {
cpio_warnc(archive_errno(ar),
"%s", archive_error_string(ar));
return (r);
}
r = archive_write_data_block(aw, block, size, offset);
if (r != ARCHIVE_OK) {
cpio_warnc(archive_errno(aw),
archive_error_string(aw));
return (r);
}
}
}
static void
mode_list(struct cpio *cpio)
{
struct archive *a;
struct archive_entry *entry;
unsigned long blocks;
int r;
a = archive_read_new();
if (a == NULL)
cpio_errc(1, 0, "Couldn't allocate archive object");
archive_read_support_compression_all(a);
archive_read_support_format_all(a);
if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
cpio_errc(1, archive_errno(a),
archive_error_string(a));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK) {
cpio_errc(1, archive_errno(a),
archive_error_string(a));
}
if (excluded(cpio, archive_entry_pathname(entry)))
continue;
if (cpio->verbose) {
/* TODO: uname/gname lookups */
/* TODO: Clean this up. */
fprintf(stdout,
"%s%3d %8s%8s " CPIO_FILESIZE_PRINTF " %s\n",
archive_entry_strmode(entry),
archive_entry_nlink(entry),
archive_entry_uname(entry),
archive_entry_gname(entry),
(CPIO_FILESIZE_TYPE)archive_entry_size(entry),
archive_entry_pathname(entry));
} else
fprintf(stdout, "%s\n", archive_entry_pathname(entry));
}
r = archive_read_close(a);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(a));
if (!cpio->quiet) {
blocks = (archive_position_uncompressed(a) + 511)
/ 512;
fprintf(stderr, "%lu %s\n", blocks,
blocks == 1 ? "block" : "blocks");
}
archive_read_finish(a);
exit(0);
}
static void
mode_pass(struct cpio *cpio, const char *destdir)
{
int r;
/* Ensure target dir has a trailing '/' to simplify path surgery. */
cpio->destdir = malloc(strlen(destdir) + 8);
strcpy(cpio->destdir, destdir);
if (destdir[strlen(destdir) - 1] != '/')
strcat(cpio->destdir, "/");
cpio->archive = archive_write_disk_new();
if (cpio->archive == NULL)
cpio_errc(1, 0, "Failed to allocate archive object");
r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(cpio->archive));
cpio->linkresolver = archive_entry_linkresolver_new();
archive_write_disk_set_standard_lookup(cpio->archive);
process_lines(cpio, "-", file_to_archive);
archive_entry_linkresolver_free(cpio->linkresolver);
r = archive_write_close(cpio->archive);
if (r != ARCHIVE_OK)
cpio_errc(1, 0, archive_error_string(cpio->archive));
archive_write_finish(cpio->archive);
}
/*
* Prompt for a new name for this entry. Returns a pointer to the
* new name or NULL if the entry should not be copied. This
* implements the semantics defined in POSIX.1-1996, which specifies
* that an input of '.' means the name should be unchanged. GNU cpio
* treats '.' as a literal new name.
*/
static const char *
cpio_rename(const char *name)
{
static char buff[1024];
FILE *t;
char *p, *ret;
t = fopen("/dev/tty", "r+");
if (t == NULL)
return (name);
fprintf(t, "%s (Enter/./(new name))? ", name);
fflush(t);
p = fgets(buff, sizeof(buff), t);
fclose(t);
if (p == NULL)
/* End-of-file is a blank line. */
return (NULL);
while (*p == ' ' || *p == '\t')
++p;
if (*p == '\n' || *p == '\0')
/* Empty line. */
return (NULL);
if (*p == '.' && p[1] == '\n')
/* Single period preserves original name. */
return (name);
ret = p;
/* Trim the final newline. */
while (*p != '\0' && *p != '\n')
++p;
/* Overwrite the final \n with a null character. */
*p = '\0';
return (ret);
}
/*
* Read lines from file and do something with each one. If option_null
* is set, lines are terminated with zero bytes; otherwise, they're
* terminated with newlines.
*
* This uses a self-sizing buffer to handle arbitrarily-long lines.
* If the "process" function returns non-zero for any line, this
* function will return non-zero after attempting to process all
* remaining lines.
*/
int
process_lines(struct cpio *cpio, const char *pathname,
int (*process)(struct cpio *, const char *))
{
FILE *f;
char *buff, *buff_end, *line_start, *line_end, *p;
size_t buff_length, bytes_read, bytes_wanted;
int separator;
int ret;
separator = cpio->option_null ? '\0' : '\n';
ret = 0;
if (strcmp(pathname, "-") == 0)
f = stdin;
else
f = fopen(pathname, "r");
if (f == NULL)
cpio_errc(1, errno, "Couldn't open %s", pathname);
buff_length = 8192;
buff = malloc(buff_length);
if (buff == NULL)
cpio_errc(1, ENOMEM, "Can't read %s", pathname);
line_start = line_end = buff_end = buff;
for (;;) {
/* Get some more data into the buffer. */
bytes_wanted = buff + buff_length - buff_end;
bytes_read = fread(buff_end, 1, bytes_wanted, f);
buff_end += bytes_read;
/* Process all complete lines in the buffer. */
while (line_end < buff_end) {
if (*line_end == separator) {
*line_end = '\0';
if ((*process)(cpio, line_start) != 0)
ret = -1;
line_start = line_end + 1;
line_end = line_start;
} else
line_end++;
}
if (feof(f))
break;
if (ferror(f))
cpio_errc(1, errno, "Can't read %s", pathname);
if (line_start > buff) {
/* Move a leftover fractional line to the beginning. */
memmove(buff, line_start, buff_end - line_start);
buff_end -= line_start - buff;
line_end -= line_start - buff;
line_start = buff;
} else {
/* Line is too big; enlarge the buffer. */
p = realloc(buff, buff_length *= 2);
if (p == NULL)
cpio_errc(1, ENOMEM,
"Line too long in %s", pathname);
buff_end = p + (buff_end - buff);
line_end = p + (line_end - buff);
line_start = buff = p;
}
}
/* At end-of-file, handle the final line. */
if (line_end > line_start) {
*line_end = '\0';
if ((*process)(cpio, line_start) != 0)
ret = -1;
}
free(buff);
if (f != stdin)
fclose(f);
return (ret);
}

106
usr.bin/cpio/cpio.h Normal file
View File

@ -0,0 +1,106 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef CPIO_H_INCLUDED
#define CPIO_H_INCLUDED
#include "cpio_platform.h"
#include <stdio.h>
#define DEFAULT_BYTES_PER_BLOCK (20*512)
/*
* The internal state for the "cpio" program.
*
* Keeping all of the state in a structure like this simplifies memory
* leak testing (at exit, anything left on the heap is suspect). A
* pointer to this structure is passed to most cpio internal
* functions.
*/
struct cpio {
/* Options */
char *filename;
char mode; /* -i -o -p */
char compress; /* -j, -y, or -z */
const char *format; /* -H format */
int bytes_per_block; /* -b block_size */
int verbose; /* -v */
int quiet; /* --quiet */
int extract_flags; /* Flags for extract operation */
char symlink_mode; /* H or L, per BSD conventions */
const char *compress_program;
int option_append; /* -A, only relevant for -o */
int option_atime_restore; /* -a */
int option_follow_links; /* -L */
int option_link; /* -l */
int option_list; /* -t */
int option_null; /* -0 --null */
int option_rename; /* -r */
char *destdir;
size_t pass_destpath_alloc;
char *pass_destpath;
int uid_override;
int gid_override;
/* If >= 0, then close this when done. */
int fd;
/* Miscellaneous state information */
struct archive *archive;
int argc;
char **argv;
int return_value; /* Value returned by main() */
struct archive_entry_linkresolver *linkresolver;
/* Work data. */
struct matching *matching;
char *buff;
size_t buff_size;
};
/* Name of this program; used in error reporting, initialized in main(). */
const char *cpio_progname;
void cpio_errc(int _eval, int _code, const char *fmt, ...);
void cpio_warnc(int _code, const char *fmt, ...);
int owner_parse(const char *, int *, int *);
/* Fake short equivalents for long options that otherwise lack them. */
enum {
OPTION_INSECURE = 1,
OPTION_QUIET,
OPTION_VERSION
};
int cpio_getopt(struct cpio *cpio);
int process_lines(struct cpio *cpio, const char *pathname,
int (*process)(struct cpio *, const char *));
int include_from_file(struct cpio *, const char *);
#endif

View File

@ -0,0 +1,84 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* This header is the first thing included in any of the cpio
* source files. As far as possible, platform-specific issues should
* be dealt with here and not within individual source files.
*/
#ifndef CPIO_PLATFORM_H_INCLUDED
#define CPIO_PLATFORM_H_INCLUDED
#if defined(PLATFORM_CONFIG_H)
/* Use hand-built config.h in environments that need it. */
#include PLATFORM_CONFIG_H
#elif defined(HAVE_CONFIG_H)
/* Most POSIX platforms use the 'configure' script to build config.h */
#include "../config.h"
#else
/* Warn if cpio hasn't been (automatically or manually) configured. */
#error Oops: No config.h and no built-in configuration in cpio_platform.h.
#endif /* !HAVE_CONFIG_H */
/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
#ifdef __FreeBSD__
#include <sys/cdefs.h> /* For __FBSDID */
#else
/* Just leaving this macro replacement empty leads to a dangling semicolon. */
#define __FBSDID(a) struct _undefined_hack
#endif
#ifdef HAVE_LIBARCHIVE
/* If we're using the platform libarchive, include system headers. */
#include <archive.h>
#include <archive_entry.h>
#else
/* Otherwise, include user headers. */
#include "archive.h"
#include "archive_entry.h"
#endif
/*
* We need to be able to display a filesize using printf(). The type
* and format string here must be compatible with one another and
* large enough for any file.
*/
#if HAVE_UINTMAX_T
#define CPIO_FILESIZE_TYPE uintmax_t
#define CPIO_FILESIZE_PRINTF "%ju"
#else
#if HAVE_UNSIGNED_LONG_LONG
#define CPIO_FILESIZE_TYPE unsigned long long
#define CPIO_FILESIZE_PRINTF "%llu"
#else
#define CPIO_FILESIZE_TYPE unsigned long
#define CPIO_FILESIZE_PRINTF "%lu"
#endif
#endif
#endif /* !CPIO_PLATFORM_H_INCLUDED */

73
usr.bin/cpio/err.c Normal file
View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cpio_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "cpio.h"
static void
cpio_vwarnc(int code, const char *fmt, va_list ap)
{
fprintf(stderr, "%s: ", cpio_progname);
vfprintf(stderr, fmt, ap);
if (code != 0)
fprintf(stderr, ": %s", strerror(code));
fprintf(stderr, "\n");
}
void
cpio_warnc(int code, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
cpio_vwarnc(code, fmt, ap);
va_end(ap);
}
void
cpio_errc(int eval, int code, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
cpio_vwarnc(code, fmt, ap);
va_end(ap);
exit(eval);
}

250
usr.bin/cpio/matching.c Normal file
View File

@ -0,0 +1,250 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cpio_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "matching.h"
#include "pathmatch.h"
struct match {
struct match *next;
int matches;
char pattern[1];
};
struct matching {
struct match *exclusions;
int exclusions_count;
struct match *inclusions;
int inclusions_count;
int inclusions_unmatched_count;
};
static void add_pattern(struct match **list, const char *pattern);
static void initialize_matching(struct cpio *);
static int match_exclusion(struct match *, const char *pathname);
static int match_inclusion(struct match *, const char *pathname);
/*
* The matching logic here needs to be re-thought. I started out to
* try to mimic gtar's matching logic, but it's not entirely
* consistent. In particular 'tar -t' and 'tar -x' interpret patterns
* on the command line as anchored, but --exclude doesn't.
*/
/*
* Utility functions to manage exclusion/inclusion patterns
*/
int
exclude(struct cpio *cpio, const char *pattern)
{
struct matching *matching;
if (cpio->matching == NULL)
initialize_matching(cpio);
matching = cpio->matching;
add_pattern(&(matching->exclusions), pattern);
matching->exclusions_count++;
return (0);
}
#if 0
int
exclude_from_file(struct cpio *cpio, const char *pathname)
{
return (process_lines(cpio, pathname, &exclude));
}
#endif
int
include(struct cpio *cpio, const char *pattern)
{
struct matching *matching;
if (cpio->matching == NULL)
initialize_matching(cpio);
matching = cpio->matching;
add_pattern(&(matching->inclusions), pattern);
matching->inclusions_count++;
matching->inclusions_unmatched_count++;
return (0);
}
int
include_from_file(struct cpio *cpio, const char *pathname)
{
return (process_lines(cpio, pathname, &include));
}
static void
add_pattern(struct match **list, const char *pattern)
{
struct match *match;
match = malloc(sizeof(*match) + strlen(pattern) + 1);
if (match == NULL)
cpio_errc(1, errno, "Out of memory");
if (pattern[0] == '/')
pattern++;
strcpy(match->pattern, pattern);
/* Both "foo/" and "foo" should match "foo/bar". */
if (match->pattern[strlen(match->pattern)-1] == '/')
match->pattern[strlen(match->pattern)-1] = '\0';
match->next = *list;
*list = match;
match->matches = 0;
}
int
excluded(struct cpio *cpio, const char *pathname)
{
struct matching *matching;
struct match *match;
struct match *matched;
matching = cpio->matching;
if (matching == NULL)
return (0);
/* Exclusions take priority */
for (match = matching->exclusions; match != NULL; match = match->next){
if (match_exclusion(match, pathname))
return (1);
}
/* Then check for inclusions */
matched = NULL;
for (match = matching->inclusions; match != NULL; match = match->next){
if (match_inclusion(match, pathname)) {
/*
* If this pattern has never been matched,
* then we're done.
*/
if (match->matches == 0) {
match->matches++;
matching->inclusions_unmatched_count++;
return (0);
}
/*
* Otherwise, remember the match but keep checking
* in case we can tick off an unmatched pattern.
*/
matched = match;
}
}
/*
* We didn't find a pattern that had never been matched, but
* we did find a match, so count it and exit.
*/
if (matched != NULL) {
matched->matches++;
return (0);
}
/* If there were inclusions, default is to exclude. */
if (matching->inclusions != NULL)
return (1);
/* No explicit inclusions, default is to match. */
return (0);
}
/*
* This is a little odd, but it matches the default behavior of
* gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
*
*/
int
match_exclusion(struct match *match, const char *pathname)
{
return (pathmatch(match->pattern,
pathname,
PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
}
/*
* Again, mimic gtar: inclusions are always anchored (have to match
* the beginning of the path) even though exclusions are not anchored.
*/
int
match_inclusion(struct match *match, const char *pathname)
{
return (pathmatch(match->pattern, pathname, 0));
}
void
cleanup_exclusions(struct cpio *cpio)
{
struct match *p, *q;
if (cpio->matching) {
p = cpio->matching->inclusions;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
p = cpio->matching->exclusions;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
free(cpio->matching);
}
}
static void
initialize_matching(struct cpio *cpio)
{
cpio->matching = malloc(sizeof(*cpio->matching));
if (cpio->matching == NULL)
cpio_errc(1, errno, "No memory");
memset(cpio->matching, 0, sizeof(*cpio->matching));
}
int
unmatched_inclusions(struct cpio *cpio)
{
struct matching *matching;
matching = cpio->matching;
if (matching == NULL)
return (0);
return (matching->inclusions_unmatched_count);
}

40
usr.bin/cpio/matching.h Normal file
View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef MATCHING_H
#define MATCHING_H
#include "cpio.h"
int exclude(struct cpio *, const char *pattern);
int include(struct cpio *, const char *pattern);
int excluded(struct cpio *cpio, const char *pathname);
void cleanup_exclusions(struct cpio *cpio);
int unmatched_inclusions(struct cpio *cpio);
#endif

250
usr.bin/cpio/pathmatch.c Normal file
View File

@ -0,0 +1,250 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cpio_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "pathmatch.h"
/*
* Check whether a character 'c' is matched by a list specification [...]:
* * Leading '!' negates the class.
* * <char>-<char> is a range of characters
* * \<char> removes any special meaning for <char>
*
* Some interesting boundary cases:
* a-d-e is one range (a-d) followed by two single characters - and e.
* \a-\d is same as a-d
* a\-d is three single characters: a, d, -
* Trailing - is not special (so [a-] is two characters a and -).
* Initial - is not special ([a-] is same as [-a] is same as [\\-a])
* This function never sees a trailing \.
* [] always fails
* [!] always succeeds
*/
static int
pm_list(const char *start, const char *end, const char c, int flags)
{
const char *p = start;
char rangeStart = '\0', nextRangeStart;
int match = 1, nomatch = 0;
/* This will be used soon... */
(void)flags; /* UNUSED */
/* If this is a negated class, return success for nomatch. */
if (*p == '!' && p < end) {
match = 0;
nomatch = 1;
++p;
}
while (p < end) {
nextRangeStart = '\0';
switch (*p) {
case '-':
/* Trailing or initial '-' is not special. */
if ((rangeStart == '\0') || (p == end - 1)) {
if (*p == c)
return (match);
} else {
char rangeEnd = *++p;
if (rangeEnd == '\\')
rangeEnd = *++p;
if ((rangeStart <= c) && (c <= rangeEnd))
return (match);
}
break;
case '\\':
++p;
/* Fall through */
default:
if (*p == c)
return (match);
nextRangeStart = *p; /* Possible start of range. */
}
rangeStart = nextRangeStart;
++p;
}
return (nomatch);
}
/*
* If s is pointing to "./", ".//", "./././" or the like, skip it.
*/
static const char *
pm_slashskip(const char *s) {
while (*s == '.' || *s == '/') {
if (s[0] != '/' && s[1] != '/')
break;
++s;
}
return (s);
}
static int
pm(const char *p, const char *s, int flags)
{
const char *end;
/*
* Ignore leading './', './/', '././', etc.
*/
if (s[0] == '.' && s[1] == '/')
s = pm_slashskip(s + 1);
if (p[0] == '.' && p[1] == '/')
p = pm_slashskip(p + 1);
for (;;) {
switch (*p) {
case '\0':
if (s[0] == '/') {
if (flags & PATHMATCH_NO_ANCHOR_END)
return (1);
/* "dir" == "dir/" == "dir/." */
s = pm_slashskip(s);
if (s[0] == '.' && s[1] == '\0')
return (1);
}
return (*s == '\0');
break;
case '?':
/* ? always succeds, unless we hit end of 's' */
if (*s == '\0')
return (0);
break;
case '*':
/* "*" == "**" == "***" ... */
while (*p == '*')
++p;
/* Trailing '*' always succeeds. */
if (*p == '\0')
return (1);
while (*s) {
if (pathmatch(p, s, flags))
return (1);
++s;
}
return (0);
break;
case '[':
/*
* Find the end of the [...] character class,
* ignoring \] that might occur within the class.
*/
end = p + 1;
while (*end != '\0' && *end != ']') {
if (*end == '\\' && end[1] != '\0')
++end;
++end;
}
if (*end == ']') {
/* We found [...], try to match it. */
if (!pm_list(p + 1, end, *s, flags))
return (0);
p = end; /* Jump to trailing ']' char. */
break;
} else
/* No final ']', so just match '['. */
if (*p != *s)
return (0);
break;
default:
if (*p == *s)
break;
if ((*s == '\0') && (*p == '/')) {
p = pm_slashskip(p);
if (*p == '\0')
return (1);
if (p[0] == '.' && p[1] == '\0')
return (1);
return (0);
}
return (0);
break;
case '\\':
/* Trailing '\\' matches itself. */
if (p[1] == '\0') {
if (*s != '\\')
return (0);
} else {
++p;
if (*p != *s)
return (0);
}
break;
}
/*
* TODO: pattern of "\/\.\/" should not match plain "/",
* it should only match explicit "/./".
*/
if (*p == '/')
p = pm_slashskip(p);
else
++p;
if (*s == '/')
s = pm_slashskip(s);
else
++s;
}
}
/* Main entry point. */
int
pathmatch(const char *p, const char *s, int flags)
{
/* Empty pattern only matches the empty string. */
if (p == NULL || *p == '\0')
return (s == NULL || *s == '\0');
/* Leading '^' anchors the start of the pattern. */
if (*p == '^') {
++p;
flags &= ~PATHMATCH_NO_ANCHOR_START;
}
/* Certain patterns anchor implicitly. */
if (*p == '*' || *p == '/')
return (pm(p, s, flags));
/* If start is unanchored, try to match start of each path element. */
if (flags & PATHMATCH_NO_ANCHOR_START) {
for ( ; p != NULL; p = strchr(p, '/')) {
if (*p == '/')
p++;
if (pm(p, s, flags))
return (1);
}
return (0);
}
/* Default: Match from beginning. */
return (pm(p, s, flags));
}

37
usr.bin/cpio/pathmatch.h Normal file
View File

@ -0,0 +1,37 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef PATHMATCH_H
#define PATHMATCH_H
#define PATHMATCH_NO_ANCHOR_START 1
#define PATHMATCH_NO_ANCHOR_END 2
int pathmatch(const char *p, const char *s, int flags);
#endif

View File

@ -0,0 +1,72 @@
# $FreeBSD$
# Where to find the cpio sources (for the internal unit tests)
CPIO_SRCDIR=${.CURDIR}/..
.PATH: ${CPIO_SRCDIR}
# Some cpio sources are pulled in for white-box tests
CPIO_SRCS= cmdline.c err.c pathmatch.c
TESTS= \
test_0.c \
test_basic.c \
test_format_newc.c \
test_gcpio_compat.c \
test_option_a.c \
test_option_B.c \
test_option_c.c \
test_option_d.c \
test_option_f.c \
test_option_help.c \
test_option_L.c \
test_option_ell.c \
test_option_m.c \
test_option_t.c \
test_option_u.c \
test_option_version.c \
test_option_y.c \
test_option_z.c \
test_owner_parse.c \
test_pathmatch.c \
# Build the test program
SRCS= list.h \
${CPIO_SRCS} \
${TESTS} \
main.c
CLEANFILES+= list.h
NO_MAN=yes
PROG=bsdcpio_test
DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\"
CFLAGS+= -I..
LDADD= -larchive -lz -lbz2
CFLAGS+= -static -g -O2 -Wall
CFLAGS+= -I${.OBJDIR}
CFLAGS+= -I${CPIO_SRCDIR}
# Uncomment to link against dmalloc
#LDADD+= -L/usr/local/lib -ldmalloc
#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
WARNS=6
check test: bsdcpio_test
${.OBJDIR}/bsdcpio_test -p ${.OBJDIR}/../bsdcpio -r ${.CURDIR}
${.OBJDIR}/list.h list.h: ${TESTS} Makefile
(cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
clean:
rm -f *.out
rm -f *.o
rm -f *.core
rm -f *~
rm -f list.h
rm -f archive.h ../archive.h
-chmod -R +w /tmp/bsdcpio_test.*
rm -rf /tmp/bsdcpio_test.*
.include <bsd.prog.mk>

970
usr.bin/cpio/test/main.c Normal file
View File

@ -0,0 +1,970 @@
/*
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Various utility routines useful for test programs.
* Each test program is linked against this file.
*/
#include <errno.h>
#include <locale.h>
#include <stdarg.h>
#include <time.h>
#include "test.h"
/*
* This same file is used pretty much verbatim for all test harnesses.
*
* The next few lines are the only differences.
*/
#define PROGRAM "bsdcpio" /* Name of program being tested. */
#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */
#undef EXTRA_DUMP /* How to dump extra data */
/* How to generate extra version info. */
#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "")
__FBSDID("$FreeBSD$");
/*
* "list.h" is simply created by "grep DEFINE_TEST"; it has
* a line like
* DEFINE_TEST(test_function)
* for each test.
* Include it here with a suitable DEFINE_TEST to declare all of the
* test functions.
*/
#undef DEFINE_TEST
#define DEFINE_TEST(name) void name(void);
#include "list.h"
/* Interix doesn't define these in a standard header. */
#if __INTERIX__
extern char *optarg;
extern int optind;
#endif
/* Enable core dump on failure. */
static int dump_on_failure = 0;
/* Default is to remove temp dirs for successful tests. */
static int keep_temp_files = 0;
/* Default is to print some basic information about each test. */
static int quiet_flag = 0;
/* Default is to summarize repeated failures. */
static int verbose = 0;
/* Cumulative count of component failures. */
static int failures = 0;
/* Cumulative count of skipped component tests. */
static int skips = 0;
/* Cumulative count of assertions. */
static int assertions = 0;
/* Directory where uuencoded reference files can be found. */
static char *refdir;
/*
* My own implementation of the standard assert() macro emits the
* message in the same format as GCC (file:line: message).
* It also includes some additional useful information.
* This makes it a lot easier to skim through test failures in
* Emacs. ;-)
*
* It also supports a few special features specifically to simplify
* test harnesses:
* failure(fmt, args) -- Stores a text string that gets
* printed if the following assertion fails, good for
* explaining subtle tests.
*/
static char msg[4096];
/*
* For each test source file, we remember how many times each
* failure was reported.
*/
static const char *failed_filename = NULL;
static struct line {
int line;
int count;
} failed_lines[1000];
/*
* Count this failure; return the number of previous failures.
*/
static int
previous_failures(const char *filename, int line)
{
unsigned int i;
int count;
if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
memset(failed_lines, 0, sizeof(failed_lines));
failed_filename = filename;
for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
if (failed_lines[i].line == line) {
count = failed_lines[i].count;
failed_lines[i].count++;
return (count);
}
if (failed_lines[i].line == 0) {
failed_lines[i].line = line;
failed_lines[i].count = 1;
return (0);
}
}
return (0);
}
/*
* Copy arguments into file-local variables.
*/
static const char *test_filename;
static int test_line;
static void *test_extra;
void test_setup(const char *filename, int line)
{
test_filename = filename;
test_line = line;
}
/*
* Inform user that we're skipping a test.
*/
void
test_skipping(const char *fmt, ...)
{
va_list ap;
if (previous_failures(test_filename, test_line))
return;
va_start(ap, fmt);
fprintf(stderr, " *** SKIPPING: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
++skips;
}
/* Common handling of failed tests. */
static void
report_failure(void *extra)
{
if (msg[0] != '\0') {
fprintf(stderr, " Description: %s\n", msg);
msg[0] = '\0';
}
#ifdef EXTRA_DUMP
if (extra != NULL)
fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra));
#else
(void)extra; /* UNUSED */
#endif
if (dump_on_failure) {
fprintf(stderr,
" *** forcing core dump so failure can be debugged ***\n");
*(char *)(NULL) = 0;
exit(1);
}
}
/*
* Summarize repeated failures in the just-completed test file.
* The reports above suppress multiple failures from the same source
* line; this reports on any tests that did fail multiple times.
*/
static int
summarize_comparator(const void *a0, const void *b0)
{
const struct line *a = a0, *b = b0;
if (a->line == 0 && b->line == 0)
return (0);
if (a->line == 0)
return (1);
if (b->line == 0)
return (-1);
return (a->line - b->line);
}
static void
summarize(void)
{
unsigned int i;
qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
sizeof(failed_lines[0]), summarize_comparator);
for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
if (failed_lines[i].line == 0)
break;
if (failed_lines[i].count > 1)
fprintf(stderr, "%s:%d: Failed %d times\n",
failed_filename, failed_lines[i].line,
failed_lines[i].count);
}
/* Clear the failure history for the next file. */
memset(failed_lines, 0, sizeof(failed_lines));
}
/* Set up a message to display only after a test fails. */
void
failure(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsprintf(msg, fmt, ap);
va_end(ap);
}
/* Generic assert() just displays the failed condition. */
int
test_assert(const char *file, int line, int value, const char *condition, void *extra)
{
++assertions;
if (value) {
msg[0] = '\0';
return (value);
}
failures ++;
if (!verbose && previous_failures(file, line))
return (value);
fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
fprintf(stderr, " Condition: %s\n", condition);
report_failure(extra);
return (value);
}
/* assertEqualInt() displays the values of the two integers. */
int
test_assert_equal_int(const char *file, int line,
int v1, const char *e1, int v2, const char *e2, void *extra)
{
++assertions;
if (v1 == v2) {
msg[0] = '\0';
return (1);
}
failures ++;
if (!verbose && previous_failures(file, line))
return (0);
fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
file, line);
fprintf(stderr, " %s=%d\n", e1, v1);
fprintf(stderr, " %s=%d\n", e2, v2);
report_failure(extra);
return (0);
}
static void strdump(const char *p)
{
if (p == NULL) {
fprintf(stderr, "(null)");
return;
}
fprintf(stderr, "\"");
while (*p != '\0') {
unsigned int c = 0xff & *p++;
switch (c) {
case '\a': fprintf(stderr, "\a"); break;
case '\b': fprintf(stderr, "\b"); break;
case '\n': fprintf(stderr, "\n"); break;
case '\r': fprintf(stderr, "\r"); break;
default:
if (c >= 32 && c < 127)
fprintf(stderr, "%c", c);
else
fprintf(stderr, "\\x%02X", c);
}
}
fprintf(stderr, "\"");
}
/* assertEqualString() displays the values of the two strings. */
int
test_assert_equal_string(const char *file, int line,
const char *v1, const char *e1,
const char *v2, const char *e2,
void *extra)
{
++assertions;
if (v1 == NULL || v2 == NULL) {
if (v1 == v2) {
msg[0] = '\0';
return (1);
}
} else if (strcmp(v1, v2) == 0) {
msg[0] = '\0';
return (1);
}
failures ++;
if (!verbose && previous_failures(file, line))
return (0);
fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
file, line);
fprintf(stderr, " %s = ", e1);
strdump(v1);
fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1));
fprintf(stderr, " %s = ", e2);
strdump(v2);
fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2));
report_failure(extra);
return (0);
}
static void wcsdump(const wchar_t *w)
{
if (w == NULL) {
fprintf(stderr, "(null)");
return;
}
fprintf(stderr, "\"");
while (*w != L'\0') {
unsigned int c = *w++;
if (c >= 32 && c < 127)
fprintf(stderr, "%c", c);
else if (c < 256)
fprintf(stderr, "\\x%02X", c);
else if (c < 0x10000)
fprintf(stderr, "\\u%04X", c);
else
fprintf(stderr, "\\U%08X", c);
}
fprintf(stderr, "\"");
}
/* assertEqualWString() displays the values of the two strings. */
int
test_assert_equal_wstring(const char *file, int line,
const wchar_t *v1, const char *e1,
const wchar_t *v2, const char *e2,
void *extra)
{
++assertions;
if (v1 == NULL) {
if (v2 == NULL) {
msg[0] = '\0';
return (1);
}
} else if (v2 == NULL) {
if (v1 == NULL) {
msg[0] = '\0';
return (1);
}
} else if (wcscmp(v1, v2) == 0) {
msg[0] = '\0';
return (1);
}
failures ++;
if (!verbose && previous_failures(file, line))
return (0);
fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
file, line);
fprintf(stderr, " %s = ", e1);
wcsdump(v1);
fprintf(stderr, "\n");
fprintf(stderr, " %s = ", e2);
wcsdump(v2);
fprintf(stderr, "\n");
report_failure(extra);
return (0);
}
/*
* Pretty standard hexdump routine. As a bonus, if ref != NULL, then
* any bytes in p that differ from ref will be highlighted with '_'
* before and after the hex value.
*/
static void
hexdump(const char *p, const char *ref, size_t l, size_t offset)
{
size_t i, j;
char sep;
for(i=0; i < l; i+=16) {
fprintf(stderr, "%04x", i + offset);
sep = ' ';
for (j = 0; j < 16 && i + j < l; j++) {
if (ref != NULL && p[i + j] != ref[i + j])
sep = '_';
fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]);
if (ref != NULL && p[i + j] == ref[i + j])
sep = ' ';
}
for (; j < 16; j++) {
fprintf(stderr, "%c ", sep);
sep = ' ';
}
fprintf(stderr, "%c", sep);
for (j=0; j < 16 && i + j < l; j++) {
int c = p[i + j];
if (c >= ' ' && c <= 126)
fprintf(stderr, "%c", c);
else
fprintf(stderr, ".");
}
fprintf(stderr, "\n");
}
}
/* assertEqualMem() displays the values of the two memory blocks. */
/* TODO: For long blocks, hexdump the first bytes that actually differ. */
int
test_assert_equal_mem(const char *file, int line,
const char *v1, const char *e1,
const char *v2, const char *e2,
size_t l, const char *ld, void *extra)
{
++assertions;
if (v1 == NULL || v2 == NULL) {
if (v1 == v2) {
msg[0] = '\0';
return (1);
}
} else if (memcmp(v1, v2, l) == 0) {
msg[0] = '\0';
return (1);
}
failures ++;
if (!verbose && previous_failures(file, line))
return (0);
fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
file, line);
fprintf(stderr, " size %s = %d\n", ld, (int)l);
fprintf(stderr, " Dump of %s\n", e1);
hexdump(v1, v2, l < 32 ? l : 32, 0);
fprintf(stderr, " Dump of %s\n", e2);
hexdump(v2, v1, l < 32 ? l : 32, 0);
fprintf(stderr, "\n");
report_failure(extra);
return (0);
}
int
test_assert_empty_file(const char *f1fmt, ...)
{
char buff[1024];
char f1[1024];
struct stat st;
va_list ap;
ssize_t s;
int fd;
va_start(ap, f1fmt);
vsprintf(f1, f1fmt, ap);
va_end(ap);
if (stat(f1, &st) != 0) {
fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
report_failure(NULL);
return (0);
}
if (st.st_size == 0)
return (1);
failures ++;
if (!verbose && previous_failures(test_filename, test_line))
return (0);
fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
fprintf(stderr, " File size: %d\n", (int)st.st_size);
fprintf(stderr, " Contents:\n");
fd = open(f1, O_RDONLY);
if (fd < 0) {
fprintf(stderr, " Unable to open %s\n", f1);
} else {
s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size;
s = read(fd, buff, s);
hexdump(buff, NULL, s, 0);
}
report_failure(NULL);
return (0);
}
/* assertEqualFile() asserts that two files have the same contents. */
/* TODO: hexdump the first bytes that actually differ. */
int
test_assert_equal_file(const char *f1, const char *f2pattern, ...)
{
char f2[1024];
va_list ap;
char buff1[1024];
char buff2[1024];
int fd1, fd2;
int n1, n2;
va_start(ap, f2pattern);
vsprintf(f2, f2pattern, ap);
va_end(ap);
fd1 = open(f1, O_RDONLY);
fd2 = open(f2, O_RDONLY);
for (;;) {
n1 = read(fd1, buff1, sizeof(buff1));
n2 = read(fd2, buff2, sizeof(buff2));
if (n1 != n2)
break;
if (n1 == 0 && n2 == 0)
return (1);
if (memcmp(buff1, buff2, n1) != 0)
break;
}
failures ++;
if (!verbose && previous_failures(test_filename, test_line))
return (0);
fprintf(stderr, "%s:%d: Files are not identical\n",
test_filename, test_line);
fprintf(stderr, " file1=\"%s\"\n", f1);
fprintf(stderr, " file2=\"%s\"\n", f2);
report_failure(test_extra);
return (0);
}
/* assertFileContents() asserts the contents of a file. */
int
test_assert_file_contents(const void *buff, int s, const char *fpattern, ...)
{
char f[1024];
va_list ap;
char *contents;
int fd;
int n;
va_start(ap, fpattern);
vsprintf(f, fpattern, ap);
va_end(ap);
fd = open(f, O_RDONLY);
contents = malloc(s * 2);
n = read(fd, contents, s * 2);
if (n == s && memcmp(buff, contents, s) == 0) {
free(contents);
return (1);
}
failures ++;
if (!previous_failures(test_filename, test_line)) {
fprintf(stderr, "%s:%d: File contents don't match\n",
test_filename, test_line);
fprintf(stderr, " file=\"%s\"\n", f);
if (n > 0)
hexdump(contents, buff, n, 0);
else {
fprintf(stderr, " File empty, contents should be:\n");
hexdump(buff, NULL, s, 0);
}
report_failure(test_extra);
}
free(contents);
return (0);
}
/*
* Call standard system() call, but build up the command line using
* sprintf() conventions.
*/
int
systemf(const char *fmt, ...)
{
char buff[8192];
va_list ap;
int r;
va_start(ap, fmt);
vsprintf(buff, fmt, ap);
r = system(buff);
va_end(ap);
return (r);
}
/*
* Slurp a file into memory for ease of comparison and testing.
* Returns size of file in 'sizep' if non-NULL, null-terminates
* data in memory for ease of use.
*/
char *
slurpfile(size_t * sizep, const char *fmt, ...)
{
char filename[8192];
struct stat st;
va_list ap;
char *p;
ssize_t bytes_read;
int fd;
int r;
va_start(ap, fmt);
vsprintf(filename, fmt, ap);
va_end(ap);
fd = open(filename, O_RDONLY);
if (fd < 0) {
/* Note: No error; non-existent file is okay here. */
return (NULL);
}
r = fstat(fd, &st);
if (r != 0) {
fprintf(stderr, "Can't stat file %s\n", filename);
close(fd);
return (NULL);
}
p = malloc(st.st_size + 1);
if (p == NULL) {
fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename);
close(fd);
return (NULL);
}
bytes_read = read(fd, p, st.st_size);
if (bytes_read < st.st_size) {
fprintf(stderr, "Can't read file %s\n", filename);
close(fd);
free(p);
return (NULL);
}
p[st.st_size] = '\0';
if (sizep != NULL)
*sizep = (size_t)st.st_size;
close(fd);
return (p);
}
/*
* "list.h" is automatically generated; it just has a lot of lines like:
* DEFINE_TEST(function_name)
* It's used above to declare all of the test functions.
* We reuse it here to define a list of all tests (functions and names).
*/
#undef DEFINE_TEST
#define DEFINE_TEST(n) { n, #n },
struct { void (*func)(void); const char *name; } tests[] = {
#include "list.h"
};
/*
* Each test is run in a private work dir. Those work dirs
* do have consistent and predictable names, in case a group
* of tests need to collaborate. However, there is no provision
* for requiring that tests run in a certain order.
*/
static int test_run(int i, const char *tmpdir)
{
int failures_before = failures;
if (!quiet_flag)
printf("%d: %s\n", i, tests[i].name);
/*
* Always explicitly chdir() in case the last test moved us to
* a strange place.
*/
if (chdir(tmpdir)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir %s\n",
tmpdir);
exit(1);
}
/* Create a temp directory for this specific test. */
if (mkdir(tests[i].name, 0755)) {
fprintf(stderr,
"ERROR: Couldn't create temp dir ``%s''\n",
tests[i].name);
exit(1);
}
/* Chdir() to that work directory. */
if (chdir(tests[i].name)) {
fprintf(stderr,
"ERROR: Couldn't chdir to temp dir ``%s''\n",
tests[i].name);
exit(1);
}
/* Explicitly reset the locale before each test. */
setlocale(LC_ALL, "C");
/* Run the actual test. */
(*tests[i].func)();
/* Summarize the results of this test. */
summarize();
/* If there were no failures, we can remove the work dir. */
if (failures == failures_before) {
if (!keep_temp_files && chdir(tmpdir) == 0) {
systemf("rm -rf %s", tests[i].name);
}
}
/* Return appropriate status. */
return (failures == failures_before ? 0 : 1);
}
static void usage(const char *program)
{
static const int limit = sizeof(tests) / sizeof(tests[0]);
int i;
printf("Usage: %s [options] <test> <test> ...\n", program);
printf("Default is to run all tests.\n");
printf("Otherwise, specify the numbers of the tests you wish to run.\n");
printf("Options:\n");
printf(" -d Dump core after any failure, for debugging.\n");
printf(" -k Keep all temp files.\n");
printf(" Default: temp files for successful tests deleted.\n");
#ifdef PROGRAM
printf(" -p <path> Path to executable to be tested.\n");
printf(" Default: path taken from " ENVBASE " environment variable.\n");
#endif
printf(" -q Quiet.\n");
printf(" -r <dir> Path to dir containing reference files.\n");
printf(" Default: Current directory.\n");
printf(" -v Verbose.\n");
printf("Available tests:\n");
for (i = 0; i < limit; i++)
printf(" %d: %s\n", i, tests[i].name);
exit(1);
}
#define UUDECODE(c) (((c) - 0x20) & 0x3f)
void
extract_reference_file(const char *name)
{
char buff[1024];
FILE *in, *out;
sprintf(buff, "%s/%s.uu", refdir, name);
in = fopen(buff, "r");
failure("Couldn't open reference file %s", buff);
assert(in != NULL);
if (in == NULL)
return;
/* Read up to and including the 'begin' line. */
for (;;) {
if (fgets(buff, sizeof(buff), in) == NULL) {
/* TODO: This is a failure. */
return;
}
if (memcmp(buff, "begin ", 6) == 0)
break;
}
/* Now, decode the rest and write it. */
/* Not a lot of error checking here; the input better be right. */
out = fopen(name, "w");
while (fgets(buff, sizeof(buff), in) != NULL) {
char *p = buff;
int bytes;
if (memcmp(buff, "end", 3) == 0)
break;
bytes = UUDECODE(*p++);
while (bytes > 0) {
int n = 0;
/* Write out 1-3 bytes from that. */
if (bytes > 0) {
n = UUDECODE(*p++) << 18;
n |= UUDECODE(*p++) << 12;
fputc(n >> 16, out);
--bytes;
}
if (bytes > 0) {
n |= UUDECODE(*p++) << 6;
fputc((n >> 8) & 0xFF, out);
--bytes;
}
if (bytes > 0) {
n |= UUDECODE(*p++);
fputc(n & 0xFF, out);
--bytes;
}
}
}
fclose(out);
fclose(in);
}
int main(int argc, char **argv)
{
static const int limit = sizeof(tests) / sizeof(tests[0]);
int i, tests_run = 0, tests_failed = 0, opt;
time_t now;
char *refdir_alloc = NULL;
char *progname, *p;
char tmpdir[256];
char tmpdir_timestamp[256];
/*
* Name of this program, used to build root of our temp directory
* tree.
*/
progname = p = argv[0];
while (*p != '\0') {
if (*p == '/')
progname = p + 1;
++p;
}
#ifdef PROGRAM
/* Get the target program from environment, if available. */
testprog = getenv(ENVBASE);
#endif
/* Allow -d to be controlled through the environment. */
if (getenv(ENVBASE "_DEBUG") != NULL)
dump_on_failure = 1;
/* Get the directory holding test files from environment. */
refdir = getenv(ENVBASE "_TEST_FILES");
/*
* Parse options.
*/
while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) {
switch (opt) {
case 'd':
dump_on_failure = 1;
break;
case 'k':
keep_temp_files = 1;
break;
case 'p':
#ifdef PROGRAM
testprog = optarg;
#else
usage(progname);
#endif
break;
case 'q':
quiet_flag++;
break;
case 'r':
refdir = optarg;
break;
case 'v':
verbose = 1;
break;
case '?':
default:
usage(progname);
}
}
argc -= optind;
argv += optind;
/*
* Sanity-check that our options make sense.
*/
#ifdef PROGRAM
if (testprog == NULL)
usage(progname);
#endif
/*
* Create a temp directory for the following tests.
* Include the time the tests started as part of the name,
* to make it easier to track the results of multiple tests.
*/
now = time(NULL);
for (i = 0; i < 1000; i++) {
strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
"%Y-%m-%dT%H.%M.%S",
localtime(&now));
sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i);
if (mkdir(tmpdir,0755) == 0)
break;
if (errno == EEXIST)
continue;
fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
tmpdir);
exit(1);
}
/*
* If the user didn't specify a directory for locating
* reference files, use the current directory for that.
*/
if (refdir == NULL) {
systemf("/bin/pwd > %s/refdir", tmpdir);
refdir = refdir_alloc = slurpfile(NULL, "%s/refdir", tmpdir);
p = refdir + strlen(refdir);
while (p[-1] == '\n') {
--p;
*p = '\0';
}
systemf("rm %s/refdir", tmpdir);
}
/*
* Banner with basic information.
*/
if (!quiet_flag) {
printf("Running tests in: %s\n", tmpdir);
printf("Reference files will be read from: %s\n", refdir);
#ifdef PROGRAM
printf("Running tests on: %s\n", testprog);
#endif
printf("Exercising: ");
fflush(stdout);
printf("%s\n", EXTRA_VERSION);
}
/*
* Run some or all of the individual tests.
*/
if (argc == 0) {
/* Default: Run all tests. */
for (i = 0; i < limit; i++) {
if (test_run(i, tmpdir))
tests_failed++;
tests_run++;
}
} else {
while (*(argv) != NULL) {
i = atoi(*argv);
if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
printf("*** INVALID Test %s\n", *argv);
usage(progname);
} else {
if (test_run(i, tmpdir))
tests_failed++;
tests_run++;
}
argv++;
}
}
/*
* Report summary statistics.
*/
if (!quiet_flag) {
printf("\n");
printf("%d of %d tests reported failures\n",
tests_failed, tests_run);
printf(" Total of %d assertions checked.\n", assertions);
printf(" Total of %d assertions failed.\n", failures);
printf(" Total of %d assertions skipped.\n", skips);
}
free(refdir_alloc);
/* If the final tmpdir is empty, we can remove it. */
/* This should be the usual case when all tests succeed. */
rmdir(tmpdir);
return (tests_failed);
}

147
usr.bin/cpio/test/test.h Normal file
View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2003-2006 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
/* Every test program should #include "test.h" as the first thing. */
/*
* The goal of this file (and the matching test.c) is to
* simplify the very repetitive test-*.c test programs.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <wchar.h>
#ifdef USE_DMALLOC
#include <dmalloc.h>
#endif
#if defined(HAVE_CONFIG_H)
/* Most POSIX platforms use the 'configure' script to build config.h */
#include "../../config.h"
#elif defined(__FreeBSD__)
/* Building as part of FreeBSD system requires a pre-built config.h. */
#include "../config_freebsd.h"
#elif defined(_WIN32)
/* Win32 can't run the 'configure' script. */
#include "../config_windows.h"
#else
/* Warn if the library hasn't been (automatically or manually) configured. */
#error Oops: No config.h and no pre-built configuration in test.h.
#endif
/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
#ifdef __FreeBSD__
#include <sys/cdefs.h> /* For __FBSDID */
#else
#define __FBSDID(a) /* null */
#endif
/*
* Redefine DEFINE_TEST for use in defining the test functions.
*/
#undef DEFINE_TEST
#define DEFINE_TEST(name) void name(void); void name(void)
/* An implementation of the standard assert() macro */
#define assert(e) test_assert(__FILE__, __LINE__, (e), #e, NULL)
/* Assert two integers are the same. Reports value of each one if not. */
#define assertEqualInt(v1,v2) \
test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* Assert two strings are the same. Reports value of each one if not. */
#define assertEqualString(v1,v2) \
test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* As above, but v1 and v2 are wchar_t * */
#define assertEqualWString(v1,v2) \
test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
/* As above, but raw blocks of bytes. */
#define assertEqualMem(v1, v2, l) \
test_assert_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
/* Assert two files are the same; allow printf-style expansion of second name.
* See below for comments about variable arguments here...
*/
#define assertEqualFile \
test_setup(__FILE__, __LINE__);test_assert_equal_file
/* Assert that a file is empty; supports printf-style arguments. */
#define assertEmptyFile \
test_setup(__FILE__, __LINE__);test_assert_empty_file
/* Assert that file contents match a string; supports printf-style arguments. */
#define assertFileContents \
test_setup(__FILE__, __LINE__);test_assert_file_contents
/*
* This would be simple with C99 variadic macros, but I don't want to
* require that. Instead, I insert a function call before each
* skipping() call to pass the file and line information down. Crude,
* but effective.
*/
#define skipping \
test_setup(__FILE__, __LINE__);test_skipping
/* Function declarations. These are defined in test_utility.c. */
void failure(const char *fmt, ...);
void test_setup(const char *, int);
void test_skipping(const char *fmt, ...);
int test_assert(const char *, int, int, const char *, void *);
int test_assert_empty_file(const char *, ...);
int test_assert_equal_file(const char *, const char *, ...);
int test_assert_equal_int(const char *, int, int, const char *, int, const char *, void *);
int test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *);
int test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
int test_assert_equal_mem(const char *, int, const char *, const char *, const char *, const char *, size_t, const char *, void *);
int test_assert_file_contents(const void *, int, const char *, ...);
/* Like sprintf, then system() */
int systemf(const char * fmt, ...);
/* Suck file into string allocated via malloc(). Call free() when done. */
/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
char *slurpfile(size_t *, const char *fmt, ...);
/* Extracts named reference file to the current directory. */
void extract_reference_file(const char *);
/*
* Special interfaces for program test harness.
*/
/* Pathname of exe to be tested. */
char *testprog;

View File

@ -0,0 +1,62 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
/*
* This first test does basic sanity checks on the environment. For
* most of these, we just exit on failure.
*/
DEFINE_TEST(test_0)
{
struct stat st;
failure("File %s does not exist?!", testprog);
if (!assertEqualInt(0, stat(testprog, &st)))
exit(1);
failure("%s is not executable?!", testprog);
if (!assert((st.st_mode & 0111) != 0))
exit(1);
/*
* Try to succesfully run the program; this requires that
* we know some option that will succeed.
*/
if (0 == systemf("%s --version >/dev/null", testprog)) {
/* This worked. */
} else if (0 == systemf("%s -W version >/dev/null", testprog)) {
/* This worked. */
} else {
failure("Unable to successfully run any of the following:\n"
" * %s --version\n"
" * %s -W version\n",
testprog, testprog);
assert(0);
}
/* TODO: Ensure that our reference files are available. */
}

View File

@ -0,0 +1,204 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
static void
verify_files(const char *target)
{
struct stat st, st2;
char buff[128];
int r;
/*
* Verify unpacked files.
*/
/* Regular file with 2 links. */
r = lstat("file", &st);
failure("Failed to stat file %s/file, errno=%d", target, errno);
assertEqualInt(r, 0);
if (r == 0) {
assert(S_ISREG(st.st_mode));
assertEqualInt(0644, st.st_mode & 0777);
assertEqualInt(10, st.st_size);
failure("file %s/file should have 2 links", target);
assertEqualInt(2, st.st_nlink);
}
/* Another name for the same file. */
r = lstat("linkfile", &st2);
failure("Failed to stat file %s/linkfile, errno=%d", target, errno);
assertEqualInt(r, 0);
if (r == 0) {
assert(S_ISREG(st2.st_mode));
assertEqualInt(0644, st2.st_mode & 0777);
assertEqualInt(10, st2.st_size);
failure("file %s/linkfile should have 2 links", target);
assertEqualInt(2, st2.st_nlink);
/* Verify that the two are really hardlinked. */
assertEqualInt(st.st_dev, st2.st_dev);
failure("%s/linkfile and %s/file should be hardlinked",
target, target);
assertEqualInt(st.st_ino, st2.st_ino);
}
/* Symlink */
r = lstat("symlink", &st);
failure("Failed to stat file %s/symlink, errno=%d", target, errno);
assertEqualInt(r, 0);
if (r == 0) {
failure("symlink should be a symlink; actual mode is %o",
st.st_mode);
assert(S_ISLNK(st.st_mode));
if (S_ISLNK(st.st_mode)) {
r = readlink("symlink", buff, sizeof(buff));
assertEqualInt(r, 4);
buff[r] = '\0';
assertEqualString(buff, "file");
}
}
/* dir */
r = lstat("dir", &st);
if (r == 0) {
assertEqualInt(r, 0);
assert(S_ISDIR(st.st_mode));
assertEqualInt(0775, st.st_mode & 0777);
}
}
static void
basic_cpio(const char *target,
const char *pack_options,
const char *unpack_options,
const char *se)
{
int r;
if (!assertEqualInt(0, mkdir(target, 0775)))
return;
/* Use the cpio program to create an archive. */
r = systemf("%s -o %s < filelist >%s/archive 2>%s/pack.err",
testprog, pack_options, target, target);
failure("Error invoking %s -o %s", testprog, pack_options);
assertEqualInt(r, 0);
chdir(target);
/* Verify stderr. */
failure("Expected: %s, options=%s", se, pack_options);
assertFileContents(se, strlen(se), "pack.err");
/*
* Use cpio to unpack the archive into another directory.
*/
r = systemf("%s -i %s< archive >unpack.out 2>unpack.err",
testprog, unpack_options);
failure("Error invoking %s -i %s", testprog, unpack_options);
assertEqualInt(r, 0);
/* Verify stderr. */
failure("Error invoking %s -i %s in dir %s", testprog, unpack_options, target);
assertFileContents(se, strlen(se), "unpack.err");
verify_files(target);
chdir("..");
}
static void
passthrough(const char *target)
{
int r;
if (!assertEqualInt(0, mkdir(target, 0775)))
return;
/*
* Use cpio passthrough mode to copy files to another directory.
*/
r = systemf("%s -p -W quiet %s <filelist >%s/stdout 2>%s/stderr",
testprog, target, target, target);
failure("Error invoking %s -p", testprog);
assertEqualInt(r, 0);
chdir(target);
/* Verify stderr. */
failure("Error invoking %s -p in dir %s",
testprog, target);
assertEmptyFile("stderr");
verify_files(target);
chdir("..");
}
DEFINE_TEST(test_basic)
{
int fd;
int filelist;
int oldumask;
oldumask = umask(0);
/*
* Create an assortment of files on disk.
*/
filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
/* File with 10 bytes content. */
fd = open("file", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(10, write(fd, "123456789", 10));
close(fd);
write(filelist, "file\n", 5);
/* hardlink to above file. */
assertEqualInt(0, link("file", "linkfile"));
write(filelist, "linkfile\n", 9);
/* Symlink to above file. */
assertEqualInt(0, symlink("file", "symlink"));
write(filelist, "symlink\n", 8);
/* Directory. */
assertEqualInt(0, mkdir("dir", 0775));
write(filelist, "dir\n", 4);
/* All done. */
close(filelist);
/* Archive/dearchive with a variety of options. */
basic_cpio("copy", "", "", "1 block\n");
basic_cpio("copy_odc", "--format=odc", "", "1 block\n");
basic_cpio("copy_newc", "-H newc", "", "2 blocks\n");
basic_cpio("copy_cpio", "-H odc", "", "1 block\n");
basic_cpio("copy_ustar", "-H ustar", "", "7 blocks\n");
/* Copy in one step using -p */
passthrough("passthrough");
umask(oldumask);
}

View File

@ -0,0 +1,245 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
static int
is_hex(const char *p, size_t l)
{
while (l > 0) {
if ((*p >= '0' && *p <= '9')
|| (*p >= 'a' && *p <= 'f')
|| (*p >= 'A' && *p <= 'F'))
{
--l;
++p;
} else
return (0);
}
return (1);
}
static int
from_hex(const char *p, size_t l)
{
int r = 0;
while (l > 0) {
r *= 16;
if (*p >= 'a' && *p <= 'f')
r += *p + 10 - 'a';
else if (*p >= 'A' && *p <= 'F')
r += *p + 10 - 'A';
else
r += *p - '0';
--l;
++p;
}
return (r);
}
DEFINE_TEST(test_format_newc)
{
int fd, list;
int r;
int devmajor, devminor, ino, gid;
time_t t, t2, now;
char *p, *e;
size_t s;
mode_t oldmask;
oldmask = umask(0);
/*
* Create an assortment of files.
* TODO: Extend this to cover more filetypes.
*/
list = open("list", O_CREAT | O_WRONLY, 0644);
/* "file1" */
fd = open("file1", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(10, write(fd, "123456789", 10));
close(fd);
assertEqualInt(6, write(list, "file1\n", 6));
/* "hardlink" */
assertEqualInt(0, link("file1", "hardlink"));
assertEqualInt(9, write(list, "hardlink\n", 9));
/* Another hardlink, but this one won't be archived. */
assertEqualInt(0, link("file1", "hardlink2"));
/* "symlink" */
assertEqualInt(0, symlink("file1", "symlink"));
assertEqualInt(8, write(list, "symlink\n", 8));
/* "dir" */
assertEqualInt(0, mkdir("dir", 0775));
assertEqualInt(4, write(list, "dir\n", 4));
/* Record some facts about what we just created: */
now = time(NULL); /* They were all created w/in last two seconds. */
/* Use the cpio program to create an archive. */
close(list);
r = systemf("%s -o --format=newc <list >newc.out 2>newc.err",
testprog);
if (!assertEqualInt(r, 0))
return;
/* Verify that nothing went to stderr. */
assertFileContents("2 blocks\n", 9, "newc.err");
/* Verify that stdout is a well-formed cpio file in "newc" format. */
p = slurpfile(&s, "newc.out");
assertEqualInt(s, 1024);
e = p;
/*
* Some of these assertions could be stronger, but it's
* a little tricky because they depend on the local environment.
*/
/* First entry is "file1" */
assert(is_hex(e, 110)); /* Entire header is octal digits. */
assertEqualMem(e + 0, "070701", 6); /* Magic */
ino = from_hex(e + 6, 8); /* ino */
assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
gid = from_hex(e + 30, 8); /* gid */
assertEqualMem(e + 38, "00000003", 8); /* nlink */
t = from_hex(e + 46, 8); /* mtime */
failure("t=0x%08x now=0x%08x=%d", t, now, now);
assert(t <= now); /* File wasn't created in future. */
failure("t=0x%08x now - 2=0x%08x = %d", t, now - 2, now - 2);
assert(t >= now - 2); /* File was created w/in last 2 secs. */
failure("newc format stores body only with last appearance of a link\n"
" first appearance should be empty, so this file size\n"
" field should be zero");
assertEqualInt(0, from_hex(e + 54, 8)); /* File size */
devmajor = from_hex(e + 62, 8); /* devmajor */
devminor = from_hex(e + 70, 8); /* devminor */
assert(is_hex(e + 78, 8)); /* rdevmajor */
assert(is_hex(e + 86, 8)); /* rdevminor */
assertEqualMem(e + 94, "00000006", 8); /* Name size */
assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
assertEqualMem(e + 110, "file1\0", 6); /* Name contents */
/* Since there's another link, no file contents here. */
/* But add in file size so that an error here doesn't cascade. */
e += 116 + from_hex(e + 54, 8) + (3 & -from_hex(e + 54, 8));
/* "symlink" pointing to "file1" */
assert(is_hex(e, 110));
assertEqualMem(e + 0, "070701", 6); /* Magic */
assert(is_hex(e + 6, 8)); /* ino */
assertEqualInt(0xa1ff, from_hex(e + 14, 8)); /* Mode */
assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
assertEqualMem(e + 38, "00000001", 8); /* nlink */
t2 = from_hex(e + 46, 8); /* mtime */
failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
assertEqualMem(e + 54, "00000005", 8); /* File size */
assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
assert(is_hex(e + 78, 8)); /* rdevmajor */
assert(is_hex(e + 86, 8)); /* rdevminor */
assertEqualMem(e + 94, "00000008", 8); /* Name size */
assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
assertEqualMem(e + 110, "symlink\0\0\0", 10); /* Name contents */
assertEqualMem(e + 120, "file1\0\0\0", 8); /* symlink target */
e += 120 + from_hex(e + 54, 8) + (3 & -from_hex(e + 54, 8));
/* "dir" */
assert(is_hex(e, 110));
assertEqualMem(e + 0, "070701", 6); /* Magic */
assert(is_hex(e + 6, 8)); /* ino */
assertEqualInt(0x41fd, from_hex(e + 14, 8)); /* Mode */
assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
assertEqualMem(e + 38, "00000002", 8); /* nlink */
t2 = from_hex(e + 46, 8); /* mtime */
failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
assertEqualMem(e + 54, "00000000", 8); /* File size */
assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
assert(is_hex(e + 78, 8)); /* rdevmajor */
assert(is_hex(e + 86, 8)); /* rdevminor */
assertEqualMem(e + 94, "00000004", 8); /* Name size */
assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
assertEqualMem(e + 110, "dir\0\0\0", 6); /* Name contents */
e += 116;
/* TODO: Verify other types of entries. */
/* Hardlink identical to "file1" */
/* Since we only wrote two of the three links to this
* file, this link should get deferred by the hardlink logic. */
assert(is_hex(e, 110));
assertEqualMem(e + 0, "070701", 6); /* Magic */
failure("If these aren't the same, then the hardlink detection failed to match them.");
assertEqualInt(ino, from_hex(e + 6, 8)); /* ino */
assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
assertEqualInt(from_hex(e + 22, 8), getuid()); /* uid */
assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
assertEqualMem(e + 38, "00000003", 8); /* nlink */
t2 = from_hex(e + 46, 8); /* mtime */
failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
assertEqualInt(10, from_hex(e + 54, 8)); /* File size */
assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
assert(is_hex(e + 78, 8)); /* rdevmajor */
assert(is_hex(e + 86, 8)); /* rdevminor */
assertEqualMem(e + 94, "00000009", 8); /* Name size */
assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
assertEqualMem(e + 110, "hardlink\0\0", 10); /* Name contents */
assertEqualMem(e + 120, "123456789\0\0\0", 12); /* File contents */
e += 120 + from_hex(e + 54, 8) + (3 & -from_hex(e + 54, 8));
/* Last entry is end-of-archive marker. */
assert(is_hex(e, 110));
assertEqualMem(e + 0, "070701", 6); /* Magic */
assertEqualMem(e + 8, "00000000", 8); /* ino */
assertEqualMem(e + 14, "00000000", 8); /* mode */
assertEqualMem(e + 22, "00000000", 8); /* uid */
assertEqualMem(e + 30, "00000000", 8); /* gid */
assertEqualMem(e + 38, "00000001", 8); /* nlink */
assertEqualMem(e + 46, "00000000", 8); /* mtime */
assertEqualMem(e + 54, "00000000", 8); /* size */
assertEqualMem(e + 62, "00000000", 8); /* devmajor */
assertEqualMem(e + 70, "00000000", 8); /* devminor */
assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
assertEqualInt(11, from_hex(e + 94, 8)); /* name size */
assertEqualMem(e + 102, "00000000", 8); /* check field */
assertEqualMem(e + 110, "TRAILER!!!\0\0", 12); /* Name */
free(p);
umask(oldmask);
}

View File

@ -0,0 +1,127 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
static void
unpack_test(const char *from, const char *options, const char *se)
{
struct stat st, st2;
char buff[128];
int r;
/* Create a work dir named after the file we're unpacking. */
assertEqualInt(0, mkdir(from, 0775));
chdir(from);
/*
* Use cpio to unpack the sample archive
*/
extract_reference_file(from);
r = systemf("%s -i %s < %s >unpack.out 2>unpack.err",
testprog, options, from);
failure("Error invoking %s -i %s < %s",
testprog, options, from);
assertEqualInt(r, 0);
/* Verify that nothing went to stderr. */
assertFileContents(se, strlen(se), "unpack.err");
/*
* Verify unpacked files.
*/
/* Regular file with 2 links. */
r = lstat("file", &st);
failure("Failed to stat file %s/file, errno=%d", from, errno);
assertEqualInt(r, 0);
if (r == 0) {
assert(S_ISREG(st.st_mode));
assertEqualInt(0644, st.st_mode & 0777);
failure("file %s/file", from);
assertEqualInt(10, st.st_size);
failure("file %s/file", from);
assertEqualInt(2, st.st_nlink);
}
/* Another name for the same file. */
r = lstat("linkfile", &st2);
failure("Failed to stat file %s/linkfile, errno=%d", from, errno);
assertEqualInt(r, 0);
if (r == 0) {
assert(S_ISREG(st2.st_mode));
assertEqualInt(0644, st2.st_mode & 0777);
failure("file %s/file", from);
assertEqualInt(10, st2.st_size);
failure("file %s/file", from);
assertEqualInt(2, st2.st_nlink);
failure("file and linkfile should be hardlinked");
assertEqualInt(st.st_dev, st2.st_dev);
failure("file %s/file", from);
assertEqualInt(st.st_ino, st2.st_ino);
}
/* Symlink */
r = lstat("symlink", &st);
failure("Failed to stat file %s/symlink, errno=%d", from, errno);
assertEqualInt(r, 0);
if (r == 0) {
failure("symlink should be a symlink; actual mode is %o",
st.st_mode);
assert(S_ISLNK(st.st_mode));
if (S_ISLNK(st.st_mode)) {
r = readlink("symlink", buff, sizeof(buff));
assertEqualInt(r, 4);
buff[r] = '\0';
assertEqualString(buff, "file");
}
}
/* dir */
r = lstat("dir", &st);
if (r == 0) {
assertEqualInt(r, 0);
assert(S_ISDIR(st.st_mode));
assertEqualInt(0775, st.st_mode & 0777);
}
chdir("..");
}
DEFINE_TEST(test_gcpio_compat)
{
int oldumask;
oldumask = umask(0);
/* Dearchive sample files with a variety of options. */
unpack_test("test_gcpio_compat_ref.bin", "", "1 block\n");
unpack_test("test_gcpio_compat_ref.crc", "", "2 blocks\n");
unpack_test("test_gcpio_compat_ref.newc", "", "2 blocks\n");
unpack_test("test_gcpio_compat_ref.ustar", "", "7 blocks\n");
umask(oldumask);
}

View File

@ -0,0 +1,16 @@
$FreeBSD$
begin 644 test_gcpio_compat_ref.bin
MQW%9`*IWI('H`^@#`@````U'=YD%````"@!F:6QE```Q,C,T-38W.#D*QW%9
M`*IWI('H`^@#`@````U'=YD)````"@!L:6YK9FEL90``,3(S-#4V-S@Y"L=Q
M60"K=^VAZ`/H`P$````-1X29"`````0`<WEM;&EN:P!F:6QEQW%9`*YW_4'H
M`^@#`@````U'A9D$``````!D:7(`QW$``````````````0`````````+````
M``!44D%)3$52(2$A````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
1````````````````````````
`
end

View File

@ -0,0 +1,27 @@
$FreeBSD$
begin 644 test_gcpio_compat_ref.crc
M,#<P-S`R,#`S,S<W86$P,#`P.#%A-#`P,#`P,V4X,#`P,#`S93@P,#`P,#`P
M,C0W,&0Y.3<W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y,#`P,#`P,#`P,#`P
M,#`P,#`P,#`P,#`U,#`P,#`P,#!F:6QE```P-S`W,#(P,#,S-S=A83`P,#`X
M,6$T,#`P,#`S93@P,#`P,#-E.#`P,#`P,#`R-#<P9#DY-S<P,#`P,#`P83`P
M,#`P,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#DP,#`P,#%E
M-VQI;FMF:6QE```Q,C,T-38W.#D*```P-S`W,#(P,#,S-S=A8C`P,#!A,65D
M,#`P,#`S93@P,#`P,#-E.#`P,#`P,#`Q-#<P9#DY.#0P,#`P,#`P-#`P,#`P
M,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#@P,#`P,#`P,'-Y
M;6QI;FL```!F:6QE,#<P-S`R,#`S,S<W864P,#`P-#%F9#`P,#`P,V4X,#`P
M,#`S93@P,#`P,#`P,C0W,&0Y.3@U,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`T,#`P,#`P,#!D:7(````P-S`W,#(P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
M,#`P,&(P,#`P,#`P,%1204E,15(A(2$`````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
B````````````````````````````````````````````````
`
end

View File

@ -0,0 +1,27 @@
$FreeBSD$
begin 644 test_gcpio_compat_ref.newc
M,#<P-S`Q,#`S,S<W86$P,#`P.#%A-#`P,#`P,V4X,#`P,#`S93@P,#`P,#`P
M,C0W,&0Y.3<W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y,#`P,#`P,#`P,#`P
M,#`P,#`P,#`P,#`U,#`P,#`P,#!F:6QE```P-S`W,#$P,#,S-S=A83`P,#`X
M,6$T,#`P,#`S93@P,#`P,#-E.#`P,#`P,#`R-#<P9#DY-S<P,#`P,#`P83`P
M,#`P,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#DP,#`P,#`P
M,&QI;FMF:6QE```Q,C,T-38W.#D*```P-S`W,#$P,#,S-S=A8C`P,#!A,65D
M,#`P,#`S93@P,#`P,#-E.#`P,#`P,#`Q-#<P9#DY.#0P,#`P,#`P-#`P,#`P
M,#`P,#`P,#`P-3DP,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#@P,#`P,#`P,'-Y
M;6QI;FL```!F:6QE,#<P-S`Q,#`S,S<W864P,#`P-#%F9#`P,#`P,V4X,#`P
M,#`S93@P,#`P,#`P,C0W,&0Y.3@U,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#4Y
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`T,#`P,#`P,#!D:7(````P-S`W,#$P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
M,#`P,&(P,#`P,#`P,%1204E,15(A(2$`````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
B````````````````````````````````````````````````
`
end

View File

@ -0,0 +1,84 @@
$FreeBSD$
begin 644 test_gcpio_compat_ref.ustar
M9FEL90``````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````#`P,#`V-#0`,#`P,3<U,``P,#`Q-S4P`#`P,#`P,#`P,#$R
M`#$P-S`S,S$T-38W`#`P,3$S-C,`,```````````````````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````````````````!U<W1A<@`P,'1I;0``
M````````````````````````````````````=&EM````````````````````
M```````````````````P,#`P,#`P`#`P,#`P,#``````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M```````````````````````Q,C,T-38W.#D*````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````&QI;FMF:6QE````
M````````````````````````````````````````````````````````````
M```````````````````````````````````````````````````````````P
M,#`P-C0T`#`P,#$W-3``,#`P,3<U,``P,#`P,#`P,#`P,``Q,#<P,S,Q-#4V
M-P`P,#$S,#<W`#%F:6QE````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````=7-T87(`,#!T:6T`````````````````
M`````````````````````'1I;0``````````````````````````````````
M````,#`P,#`P,``P,#`P,#`P````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````<WEM;&EN:P``````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````#`P,#`W-34`,#`P,3<U,``P,#`Q-S4P`#`P,#`P
M,#`P,#`P`#$P-S`S,S$T-C`T`#`P,3(W-C0`,F9I;&4`````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````````````````````````!U<W1A<@`P
M,'1I;0``````````````````````````````````````=&EM````````````
M```````````````````````````P,#`P,#`P`#`P,#`P,#``````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````!D:7(O````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````,#`P,#<W-0`P,#`Q
M-S4P`#`P,#$W-3``,#`P,#`P,#`P,#``,3`W,#,S,30V,#4`,#`Q,3,P,0`U
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````'5S=&%R`#`P=&EM````````````````````````````````
M``````!T:6T``````````````````````````````````````#`P,#`P,#``
M,#`P,#`P,```````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
=````````````````````````````````````````
`
end

View File

@ -0,0 +1,54 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_B)
{
struct stat st;
int r, fd;
/*
* Create a file on disk.
*/
fd = open("file", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
close(fd);
/* Create an archive without -B; this should be 512 bytes. */
r = systemf("echo file | %s -o > small.cpio 2>small.err", testprog);
assertEqualInt(r, 0);
assertFileContents("1 block\n", 8, "small.err");
assertEqualInt(0, stat("small.cpio", &st));
assertEqualInt(512, st.st_size);
/* Create an archive with -B; this should be 5120 bytes. */
r = systemf("echo file | %s -oB > large.cpio 2>large.err", testprog);
assertEqualInt(r, 0);
assertFileContents("1 block\n", 8, "large.err");
assertEqualInt(0, stat("large.cpio", &st));
assertEqualInt(5120, st.st_size);
}

View File

@ -0,0 +1,84 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_L)
{
struct stat st;
int fd, filelist;
int r;
filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
/* Create a file and a symlink to the file. */
fd = open("file", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(10, write(fd, "123456789", 10));
close(fd);
write(filelist, "file\n", 5);
/* Symlink to above file. */
assertEqualInt(0, symlink("file", "symlink"));
write(filelist, "symlink\n", 8);
close(filelist);
r = systemf("cat filelist | %s -pd copy >copy.out 2>copy.err", testprog);
assertEqualInt(r, 0);
assertEqualInt(0, lstat("copy/symlink", &st));
failure("Regular -p without -L should preserve symlinks.");
assert(S_ISLNK(st.st_mode));
r = systemf("cat filelist | %s -pd -L copy-L >copy-L.out 2>copy-L.err", testprog);
assertEqualInt(r, 0);
assertEmptyFile("copy-L.out");
assertEmptyFile("copy-L.err");
assertEqualInt(0, lstat("copy-L/symlink", &st));
failure("-pdL should dereference symlinks and turn them into files.");
assert(!S_ISLNK(st.st_mode));
r = systemf("cat filelist | %s -o >archive.out 2>archive.err", testprog);
failure("Error invoking %s -o ", testprog);
assertEqualInt(r, 0);
assertEqualInt(0, mkdir("unpack", 0755));
r = systemf("cat archive.out | (cd unpack ; %s -i >unpack.out 2>unpack.err)", testprog);
failure("Error invoking %s -i", testprog);
assertEqualInt(r, 0);
assertEqualInt(0, lstat("unpack/symlink", &st));
assert(S_ISLNK(st.st_mode));
r = systemf("cat filelist | %s -oL >archive-L.out 2>archive-L.err", testprog);
failure("Error invoking %s -oL", testprog);
assertEqualInt(r, 0);
assertEqualInt(0, mkdir("unpack-L", 0755));
r = systemf("cat archive-L.out | (cd unpack-L ; %s -i >unpack-L.out 2>unpack-L.err)", testprog);
failure("Error invoking %s -i < archive-L.out", testprog);
assertEqualInt(r, 0);
assertEqualInt(0, lstat("unpack-L/symlink", &st));
assert(!S_ISLNK(st.st_mode));
}

View File

@ -0,0 +1,159 @@
/*-
* Copyright (c) 2003-2008 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
#include <utime.h>
__FBSDID("$FreeBSD$");
static struct {
const char *name;
time_t atime_sec;
} files[] = {
{ "f0", 0 },
{ "f1", 0 },
{ "f2", 0 },
{ "f3", 0 },
{ "f4", 0 },
{ "f5", 0 }
};
/*
* Create a bunch of test files and record their atimes.
* For the atime preserve/change tests, the files must have
* atimes in the past. We can accomplish this by explicitly invoking
* utime() on platforms that support it or by simply sleeping
* for a second after creating the files. (Creating all of the files
* at once means we only need to sleep once.)
*/
static void
test_create(void)
{
struct stat st;
struct utimbuf times;
static const int numfiles = sizeof(files) / sizeof(files[0]);
int i;
int fd;
for (i = 0; i < numfiles; ++i) {
fd = open(files[i].name, O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
/*
* Note: Have to write at least one byte to the file.
* cpio doesn't bother reading the file if it's zero length,
* so the atime never gets changed in that case, which
* makes the tests below rather pointless.
*/
assertEqualInt(1, write(fd, "a", 1));
close(fd);
/* If utime() isn't supported on your platform, just
* #ifdef this section out. Most of the test below is
* still valid. */
memset(&times, 0, sizeof(times));
times.actime = 1;
times.modtime = 3;
assertEqualInt(0, utime(files[i].name, &times));
/* Record whatever atime the file ended up with. */
/* If utime() is available, this should be 1, but there's
* no harm in being careful. */
assertEqualInt(0, stat(files[i].name, &st));
files[i].atime_sec = st.st_atime;
}
/* Wait until the atime on the last file is actually in the past. */
/* If utime() is supported above, there's no sleep here which
* makes the test faster. */
while (files[numfiles - 1].atime_sec >= time(NULL))
sleep(1);
}
DEFINE_TEST(test_option_a)
{
struct stat st;
int r;
int f;
char buff[64];
/* Create all of the test files. */
test_create();
/* Sanity check; verify that atimes really do get modified. */
f = open(files[0].name, O_RDONLY);
assertEqualInt(1, read(f,buff, 1));
assertEqualInt(0, close(f));
assertEqualInt(0, stat("f0", &st));
if (st.st_atime == files[0].atime_sec) {
skipping("Cannot verify -a option\n"
" Your system appears to not support atime.");
}
else
{
/*
* If this disk is mounted noatime, then we can't
* verify correct operation without -a.
*/
/* Copy the file without -a; should change the atime. */
r = systemf("echo %s | %s -pd copy-no-a > copy-no-a.out 2>copy-no-a.err", files[1].name, testprog);
assertEqualInt(r, 0);
assertEmptyFile("copy-no-a.err");
assertEmptyFile("copy-no-a.out");
assertEqualInt(0, stat(files[1].name, &st));
failure("Copying file without -a should have changed atime.");
assert(st.st_atime != files[1].atime_sec);
/* Archive the file without -a; should change the atime. */
r = systemf("echo %s | %s -o > archive-no-a.out 2>archive-no-a.err", files[2].name, testprog);
assertEqualInt(r, 0);
assertEmptyFile("copy-no-a.err");
assertEqualInt(0, stat(files[2].name, &st));
failure("Archiving file without -a should have changed atime.");
assert(st.st_atime != files[2].atime_sec);
}
/*
* We can, of course, still verify that the atime is unchanged
* when using the -a option.
*/
/* Copy the file with -a; should not change the atime. */
r = systemf("echo %s | %s -pad copy-a > copy-a.out 2>copy-a.err",
files[3].name, testprog);
assertEqualInt(r, 0);
assertEmptyFile("copy-a.err");
assertEmptyFile("copy-a.out");
assertEqualInt(0, stat(files[3].name, &st));
failure("Copying file with -a should not have changed atime.");
assertEqualInt(st.st_atime, files[3].atime_sec);
/* Archive the file with -a; should not change the atime. */
r = systemf("echo %s | %s -oa > archive-a.out 2>archive-a.err",
files[4].name, testprog);
assertEqualInt(r, 0);
assertEmptyFile("copy-a.err");
assertEqualInt(0, stat(files[4].name, &st));
failure("Archiving file with -a should not have changed atime.");
assertEqualInt(st.st_atime, files[4].atime_sec);
}

View File

@ -0,0 +1,198 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
static int
is_octal(const char *p, size_t l)
{
while (l > 0) {
if (*p < '0' || *p > '7')
return (0);
--l;
++p;
}
return (1);
}
static int
from_octal(const char *p, size_t l)
{
int r = 0;
while (l > 0) {
r *= 8;
r += *p - '0';
--l;
++p;
}
return (r);
}
DEFINE_TEST(test_option_c)
{
int fd, filelist;
int r;
int dev, ino, gid;
time_t t, now;
char *p, *e;
size_t s;
mode_t oldmask;
oldmask = umask(0);
/*
* Create an assortment of files.
* TODO: Extend this to cover more filetypes.
*/
filelist = open("filelist", O_CREAT | O_WRONLY, 0644);
/* "file" */
fd = open("file", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(10, write(fd, "123456789", 10));
close(fd);
assertEqualInt(5, write(filelist, "file\n", 5));
/* "symlink" */
assertEqualInt(0, symlink("file", "symlink"));
assertEqualInt(8, write(filelist, "symlink\n", 8));
/* "dir" */
assertEqualInt(0, mkdir("dir", 0775));
/* Record some facts about what we just created: */
now = time(NULL); /* They were all created w/in last two seconds. */
assertEqualInt(4, write(filelist, "dir\n", 4));
/* Use the cpio program to create an archive. */
close(filelist);
r = systemf("%s -oc <filelist >basic.out 2>basic.err", testprog);
/* Verify that nothing went to stderr. */
assertFileContents("1 block\n", 8, "basic.err");
/* Assert that the program finished. */
failure("%s -oc crashed", testprog);
if (!assertEqualInt(r, 0))
return;
/* Verify that stdout is a well-formed cpio file in "odc" format. */
p = slurpfile(&s, "basic.out");
assertEqualInt(s, 512);
e = p;
/*
* Some of these assertions could be stronger, but it's
* a little tricky because they depend on the local environment.
*/
/* First entry is "file" */
assert(is_octal(e, 76)); /* Entire header is octal digits. */
assertEqualMem(e + 0, "070707", 6); /* Magic */
assert(is_octal(e + 6, 6)); /* dev */
dev = from_octal(e + 6, 6);
assert(is_octal(e + 12, 6)); /* ino */
ino = from_octal(e + 12, 6);
assertEqualMem(e + 18, "100644", 6); /* Mode */
assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
assert(is_octal(e + 30, 6)); /* gid */
gid = from_octal(e + 30, 6);
assertEqualMem(e + 36, "000001", 6); /* nlink */
failure("file entries should not have rdev set (dev field was 0%o)",
dev);
assertEqualMem(e + 42, "000000", 6); /* rdev */
t = from_octal(e + 48, 11); /* mtime */
assert(t <= now); /* File wasn't created in future. */
assert(t >= now - 2); /* File was created w/in last 2 secs. */
assertEqualMem(e + 59, "000005", 6); /* Name size */
assertEqualMem(e + 65, "00000000012", 11); /* File size */
assertEqualMem(e + 76, "file\0", 5); /* Name contents */
assertEqualMem(e + 81, "123456789\0", 10); /* File contents */
e += 91;
/* Second entry is "symlink" pointing to "file" */
assert(is_octal(e, 76)); /* Entire header is octal digits. */
assertEqualMem(e + 0, "070707", 6); /* Magic */
assertEqualInt(dev, from_octal(e + 6, 6)); /* dev */
assert(dev != from_octal(e + 12, 6)); /* ino */
assertEqualMem(e + 18, "120777", 6); /* Mode */
assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
assertEqualInt(gid, from_octal(e + 30, 6)); /* gid */
assertEqualMem(e + 36, "000001", 6); /* nlink */
failure("file entries should have rdev == 0 (dev was 0%o)",
from_octal(e + 6, 6));
assertEqualMem(e + 42, "000000", 6); /* rdev */
t = from_octal(e + 48, 11); /* mtime */
assert(t <= now); /* File wasn't created in future. */
assert(t >= now - 2); /* File was created w/in last 2 secs. */
assertEqualMem(e + 59, "000010", 6); /* Name size */
assertEqualMem(e + 65, "00000000004", 11); /* File size */
assertEqualMem(e + 76, "symlink\0", 8); /* Name contents */
assertEqualMem(e + 84, "file", 4); /* Symlink target. */
e += 88;
/* Second entry is "dir" */
assert(is_octal(e, 76));
assertEqualMem(e + 0, "070707", 6); /* Magic */
/* Dev should be same as first entry. */
assert(is_octal(e + 6, 6)); /* dev */
assertEqualInt(dev, from_octal(e + 6, 6));
/* Ino must be different from first entry. */
assert(is_octal(e + 12, 6)); /* ino */
assert(dev != from_octal(e + 12, 6));
assertEqualMem(e + 18, "040775", 6); /* Mode */
assertEqualInt(from_octal(e + 24, 6), getuid()); /* uid */
/* Gid should be same as first entry. */
assert(is_octal(e + 30, 6)); /* gid */
assertEqualInt(gid, from_octal(e + 30, 6));
assertEqualMem(e + 36, "000002", 6); /* Nlink */
t = from_octal(e + 48, 11); /* mtime */
assert(t <= now); /* File wasn't created in future. */
assert(t >= now - 2); /* File was created w/in last 2 secs. */
assertEqualMem(e + 59, "000004", 6); /* Name size */
assertEqualMem(e + 65, "00000000000", 11); /* File size */
assertEqualMem(e + 76, "dir\0", 4); /* name */
e += 80;
/* TODO: Verify other types of entries. */
/* Last entry is end-of-archive marker. */
assert(is_octal(e, 76));
assertEqualMem(e + 0, "070707", 6); /* Magic */
assertEqualMem(e + 6, "000000", 6); /* dev */
assertEqualMem(e + 12, "000000", 6); /* ino */
assertEqualMem(e + 18, "000000", 6); /* Mode */
assertEqualMem(e + 24, "000000", 6); /* uid */
assertEqualMem(e + 30, "000000", 6); /* gid */
assertEqualMem(e + 36, "000001", 6); /* Nlink */
assertEqualMem(e + 42, "000000", 6); /* rdev */
assertEqualMem(e + 48, "00000000000", 11); /* mtime */
assertEqualMem(e + 59, "000013", 6); /* Name size */
assertEqualMem(e + 65, "00000000000", 11); /* File size */
assertEqualMem(e + 76, "TRAILER!!!\0", 11); /* Name */
free(p);
umask(oldmask);
}

View File

@ -0,0 +1,68 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_d)
{
struct stat st;
int r, fd;
/*
* Create a file in a directory.
*/
assertEqualInt(0, mkdir("dir", 0755));
fd = open("dir/file", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
close(fd);
/* Create an archive. */
r = systemf("echo dir/file | %s -o > archive.cpio 2>archive.err", testprog);
assertEqualInt(r, 0);
assertFileContents("1 block\n", 8, "archive.err");
assertEqualInt(0, stat("archive.cpio", &st));
assertEqualInt(512, st.st_size);
/* Dearchive without -d, this should fail. */
assertEqualInt(0, mkdir("without-d", 0755));
assertEqualInt(0, chdir("without-d"));
r = systemf("%s -i < ../archive.cpio >out 2>err", testprog);
assertEqualInt(r, 0);
assertEmptyFile("out");
/* And the file should not be restored. */
assert(0 != stat("dir/file", &st));
/* Dearchive with -d, this should succeed. */
assertEqualInt(0, chdir(".."));
assertEqualInt(0, mkdir("with-d", 0755));
assertEqualInt(0, chdir("with-d"));
r = systemf("%s -id < ../archive.cpio >out 2>err", testprog);
assertEqualInt(r, 0);
assertEmptyFile("out");
assertFileContents("1 block\n", 8, "err");
/* And the file should be restored. */
assertEqualInt(0, stat("dir/file", &st));
}

View File

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
/*
* This is called "test_option_ell" instead of "test_option_l" to
* avoid any conflicts with "test_option_L" on case-insensitive
* filesystems.
*/
DEFINE_TEST(test_option_ell)
{
struct stat st, st2;
int fd;
int r;
/* Create a file. */
fd = open("f", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(1, write(fd, "a", 1));
close(fd);
/* Stat it. */
assertEqualInt(0, stat("f", &st));
/* Copy the file to the "copy" dir. */
r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
testprog);
assertEqualInt(r, 0);
/* Check that the copy is a true copy and not a link. */
assertEqualInt(0, stat("copy/f", &st2));
assert(st2.st_ino != st.st_ino);
/* Copy the file to the "link" dir with the -l option. */
r = systemf("echo f | %s -pld link >link.out 2>link.err",
testprog);
assertEqualInt(r, 0);
/* Check that this is a link and not a copy. */
assertEqualInt(0, stat("link/f", &st2));
assert(st2.st_ino == st.st_ino);
}

View File

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
/*
* Unpack the archive in a new dir.
*/
static void
unpack(const char *dirname, const char *option)
{
int r;
assertEqualInt(0, mkdir(dirname, 0755));
assertEqualInt(0, chdir(dirname));
extract_reference_file("test_option_f.cpio");
r = systemf("%s -i %s < test_option_f.cpio > copy-no-a.out 2>copy-no-a.err", testprog, option);
assertEqualInt(0, r);
assertEqualInt(0, chdir(".."));
}
DEFINE_TEST(test_option_f)
{
/* Calibrate: No -f option, so everything should be extracted. */
unpack("t0", "");
assertEqualInt(0, access("t0/a123", F_OK));
assertEqualInt(0, access("t0/a234", F_OK));
assertEqualInt(0, access("t0/b123", F_OK));
assertEqualInt(0, access("t0/b234", F_OK));
/* Don't extract 'a*' files. */
unpack("t1", "-f 'a*'");
assert(0 != access("t1/a123", F_OK));
assert(0 != access("t1/a234", F_OK));
assertEqualInt(0, access("t1/b123", F_OK));
assertEqualInt(0, access("t1/b234", F_OK));
/* Don't extract 'b*' files. */
unpack("t2", "-f 'b*'");
assertEqualInt(0, access("t2/a123", F_OK));
assertEqualInt(0, access("t2/a234", F_OK));
assert(0 != access("t2/b123", F_OK));
assert(0 != access("t2/b234", F_OK));
}

View File

@ -0,0 +1,16 @@
$FreeBSD$
begin 644 test_option_f.cpio
M,#<P-S`W,#`P,3,Q-C(Q-38Q,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
M,#`P,3`W,S4Q,3(U,C8P,#`P,#4P,#`P,#`P,#`P,&$Q,C,`,#<P-S`W,#`P
M,3,Q-C(Q-38S,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q
M,3(U-#`P,#`P,#4P,#`P,#`P,#`P,&$R,S0`,#<P-S`W,#`P,3,Q-C(Q-38R
M,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q,3(U,S0P,#`P
M,#4P,#`P,#`P,#`P,&(Q,C,`,#<P-S`W,#`P,3,Q-C(Q-38T,3`P-C0T,#`Q
M-S4P,#`Q-S4P,#`P,#`Q,#`P,#`P,3`W,S4Q,3(U-#,P,#`P,#4P,#`P,#`P
M,#`P,&(R,S0`,#<P-S`W,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
M,#`P,#`Q,#`P,#`P,#`P,#`P,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,
M15(A(2$`````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
1````````````````````````
`
end

View File

@ -0,0 +1,81 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
/*
* Test that "--help", "-h", and "-W help" options all work and
* generate reasonable output.
*/
static int
in_first_line(const char *p, const char *substring)
{
size_t l = strlen(substring);
while (*p != '\0' && *p != '\n') {
if (memcmp(p, substring, l) == 0)
return (1);
++p;
}
return (0);
}
DEFINE_TEST(test_option_help)
{
int r;
char *p;
size_t plen;
/* Exercise --help option. */
r = systemf("%s --help >help.stdout 2>help.stderr", testprog);
failure("--help should generate nothing to stderr.");
assertEmptyFile("help.stderr");
/* Help message should start with name of program. */
p = slurpfile(&plen, "help.stdout");
failure("Help output should be long enough.");
assert(plen >= 7);
failure("First line of help output should contain string 'bsdcpio'");
assert(in_first_line(p, "bsdcpio"));
/*
* TODO: Extend this check to further verify that --help output
* looks approximately right.
*/
free(p);
/* -h option should generate the same output. */
r = systemf("%s -h >h.stdout 2>h.stderr", testprog);
failure("-h should generate nothing to stderr.");
assertEmptyFile("h.stderr");
failure("stdout should be same for -h and --help");
assertEqualFile("h.stdout", "help.stdout");
/* -W help should be another synonym. */
r = systemf("%s -W help >Whelp.stdout 2>Whelp.stderr", testprog);
failure("-W help should generate nothing to stderr.");
assertEmptyFile("Whelp.stderr");
failure("stdout should be same for -W help and --help");
assertEqualFile("Whelp.stdout", "help.stdout");
}

View File

@ -0,0 +1,70 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_m)
{
struct stat st;
int r;
time_t now;
/*
* The reference archive has one file with an mtime in 1970, 1
* second after the start of the epoch.
*/
/* Restored without -m, the result should have a current mtime. */
assertEqualInt(0, mkdir("without-m", 0755));
assertEqualInt(0, chdir("without-m"));
extract_reference_file("test_option_m.cpio");
r = systemf("%s -i < test_option_m.cpio >out 2>err", testprog);
now = time(NULL);
assertEqualInt(r, 0);
assertEmptyFile("out");
assertFileContents("1 block\n", 8, "err");
assertEqualInt(0, stat("file", &st));
/* Should have been created within the last few seconds. */
assert(st.st_mtime <= now);
assert(st.st_mtime > now - 5);
/* With -m, it should have an mtime in 1970. */
assertEqualInt(0, chdir(".."));
assertEqualInt(0, mkdir("with-m", 0755));
assertEqualInt(0, chdir("with-m"));
extract_reference_file("test_option_m.cpio");
r = systemf("%s -im < test_option_m.cpio >out 2>err", testprog);
now = time(NULL);
assertEqualInt(r, 0);
assertEmptyFile("out");
assertFileContents("1 block\n", 8, "err");
assertEqualInt(0, stat("file", &st));
/*
* mtime in reference archive is '1' == 1 second after
* midnight Jan 1, 1970 UTC.
*/
assertEqualInt(1, st.st_mtime);
}

View File

@ -0,0 +1,16 @@
$FreeBSD$
begin 644 test_option_m.cpio
M,#<P-S`W,#`P,3,Q-#4P,#8T,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
M,#`P,#`P,#`P,#`P,#$P,#`P,#4P,#`P,#`P,#`P,&9I;&4`,#<P-S`W,#`P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P,#`P,#`P
M,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,15(A(2$`````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
1````````````````````````
`
end

View File

@ -0,0 +1,47 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_t)
{
int r;
/* List reference archive, make sure the TOC is correct. */
extract_reference_file("test_option_t.cpio");
r = systemf("%s -it < test_option_t.cpio >t.out 2>t.err", testprog);
assertEqualInt(r, 0);
assertFileContents("1 block\n", 8, "t.err");
extract_reference_file("test_option_t.stdout");
assertEqualFile("t.out", "test_option_t.stdout");
/* List reference archive verbosely, make sure the TOC is correct. */
r = systemf("%s -itv < test_option_t.cpio >tv.out 2>tv.err", testprog);
assertEqualInt(r, 0);
assertFileContents("1 block\n", 8, "tv.err");
extract_reference_file("test_option_tv.stdout");
assertEqualFile("tv.out", "test_option_tv.stdout");
}

View File

@ -0,0 +1,16 @@
$FreeBSD$
begin 644 test_option_t.cpio
M,#<P-S`W,#`P,3,Q-#4P,#8T,3`P-C0T,#`Q-S4P,#`Q-S4P,#`P,#`Q,#`P
M,#`P,#`P,#`P,#`P,#$P,#`P,#4P,#`P,#`P,#`P,&9I;&4`,#<P-S`W,#`P
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`Q,#`P,#`P,#`P,#`P
M,#`P,#`P,#`P,3,P,#`P,#`P,#`P,%1204E,15(A(2$`````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
1````````````````````````
`
end

View File

@ -0,0 +1,5 @@
$FreeBSD$
begin 644 test_option_t.stdout
%9FEL90H`
`
end

View File

@ -0,0 +1,5 @@
$FreeBSD$
begin 644 test_option_tv.stdout
G+7)W+7(M+7(M+2`@(#$@("`H;G5L;"D@("AN=6QL*2`P(&9I;&4*
`
end

View File

@ -0,0 +1,84 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
#include <utime.h>
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_u)
{
struct utimbuf times;
char *p;
size_t s;
int fd;
int r;
/* Create a file. */
fd = open("f", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(1, write(fd, "a", 1));
close(fd);
/* Copy the file to the "copy" dir. */
r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
testprog);
assertEqualInt(r, 0);
/* Check that the file contains only a single "a" */
p = slurpfile(&s, "copy/f");
assertEqualInt(s, 1);
assertEqualMem(p, "a", 1);
/* Recreate the file with a single "b" */
fd = open("f", O_CREAT | O_TRUNC | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(1, write(fd, "b", 1));
close(fd);
/* Set the mtime to the distant past. */
memset(&times, 0, sizeof(times));
times.actime = 1;
times.modtime = 3;
assertEqualInt(0, utime("f", &times));
/* Copy the file to the "copy" dir. */
r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
testprog);
assertEqualInt(r, 0);
/* Verify that the file hasn't changed (it wasn't overwritten) */
p = slurpfile(&s, "copy/f");
assertEqualInt(s, 1);
assertEqualMem(p, "a", 1);
/* Copy the file to the "copy" dir with -u (force) */
r = systemf("echo f | %s -pud copy >copy.out 2>copy.err",
testprog);
assertEqualInt(r, 0);
/* Verify that the file has changed (it was overwritten) */
p = slurpfile(&s, "copy/f");
assertEqualInt(s, 1);
assertEqualMem(p, "b", 1);
}

View File

@ -0,0 +1,105 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
/*
* Test that --version option works and generates reasonable output.
*/
static void
verify(const char *p, size_t s)
{
const char *q = p;
/* Version message should start with name of program, then space. */
failure("version message too short:", p);
if (!assert(s > 6))
return;
failure("Version message should begin with 'bsdcpio': %s", p);
if (!assertEqualMem(q, "bsdcpio ", 8))
/* If we're not testing bsdcpio, don't keep going. */
return;
q += 8; s -= 8;
/* Version number is a series of digits and periods. */
while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
++q;
--s;
}
/* Version number terminated by space. */
failure("Version: %s", p);
assert(s > 1);
/* Skip a single trailing a,b,c, or d. */
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
failure("Version: %s", p);
assert(*q == ' ');
++q; --s;
/* Separator. */
failure("Version: %s", p);
assertEqualMem(q, "-- ", 3);
q += 3; s -= 3;
/* libarchive name and version number */
assert(s > 11);
failure("Version: %s", p);
assertEqualMem(q, "libarchive ", 11);
q += 11; s -= 11;
/* Version number is a series of digits and periods. */
while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
++q;
--s;
}
/* Skip a single trailing a,b,c, or d. */
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
/* All terminated by a newline. */
assert(s >= 1);
failure("Version: %s", p);
assertEqualMem(q, "\n", 1);
}
DEFINE_TEST(test_option_version)
{
int r;
char *p;
size_t s;
r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
if (r != 0)
r = systemf("%s -W version >version.stdout 2>version.stderr",
testprog);
failure("Unable to run either %s --version or %s -W version",
testprog, testprog);
if (!assert(r == 0))
return;
/* --version should generate nothing to stderr. */
assertEmptyFile("version.stderr");
/* Verify format of version message. */
p = slurpfile(&s, "version.stdout");
verify(p, s);
free(p);
}

View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_y)
{
char *p;
int fd;
int r;
size_t s;
/* Create a file. */
fd = open("f", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(1, write(fd, "a", 1));
close(fd);
/* Archive it with bzip2 compression. */
r = systemf("echo f | %s -oy >archive.out 2>archive.err",
testprog);
assertFileContents("1 block\n", 8, "archive.err");
failure("-y (bzip) option seems to be broken");
if (assertEqualInt(r, 0)) {
/* Check that the archive file has a bzip2 signature. */
p = slurpfile(&s, "archive.out");
assert(s > 2);
assertEqualMem(p, "BZh9", 4);
}
}

View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
DEFINE_TEST(test_option_z)
{
char *p;
int fd;
int r;
size_t s;
/* Create a file. */
fd = open("f", O_CREAT | O_WRONLY, 0644);
assert(fd >= 0);
assertEqualInt(1, write(fd, "a", 1));
close(fd);
/* Archive it with gzip compression. */
r = systemf("echo f | %s -oz >archive.out 2>archive.err",
testprog);
failure("-z option seems to be broken");
assertEqualInt(r, 0);
if (r == 0) {
/* Check that the archive file has a gzip signature. */
p = slurpfile(&s, "archive.out");
assert(s > 2);
assertEqualMem(p, "\x1f\x8b\x08\x00", 4);
}
}

View File

@ -0,0 +1,68 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
#include "../cpio.h"
DEFINE_TEST(test_owner_parse)
{
int uid, gid;
cpio_progname = "Ignore this message";
assertEqualInt(0, owner_parse("root", &uid, &gid));
assertEqualInt(0, uid);
assertEqualInt(-1, gid);
assertEqualInt(0, owner_parse("root:", &uid, &gid));
assertEqualInt(0, uid);
assertEqualInt(0, gid);
assertEqualInt(0, owner_parse("root.", &uid, &gid));
assertEqualInt(0, uid);
assertEqualInt(0, gid);
/*
* TODO: Lookup current user/group name, build strings and
* use those to verify username/groupname lookups for ordinary
* users.
*/
/*
* TODO: Rework owner_parse to either return a char * pointing
* to an error message or accept a function pointer to an
* error-reporting routine so that the following tests don't
* generate any output.
*
* Alternatively, redirect stderr temporarily to suppress the output.
*/
assertEqualInt(1, owner_parse(":nonexistentgroup", &uid, &gid));
assertEqualInt(1, owner_parse("root:nonexistentgroup", &uid, &gid));
assertEqualInt(1,
owner_parse("nonexistentuser:nonexistentgroup", &uid, &gid));
}

View File

@ -0,0 +1,165 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.h"
__FBSDID("$FreeBSD$");
#include "../pathmatch.h"
/*
* Verify that the pattern matcher implements the wildcard logic specified
* in SUSv2 for the cpio command. This is essentially the
* shell glob syntax:
* * - matches any sequence of chars, including '/'
* ? - matches any single char, including '/'
* [...] - matches any of a set of chars, '-' specifies a range,
* initial '!' is undefined
*
* The specification in SUSv2 is a bit incomplete, I assume the following:
* Trailing '-' in [...] is not special.
*/
DEFINE_TEST(test_pathmatch)
{
assertEqualInt(1, pathmatch("*","", 0));
assertEqualInt(1, pathmatch("*","a", 0));
assertEqualInt(1, pathmatch("*","abcd", 0));
/* SUSv2: * matches / */
assertEqualInt(1, pathmatch("*","abcd/efgh/ijkl", 0));
assertEqualInt(1, pathmatch("abcd*efgh/ijkl","abcd/efgh/ijkl", 0));
assertEqualInt(1, pathmatch("abcd***efgh/ijkl","abcd/efgh/ijkl", 0));
assertEqualInt(1, pathmatch("abcd***/efgh/ijkl","abcd/efgh/ijkl", 0));
assertEqualInt(0, pathmatch("?", "", 0));
assertEqualInt(0, pathmatch("?", "\0", 0));
assertEqualInt(1, pathmatch("?", "a", 0));
assertEqualInt(0, pathmatch("?", "ab", 0));
assertEqualInt(1, pathmatch("?", ".", 0));
assertEqualInt(1, pathmatch("?", "?", 0));
assertEqualInt(1, pathmatch("a", "a", 0));
assertEqualInt(0, pathmatch("a", "ab", 0));
assertEqualInt(0, pathmatch("a", "ab", 0));
assertEqualInt(1, pathmatch("a?c", "abc", 0));
/* SUSv2: ? matches / */
assertEqualInt(1, pathmatch("a?c", "a/c", 0));
assertEqualInt(1, pathmatch("a?*c*", "a/c", 0));
assertEqualInt(1, pathmatch("*a*", "a/c", 0));
assertEqualInt(1, pathmatch("*a*", "/a/c", 0));
assertEqualInt(1, pathmatch("*a*", "defaaaaaaa", 0));
assertEqualInt(0, pathmatch("a*", "defghi", 0));
assertEqualInt(0, pathmatch("*a*", "defghi", 0));
assertEqualInt(1, pathmatch("abc[def", "abc[def", 0));
assertEqualInt(0, pathmatch("abc[def]", "abc[def", 0));
assertEqualInt(0, pathmatch("abc[def", "abcd", 0));
assertEqualInt(1, pathmatch("abc[def]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[def]", "abce", 0));
assertEqualInt(1, pathmatch("abc[def]", "abcf", 0));
assertEqualInt(0, pathmatch("abc[def]", "abcg", 0));
assertEqualInt(1, pathmatch("abc[d*f]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[d*f]", "abc*", 0));
assertEqualInt(0, pathmatch("abc[d*f]", "abcdefghi", 0));
assertEqualInt(0, pathmatch("abc[d*", "abcdefghi", 0));
assertEqualInt(1, pathmatch("abc[d*", "abc[defghi", 0));
assertEqualInt(1, pathmatch("abc[d-f]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[d-f]", "abce", 0));
assertEqualInt(1, pathmatch("abc[d-f]", "abcf", 0));
assertEqualInt(0, pathmatch("abc[d-f]", "abcg", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abce", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcf", 0));
assertEqualInt(0, pathmatch("abc[d-fh-k]", "abcg", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abch", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abci", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abcj", 0));
assertEqualInt(1, pathmatch("abc[d-fh-k]", "abck", 0));
assertEqualInt(0, pathmatch("abc[d-fh-k]", "abcl", 0));
assertEqualInt(0, pathmatch("abc[d-fh-k]", "abc-", 0));
/* I assume: Trailing '-' is non-special. */
assertEqualInt(0, pathmatch("abc[d-fh-]", "abcl", 0));
assertEqualInt(1, pathmatch("abc[d-fh-]", "abch", 0));
assertEqualInt(1, pathmatch("abc[d-fh-]", "abc-", 0));
assertEqualInt(1, pathmatch("abc[d-fh-]", "abc-", 0));
/* ']' can be backslash-quoted within a character class. */
assertEqualInt(1, pathmatch("abc[\\]]", "abc]", 0));
assertEqualInt(1, pathmatch("abc[\\]d]", "abc]", 0));
assertEqualInt(1, pathmatch("abc[\\]d]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[d\\]]", "abc]", 0));
assertEqualInt(1, pathmatch("abc[d\\]]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[d]e]", "abcde]", 0));
assertEqualInt(1, pathmatch("abc[d\\]e]", "abc]", 0));
assertEqualInt(0, pathmatch("abc[d\\]e]", "abcd]e", 0));
assertEqualInt(0, pathmatch("abc[d]e]", "abc]", 0));
/* backslash-quoted chars can appear as either end of a range. */
assertEqualInt(1, pathmatch("abc[\\d-f]gh", "abcegh", 0));
assertEqualInt(0, pathmatch("abc[\\d-f]gh", "abcggh", 0));
assertEqualInt(0, pathmatch("abc[\\d-f]gh", "abc\\gh", 0));
assertEqualInt(1, pathmatch("abc[d-\\f]gh", "abcegh", 0));
assertEqualInt(1, pathmatch("abc[\\d-\\f]gh", "abcegh", 0));
assertEqualInt(1, pathmatch("abc[\\d-\\f]gh", "abcegh", 0));
/* backslash-quoted '-' isn't special. */
assertEqualInt(0, pathmatch("abc[d\\-f]gh", "abcegh", 0));
assertEqualInt(1, pathmatch("abc[d\\-f]gh", "abc-gh", 0));
/* Leading '!' negates a character class. */
assertEqualInt(0, pathmatch("abc[!d]", "abcd", 0));
assertEqualInt(1, pathmatch("abc[!d]", "abce", 0));
assertEqualInt(1, pathmatch("abc[!d]", "abcc", 0));
assertEqualInt(0, pathmatch("abc[!d-z]", "abcq", 0));
assertEqualInt(1, pathmatch("abc[!d-gi-z]", "abch", 0));
assertEqualInt(1, pathmatch("abc[!fgijkl]", "abch", 0));
assertEqualInt(0, pathmatch("abc[!fghijkl]", "abch", 0));
/* Backslash quotes next character. */
assertEqualInt(0, pathmatch("abc\\[def]", "abc\\d", 0));
assertEqualInt(1, pathmatch("abc\\[def]", "abc[def]", 0));
assertEqualInt(0, pathmatch("abc\\\\[def]", "abc[def]", 0));
assertEqualInt(0, pathmatch("abc\\\\[def]", "abc\\[def]", 0));
assertEqualInt(1, pathmatch("abc\\\\[def]", "abc\\d", 0));
/*
* Because '.' and '/' have special meanings, we can
* identify many equivalent paths even if they're expressed
* differently.
*/
assertEqualInt(1, pathmatch("./abc/./def/", "abc/def/", 0));
assertEqualInt(1, pathmatch("abc/def", "./././abc/./def", 0));
assertEqualInt(1, pathmatch("abc/def/././//", "./././abc/./def/", 0));
assertEqualInt(1, pathmatch(".////abc/.//def", "./././abc/./def", 0));
assertEqualInt(1, pathmatch("./abc?def/", "abc/def/", 0));
failure("\"?./\" is not the same as \"/./\"");
assertEqualInt(0, pathmatch("./abc?./def/", "abc/def/", 0));
failure("Trailing '/' should match no trailing '/'");
assertEqualInt(1, pathmatch("./abc/./def/", "abc/def", 0));
failure("Trailing '/./' is still the same directory.");
assertEqualInt(1, pathmatch("./abc/./def/./", "abc/def", 0));
failure("Trailing '/.' is still the same directory.");
assertEqualInt(1, pathmatch("./abc/./def/.", "abc/def", 0));
assertEqualInt(1, pathmatch("./abc/./def", "abc/def/", 0));
failure("Trailing '/./' is still the same directory.");
assertEqualInt(1, pathmatch("./abc/./def", "abc/def/./", 0));
failure("Trailing '/.' is still the same directory.");
assertEqualInt(1, pathmatch("./abc*/./def", "abc/def/.", 0));
}