Blame support/test-container.c

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