1/* Run a test case in an isolated namespace.
2 Copyright (C) 2018-2019 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 <http://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 uid_t original_uid;
680 gid_t original_gid;
681 /* If set, the test runs as root instead of the user running the testsuite. */
682 int be_su = 0;
683 int UMAP;
684 int GMAP;
685 /* Used for "%lld %lld 1" so need not be large. */
686 char tmp[100];
687 struct stat st;
688 int lock_fd;
689
690 setbuf (stdout, NULL);
691
692 /* The command line we're expecting looks like this:
693 env <set some vars> ld.so <library path> test-binary
694
695 We need to peel off any "env" or "ld.so" portion of the command
696 line, and keep track of which env vars we should preserve and
697 which we drop. */
698
699 if (argc < 2)
700 {
701 fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
702 exit (1);
703 }
704
705 if (strcmp (argv[1], "-v") == 0)
706 {
707 verbose = 1;
708 ++argv;
709 --argc;
710 }
711
712 if (strcmp (argv[1], "env") == 0)
713 {
714 ++argv;
715 --argc;
716 while (is_env_setting (argv[1]))
717 {
718 /* If there are variables we do NOT want to propogate, this
719 is where the test for them goes. */
720 {
721 /* Need to keep these. Note that putenv stores a
722 pointer to our argv. */
723 putenv (argv[1]);
724 }
725 ++argv;
726 --argc;
727 }
728 }
729
730 if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
731 {
732 ++argv;
733 --argc;
734 while (argv[1][0] == '-')
735 {
736 if (strcmp (argv[1], "--library-path") == 0)
737 {
738 ++argv;
739 --argc;
740 }
741 ++argv;
742 --argc;
743 }
744 }
745
746 pristine_root_path = strdup (concat (support_objdir_root,
747 "/testroot.pristine", NULL));
748 new_root_path = strdup (concat (support_objdir_root,
749 "/testroot.root", NULL));
750 new_cwd_path = get_current_dir_name ();
751 new_child_proc = argv + 1;
752
753 lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
754 O_CREAT | O_TRUNC | O_RDWR, 0666);
755 if (lock_fd < 0)
756 FAIL_EXIT1 ("Cannot create testroot lock.\n");
757
758 while (flock (lock_fd, LOCK_EX) != 0)
759 {
760 if (errno != EINTR)
761 FAIL_EXIT1 ("Cannot lock testroot.\n");
762 }
763
764 xmkdirp (new_root_path, 0755);
765
766 /* We look for extra setup info in a subdir in the same spot as the
767 test, with the same name but a ".root" extension. This is that
768 directory. We try to look in the source tree if the path we're
769 given refers to the build tree, but we rely on the path to be
770 absolute. This is what the glibc makefiles do. */
771 command_root = concat (argv[1], ".root", NULL);
772 if (strncmp (command_root, support_objdir_root,
773 strlen (support_objdir_root)) == 0
774 && command_root[strlen (support_objdir_root)] == '/')
775 command_root = concat (support_srcdir_root,
776 argv[1] + strlen (support_objdir_root),
777 ".root", NULL);
778 command_root = strdup (command_root);
779
780 /* This cuts off the ".root" we appended above. */
781 command_base = strdup (command_root);
782 command_base[strlen (command_base) - 5] = 0;
783
784 /* This is the basename of the test we're running. */
785 command_basename = strrchr (command_base, '/');
786 if (command_basename == NULL)
787 command_basename = command_base;
788 else
789 ++command_basename;
790
791 /* Shared object base directory. */
792 so_base = strdup (argv[1]);
793 if (strrchr (so_base, '/') != NULL)
794 strrchr (so_base, '/')[1] = 0;
795
796 if (file_exists (concat (command_root, "/postclean.req", NULL)))
797 do_postclean = 1;
798
799 rsync (pristine_root_path, new_root_path,
800 file_exists (concat (command_root, "/preclean.req", NULL)));
801
802 if (stat (command_root, &st) >= 0
803 && S_ISDIR (st.st_mode))
804 rsync (command_root, new_root_path, 0);
805
806 new_objdir_path = strdup (concat (new_root_path,
807 support_objdir_root, NULL));
808 new_srcdir_path = strdup (concat (new_root_path,
809 support_srcdir_root, NULL));
810
811 /* new_cwd_path starts with '/' so no "/" needed between the two. */
812 xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
813 xmkdirp (new_srcdir_path, 0755);
814 xmkdirp (new_objdir_path, 0755);
815
816 original_uid = getuid ();
817 original_gid = getgid ();
818
819 /* Handle the cp/mv/rm "script" here. */
820 {
821 char *the_line = NULL;
822 size_t line_len = 0;
823 char *fname = concat (command_root, "/",
824 command_basename, ".script", NULL);
825 char *the_words[3];
826 FILE *f = fopen (fname, "r");
827
828 if (verbose && f)
829 fprintf (stderr, "running %s\n", fname);
830
831 if (f == NULL)
832 {
833 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
834 fname = concat (command_base, ".script", NULL);
835 f = fopen (fname, "r");
836 if (verbose && f)
837 fprintf (stderr, "running %s\n", fname);
838 }
839
840 /* Note that we do NOT look for a Makefile-generated foo.script in
841 the build directory. If that is ever needed, this is the place
842 to add it. */
843
844 /* This is where we "interpret" the mini-script which is <test>.script. */
845 if (f != NULL)
846 {
847 while (getline (&the_line, &line_len, f) > 0)
848 {
849 int nt = tokenize (the_line, the_words, 3);
850 int i;
851
852 for (i = 1; i < nt; ++i)
853 {
854 if (memcmp (the_words[i], "$B/", 3) == 0)
855 the_words[i] = concat (support_objdir_root,
856 the_words[i] + 2, NULL);
857 else if (memcmp (the_words[i], "$S/", 3) == 0)
858 the_words[i] = concat (support_srcdir_root,
859 the_words[i] + 2, NULL);
860 else if (memcmp (the_words[i], "$I/", 3) == 0)
861 the_words[i] = concat (new_root_path,
862 support_install_prefix,
863 the_words[i] + 2, NULL);
864 else if (memcmp (the_words[i], "$L/", 3) == 0)
865 the_words[i] = concat (new_root_path,
866 support_libdir_prefix,
867 the_words[i] + 2, NULL);
868 else if (the_words[i][0] == '/')
869 the_words[i] = concat (new_root_path,
870 the_words[i], NULL);
871 }
872
873 if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
874 {
875 char *r = strrchr (the_words[1], '/');
876 if (r)
877 the_words[2] = concat (the_words[2], r + 1, NULL);
878 else
879 the_words[2] = concat (the_words[2], the_words[1], NULL);
880 }
881
882 if (nt == 2 && strcmp (the_words[0], "so") == 0)
883 {
884 the_words[2] = concat (new_root_path, support_libdir_prefix,
885 "/", the_words[1], NULL);
886 the_words[1] = concat (so_base, the_words[1], NULL);
887 copy_one_file (the_words[1], the_words[2]);
888 }
889 else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
890 {
891 copy_one_file (the_words[1], the_words[2]);
892 }
893 else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
894 {
895 if (rename (the_words[1], the_words[2]) < 0)
896 FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
897 the_words[2], strerror (errno));
898 }
899 else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
900 {
901 long int m;
902 m = strtol (the_words[1], NULL, 0);
903 if (chmod (the_words[2], m) < 0)
904 FAIL_EXIT1 ("chmod %s: %s\n",
905 the_words[2], strerror (errno));
906
907 }
908 else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
909 {
910 maybe_xunlink (the_words[1]);
911 }
912 else if (nt == 1 && strcmp (the_words[0], "su") == 0)
913 {
914 be_su = 1;
915 }
916 else if (nt > 0 && the_words[0][0] != '#')
917 {
918 printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
919 }
920 }
921 fclose (f);
922 }
923 }
924
925 if (do_postclean)
926 {
927 pid_t pc_pid = fork ();
928
929 if (pc_pid < 0)
930 {
931 FAIL_EXIT1 ("Can't fork for post-clean");
932 }
933 else if (pc_pid > 0)
934 {
935 /* Parent. */
936 int status;
937 waitpid (pc_pid, &status, 0);
938
939 /* Child has exited, we can post-clean the test root. */
940 printf("running post-clean rsync\n");
941 rsync (pristine_root_path, new_root_path, 1);
942
943 if (WIFEXITED (status))
944 exit (WEXITSTATUS (status));
945
946 if (WIFSIGNALED (status))
947 {
948 printf ("%%SIGNALLED%%\n");
949 exit (77);
950 }
951
952 printf ("%%EXITERROR%%\n");
953 exit (78);
954 }
955
956 /* Child continues. */
957 }
958
959 /* This is the last point in the program where we're still in the
960 "normal" namespace. */
961
962#ifdef CLONE_NEWNS
963 /* The unshare here gives us our own spaces and capabilities. */
964 if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
965 {
966 /* Older kernels may not support all the options, or security
967 policy may block this call. */
968 if (errno == EINVAL || errno == EPERM)
969 {
970 int saved_errno = errno;
971 if (errno == EPERM)
972 check_for_unshare_hints ();
973 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
974 }
975 else
976 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
977 }
978#else
979 /* Some targets may not support unshare at all. */
980 FAIL_UNSUPPORTED ("unshare support missing");
981#endif
982
983 /* Some systems, by default, all mounts leak out of the namespace. */
984 if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
985 FAIL_EXIT1 ("could not create a private mount namespace\n");
986
987 trymount (support_srcdir_root, new_srcdir_path);
988 trymount (support_objdir_root, new_objdir_path);
989
990 xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
991 devmount (new_root_path, "null");
992 devmount (new_root_path, "zero");
993 devmount (new_root_path, "urandom");
994
995 /* We're done with the "old" root, switch to the new one. */
996 if (chroot (new_root_path) < 0)
997 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
998
999 if (chdir (new_cwd_path) < 0)
1000 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
1001
1002 /* To complete the containerization, we need to fork () at least
1003 once. We can't exec, nor can we somehow link the new child to
1004 our parent. So we run the child and propogate it's exit status
1005 up. */
1006 child = fork ();
1007 if (child < 0)
1008 FAIL_EXIT1 ("Unable to fork");
1009 else if (child > 0)
1010 {
1011 /* Parent. */
1012 int status;
1013 waitpid (child, &status, 0);
1014
1015 if (WIFEXITED (status))
1016 exit (WEXITSTATUS (status));
1017
1018 if (WIFSIGNALED (status))
1019 {
1020 printf ("%%SIGNALLED%%\n");
1021 exit (77);
1022 }
1023
1024 printf ("%%EXITERROR%%\n");
1025 exit (78);
1026 }
1027
1028 /* The rest is the child process, which is now PID 1 and "in" the
1029 new root. */
1030
1031 maybe_xmkdir ("/tmp", 0755);
1032
1033 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1034 maybe_xmkdir ("/proc", 0777);
1035 if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
1036 FAIL_EXIT1 ("Unable to mount /proc: ");
1037
1038 /* We map our original UID to the same UID in the container so we
1039 can own our own files normally. */
1040 UMAP = open ("/proc/self/uid_map", O_WRONLY);
1041 if (UMAP < 0)
1042 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1043
1044 sprintf (tmp, "%lld %lld 1\n",
1045 (long long) (be_su ? 0 : original_uid), (long long) original_uid);
1046 write (UMAP, tmp, strlen (tmp));
1047 xclose (UMAP);
1048
1049 /* We must disable setgroups () before we can map our groups, else we
1050 get EPERM. */
1051 GMAP = open ("/proc/self/setgroups", O_WRONLY);
1052 if (GMAP >= 0)
1053 {
1054 /* We support kernels old enough to not have this. */
1055 write (GMAP, "deny\n", 5);
1056 xclose (GMAP);
1057 }
1058
1059 /* We map our original GID to the same GID in the container so we
1060 can own our own files normally. */
1061 GMAP = open ("/proc/self/gid_map", O_WRONLY);
1062 if (GMAP < 0)
1063 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1064
1065 sprintf (tmp, "%lld %lld 1\n",
1066 (long long) (be_su ? 0 : original_gid), (long long) original_gid);
1067 write (GMAP, tmp, strlen (tmp));
1068 xclose (GMAP);
1069
1070 /* Now run the child. */
1071 execvp (new_child_proc[0], new_child_proc);
1072
1073 /* Or don't run the child? */
1074 FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
1075
1076 /* Because gcc won't know error () never returns... */
1077 exit (EXIT_UNSUPPORTED);
1078}
1079