Blame support/test-container.c

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