1 /* Run a test case in an isolated namespace.
2    Copyright (C) 2018-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #define _FILE_OFFSET_BITS 64
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sched.h>
25 #include <sys/syscall.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/fcntl.h>
32 #include <sys/file.h>
33 #include <sys/wait.h>
34 #include <stdarg.h>
35 #include <sys/sysmacros.h>
36 #include <ctype.h>
37 #include <utime.h>
38 #include <errno.h>
39 #include <error.h>
40 #include <libc-pointer-arith.h>
41 
42 #ifdef __linux__
43 #include <sys/mount.h>
44 #endif
45 
46 #include <support/support.h>
47 #include <support/xunistd.h>
48 #include <support/capture_subprocess.h>
49 #include "check.h"
50 #include "test-driver.h"
51 
52 #ifndef __linux__
53 #define mount(s,t,fs,f,d) no_mount()
no_mount(void)54 int no_mount (void)
55 {
56   FAIL_UNSUPPORTED("mount not supported; port needed");
57 }
58 #endif
59 
60 int verbose = 0;
61 
62 /* Running a test in a container is tricky.  There are two main
63    categories of things to do:
64 
65    1. "Once" actions, like setting up the container and doing an
66       install into it.
67 
68    2. "Per-test" actions, like copying in support files and
69       configuring the container.
70 
71 
72    "Once" actions:
73 
74    * mkdir $buildroot/testroot.pristine/
75    * install into it
76      * default glibc install
77      * create /bin for /bin/sh
78      * create $(complocaledir) so localedef tests work with default paths.
79      * install /bin/sh, /bin/echo, and /bin/true.
80    * rsync to $buildroot/testroot.root/
81 
82    "Per-test" actions:
83    * maybe rsync to $buildroot/testroot.root/
84    * copy support files and test binary
85    * chroot/unshare
86    * set up any mounts (like /proc)
87    * run ldconfig
88 
89    Magic files:
90 
91    For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
92    and, if found...
93 
94    * mytest.root/ is rsync'd into container
95    * mytest.root/preclean.req causes fresh rsync (with delete) before
96      test if present
97    * mytest.root/mytest.script has a list of "commands" to run:
98        syntax:
99          # comment
100          su
101          mv FILE FILE
102 	 cp FILE FILE
103 	 rm FILE
104 	 cwd PATH
105 	 exec FILE
106 	 mkdirp MODE DIR
107 
108        variables:
109 	 $B/ build dir, equivalent to $(common-objpfx)
110 	 $S/ source dir, equivalent to $(srcdir)
111 	 $I/ install dir, equivalent to $(prefix)
112 	 $L/ library dir (in container), equivalent to $(libdir)
113 	 $complocaledir/ compiled locale dir, equivalent to $(complocaledir)
114 	 / container's root
115 
116 	 If FILE begins with any of these variables then they will be
117 	 substituted for the described value.
118 
119 	 The goal is to expose as many of the runtime's configured paths
120 	 via variables so they can be used to setup the container environment
121 	 before execution reaches the test.
122 
123        details:
124          - '#': A comment.
125          - 'su': Enables running test as root in the container.
126          - 'mv': A minimal move files command.
127          - 'cp': A minimal copy files command.
128          - 'rm': A minimal remove files command.
129 	 - 'cwd': set test working directory
130 	 - 'exec': change test binary location (may end in /)
131 	 - 'mkdirp': A minimal "mkdir -p FILE" command.
132 
133    * mytest.root/postclean.req causes fresh rsync (with delete) after
134      test if present
135 
136    * mytest.root/ldconfig.run causes ldconfig to be issued prior
137      test execution (to setup the initial ld.so.cache).
138 
139    Note that $srcdir/foo/mytest.script may be used instead of a
140    $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
141    there is no other reason for a sysroot.
142 
143    Design goals:
144 
145    * independent of other packages which may not be installed (like
146      rsync or Docker, or even "cp")
147 
148    * Simple, easy to review code (i.e. prefer simple naive code over
149      complex efficient code)
150 
151    * The current implementation ist parallel-make-safe, but only in
152      that it uses a lock to prevent parallel access to the testroot.  */
153 
154 
155 /* Utility Functions */
156 
157 /* Like xunlink, but it's OK if the file already doesn't exist.  */
158 void
maybe_xunlink(const char * path)159 maybe_xunlink (const char *path)
160 {
161   int rv = unlink (path);
162   if (rv < 0 && errno != ENOENT)
163     FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
164 }
165 
166 /* Like xmkdir, but it's OK if the directory already exists.  */
167 void
maybe_xmkdir(const char * path,mode_t mode)168 maybe_xmkdir (const char *path, mode_t mode)
169 {
170   struct stat st;
171 
172   if (stat (path, &st) == 0
173       && S_ISDIR (st.st_mode))
174     return;
175   xmkdir (path, mode);
176 }
177 
178 /* Temporarily concatenate multiple strings into one.  Allows up to 10
179    temporary results; use xstrdup () if you need them to be
180    permanent.  */
181 static char *
concat(const char * str,...)182 concat (const char *str, ...)
183 {
184   /* Assume initialized to NULL/zero.  */
185   static char *bufs[10];
186   static size_t buflens[10];
187   static int bufn = 0;
188   int n;
189   size_t len;
190   va_list ap, ap2;
191   char *cp;
192   char *next;
193 
194   va_start (ap, str);
195   va_copy (ap2, ap);
196 
197   n = bufn;
198   bufn = (bufn + 1) % 10;
199   len = strlen (str);
200 
201   while ((next = va_arg (ap, char *)) != NULL)
202     len = len + strlen (next);
203 
204   va_end (ap);
205 
206   if (bufs[n] == NULL)
207     {
208       bufs[n] = xmalloc (len + 1); /* NUL */
209       buflens[n] = len + 1;
210     }
211   else if (buflens[n] < len + 1)
212     {
213       bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
214       buflens[n] = len + 1;
215     }
216 
217   strcpy (bufs[n], str);
218   cp = strchr (bufs[n], '\0');
219   while ((next = va_arg (ap2, char *)) != NULL)
220     {
221       strcpy (cp, next);
222       cp = strchr (cp, '\0');
223     }
224   *cp = 0;
225   va_end (ap2);
226 
227   return bufs[n];
228 }
229 
230 /* Try to mount SRC onto DEST.  */
231 static void
trymount(const char * src,const char * dest)232 trymount (const char *src, const char *dest)
233 {
234   if (mount (src, dest, "", MS_BIND, NULL) < 0)
235     FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
236 }
237 
238 /* Special case of above for devices like /dev/zero where we have to
239    mount a device over a device, not a directory over a directory.  */
240 static void
devmount(const char * new_root_path,const char * which)241 devmount (const char *new_root_path, const char *which)
242 {
243   int fd;
244   fd = open (concat (new_root_path, "/dev/", which, NULL),
245 	     O_CREAT | O_TRUNC | O_RDWR, 0777);
246   xclose (fd);
247 
248   trymount (concat ("/dev/", which, NULL),
249 	    concat (new_root_path, "/dev/", which, NULL));
250 }
251 
252 /* Returns true if the string "looks like" an environement variable
253    being set.  */
254 static int
is_env_setting(const char * a)255 is_env_setting (const char *a)
256 {
257   int count_name = 0;
258 
259   while (*a)
260     {
261       if (isalnum (*a) || *a == '_')
262 	++count_name;
263       else if (*a == '=' && count_name > 0)
264 	return 1;
265       else
266 	return 0;
267       ++a;
268     }
269   return 0;
270 }
271 
272 /* Break the_line into words and store in the_words.  Max nwords,
273    returns actual count.  */
274 static int
tokenize(char * the_line,char ** the_words,int nwords)275 tokenize (char *the_line, char **the_words, int nwords)
276 {
277   int rv = 0;
278 
279   while (nwords > 0)
280     {
281       /* Skip leading whitespace, if any.  */
282       while (*the_line && isspace (*the_line))
283 	++the_line;
284 
285       /* End of line?  */
286       if (*the_line == 0)
287 	return rv;
288 
289       /* THE_LINE points to a non-whitespace character, so we have a
290 	 word.  */
291       *the_words = the_line;
292       ++the_words;
293       nwords--;
294       ++rv;
295 
296       /* Skip leading whitespace, if any.  */
297       while (*the_line && ! isspace (*the_line))
298 	++the_line;
299 
300       /* We now point at the trailing NUL *or* some whitespace.  */
301       if (*the_line == 0)
302 	return rv;
303 
304       /* It was whitespace, skip and keep tokenizing.  */
305       *the_line++ = 0;
306     }
307 
308   /* We get here if we filled the words buffer.  */
309   return rv;
310 }
311 
312 
313 /* Mini-RSYNC implementation.  Optimize later.      */
314 
315 /* A few routines for an "rsync buffer" which stores the paths we're
316    working on.  We continuously grow and shrink the paths in each
317    buffer so there's lot of re-use.  */
318 
319 /* We rely on "initialized to zero" to set these up.  */
320 typedef struct
321 {
322   char *buf;
323   size_t len;
324   size_t size;
325 } path_buf;
326 
327 static path_buf spath, dpath;
328 
329 static void
r_setup(char * path,path_buf * pb)330 r_setup (char *path, path_buf * pb)
331 {
332   size_t len = strlen (path);
333   if (pb->buf == NULL || pb->size < len + 1)
334     {
335       /* Round up.  This is an arbitrary number, just to keep from
336 	 reallocing too often.  */
337       size_t sz = ALIGN_UP (len + 1, 512);
338       if (pb->buf == NULL)
339 	pb->buf = (char *) xmalloc (sz);
340       else
341 	pb->buf = (char *) xrealloc (pb->buf, sz);
342       if (pb->buf == NULL)
343 	FAIL_EXIT1 ("Out of memory while rsyncing\n");
344 
345       pb->size = sz;
346     }
347   strcpy (pb->buf, path);
348   pb->len = len;
349 }
350 
351 static void
r_append(const char * path,path_buf * pb)352 r_append (const char *path, path_buf * pb)
353 {
354   size_t len = strlen (path) + pb->len;
355   if (pb->size < len + 1)
356     {
357       /* Round up */
358       size_t sz = ALIGN_UP (len + 1, 512);
359       pb->buf = (char *) xrealloc (pb->buf, sz);
360       if (pb->buf == NULL)
361 	FAIL_EXIT1 ("Out of memory while rsyncing\n");
362 
363       pb->size = sz;
364     }
365   strcpy (pb->buf + pb->len, path);
366   pb->len = len;
367 }
368 
369 static int
file_exists(char * path)370 file_exists (char *path)
371 {
372   struct stat st;
373   if (lstat (path, &st) == 0)
374     return 1;
375   return 0;
376 }
377 
378 static void
recursive_remove(char * path)379 recursive_remove (char *path)
380 {
381   pid_t child;
382   int status;
383 
384   child = fork ();
385 
386   switch (child) {
387   case -1:
388     perror("fork");
389     FAIL_EXIT1 ("Unable to fork");
390   case 0:
391     /* Child.  */
392     execlp ("rm", "rm", "-rf", path, NULL);
393     FAIL_EXIT1 ("exec rm: %m");
394   default:
395     /* Parent.  */
396     waitpid (child, &status, 0);
397     /* "rm" would have already printed a suitable error message.  */
398     if (! WIFEXITED (status)
399 	|| WEXITSTATUS (status) != 0)
400       FAIL_EXIT1 ("exec child returned status: %d", status);
401 
402     break;
403   }
404 }
405 
406 /* Used for both rsync and the mytest.script "cp" command.  */
407 static void
copy_one_file(const char * sname,const char * dname)408 copy_one_file (const char *sname, const char *dname)
409 {
410   int sfd, dfd;
411   struct stat st;
412   struct utimbuf times;
413 
414   sfd = open (sname, O_RDONLY);
415   if (sfd < 0)
416     FAIL_EXIT1 ("unable to open %s for reading\n", sname);
417 
418   if (fstat (sfd, &st) < 0)
419     FAIL_EXIT1 ("unable to fstat %s\n", sname);
420 
421   dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
422   if (dfd < 0)
423     FAIL_EXIT1 ("unable to open %s for writing\n", dname);
424 
425   xcopy_file_range (sfd, 0, dfd, 0, st.st_size, 0);
426 
427   xclose (sfd);
428   xclose (dfd);
429 
430   if (chmod (dname, st.st_mode & 0777) < 0)
431     FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
432 
433   times.actime = st.st_atime;
434   times.modtime = st.st_mtime;
435   if (utime (dname, &times) < 0)
436     FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
437 }
438 
439 /* We don't check *everything* about the two files to see if a copy is
440    needed, just the minimum to make sure we get the latest copy.  */
441 static int
need_sync(char * ap,char * bp,struct stat * a,struct stat * b)442 need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
443 {
444   if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
445     return 1;
446 
447   if (S_ISLNK (a->st_mode))
448     {
449       int rv;
450       char *al, *bl;
451 
452       if (a->st_size != b->st_size)
453 	return 1;
454 
455       al = xreadlink (ap);
456       bl = xreadlink (bp);
457       rv = strcmp (al, bl);
458       free (al);
459       free (bl);
460       if (rv == 0)
461 	return 0; /* links are same */
462       return 1; /* links differ */
463     }
464 
465   if (verbose)
466     {
467       if (a->st_size != b->st_size)
468 	printf ("SIZE\n");
469       if ((a->st_mode & 0777) != (b->st_mode & 0777))
470 	printf ("MODE\n");
471       if (a->st_mtime != b->st_mtime)
472 	printf ("TIME\n");
473     }
474 
475   if (a->st_size == b->st_size
476       && ((a->st_mode & 0777) == (b->st_mode & 0777))
477       && a->st_mtime == b->st_mtime)
478     return 0;
479 
480   return 1;
481 }
482 
483 static void
rsync_1(path_buf * src,path_buf * dest,int and_delete,int force_copies)484 rsync_1 (path_buf * src, path_buf * dest, int and_delete, int force_copies)
485 {
486   DIR *dir;
487   struct dirent *de;
488   struct stat s, d;
489 
490   r_append ("/", src);
491   r_append ("/", dest);
492 
493   if (verbose)
494     printf ("sync %s to %s%s%s\n", src->buf, dest->buf,
495 	    and_delete ? " and delete" : "",
496 	    force_copies ? " (forced)" : "");
497 
498   size_t staillen = src->len;
499 
500   size_t dtaillen = dest->len;
501 
502   dir = opendir (src->buf);
503 
504   while ((de = readdir (dir)) != NULL)
505     {
506       if (strcmp (de->d_name, ".") == 0
507 	  || strcmp (de->d_name, "..") == 0)
508 	continue;
509 
510       src->len = staillen;
511       r_append (de->d_name, src);
512       dest->len = dtaillen;
513       r_append (de->d_name, dest);
514 
515       s.st_mode = ~0;
516       d.st_mode = ~0;
517 
518       if (lstat (src->buf, &s) != 0)
519 	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
520 
521       /* It's OK if this one fails, since we know the file might be
522 	 missing.  */
523       lstat (dest->buf, &d);
524 
525       if (! force_copies && ! need_sync (src->buf, dest->buf, &s, &d))
526 	{
527 	  if (S_ISDIR (s.st_mode))
528 	    rsync_1 (src, dest, and_delete, force_copies);
529 	  continue;
530 	}
531 
532       if (d.st_mode != ~0)
533 	switch (d.st_mode & S_IFMT)
534 	  {
535 	  case S_IFDIR:
536 	    if (!S_ISDIR (s.st_mode))
537 	      {
538 		if (verbose)
539 		  printf ("-D %s\n", dest->buf);
540 		recursive_remove (dest->buf);
541 	      }
542 	    break;
543 
544 	  default:
545 	    if (verbose)
546 	      printf ("-F %s\n", dest->buf);
547 	    maybe_xunlink (dest->buf);
548 	    break;
549 	  }
550 
551       switch (s.st_mode & S_IFMT)
552 	{
553 	case S_IFREG:
554 	  if (verbose)
555 	    printf ("+F %s\n", dest->buf);
556 	  copy_one_file (src->buf, dest->buf);
557 	  break;
558 
559 	case S_IFDIR:
560 	  if (verbose)
561 	    printf ("+D %s\n", dest->buf);
562 	  maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
563 	  rsync_1 (src, dest, and_delete, force_copies);
564 	  break;
565 
566 	case S_IFLNK:
567 	  {
568 	    char *lp;
569 	    if (verbose)
570 	      printf ("+L %s\n", dest->buf);
571 	    lp = xreadlink (src->buf);
572 	    xsymlink (lp, dest->buf);
573 	    free (lp);
574 	    break;
575 	  }
576 
577 	default:
578 	  break;
579 	}
580     }
581 
582   closedir (dir);
583   src->len = staillen;
584   src->buf[staillen] = 0;
585   dest->len = dtaillen;
586   dest->buf[dtaillen] = 0;
587 
588   if (!and_delete)
589     return;
590 
591   /* The rest of this function removes any files/directories in DEST
592      that do not exist in SRC.  This is triggered as part of a
593      preclean or postsclean step.  */
594 
595   dir = opendir (dest->buf);
596 
597   while ((de = readdir (dir)) != NULL)
598     {
599       if (strcmp (de->d_name, ".") == 0
600 	  || strcmp (de->d_name, "..") == 0)
601 	continue;
602 
603       src->len = staillen;
604       r_append (de->d_name, src);
605       dest->len = dtaillen;
606       r_append (de->d_name, dest);
607 
608       s.st_mode = ~0;
609       d.st_mode = ~0;
610 
611       lstat (src->buf, &s);
612 
613       if (lstat (dest->buf, &d) != 0)
614 	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
615 
616       if (s.st_mode == ~0)
617 	{
618 	  /* dest exists and src doesn't, clean it.  */
619 	  switch (d.st_mode & S_IFMT)
620 	    {
621 	    case S_IFDIR:
622 	      if (!S_ISDIR (s.st_mode))
623 		{
624 		  if (verbose)
625 		    printf ("-D %s\n", dest->buf);
626 		  recursive_remove (dest->buf);
627 		}
628 	      break;
629 
630 	    default:
631 	      if (verbose)
632 		printf ("-F %s\n", dest->buf);
633 	      maybe_xunlink (dest->buf);
634 	      break;
635 	    }
636 	}
637     }
638 
639   closedir (dir);
640 }
641 
642 static void
rsync(char * src,char * dest,int and_delete,int force_copies)643 rsync (char *src, char *dest, int and_delete, int force_copies)
644 {
645   r_setup (src, &spath);
646   r_setup (dest, &dpath);
647 
648   rsync_1 (&spath, &dpath, and_delete, force_copies);
649 }
650 
651 
652 
653 /* See if we can detect what the user needs to do to get unshare
654    support working for us.  */
655 void
check_for_unshare_hints(void)656 check_for_unshare_hints (void)
657 {
658   FILE *f;
659   int i;
660 
661   /* Default Debian Linux disables user namespaces, but allows a way
662      to enable them.  */
663   f = fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
664   if (f != NULL)
665     {
666       i = 99; /* Sentinel.  */
667       fscanf (f, "%d", &i);
668       if (i == 0)
669 	{
670 	  printf ("To enable test-container, please run this as root:\n");
671 	  printf ("  echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
672 	}
673       fclose (f);
674       return;
675     }
676 
677   /* ALT Linux has an alternate way of doing the same.  */
678   f = fopen ("/proc/sys/kernel/userns_restrict", "r");
679   if (f != NULL)
680     {
681       i = 99; /* Sentinel.  */
682       fscanf (f, "%d", &i);
683       if (i == 1)
684 	{
685 	  printf ("To enable test-container, please run this as root:\n");
686 	  printf ("  echo 0 > /proc/sys/kernel/userns_restrict\n");
687 	}
688       fclose (f);
689       return;
690     }
691 }
692 
693 static void
run_ldconfig(void * x)694 run_ldconfig (void *x __attribute__((unused)))
695 {
696   char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
697   char *args[] = { prog, NULL };
698 
699   execv (args[0], args);
700   FAIL_EXIT1 ("execv: %m");
701 }
702 
703 int
main(int argc,char ** argv)704 main (int argc, char **argv)
705 {
706   pid_t child;
707   char *pristine_root_path;
708   char *new_root_path;
709   char *new_cwd_path;
710   char *new_objdir_path;
711   char *new_srcdir_path;
712   char **new_child_proc;
713   char *new_child_exec;
714   char *command_root;
715   char *command_base;
716   char *command_basename;
717   char *so_base;
718   int do_postclean = 0;
719   bool do_ldconfig = false;
720   char *change_cwd = NULL;
721 
722   int pipes[2];
723   char pid_buf[20];
724 
725   uid_t original_uid;
726   gid_t original_gid;
727   /* If set, the test runs as root instead of the user running the testsuite.  */
728   int be_su = 0;
729   int UMAP;
730   int GMAP;
731   /* Used for "%lld %lld 1" so need not be large.  */
732   char tmp[100];
733   struct stat st;
734   int lock_fd;
735 
736   setbuf (stdout, NULL);
737 
738   /* The command line we're expecting looks like this:
739      env <set some vars> ld.so <library path> test-binary
740 
741      We need to peel off any "env" or "ld.so" portion of the command
742      line, and keep track of which env vars we should preserve and
743      which we drop.  */
744 
745   if (argc < 2)
746     {
747       fprintf (stderr, "Usage: test-container <program to run> <args...>\n");
748       exit (1);
749     }
750 
751   if (strcmp (argv[1], "-v") == 0)
752     {
753       verbose = 1;
754       ++argv;
755       --argc;
756     }
757 
758   if (strcmp (argv[1], "env") == 0)
759     {
760       ++argv;
761       --argc;
762       while (is_env_setting (argv[1]))
763 	{
764 	  /* If there are variables we do NOT want to propogate, this
765 	     is where the test for them goes.  */
766 	    {
767 	      /* Need to keep these.  Note that putenv stores a
768 	         pointer to our argv.  */
769 	      putenv (argv[1]);
770 	    }
771 	  ++argv;
772 	  --argc;
773 	}
774     }
775 
776   if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
777     {
778       ++argv;
779       --argc;
780       while (argv[1][0] == '-')
781 	{
782 	  if (strcmp (argv[1], "--library-path") == 0)
783 	    {
784 	      ++argv;
785 	      --argc;
786 	    }
787 	  ++argv;
788 	  --argc;
789 	}
790     }
791 
792   pristine_root_path = xstrdup (concat (support_objdir_root,
793 				       "/testroot.pristine", NULL));
794   new_root_path = xstrdup (concat (support_objdir_root,
795 				  "/testroot.root", NULL));
796   new_cwd_path = get_current_dir_name ();
797   new_child_proc = argv + 1;
798   new_child_exec = argv[1];
799 
800   lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
801 		 O_CREAT | O_TRUNC | O_RDWR, 0666);
802   if (lock_fd < 0)
803     FAIL_EXIT1 ("Cannot create testroot lock.\n");
804 
805   while (flock (lock_fd, LOCK_EX) != 0)
806     {
807       if (errno != EINTR)
808 	FAIL_EXIT1 ("Cannot lock testroot.\n");
809     }
810 
811   xmkdirp (new_root_path, 0755);
812 
813   /* We look for extra setup info in a subdir in the same spot as the
814      test, with the same name but a ".root" extension.  This is that
815      directory.  We try to look in the source tree if the path we're
816      given refers to the build tree, but we rely on the path to be
817      absolute.  This is what the glibc makefiles do.  */
818   command_root = concat (argv[1], ".root", NULL);
819   if (strncmp (command_root, support_objdir_root,
820 	       strlen (support_objdir_root)) == 0
821       && command_root[strlen (support_objdir_root)] == '/')
822     command_root = concat (support_srcdir_root,
823 			   argv[1] + strlen (support_objdir_root),
824 			   ".root", NULL);
825   command_root = xstrdup (command_root);
826 
827   /* This cuts off the ".root" we appended above.  */
828   command_base = xstrdup (command_root);
829   command_base[strlen (command_base) - 5] = 0;
830 
831   /* This is the basename of the test we're running.  */
832   command_basename = strrchr (command_base, '/');
833   if (command_basename == NULL)
834     command_basename = command_base;
835   else
836     ++command_basename;
837 
838   /* Shared object base directory.  */
839   so_base = xstrdup (argv[1]);
840   if (strrchr (so_base, '/') != NULL)
841     strrchr (so_base, '/')[1] = 0;
842 
843   if (file_exists (concat (command_root, "/postclean.req", NULL)))
844     do_postclean = 1;
845 
846   if (file_exists (concat (command_root, "/ldconfig.run", NULL)))
847     do_ldconfig = true;
848 
849   rsync (pristine_root_path, new_root_path,
850 	 file_exists (concat (command_root, "/preclean.req", NULL)), 0);
851 
852   if (stat (command_root, &st) >= 0
853       && S_ISDIR (st.st_mode))
854     rsync (command_root, new_root_path, 0, 1);
855 
856   new_objdir_path = xstrdup (concat (new_root_path,
857 				    support_objdir_root, NULL));
858   new_srcdir_path = xstrdup (concat (new_root_path,
859 				    support_srcdir_root, NULL));
860 
861   /* new_cwd_path starts with '/' so no "/" needed between the two.  */
862   xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
863   xmkdirp (new_srcdir_path, 0755);
864   xmkdirp (new_objdir_path, 0755);
865 
866   original_uid = getuid ();
867   original_gid = getgid ();
868 
869   /* Handle the cp/mv/rm "script" here.  */
870   {
871     char *the_line = NULL;
872     size_t line_len = 0;
873     char *fname = concat (command_root, "/",
874 			  command_basename, ".script", NULL);
875     char *the_words[3];
876     FILE *f = fopen (fname, "r");
877 
878     if (verbose && f)
879       fprintf (stderr, "running %s\n", fname);
880 
881     if (f == NULL)
882       {
883 	/* Try foo.script instead of foo.root/foo.script, as a shortcut.  */
884 	fname = concat (command_base, ".script", NULL);
885 	f = fopen (fname, "r");
886 	if (verbose && f)
887 	  fprintf (stderr, "running %s\n", fname);
888       }
889 
890     /* Note that we do NOT look for a Makefile-generated foo.script in
891        the build directory.  If that is ever needed, this is the place
892        to add it.  */
893 
894     /* This is where we "interpret" the mini-script which is <test>.script.  */
895     if (f != NULL)
896       {
897 	while (getline (&the_line, &line_len, f) > 0)
898 	  {
899 	    int nt = tokenize (the_line, the_words, 3);
900 	    int i;
901 
902 	    /* Expand variables.  */
903 	    for (i = 1; i < nt; ++i)
904 	      {
905 		if (memcmp (the_words[i], "$B/", 3) == 0)
906 		  the_words[i] = concat (support_objdir_root,
907 					 the_words[i] + 2, NULL);
908 		else if (memcmp (the_words[i], "$S/", 3) == 0)
909 		  the_words[i] = concat (support_srcdir_root,
910 					 the_words[i] + 2, NULL);
911 		else if (memcmp (the_words[i], "$I/", 3) == 0)
912 		  the_words[i] = concat (new_root_path,
913 					 support_install_prefix,
914 					 the_words[i] + 2, NULL);
915 		else if (memcmp (the_words[i], "$L/", 3) == 0)
916 		  the_words[i] = concat (new_root_path,
917 					 support_libdir_prefix,
918 					 the_words[i] + 2, NULL);
919 		else if (memcmp (the_words[i], "$complocaledir/", 15) == 0)
920 		  the_words[i] = concat (new_root_path,
921 					 support_complocaledir_prefix,
922 					 the_words[i] + 14, NULL);
923 		/* "exec" and "cwd" use inside-root paths.  */
924 		else if (strcmp (the_words[0], "exec") != 0
925 			 && strcmp (the_words[0], "cwd") != 0
926 			 && the_words[i][0] == '/')
927 		  the_words[i] = concat (new_root_path,
928 					 the_words[i], NULL);
929 	      }
930 
931 	    if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
932 	      {
933 		char *r = strrchr (the_words[1], '/');
934 		if (r)
935 		  the_words[2] = concat (the_words[2], r + 1, NULL);
936 		else
937 		  the_words[2] = concat (the_words[2], the_words[1], NULL);
938 	      }
939 
940 	    /* Run the following commands in the_words[0] with NT number of
941 	       arguments (including the command).  */
942 
943 	    if (nt == 2 && strcmp (the_words[0], "so") == 0)
944 	      {
945 		the_words[2] = concat (new_root_path, support_libdir_prefix,
946 				       "/", the_words[1], NULL);
947 		the_words[1] = concat (so_base, the_words[1], NULL);
948 		copy_one_file (the_words[1], the_words[2]);
949 	      }
950 	    else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
951 	      {
952 		copy_one_file (the_words[1], the_words[2]);
953 	      }
954 	    else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
955 	      {
956 		if (rename (the_words[1], the_words[2]) < 0)
957 		  FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
958 			      the_words[2], strerror (errno));
959 	      }
960 	    else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
961 	      {
962 		long int m;
963 		errno = 0;
964 		m = strtol (the_words[1], NULL, 0);
965 		TEST_COMPARE (errno, 0);
966 		if (chmod (the_words[2], m) < 0)
967 		    FAIL_EXIT1 ("chmod %s: %s\n",
968 				the_words[2], strerror (errno));
969 
970 	      }
971 	    else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
972 	      {
973 		maybe_xunlink (the_words[1]);
974 	      }
975 	    else if (nt >= 2 && strcmp (the_words[0], "exec") == 0)
976 	      {
977 		/* The first argument is the desired location and name
978 		   of the test binary as we wish to exec it; we will
979 		   copy the binary there.  The second (optional)
980 		   argument is the value to pass as argv[0], it
981 		   defaults to the same as the first argument.  */
982 		char *new_exec_path = the_words[1];
983 
984 		/* If the new exec path ends with a slash, that's the
985 		 * directory, and use the old test base name.  */
986 		if (new_exec_path [strlen(new_exec_path) - 1] == '/')
987 		    new_exec_path = concat (new_exec_path,
988 					    basename (new_child_proc[0]),
989 					    NULL);
990 
991 
992 		/* new_child_proc is in the build tree, so has the
993 		   same path inside the chroot as outside.  The new
994 		   exec path is, by definition, relative to the
995 		   chroot.  */
996 		copy_one_file (new_child_proc[0],  concat (new_root_path,
997 							   new_exec_path,
998 							   NULL));
999 
1000 		new_child_exec =  xstrdup (new_exec_path);
1001 		if (the_words[2])
1002 		  new_child_proc[0] = xstrdup (the_words[2]);
1003 		else
1004 		  new_child_proc[0] = new_child_exec;
1005 	      }
1006 	    else if (nt == 2 && strcmp (the_words[0], "cwd") == 0)
1007 	      {
1008 		change_cwd = xstrdup (the_words[1]);
1009 	      }
1010 	    else if (nt == 1 && strcmp (the_words[0], "su") == 0)
1011 	      {
1012 		be_su = 1;
1013 	      }
1014 	    else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0)
1015 	      {
1016 		long int m;
1017 		errno = 0;
1018 		m = strtol (the_words[1], NULL, 0);
1019 		TEST_COMPARE (errno, 0);
1020 		xmkdirp (the_words[2], m);
1021 	      }
1022 	    else if (nt > 0 && the_words[0][0] != '#')
1023 	      {
1024 		fprintf (stderr, "\033[31minvalid [%s]\033[0m\n", the_words[0]);
1025 		exit (1);
1026 	      }
1027 	  }
1028 	fclose (f);
1029       }
1030   }
1031 
1032   if (do_postclean)
1033     {
1034       pid_t pc_pid = fork ();
1035 
1036       if (pc_pid < 0)
1037 	{
1038 	  FAIL_EXIT1 ("Can't fork for post-clean");
1039 	}
1040       else if (pc_pid > 0)
1041 	{
1042 	  /* Parent.  */
1043 	  int status;
1044 	  waitpid (pc_pid, &status, 0);
1045 
1046 	  /* Child has exited, we can post-clean the test root.  */
1047 	  printf("running post-clean rsync\n");
1048 	  rsync (pristine_root_path, new_root_path, 1, 0);
1049 
1050 	  if (WIFEXITED (status))
1051 	    exit (WEXITSTATUS (status));
1052 
1053 	  if (WIFSIGNALED (status))
1054 	    {
1055 	      printf ("%%SIGNALLED%%\n");
1056 	      exit (77);
1057 	    }
1058 
1059 	  printf ("%%EXITERROR%%\n");
1060 	  exit (78);
1061 	}
1062 
1063       /* Child continues.  */
1064     }
1065 
1066   /* This is the last point in the program where we're still in the
1067      "normal" namespace.  */
1068 
1069 #ifdef CLONE_NEWNS
1070   /* The unshare here gives us our own spaces and capabilities.  */
1071   if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
1072     {
1073       /* Older kernels may not support all the options, or security
1074 	 policy may block this call.  */
1075       if (errno == EINVAL || errno == EPERM)
1076 	{
1077 	  int saved_errno = errno;
1078 	  if (errno == EPERM)
1079 	    check_for_unshare_hints ();
1080 	  FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
1081 	}
1082       else
1083 	FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
1084     }
1085 #else
1086   /* Some targets may not support unshare at all.  */
1087   FAIL_UNSUPPORTED ("unshare support missing");
1088 #endif
1089 
1090   /* Some systems, by default, all mounts leak out of the namespace.  */
1091   if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
1092     FAIL_EXIT1 ("could not create a private mount namespace\n");
1093 
1094   trymount (support_srcdir_root, new_srcdir_path);
1095   trymount (support_objdir_root, new_objdir_path);
1096 
1097   xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
1098   devmount (new_root_path, "null");
1099   devmount (new_root_path, "zero");
1100   devmount (new_root_path, "urandom");
1101 
1102   /* We're done with the "old" root, switch to the new one.  */
1103   if (chroot (new_root_path) < 0)
1104     FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
1105 
1106   if (chdir (new_cwd_path) < 0)
1107     FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
1108 
1109   /* This is to pass the "outside" PID to the child, which will be PID
1110      1.  */
1111   if (pipe2 (pipes, O_CLOEXEC) < 0)
1112     FAIL_EXIT1 ("Can't create pid pipe");
1113 
1114   /* To complete the containerization, we need to fork () at least
1115      once.  We can't exec, nor can we somehow link the new child to
1116      our parent.  So we run the child and propogate it's exit status
1117      up.  */
1118   child = fork ();
1119   if (child < 0)
1120     FAIL_EXIT1 ("Unable to fork");
1121   else if (child > 0)
1122     {
1123       /* Parent.  */
1124       int status;
1125 
1126       /* Send the child's "outside" pid to it.  */
1127       write (pipes[1], &child, sizeof(child));
1128       close (pipes[0]);
1129       close (pipes[1]);
1130 
1131       waitpid (child, &status, 0);
1132 
1133       if (WIFEXITED (status))
1134 	exit (WEXITSTATUS (status));
1135 
1136       if (WIFSIGNALED (status))
1137 	{
1138 	  printf ("%%SIGNALLED%%\n");
1139 	  exit (77);
1140 	}
1141 
1142       printf ("%%EXITERROR%%\n");
1143       exit (78);
1144     }
1145 
1146   /* The rest is the child process, which is now PID 1 and "in" the
1147      new root.  */
1148 
1149   if (do_ldconfig)
1150     {
1151       struct support_capture_subprocess result =
1152         support_capture_subprocess (run_ldconfig, NULL);
1153       support_capture_subprocess_check (&result, "execv", 0, sc_allow_none);
1154     }
1155 
1156   /* Get our "outside" pid from our parent.  We use this to help with
1157      debugging from outside the container.  */
1158   read (pipes[0], &child, sizeof(child));
1159   close (pipes[0]);
1160   close (pipes[1]);
1161   sprintf (pid_buf, "%lu", (long unsigned)child);
1162   setenv ("PID_OUTSIDE_CONTAINER", pid_buf, 0);
1163 
1164   maybe_xmkdir ("/tmp", 0755);
1165 
1166   /* Now that we're pid 1 (effectively "root") we can mount /proc  */
1167   maybe_xmkdir ("/proc", 0777);
1168   if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
1169     FAIL_EXIT1 ("Unable to mount /proc: ");
1170 
1171   /* We map our original UID to the same UID in the container so we
1172      can own our own files normally.  */
1173   UMAP = open ("/proc/self/uid_map", O_WRONLY);
1174   if (UMAP < 0)
1175     FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1176 
1177   sprintf (tmp, "%lld %lld 1\n",
1178 	   (long long) (be_su ? 0 : original_uid), (long long) original_uid);
1179   write (UMAP, tmp, strlen (tmp));
1180   xclose (UMAP);
1181 
1182   /* We must disable setgroups () before we can map our groups, else we
1183      get EPERM.  */
1184   GMAP = open ("/proc/self/setgroups", O_WRONLY);
1185   if (GMAP >= 0)
1186     {
1187       /* We support kernels old enough to not have this.  */
1188       write (GMAP, "deny\n", 5);
1189       xclose (GMAP);
1190     }
1191 
1192   /* We map our original GID to the same GID in the container so we
1193      can own our own files normally.  */
1194   GMAP = open ("/proc/self/gid_map", O_WRONLY);
1195   if (GMAP < 0)
1196     FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1197 
1198   sprintf (tmp, "%lld %lld 1\n",
1199 	   (long long) (be_su ? 0 : original_gid), (long long) original_gid);
1200   write (GMAP, tmp, strlen (tmp));
1201   xclose (GMAP);
1202 
1203   if (change_cwd)
1204     {
1205       if (chdir (change_cwd) < 0)
1206 	FAIL_EXIT1 ("Can't cd to %s inside container - ", change_cwd);
1207     }
1208 
1209   /* Now run the child.  */
1210   execvp (new_child_exec, new_child_proc);
1211 
1212   /* Or don't run the child?  */
1213   FAIL_EXIT1 ("Unable to exec %s: %s\n", new_child_exec, strerror (errno));
1214 
1215   /* Because gcc won't know error () never returns...  */
1216   exit (EXIT_UNSUPPORTED);
1217 }
1218