Blame support/test-container.c

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