|
Packit Bot |
3f3af5 |
/* Run a test case in an isolated namespace.
|
|
Packit Bot |
3f3af5 |
Copyright (C) 2018 Free Software Foundation, Inc.
|
|
Packit Bot |
3f3af5 |
This file is part of the GNU C Library.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
The GNU C Library is free software; you can redistribute it and/or
|
|
Packit Bot |
3f3af5 |
modify it under the terms of the GNU Lesser General Public
|
|
Packit Bot |
3f3af5 |
License as published by the Free Software Foundation; either
|
|
Packit Bot |
3f3af5 |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
The GNU C Library is distributed in the hope that it will be useful,
|
|
Packit Bot |
3f3af5 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Bot |
3f3af5 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Bot |
3f3af5 |
Lesser General Public License for more details.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit Bot |
3f3af5 |
License along with the GNU C Library; if not, see
|
|
Packit Bot |
3f3af5 |
<http://www.gnu.org/licenses/>. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#define _FILE_OFFSET_BITS 64
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#include <stdio.h>
|
|
Packit Bot |
3f3af5 |
#include <stdlib.h>
|
|
Packit Bot |
3f3af5 |
#include <string.h>
|
|
Packit Bot |
3f3af5 |
#include <sched.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/syscall.h>
|
|
Packit Bot |
3f3af5 |
#include <unistd.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/types.h>
|
|
Packit Bot |
3f3af5 |
#include <dirent.h>
|
|
Packit Bot |
3f3af5 |
#include <string.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/stat.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/fcntl.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/file.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/wait.h>
|
|
Packit Bot |
3f3af5 |
#include <stdarg.h>
|
|
Packit Bot |
3f3af5 |
#include <sys/sysmacros.h>
|
|
Packit Bot |
3f3af5 |
#include <ctype.h>
|
|
Packit Bot |
3f3af5 |
#include <utime.h>
|
|
Packit Bot |
3f3af5 |
#include <errno.h>
|
|
Packit Bot |
3f3af5 |
#include <error.h>
|
|
Packit Bot |
3f3af5 |
#include <libc-pointer-arith.h>
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#ifdef __linux__
|
|
Packit Bot |
3f3af5 |
#include <sys/mount.h>
|
|
Packit Bot |
3f3af5 |
#endif
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#include <support/support.h>
|
|
Packit Bot |
3f3af5 |
#include <support/xunistd.h>
|
|
Packit Bot |
3f3af5 |
#include "check.h"
|
|
Packit Bot |
3f3af5 |
#include "test-driver.h"
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#ifndef __linux__
|
|
Packit Bot |
3f3af5 |
#define mount(s,t,fs,f,d) no_mount()
|
|
Packit Bot |
3f3af5 |
int no_mount (void)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
FAIL_UNSUPPORTED("mount not supported; port needed");
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
#endif
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
int verbose = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Running a test in a container is tricky. There are two main
|
|
Packit Bot |
3f3af5 |
categories of things to do:
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
1. "Once" actions, like setting up the container and doing an
|
|
Packit Bot |
3f3af5 |
install into it.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
2. "Per-test" actions, like copying in support files and
|
|
Packit Bot |
3f3af5 |
configuring the container.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
"Once" actions:
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
* mkdir $buildroot/testroot.pristine/
|
|
Packit Bot |
3f3af5 |
* install into it
|
|
Packit Bot |
3f3af5 |
* rsync to $buildroot/testroot.root/
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
"Per-test" actions:
|
|
Packit Bot |
3f3af5 |
* maybe rsync to $buildroot/testroot.root/
|
|
Packit Bot |
3f3af5 |
* copy support files and test binary
|
|
Packit Bot |
3f3af5 |
* chroot/unshare
|
|
Packit Bot |
3f3af5 |
* set up any mounts (like /proc)
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
Magic files:
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
|
|
Packit Bot |
3f3af5 |
and, if found...
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
* mytest.root/ is rsync'd into container
|
|
Packit Bot |
3f3af5 |
* mytest.root/preclean.req causes fresh rsync (with delete) before
|
|
Packit Bot |
3f3af5 |
test if present
|
|
Packit Bot |
3f3af5 |
* mytest.root/mytset.script has a list of "commands" to run:
|
|
Packit Bot |
3f3af5 |
syntax:
|
|
Packit Bot |
3f3af5 |
# comment
|
|
Packit Bot |
3f3af5 |
mv FILE FILE
|
|
Packit Bot |
3f3af5 |
cp FILE FILE
|
|
Packit Bot |
3f3af5 |
rm FILE
|
|
Packit Bot |
3f3af5 |
FILE must start with $B/, $S/, $I/, $L/, or /
|
|
Packit Bot |
3f3af5 |
(expands to build dir, source dir, install dir, library dir
|
|
Packit Bot |
3f3af5 |
(in container), or container's root)
|
|
Packit Bot |
3f3af5 |
* mytest.root/postclean.req causes fresh rsync (with delete) after
|
|
Packit Bot |
3f3af5 |
test if present
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
Note that $srcdir/foo/mytest.script may be used instead of a
|
|
Packit Bot |
3f3af5 |
$srcdir/foo/mytest.root/mytest.script in the sysroot template, if
|
|
Packit Bot |
3f3af5 |
there is no other reason for a sysroot.
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
Design goals:
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
* independent of other packages which may not be installed (like
|
|
Packit Bot |
3f3af5 |
rsync or Docker, or even "cp")
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
* Simple, easy to review code (i.e. prefer simple naive code over
|
|
Packit Bot |
3f3af5 |
complex efficient code)
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
* The current implementation ist parallel-make-safe, but only in
|
|
Packit Bot |
3f3af5 |
that it uses a lock to prevent parallel access to the testroot. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Utility Functions */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Like xunlink, but it's OK if the file already doesn't exist. */
|
|
Packit Bot |
3f3af5 |
void
|
|
Packit Bot |
3f3af5 |
maybe_xunlink (const char *path)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int rv = unlink (path);
|
|
Packit Bot |
3f3af5 |
if (rv < 0 && errno != ENOENT)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Like xmkdir, but it's OK if the directory already exists. */
|
|
Packit Bot |
3f3af5 |
void
|
|
Packit Bot |
3f3af5 |
maybe_xmkdir (const char *path, mode_t mode)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
struct stat st;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (stat (path, &st) == 0
|
|
Packit Bot |
3f3af5 |
&& S_ISDIR (st.st_mode))
|
|
Packit Bot |
3f3af5 |
return;
|
|
Packit Bot |
3f3af5 |
xmkdir (path, mode);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Temporarily concatenate multiple strings into one. Allows up to 10
|
|
Packit Bot |
3f3af5 |
temporary results; use strdup () if you need them to be
|
|
Packit Bot |
3f3af5 |
permanent. */
|
|
Packit Bot |
3f3af5 |
static char *
|
|
Packit Bot |
3f3af5 |
concat (const char *str, ...)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Assume initialized to NULL/zero. */
|
|
Packit Bot |
3f3af5 |
static char *bufs[10];
|
|
Packit Bot |
3f3af5 |
static size_t buflens[10];
|
|
Packit Bot |
3f3af5 |
static int bufn = 0;
|
|
Packit Bot |
3f3af5 |
int n;
|
|
Packit Bot |
3f3af5 |
size_t len;
|
|
Packit Bot |
3f3af5 |
va_list ap, ap2;
|
|
Packit Bot |
3f3af5 |
char *cp;
|
|
Packit Bot |
3f3af5 |
char *next;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
va_start (ap, str);
|
|
Packit Bot |
3f3af5 |
va_copy (ap2, ap);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
n = bufn;
|
|
Packit Bot |
3f3af5 |
bufn = (bufn + 1) % 10;
|
|
Packit Bot |
3f3af5 |
len = strlen (str);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while ((next = va_arg (ap, char *)) != NULL)
|
|
Packit Bot |
3f3af5 |
len = len + strlen (next);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
va_end (ap);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (bufs[n] == NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
bufs[n] = xmalloc (len + 1); /* NUL */
|
|
Packit Bot |
3f3af5 |
buflens[n] = len + 1;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (buflens[n] < len + 1)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
|
|
Packit Bot |
3f3af5 |
buflens[n] = len + 1;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
strcpy (bufs[n], str);
|
|
Packit Bot |
3f3af5 |
cp = strchr (bufs[n], '\0');
|
|
Packit Bot |
3f3af5 |
while ((next = va_arg (ap2, char *)) != NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
strcpy (cp, next);
|
|
Packit Bot |
3f3af5 |
cp = strchr (cp, '\0');
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
*cp = 0;
|
|
Packit Bot |
3f3af5 |
va_end (ap2);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
return bufs[n];
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Try to mount SRC onto DEST. */
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
trymount (const char *src, const char *dest)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (mount (src, dest, "", MS_BIND, NULL) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Special case of above for devices like /dev/zero where we have to
|
|
Packit Bot |
3f3af5 |
mount a device over a device, not a directory over a directory. */
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
devmount (const char *new_root_path, const char *which)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int fd;
|
|
Packit Bot |
3f3af5 |
fd = open (concat (new_root_path, "/dev/", which, NULL),
|
|
Packit Bot |
3f3af5 |
O_CREAT | O_TRUNC | O_RDWR, 0777);
|
|
Packit Bot |
3f3af5 |
xclose (fd);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
trymount (concat ("/dev/", which, NULL),
|
|
Packit Bot |
3f3af5 |
concat (new_root_path, "/dev/", which, NULL));
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Returns true if the string "looks like" an environement variable
|
|
Packit Bot |
3f3af5 |
being set. */
|
|
Packit Bot |
3f3af5 |
static int
|
|
Packit Bot |
3f3af5 |
is_env_setting (const char *a)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int count_name = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while (*a)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (isalnum (*a) || *a == '_')
|
|
Packit Bot |
3f3af5 |
++count_name;
|
|
Packit Bot |
3f3af5 |
else if (*a == '=' && count_name > 0)
|
|
Packit Bot |
3f3af5 |
return 1;
|
|
Packit Bot |
3f3af5 |
else
|
|
Packit Bot |
3f3af5 |
return 0;
|
|
Packit Bot |
3f3af5 |
++a;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
return 0;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Break the_line into words and store in the_words. Max nwords,
|
|
Packit Bot |
3f3af5 |
returns actual count. */
|
|
Packit Bot |
3f3af5 |
static int
|
|
Packit Bot |
3f3af5 |
tokenize (char *the_line, char **the_words, int nwords)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int rv = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while (nwords > 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Skip leading whitespace, if any. */
|
|
Packit Bot |
3f3af5 |
while (*the_line && isspace (*the_line))
|
|
Packit Bot |
3f3af5 |
++the_line;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* End of line? */
|
|
Packit Bot |
3f3af5 |
if (*the_line == 0)
|
|
Packit Bot |
3f3af5 |
return rv;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* THE_LINE points to a non-whitespace character, so we have a
|
|
Packit Bot |
3f3af5 |
word. */
|
|
Packit Bot |
3f3af5 |
*the_words = the_line;
|
|
Packit Bot |
3f3af5 |
++the_words;
|
|
Packit Bot |
3f3af5 |
nwords--;
|
|
Packit Bot |
3f3af5 |
++rv;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Skip leading whitespace, if any. */
|
|
Packit Bot |
3f3af5 |
while (*the_line && ! isspace (*the_line))
|
|
Packit Bot |
3f3af5 |
++the_line;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We now point at the trailing NUL *or* some whitespace. */
|
|
Packit Bot |
3f3af5 |
if (*the_line == 0)
|
|
Packit Bot |
3f3af5 |
return rv;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* It was whitespace, skip and keep tokenizing. */
|
|
Packit Bot |
3f3af5 |
*the_line++ = 0;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We get here if we filled the words buffer. */
|
|
Packit Bot |
3f3af5 |
return rv;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Mini-RSYNC implementation. Optimize later. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* A few routines for an "rsync buffer" which stores the paths we're
|
|
Packit Bot |
3f3af5 |
working on. We continuously grow and shrink the paths in each
|
|
Packit Bot |
3f3af5 |
buffer so there's lot of re-use. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We rely on "initialized to zero" to set these up. */
|
|
Packit Bot |
3f3af5 |
typedef struct
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
char *buf;
|
|
Packit Bot |
3f3af5 |
size_t len;
|
|
Packit Bot |
3f3af5 |
size_t size;
|
|
Packit Bot |
3f3af5 |
} path_buf;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static path_buf spath, dpath;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
r_setup (char *path, path_buf * pb)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
size_t len = strlen (path);
|
|
Packit Bot |
3f3af5 |
if (pb->buf == NULL || pb->size < len + 1)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Round up. This is an arbitrary number, just to keep from
|
|
Packit Bot |
3f3af5 |
reallocing too often. */
|
|
Packit Bot |
3f3af5 |
size_t sz = ALIGN_UP (len + 1, 512);
|
|
Packit Bot |
3f3af5 |
if (pb->buf == NULL)
|
|
Packit Bot |
3f3af5 |
pb->buf = (char *) xmalloc (sz);
|
|
Packit Bot |
3f3af5 |
else
|
|
Packit Bot |
3f3af5 |
pb->buf = (char *) xrealloc (pb->buf, sz);
|
|
Packit Bot |
3f3af5 |
if (pb->buf == NULL)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Out of memory while rsyncing\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
pb->size = sz;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
strcpy (pb->buf, path);
|
|
Packit Bot |
3f3af5 |
pb->len = len;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
r_append (const char *path, path_buf * pb)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
size_t len = strlen (path) + pb->len;
|
|
Packit Bot |
3f3af5 |
if (pb->size < len + 1)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Round up */
|
|
Packit Bot |
3f3af5 |
size_t sz = ALIGN_UP (len + 1, 512);
|
|
Packit Bot |
3f3af5 |
pb->buf = (char *) xrealloc (pb->buf, sz);
|
|
Packit Bot |
3f3af5 |
if (pb->buf == NULL)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Out of memory while rsyncing\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
pb->size = sz;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
strcpy (pb->buf + pb->len, path);
|
|
Packit Bot |
3f3af5 |
pb->len = len;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static int
|
|
Packit Bot |
3f3af5 |
file_exists (char *path)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
struct stat st;
|
|
Packit Bot |
3f3af5 |
if (lstat (path, &st) == 0)
|
|
Packit Bot |
3f3af5 |
return 1;
|
|
Packit Bot |
3f3af5 |
return 0;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
recursive_remove (char *path)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
pid_t child;
|
|
Packit Bot |
3f3af5 |
int status;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
child = fork ();
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
switch (child) {
|
|
Packit Bot |
3f3af5 |
case -1:
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Unable to fork");
|
|
Packit Bot |
3f3af5 |
case 0:
|
|
Packit Bot |
3f3af5 |
/* Child. */
|
|
Packit Bot |
3f3af5 |
execlp ("rm", "rm", "-rf", path, NULL);
|
|
Packit Bot |
3f3af5 |
default:
|
|
Packit Bot |
3f3af5 |
/* Parent. */
|
|
Packit Bot |
3f3af5 |
waitpid (child, &status, 0);
|
|
Packit Bot |
3f3af5 |
/* "rm" would have already printed a suitable error message. */
|
|
Packit Bot |
3f3af5 |
if (! WIFEXITED (status)
|
|
Packit Bot |
3f3af5 |
|| WEXITSTATUS (status) != 0)
|
|
Packit Bot |
3f3af5 |
exit (1);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Used for both rsync and the mytest.script "cp" command. */
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
copy_one_file (const char *sname, const char *dname)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int sfd, dfd;
|
|
Packit Bot |
3f3af5 |
struct stat st;
|
|
Packit Bot |
3f3af5 |
struct utimbuf times;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
sfd = open (sname, O_RDONLY);
|
|
Packit Bot |
3f3af5 |
if (sfd < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("unable to open %s for reading\n", sname);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (fstat (sfd, &st) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("unable to fstat %s\n", sname);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
|
|
Packit Bot |
3f3af5 |
if (dfd < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("unable to open %s for writing\n", dname);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
xcopy_file_range (sfd, 0, dfd, 0, st.st_size, 0);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
xclose (sfd);
|
|
Packit Bot |
3f3af5 |
xclose (dfd);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (chmod (dname, st.st_mode & 0777) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
times.actime = st.st_atime;
|
|
Packit Bot |
3f3af5 |
times.modtime = st.st_mtime;
|
|
Packit Bot |
3f3af5 |
if (utime (dname, ×) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We don't check *everything* about the two files to see if a copy is
|
|
Packit Bot |
3f3af5 |
needed, just the minimum to make sure we get the latest copy. */
|
|
Packit Bot |
3f3af5 |
static int
|
|
Packit Bot |
3f3af5 |
need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
|
|
Packit Bot |
3f3af5 |
return 1;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (S_ISLNK (a->st_mode))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int rv;
|
|
Packit Bot |
3f3af5 |
char *al, *bl;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (a->st_size != b->st_size)
|
|
Packit Bot |
3f3af5 |
return 1;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
al = xreadlink (ap);
|
|
Packit Bot |
3f3af5 |
bl = xreadlink (bp);
|
|
Packit Bot |
3f3af5 |
rv = strcmp (al, bl);
|
|
Packit Bot |
3f3af5 |
free (al);
|
|
Packit Bot |
3f3af5 |
free (bl);
|
|
Packit Bot |
3f3af5 |
if (rv == 0)
|
|
Packit Bot |
3f3af5 |
return 0; /* links are same */
|
|
Packit Bot |
3f3af5 |
return 1; /* links differ */
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (a->st_size != b->st_size)
|
|
Packit Bot |
3f3af5 |
printf ("SIZE\n");
|
|
Packit Bot |
3f3af5 |
if ((a->st_mode & 0777) != (b->st_mode & 0777))
|
|
Packit Bot |
3f3af5 |
printf ("MODE\n");
|
|
Packit Bot |
3f3af5 |
if (a->st_mtime != b->st_mtime)
|
|
Packit Bot |
3f3af5 |
printf ("TIME\n");
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (a->st_size == b->st_size
|
|
Packit Bot |
3f3af5 |
&& ((a->st_mode & 0777) == (b->st_mode & 0777))
|
|
Packit Bot |
3f3af5 |
&& a->st_mtime == b->st_mtime)
|
|
Packit Bot |
3f3af5 |
return 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
return 1;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
rsync_1 (path_buf * src, path_buf * dest, int and_delete)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
DIR *dir;
|
|
Packit Bot |
3f3af5 |
struct dirent *de;
|
|
Packit Bot |
3f3af5 |
struct stat s, d;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
r_append ("/", src);
|
|
Packit Bot |
3f3af5 |
r_append ("/", dest);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("sync %s to %s %s\n", src->buf, dest->buf,
|
|
Packit Bot |
3f3af5 |
and_delete ? "and delete" : "");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
size_t staillen = src->len;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
size_t dtaillen = dest->len;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
dir = opendir (src->buf);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while ((de = readdir (dir)) != NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (strcmp (de->d_name, ".") == 0
|
|
Packit Bot |
3f3af5 |
|| strcmp (de->d_name, "..") == 0)
|
|
Packit Bot |
3f3af5 |
continue;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
src->len = staillen;
|
|
Packit Bot |
3f3af5 |
r_append (de->d_name, src);
|
|
Packit Bot |
3f3af5 |
dest->len = dtaillen;
|
|
Packit Bot |
3f3af5 |
r_append (de->d_name, dest);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
s.st_mode = ~0;
|
|
Packit Bot |
3f3af5 |
d.st_mode = ~0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (lstat (src->buf, &s) != 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* It's OK if this one fails, since we know the file might be
|
|
Packit Bot |
3f3af5 |
missing. */
|
|
Packit Bot |
3f3af5 |
lstat (dest->buf, &d);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (! need_sync (src->buf, dest->buf, &s, &d))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (S_ISDIR (s.st_mode))
|
|
Packit Bot |
3f3af5 |
rsync_1 (src, dest, and_delete);
|
|
Packit Bot |
3f3af5 |
continue;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (d.st_mode != ~0)
|
|
Packit Bot |
3f3af5 |
switch (d.st_mode & S_IFMT)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
case S_IFDIR:
|
|
Packit Bot |
3f3af5 |
if (!S_ISDIR (s.st_mode))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("-D %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
recursive_remove (dest->buf);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
default:
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("-F %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
maybe_xunlink (dest->buf);
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
switch (s.st_mode & S_IFMT)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
case S_IFREG:
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("+F %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
copy_one_file (src->buf, dest->buf);
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
case S_IFDIR:
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("+D %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
|
|
Packit Bot |
3f3af5 |
rsync_1 (src, dest, and_delete);
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
case S_IFLNK:
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
char *lp;
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("+L %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
lp = xreadlink (src->buf);
|
|
Packit Bot |
3f3af5 |
xsymlink (lp, dest->buf);
|
|
Packit Bot |
3f3af5 |
free (lp);
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
default:
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
closedir (dir);
|
|
Packit Bot |
3f3af5 |
src->len = staillen;
|
|
Packit Bot |
3f3af5 |
src->buf[staillen] = 0;
|
|
Packit Bot |
3f3af5 |
dest->len = dtaillen;
|
|
Packit Bot |
3f3af5 |
dest->buf[dtaillen] = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (!and_delete)
|
|
Packit Bot |
3f3af5 |
return;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* The rest of this function removes any files/directories in DEST
|
|
Packit Bot |
3f3af5 |
that do not exist in SRC. This is triggered as part of a
|
|
Packit Bot |
3f3af5 |
preclean or postsclean step. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
dir = opendir (dest->buf);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while ((de = readdir (dir)) != NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (strcmp (de->d_name, ".") == 0
|
|
Packit Bot |
3f3af5 |
|| strcmp (de->d_name, "..") == 0)
|
|
Packit Bot |
3f3af5 |
continue;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
src->len = staillen;
|
|
Packit Bot |
3f3af5 |
r_append (de->d_name, src);
|
|
Packit Bot |
3f3af5 |
dest->len = dtaillen;
|
|
Packit Bot |
3f3af5 |
r_append (de->d_name, dest);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
s.st_mode = ~0;
|
|
Packit Bot |
3f3af5 |
d.st_mode = ~0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
lstat (src->buf, &s);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (lstat (dest->buf, &d) != 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (s.st_mode == ~0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* dest exists and src doesn't, clean it. */
|
|
Packit Bot |
3f3af5 |
switch (d.st_mode & S_IFMT)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
case S_IFDIR:
|
|
Packit Bot |
3f3af5 |
if (!S_ISDIR (s.st_mode))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("-D %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
recursive_remove (dest->buf);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
default:
|
|
Packit Bot |
3f3af5 |
if (verbose)
|
|
Packit Bot |
3f3af5 |
printf ("-F %s\n", dest->buf);
|
|
Packit Bot |
3f3af5 |
maybe_xunlink (dest->buf);
|
|
Packit Bot |
3f3af5 |
break;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
closedir (dir);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
static void
|
|
Packit Bot |
3f3af5 |
rsync (char *src, char *dest, int and_delete)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
r_setup (src, &spath);
|
|
Packit Bot |
3f3af5 |
r_setup (dest, &dpath);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
rsync_1 (&spath, &dpath, and_delete);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
int
|
|
Packit Bot |
3f3af5 |
main (int argc, char **argv)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
pid_t child;
|
|
Packit Bot |
3f3af5 |
char *pristine_root_path;
|
|
Packit Bot |
3f3af5 |
char *new_root_path;
|
|
Packit Bot |
3f3af5 |
char *new_cwd_path;
|
|
Packit Bot |
3f3af5 |
char *new_objdir_path;
|
|
Packit Bot |
3f3af5 |
char *new_srcdir_path;
|
|
Packit Bot |
3f3af5 |
char **new_child_proc;
|
|
Packit Bot |
3f3af5 |
char *command_root;
|
|
Packit Bot |
3f3af5 |
char *command_base;
|
|
Packit Bot |
3f3af5 |
char *command_basename;
|
|
Packit Bot |
3f3af5 |
char *so_base;
|
|
Packit Bot |
3f3af5 |
int do_postclean = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
uid_t original_uid;
|
|
Packit Bot |
3f3af5 |
gid_t original_gid;
|
|
Packit Bot |
3f3af5 |
int UMAP;
|
|
Packit Bot |
3f3af5 |
int GMAP;
|
|
Packit Bot |
3f3af5 |
/* Used for "%lld %lld 1" so need not be large. */
|
|
Packit Bot |
3f3af5 |
char tmp[100];
|
|
Packit Bot |
3f3af5 |
struct stat st;
|
|
Packit Bot |
3f3af5 |
int lock_fd;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
setbuf (stdout, NULL);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* The command line we're expecting looks like this:
|
|
Packit Bot |
3f3af5 |
env <set some vars> ld.so <library path> test-binary
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
We need to peel off any "env" or "ld.so" portion of the command
|
|
Packit Bot |
3f3af5 |
line, and keep track of which env vars we should preserve and
|
|
Packit Bot |
3f3af5 |
which we drop. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (argc < 2)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
|
|
Packit Bot |
3f3af5 |
exit (1);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (strcmp (argv[1], "-v") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
verbose = 1;
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (strcmp (argv[1], "env") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
while (is_env_setting (argv[1]))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* If there are variables we do NOT want to propogate, this
|
|
Packit Bot |
3f3af5 |
is where the test for them goes. */
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Need to keep these. Note that putenv stores a
|
|
Packit Bot |
3f3af5 |
pointer to our argv. */
|
|
Packit Bot |
3f3af5 |
putenv (argv[1]);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
while (argv[1][0] == '-')
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (strcmp (argv[1], "--library-path") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
++argv;
|
|
Packit Bot |
3f3af5 |
--argc;
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
pristine_root_path = strdup (concat (support_objdir_root,
|
|
Packit Bot |
3f3af5 |
"/testroot.pristine", NULL));
|
|
Packit Bot |
3f3af5 |
new_root_path = strdup (concat (support_objdir_root,
|
|
Packit Bot |
3f3af5 |
"/testroot.root", NULL));
|
|
Packit Bot |
3f3af5 |
new_cwd_path = get_current_dir_name ();
|
|
Packit Bot |
3f3af5 |
new_child_proc = argv + 1;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
|
|
Packit Bot |
3f3af5 |
O_CREAT | O_TRUNC | O_RDWR, 0666);
|
|
Packit Bot |
3f3af5 |
if (lock_fd < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Cannot create testroot lock.\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
while (flock (lock_fd, LOCK_EX) != 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (errno != EINTR)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Cannot lock testroot.\n");
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
xmkdirp (new_root_path, 0755);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We look for extra setup info in a subdir in the same spot as the
|
|
Packit Bot |
3f3af5 |
test, with the same name but a ".root" extension. This is that
|
|
Packit Bot |
3f3af5 |
directory. We try to look in the source tree if the path we're
|
|
Packit Bot |
3f3af5 |
given refers to the build tree, but we rely on the path to be
|
|
Packit Bot |
3f3af5 |
absolute. This is what the glibc makefiles do. */
|
|
Packit Bot |
3f3af5 |
command_root = concat (argv[1], ".root", NULL);
|
|
Packit Bot |
3f3af5 |
if (strncmp (command_root, support_objdir_root,
|
|
Packit Bot |
3f3af5 |
strlen (support_objdir_root)) == 0
|
|
Packit Bot |
3f3af5 |
&& command_root[strlen (support_objdir_root)] == '/')
|
|
Packit Bot |
3f3af5 |
command_root = concat (support_srcdir_root,
|
|
Packit Bot |
3f3af5 |
argv[1] + strlen (support_objdir_root),
|
|
Packit Bot |
3f3af5 |
".root", NULL);
|
|
Packit Bot |
3f3af5 |
command_root = strdup (command_root);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* This cuts off the ".root" we appended above. */
|
|
Packit Bot |
3f3af5 |
command_base = strdup (command_root);
|
|
Packit Bot |
3f3af5 |
command_base[strlen (command_base) - 5] = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* This is the basename of the test we're running. */
|
|
Packit Bot |
3f3af5 |
command_basename = strrchr (command_base, '/');
|
|
Packit Bot |
3f3af5 |
if (command_basename == NULL)
|
|
Packit Bot |
3f3af5 |
command_basename = command_base;
|
|
Packit Bot |
3f3af5 |
else
|
|
Packit Bot |
3f3af5 |
++command_basename;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Shared object base directory. */
|
|
Packit Bot |
3f3af5 |
so_base = strdup (argv[1]);
|
|
Packit Bot |
3f3af5 |
if (strrchr (so_base, '/') != NULL)
|
|
Packit Bot |
3f3af5 |
strrchr (so_base, '/')[1] = 0;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (file_exists (concat (command_root, "/postclean.req", NULL)))
|
|
Packit Bot |
3f3af5 |
do_postclean = 1;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
rsync (pristine_root_path, new_root_path,
|
|
Packit Bot |
3f3af5 |
file_exists (concat (command_root, "/preclean.req", NULL)));
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (stat (command_root, &st) >= 0
|
|
Packit Bot |
3f3af5 |
&& S_ISDIR (st.st_mode))
|
|
Packit Bot |
3f3af5 |
rsync (command_root, new_root_path, 0);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
new_objdir_path = strdup (concat (new_root_path,
|
|
Packit Bot |
3f3af5 |
support_objdir_root, NULL));
|
|
Packit Bot |
3f3af5 |
new_srcdir_path = strdup (concat (new_root_path,
|
|
Packit Bot |
3f3af5 |
support_srcdir_root, NULL));
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* new_cwd_path starts with '/' so no "/" needed between the two. */
|
|
Packit Bot |
3f3af5 |
xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
|
|
Packit Bot |
3f3af5 |
xmkdirp (new_srcdir_path, 0755);
|
|
Packit Bot |
3f3af5 |
xmkdirp (new_objdir_path, 0755);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
original_uid = getuid ();
|
|
Packit Bot |
3f3af5 |
original_gid = getgid ();
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Handle the cp/mv/rm "script" here. */
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
char *the_line = NULL;
|
|
Packit Bot |
3f3af5 |
size_t line_len = 0;
|
|
Packit Bot |
3f3af5 |
char *fname = concat (command_root, "/",
|
|
Packit Bot |
3f3af5 |
command_basename, ".script", NULL);
|
|
Packit Bot |
3f3af5 |
char *the_words[3];
|
|
Packit Bot |
3f3af5 |
FILE *f = fopen (fname, "r");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (verbose && f)
|
|
Packit Bot |
3f3af5 |
fprintf (stderr, "running %s\n", fname);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (f == NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Try foo.script instead of foo.root/foo.script, as a shortcut. */
|
|
Packit Bot |
3f3af5 |
fname = concat (command_base, ".script", NULL);
|
|
Packit Bot |
3f3af5 |
f = fopen (fname, "r");
|
|
Packit Bot |
3f3af5 |
if (verbose && f)
|
|
Packit Bot |
3f3af5 |
fprintf (stderr, "running %s\n", fname);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Note that we do NOT look for a Makefile-generated foo.script in
|
|
Packit Bot |
3f3af5 |
the build directory. If that is ever needed, this is the place
|
|
Packit Bot |
3f3af5 |
to add it. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* This is where we "interpret" the mini-script which is <test>.script. */
|
|
Packit Bot |
3f3af5 |
if (f != NULL)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
while (getline (&the_line, &line_len, f) > 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
int nt = tokenize (the_line, the_words, 3);
|
|
Packit Bot |
3f3af5 |
int i;
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
for (i = 1; i < nt; ++i)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (memcmp (the_words[i], "$B/", 3) == 0)
|
|
Packit Bot |
3f3af5 |
the_words[i] = concat (support_objdir_root,
|
|
Packit Bot |
3f3af5 |
the_words[i] + 2, NULL);
|
|
Packit Bot |
3f3af5 |
else if (memcmp (the_words[i], "$S/", 3) == 0)
|
|
Packit Bot |
3f3af5 |
the_words[i] = concat (support_srcdir_root,
|
|
Packit Bot |
3f3af5 |
the_words[i] + 2, NULL);
|
|
Packit Bot |
3f3af5 |
else if (memcmp (the_words[i], "$I/", 3) == 0)
|
|
Packit Bot |
3f3af5 |
the_words[i] = concat (new_root_path,
|
|
Packit Bot |
3f3af5 |
support_install_prefix,
|
|
Packit Bot |
3f3af5 |
the_words[i] + 2, NULL);
|
|
Packit Bot |
3f3af5 |
else if (memcmp (the_words[i], "$L/", 3) == 0)
|
|
Packit Bot |
3f3af5 |
the_words[i] = concat (new_root_path,
|
|
Packit Bot |
3f3af5 |
support_libdir_prefix,
|
|
Packit Bot |
3f3af5 |
the_words[i] + 2, NULL);
|
|
Packit Bot |
3f3af5 |
else if (the_words[i][0] == '/')
|
|
Packit Bot |
3f3af5 |
the_words[i] = concat (new_root_path,
|
|
Packit Bot |
3f3af5 |
the_words[i], NULL);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
char *r = strrchr (the_words[1], '/');
|
|
Packit Bot |
3f3af5 |
if (r)
|
|
Packit Bot |
3f3af5 |
the_words[2] = concat (the_words[2], r + 1, NULL);
|
|
Packit Bot |
3f3af5 |
else
|
|
Packit Bot |
3f3af5 |
the_words[2] = concat (the_words[2], the_words[1], NULL);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (nt == 2 && strcmp (the_words[0], "so") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
the_words[2] = concat (new_root_path, support_libdir_prefix,
|
|
Packit Bot |
3f3af5 |
"/", the_words[1], NULL);
|
|
Packit Bot |
3f3af5 |
the_words[1] = concat (so_base, the_words[1], NULL);
|
|
Packit Bot |
3f3af5 |
copy_one_file (the_words[1], the_words[2]);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
copy_one_file (the_words[1], the_words[2]);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
if (rename (the_words[1], the_words[2]) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
|
|
Packit Bot |
3f3af5 |
the_words[2], strerror (errno));
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
long int m;
|
|
Packit Bot |
3f3af5 |
m = strtol (the_words[1], NULL, 0);
|
|
Packit Bot |
3f3af5 |
if (chmod (the_words[2], m) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("chmod %s: %s\n",
|
|
Packit Bot |
3f3af5 |
the_words[2], strerror (errno));
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
maybe_xunlink (the_words[1]);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
else if (nt > 0 && the_words[0][0] != '#')
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
fclose (f);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
#ifdef CLONE_NEWNS
|
|
Packit Bot |
3f3af5 |
/* The unshare here gives us our own spaces and capabilities. */
|
|
Packit Bot |
3f3af5 |
if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Older kernels may not support all the options, or security
|
|
Packit Bot |
3f3af5 |
policy may block this call. */
|
|
Packit Bot |
3f3af5 |
if (errno == EINVAL || errno == EPERM)
|
|
Packit Bot |
3f3af5 |
FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (errno));
|
|
Packit Bot |
3f3af5 |
else
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
#else
|
|
Packit Bot |
3f3af5 |
/* Some targets may not support unshare at all. */
|
|
Packit Bot |
3f3af5 |
FAIL_UNSUPPORTED ("unshare support missing");
|
|
Packit Bot |
3f3af5 |
#endif
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Some systems, by default, all mounts leak out of the namespace. */
|
|
Packit Bot |
3f3af5 |
if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("could not create a private mount namespace\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
trymount (support_srcdir_root, new_srcdir_path);
|
|
Packit Bot |
3f3af5 |
trymount (support_objdir_root, new_objdir_path);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
|
|
Packit Bot |
3f3af5 |
devmount (new_root_path, "null");
|
|
Packit Bot |
3f3af5 |
devmount (new_root_path, "zero");
|
|
Packit Bot |
3f3af5 |
devmount (new_root_path, "urandom");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We're done with the "old" root, switch to the new one. */
|
|
Packit Bot |
3f3af5 |
if (chroot (new_root_path) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (chdir (new_cwd_path) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* To complete the containerization, we need to fork () at least
|
|
Packit Bot |
3f3af5 |
once. We can't exec, nor can we somehow link the new child to
|
|
Packit Bot |
3f3af5 |
our parent. So we run the child and propogate it's exit status
|
|
Packit Bot |
3f3af5 |
up. */
|
|
Packit Bot |
3f3af5 |
child = fork ();
|
|
Packit Bot |
3f3af5 |
if (child < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Unable to fork");
|
|
Packit Bot |
3f3af5 |
else if (child > 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* Parent. */
|
|
Packit Bot |
3f3af5 |
int status;
|
|
Packit Bot |
3f3af5 |
waitpid (child, &status, 0);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* There's a bit of magic here, since the buildroot is mounted
|
|
Packit Bot |
3f3af5 |
in our space, the paths are still valid, and since the mounts
|
|
Packit Bot |
3f3af5 |
aren't recursive, it sees *only* the built root, not anything
|
|
Packit Bot |
3f3af5 |
we would normally se if we rsync'd to "/" like mounted /dev
|
|
Packit Bot |
3f3af5 |
files. */
|
|
Packit Bot |
3f3af5 |
if (do_postclean)
|
|
Packit Bot |
3f3af5 |
rsync (pristine_root_path, new_root_path, 1);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (WIFEXITED (status))
|
|
Packit Bot |
3f3af5 |
exit (WEXITSTATUS (status));
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
if (WIFSIGNALED (status))
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
printf ("%%SIGNALLED%%\n");
|
|
Packit Bot |
3f3af5 |
exit (77);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
printf ("%%EXITERROR%%\n");
|
|
Packit Bot |
3f3af5 |
exit (78);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* The rest is the child process, which is now PID 1 and "in" the
|
|
Packit Bot |
3f3af5 |
new root. */
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
maybe_xmkdir ("/tmp", 0755);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Now that we're pid 1 (effectively "root") we can mount /proc */
|
|
Packit Bot |
3f3af5 |
maybe_xmkdir ("/proc", 0777);
|
|
Packit Bot |
3f3af5 |
if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Unable to mount /proc: ");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We map our original UID to the same UID in the container so we
|
|
Packit Bot |
3f3af5 |
can own our own files normally. */
|
|
Packit Bot |
3f3af5 |
UMAP = open ("/proc/self/uid_map", O_WRONLY);
|
|
Packit Bot |
3f3af5 |
if (UMAP < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
sprintf (tmp, "%lld %lld 1\n",
|
|
Packit Bot |
3f3af5 |
(long long) original_uid, (long long) original_uid);
|
|
Packit Bot |
3f3af5 |
write (UMAP, tmp, strlen (tmp));
|
|
Packit Bot |
3f3af5 |
xclose (UMAP);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We must disable setgroups () before we can map our groups, else we
|
|
Packit Bot |
3f3af5 |
get EPERM. */
|
|
Packit Bot |
3f3af5 |
GMAP = open ("/proc/self/setgroups", O_WRONLY);
|
|
Packit Bot |
3f3af5 |
if (GMAP >= 0)
|
|
Packit Bot |
3f3af5 |
{
|
|
Packit Bot |
3f3af5 |
/* We support kernels old enough to not have this. */
|
|
Packit Bot |
3f3af5 |
write (GMAP, "deny\n", 5);
|
|
Packit Bot |
3f3af5 |
xclose (GMAP);
|
|
Packit Bot |
3f3af5 |
}
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* We map our original GID to the same GID in the container so we
|
|
Packit Bot |
3f3af5 |
can own our own files normally. */
|
|
Packit Bot |
3f3af5 |
GMAP = open ("/proc/self/gid_map", O_WRONLY);
|
|
Packit Bot |
3f3af5 |
if (GMAP < 0)
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
sprintf (tmp, "%lld %lld 1\n",
|
|
Packit Bot |
3f3af5 |
(long long) original_gid, (long long) original_gid);
|
|
Packit Bot |
3f3af5 |
write (GMAP, tmp, strlen (tmp));
|
|
Packit Bot |
3f3af5 |
xclose (GMAP);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Now run the child. */
|
|
Packit Bot |
3f3af5 |
execvp (new_child_proc[0], new_child_proc);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Or don't run the child? */
|
|
Packit Bot |
3f3af5 |
FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
|
|
Packit Bot |
3f3af5 |
|
|
Packit Bot |
3f3af5 |
/* Because gcc won't know error () never returns... */
|
|
Packit Bot |
3f3af5 |
exit (EXIT_UNSUPPORTED);
|
|
Packit Bot |
3f3af5 |
}
|