Blame support/test-container.c

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