Blame support/test-container.c

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