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