Blame support/test-container.c

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