libio: Avoid RMW of flags2 outside lock (BZ #27842)
[glibc.git] / support / test-container.c
blob7557aac4415985ab8b2823c1e9e9b84039c394b7
1 /* Run a test case in an isolated namespace.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #define _FILE_OFFSET_BITS 64
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>
42 #ifdef __linux__
43 #include <sys/mount.h>
44 #endif
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"
52 #ifndef __linux__
53 #define mount(s,t,fs,f,d) no_mount()
54 int no_mount (void)
56 FAIL_UNSUPPORTED("mount not supported; port needed");
58 #endif
60 int verbose = 0;
62 /* Running a test in a container is tricky. There are two main
63 categories of things to do:
65 1. "Once" actions, like setting up the container and doing an
66 install into it.
68 2. "Per-test" actions, like copying in support files and
69 configuring the container.
72 "Once" actions:
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/
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
89 Magic files:
91 For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
92 and, if found...
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 pidns <comment>
102 mv FILE FILE
103 cp FILE FILE
104 rm FILE
105 cwd PATH
106 exec FILE
107 mkdirp MODE DIR
109 variables:
110 $B/ build dir, equivalent to $(common-objpfx)
111 $S/ source dir, equivalent to $(srcdir)
112 $I/ install dir, equivalent to $(prefix)
113 $L/ library dir (in container), equivalent to $(libdir)
114 $complocaledir/ compiled locale dir, equivalent to $(complocaledir)
115 / container's root
117 If FILE begins with any of these variables then they will be
118 substituted for the described value.
120 The goal is to expose as many of the runtime's configured paths
121 via variables so they can be used to setup the container environment
122 before execution reaches the test.
124 details:
125 - '#': A comment.
126 - 'pidns': Require a separate PID namespace, prints comment if it can't
127 (default is a shared pid namespace)
128 - 'su': Enables running test as root in the container.
129 - 'mv': A minimal move files command.
130 - 'cp': A minimal copy files command.
131 - 'rm': A minimal remove files command.
132 - 'cwd': set test working directory
133 - 'exec': change test binary location (may end in /)
134 - 'mkdirp': A minimal "mkdir -p FILE" command.
136 * mytest.root/postclean.req causes fresh rsync (with delete) after
137 test if present
139 * mytest.root/ldconfig.run causes ldconfig to be issued prior
140 test execution (to setup the initial ld.so.cache).
142 Note that $srcdir/foo/mytest.script may be used instead of a
143 $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
144 there is no other reason for a sysroot.
146 Design goals:
148 * independent of other packages which may not be installed (like
149 rsync or Docker, or even "cp")
151 * Simple, easy to review code (i.e. prefer simple naive code over
152 complex efficient code)
154 * The current implementation is parallel-make-safe, but only in
155 that it uses a lock to prevent parallel access to the testroot. */
158 /* Utility Functions */
160 /* Like xunlink, but it's OK if the file already doesn't exist. */
161 void
162 maybe_xunlink (const char *path)
164 int rv = unlink (path);
165 if (rv < 0 && errno != ENOENT)
166 FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
169 /* Like xmkdir, but it's OK if the directory already exists. */
170 void
171 maybe_xmkdir (const char *path, mode_t mode)
173 struct stat st;
175 if (stat (path, &st) == 0
176 && S_ISDIR (st.st_mode))
177 return;
178 xmkdir (path, mode);
181 /* Temporarily concatenate multiple strings into one. Allows up to 10
182 temporary results; use xstrdup () if you need them to be
183 permanent. */
184 static char *
185 concat (const char *str, ...)
187 /* Assume initialized to NULL/zero. */
188 static char *bufs[10];
189 static size_t buflens[10];
190 static int bufn = 0;
191 int n;
192 size_t len;
193 va_list ap, ap2;
194 char *cp;
195 char *next;
197 va_start (ap, str);
198 va_copy (ap2, ap);
200 n = bufn;
201 bufn = (bufn + 1) % 10;
202 len = strlen (str);
204 while ((next = va_arg (ap, char *)) != NULL)
205 len = len + strlen (next);
207 va_end (ap);
209 if (bufs[n] == NULL)
211 bufs[n] = xmalloc (len + 1); /* NUL */
212 buflens[n] = len + 1;
214 else if (buflens[n] < len + 1)
216 bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
217 buflens[n] = len + 1;
220 strcpy (bufs[n], str);
221 cp = strchr (bufs[n], '\0');
222 while ((next = va_arg (ap2, char *)) != NULL)
224 strcpy (cp, next);
225 cp = strchr (cp, '\0');
227 *cp = 0;
228 va_end (ap2);
230 return bufs[n];
233 #ifdef CLONE_NEWNS
234 /* Like the above, but put spaces between words. Caller frees. */
235 static char *
236 concat_words (char **words, int num_words)
238 int len = 0;
239 int i;
240 char *rv, *p;
242 for (i = 0; i < num_words; i ++)
244 len += strlen (words[i]);
245 len ++;
248 p = rv = (char *) xmalloc (len);
250 for (i = 0; i < num_words; i ++)
252 if (i > 0)
253 p = stpcpy (p, " ");
254 p = stpcpy (p, words[i]);
257 return rv;
259 #endif
261 /* Try to mount SRC onto DEST. */
262 static void
263 trymount (const char *src, const char *dest)
265 if (mount (src, dest, "", MS_BIND | MS_REC, NULL) < 0)
266 FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
269 /* Special case of above for devices like /dev/zero where we have to
270 mount a device over a device, not a directory over a directory. */
271 static void
272 devmount (const char *new_root_path, const char *which)
274 int fd;
275 fd = open (concat (new_root_path, "/dev/", which, NULL),
276 O_CREAT | O_TRUNC | O_RDWR, 0777);
277 xclose (fd);
279 trymount (concat ("/dev/", which, NULL),
280 concat (new_root_path, "/dev/", which, NULL));
283 /* Returns true if the string "looks like" an environement variable
284 being set. */
285 static int
286 is_env_setting (const char *a)
288 int count_name = 0;
290 while (*a)
292 if (isalnum (*a) || *a == '_')
293 ++count_name;
294 else if (*a == '=' && count_name > 0)
295 return 1;
296 else
297 return 0;
298 ++a;
300 return 0;
303 /* Break the_line into words and store in the_words. Max nwords,
304 returns actual count. */
305 static int
306 tokenize (char *the_line, char **the_words, int nwords)
308 int rv = 0;
310 while (nwords > 0)
312 /* Skip leading whitespace, if any. */
313 while (*the_line && isspace (*the_line))
314 ++the_line;
316 /* End of line? */
317 if (*the_line == 0)
318 return rv;
320 /* THE_LINE points to a non-whitespace character, so we have a
321 word. */
322 *the_words = the_line;
323 ++the_words;
324 nwords--;
325 ++rv;
327 /* Skip leading whitespace, if any. */
328 while (*the_line && ! isspace (*the_line))
329 ++the_line;
331 /* We now point at the trailing NUL *or* some whitespace. */
332 if (*the_line == 0)
333 return rv;
335 /* It was whitespace, skip and keep tokenizing. */
336 *the_line++ = 0;
339 /* We get here if we filled the words buffer. */
340 return rv;
344 /* Mini-RSYNC implementation. Optimize later. */
346 /* A few routines for an "rsync buffer" which stores the paths we're
347 working on. We continuously grow and shrink the paths in each
348 buffer so there's lot of re-use. */
350 /* We rely on "initialized to zero" to set these up. */
351 typedef struct
353 char *buf;
354 size_t len;
355 size_t size;
356 } path_buf;
358 static path_buf spath, dpath;
360 static void
361 r_setup (char *path, path_buf * pb)
363 size_t len = strlen (path);
364 if (pb->buf == NULL || pb->size < len + 1)
366 /* Round up. This is an arbitrary number, just to keep from
367 reallocing too often. */
368 size_t sz = ALIGN_UP (len + 1, 512);
369 if (pb->buf == NULL)
370 pb->buf = (char *) xmalloc (sz);
371 else
372 pb->buf = (char *) xrealloc (pb->buf, sz);
373 if (pb->buf == NULL)
374 FAIL_EXIT1 ("Out of memory while rsyncing\n");
376 pb->size = sz;
378 strcpy (pb->buf, path);
379 pb->len = len;
382 static void
383 r_append (const char *path, path_buf * pb)
385 size_t len = strlen (path) + pb->len;
386 if (pb->size < len + 1)
388 /* Round up */
389 size_t sz = ALIGN_UP (len + 1, 512);
390 pb->buf = (char *) xrealloc (pb->buf, sz);
391 if (pb->buf == NULL)
392 FAIL_EXIT1 ("Out of memory while rsyncing\n");
394 pb->size = sz;
396 strcpy (pb->buf + pb->len, path);
397 pb->len = len;
400 static int
401 file_exists (char *path)
403 struct stat st;
404 if (lstat (path, &st) == 0)
405 return 1;
406 return 0;
409 static void
410 recursive_remove (char *path)
412 pid_t child;
413 int status;
415 child = fork ();
417 switch (child) {
418 case -1:
419 perror("fork");
420 FAIL_EXIT1 ("Unable to fork");
421 case 0:
422 /* Child. */
423 execlp ("rm", "rm", "-rf", path, NULL);
424 FAIL_EXIT1 ("exec rm: %m");
425 default:
426 /* Parent. */
427 waitpid (child, &status, 0);
428 /* "rm" would have already printed a suitable error message. */
429 if (! WIFEXITED (status)
430 || WEXITSTATUS (status) != 0)
431 FAIL_EXIT1 ("exec child returned status: %d", status);
433 break;
437 /* Used for both rsync and the mytest.script "cp" command. */
438 static void
439 copy_one_file (const char *sname, const char *dname)
441 int sfd, dfd;
442 struct stat st;
443 struct utimbuf times;
445 sfd = open (sname, O_RDONLY);
446 if (sfd < 0)
447 FAIL_EXIT1 ("unable to open %s for reading\n", sname);
449 if (fstat (sfd, &st) < 0)
450 FAIL_EXIT1 ("unable to fstat %s\n", sname);
452 dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
453 if (dfd < 0)
454 FAIL_EXIT1 ("unable to open %s for writing\n", dname);
456 xcopy_file_range (sfd, 0, dfd, 0, st.st_size, 0);
458 xclose (sfd);
459 xclose (dfd);
461 if (chmod (dname, st.st_mode & 0777) < 0)
462 FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
464 times.actime = st.st_atime;
465 times.modtime = st.st_mtime;
466 if (utime (dname, &times) < 0)
467 FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
470 /* We don't check *everything* about the two files to see if a copy is
471 needed, just the minimum to make sure we get the latest copy. */
472 static int
473 need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
475 if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
476 return 1;
478 if (S_ISLNK (a->st_mode))
480 int rv;
481 char *al, *bl;
483 if (a->st_size != b->st_size)
484 return 1;
486 al = xreadlink (ap);
487 bl = xreadlink (bp);
488 rv = strcmp (al, bl);
489 free (al);
490 free (bl);
491 if (rv == 0)
492 return 0; /* links are same */
493 return 1; /* links differ */
496 if (verbose)
498 if (a->st_size != b->st_size)
499 printf ("SIZE\n");
500 if ((a->st_mode & 0777) != (b->st_mode & 0777))
501 printf ("MODE\n");
502 if (a->st_mtime != b->st_mtime)
503 printf ("TIME\n");
506 if (a->st_size == b->st_size
507 && ((a->st_mode & 0777) == (b->st_mode & 0777))
508 && a->st_mtime == b->st_mtime)
509 return 0;
511 return 1;
514 static void
515 rsync_1 (path_buf * src, path_buf * dest, int and_delete, int force_copies)
517 DIR *dir;
518 struct dirent *de;
519 struct stat s, d;
521 r_append ("/", src);
522 r_append ("/", dest);
524 if (verbose)
525 printf ("sync %s to %s%s%s\n", src->buf, dest->buf,
526 and_delete ? " and delete" : "",
527 force_copies ? " (forced)" : "");
529 size_t staillen = src->len;
531 size_t dtaillen = dest->len;
533 dir = opendir (src->buf);
535 while ((de = readdir (dir)) != NULL)
537 if (strcmp (de->d_name, ".") == 0
538 || strcmp (de->d_name, "..") == 0)
539 continue;
541 src->len = staillen;
542 r_append (de->d_name, src);
543 dest->len = dtaillen;
544 r_append (de->d_name, dest);
546 s.st_mode = ~0;
547 d.st_mode = ~0;
549 if (lstat (src->buf, &s) != 0)
550 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
552 /* It's OK if this one fails, since we know the file might be
553 missing. */
554 lstat (dest->buf, &d);
556 if (! force_copies && ! need_sync (src->buf, dest->buf, &s, &d))
558 if (S_ISDIR (s.st_mode))
559 rsync_1 (src, dest, and_delete, force_copies);
560 continue;
563 if (d.st_mode != ~0)
564 switch (d.st_mode & S_IFMT)
566 case S_IFDIR:
567 if (!S_ISDIR (s.st_mode))
569 if (verbose)
570 printf ("-D %s\n", dest->buf);
571 recursive_remove (dest->buf);
573 break;
575 default:
576 if (verbose)
577 printf ("-F %s\n", dest->buf);
578 maybe_xunlink (dest->buf);
579 break;
582 switch (s.st_mode & S_IFMT)
584 case S_IFREG:
585 if (verbose)
586 printf ("+F %s\n", dest->buf);
587 copy_one_file (src->buf, dest->buf);
588 break;
590 case S_IFDIR:
591 if (verbose)
592 printf ("+D %s\n", dest->buf);
593 maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
594 rsync_1 (src, dest, and_delete, force_copies);
595 break;
597 case S_IFLNK:
599 char *lp;
600 if (verbose)
601 printf ("+L %s\n", dest->buf);
602 lp = xreadlink (src->buf);
603 xsymlink (lp, dest->buf);
604 free (lp);
605 break;
608 default:
609 break;
613 closedir (dir);
614 src->len = staillen;
615 src->buf[staillen] = 0;
616 dest->len = dtaillen;
617 dest->buf[dtaillen] = 0;
619 if (!and_delete)
620 return;
622 /* The rest of this function removes any files/directories in DEST
623 that do not exist in SRC. This is triggered as part of a
624 preclean or postsclean step. */
626 dir = opendir (dest->buf);
628 while ((de = readdir (dir)) != NULL)
630 if (strcmp (de->d_name, ".") == 0
631 || strcmp (de->d_name, "..") == 0)
632 continue;
634 src->len = staillen;
635 r_append (de->d_name, src);
636 dest->len = dtaillen;
637 r_append (de->d_name, dest);
639 s.st_mode = ~0;
640 d.st_mode = ~0;
642 lstat (src->buf, &s);
644 if (lstat (dest->buf, &d) != 0)
645 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
647 if (s.st_mode == ~0)
649 /* dest exists and src doesn't, clean it. */
650 switch (d.st_mode & S_IFMT)
652 case S_IFDIR:
653 if (!S_ISDIR (s.st_mode))
655 if (verbose)
656 printf ("-D %s\n", dest->buf);
657 recursive_remove (dest->buf);
659 break;
661 default:
662 if (verbose)
663 printf ("-F %s\n", dest->buf);
664 maybe_xunlink (dest->buf);
665 break;
670 closedir (dir);
673 static void
674 rsync (char *src, char *dest, int and_delete, int force_copies)
676 r_setup (src, &spath);
677 r_setup (dest, &dpath);
679 rsync_1 (&spath, &dpath, and_delete, force_copies);
684 /* See if we can detect what the user needs to do to get unshare
685 support working for us. */
686 void
687 check_for_unshare_hints (void)
689 FILE *f;
690 int i;
692 /* Default Debian Linux disables user namespaces, but allows a way
693 to enable them. */
694 f = fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
695 if (f != NULL)
697 i = 99; /* Sentinel. */
698 fscanf (f, "%d", &i);
699 if (i == 0)
701 printf ("To enable test-container, please run this as root:\n");
702 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
704 fclose (f);
705 return;
708 /* ALT Linux has an alternate way of doing the same. */
709 f = fopen ("/proc/sys/kernel/userns_restrict", "r");
710 if (f != NULL)
712 i = 99; /* Sentinel. */
713 fscanf (f, "%d", &i);
714 if (i == 1)
716 printf ("To enable test-container, please run this as root:\n");
717 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
719 fclose (f);
720 return;
724 static void
725 run_ldconfig (void *x __attribute__((unused)))
727 char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
728 char *args[] = { prog, NULL };
730 execv (args[0], args);
731 FAIL_EXIT1 ("execv: %m");
735 main (int argc, char **argv)
737 pid_t child;
738 char *pristine_root_path;
739 char *new_root_path;
740 char *new_cwd_path;
741 char *new_objdir_path;
742 char *new_srcdir_path;
743 char **new_child_proc;
744 char *new_child_exec;
745 char *command_root;
746 char *command_base;
747 char *command_basename;
748 char *so_base;
749 int do_postclean = 0;
750 bool do_ldconfig = false;
751 char *change_cwd = NULL;
753 int pipes[2];
754 char pid_buf[20];
756 uid_t original_uid;
757 gid_t original_gid;
758 /* If set, the test runs as root instead of the user running the testsuite. */
759 int be_su = 0;
760 int require_pidns = 0;
761 #ifdef CLONE_NEWNS
762 const char *pidns_comment = NULL;
763 #endif
764 int do_proc_mounts = 0;
765 int UMAP;
766 int GMAP;
767 /* Used for "%lld %lld 1" so need not be large. */
768 char tmp[100];
769 struct stat st;
770 int lock_fd;
772 setbuf (stdout, NULL);
774 /* The command line we're expecting looks like this:
775 env <set some vars> ld.so <library path> test-binary
777 We need to peel off any "env" or "ld.so" portion of the command
778 line, and keep track of which env vars we should preserve and
779 which we drop. */
781 if (argc < 2)
783 fprintf (stderr, "Usage: test-container <program to run> <args...>\n");
784 exit (1);
787 if (strcmp (argv[1], "-v") == 0)
789 verbose = 1;
790 ++argv;
791 --argc;
794 if (strcmp (argv[1], "env") == 0)
796 ++argv;
797 --argc;
798 while (is_env_setting (argv[1]))
800 /* If there are variables we do NOT want to propogate, this
801 is where the test for them goes. */
803 /* Need to keep these. Note that putenv stores a
804 pointer to our argv. */
805 putenv (argv[1]);
807 ++argv;
808 --argc;
812 if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
814 ++argv;
815 --argc;
816 while (argv[1][0] == '-')
818 if (strcmp (argv[1], "--library-path") == 0)
820 ++argv;
821 --argc;
823 ++argv;
824 --argc;
828 pristine_root_path = xstrdup (concat (support_objdir_root,
829 "/testroot.pristine", NULL));
830 new_root_path = xstrdup (concat (support_objdir_root,
831 "/testroot.root", NULL));
832 new_cwd_path = get_current_dir_name ();
833 new_child_proc = argv + 1;
834 new_child_exec = argv[1];
836 lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
837 O_CREAT | O_TRUNC | O_RDWR, 0666);
838 if (lock_fd < 0)
839 FAIL_EXIT1 ("Cannot create testroot lock.\n");
841 while (flock (lock_fd, LOCK_EX) != 0)
843 if (errno != EINTR)
844 FAIL_EXIT1 ("Cannot lock testroot.\n");
847 xmkdirp (new_root_path, 0755);
849 /* We look for extra setup info in a subdir in the same spot as the
850 test, with the same name but a ".root" extension. This is that
851 directory. We try to look in the source tree if the path we're
852 given refers to the build tree, but we rely on the path to be
853 absolute. This is what the glibc makefiles do. */
854 command_root = concat (argv[1], ".root", NULL);
855 if (strncmp (command_root, support_objdir_root,
856 strlen (support_objdir_root)) == 0
857 && command_root[strlen (support_objdir_root)] == '/')
858 command_root = concat (support_srcdir_root,
859 argv[1] + strlen (support_objdir_root),
860 ".root", NULL);
861 command_root = xstrdup (command_root);
863 /* This cuts off the ".root" we appended above. */
864 command_base = xstrdup (command_root);
865 command_base[strlen (command_base) - 5] = 0;
867 /* This is the basename of the test we're running. */
868 command_basename = strrchr (command_base, '/');
869 if (command_basename == NULL)
870 command_basename = command_base;
871 else
872 ++command_basename;
874 /* Shared object base directory. */
875 so_base = xstrdup (argv[1]);
876 if (strrchr (so_base, '/') != NULL)
877 strrchr (so_base, '/')[1] = 0;
879 if (file_exists (concat (command_root, "/postclean.req", NULL)))
880 do_postclean = 1;
882 if (file_exists (concat (command_root, "/ldconfig.run", NULL)))
883 do_ldconfig = true;
885 rsync (pristine_root_path, new_root_path,
886 file_exists (concat (command_root, "/preclean.req", NULL)), 0);
888 if (stat (command_root, &st) >= 0
889 && S_ISDIR (st.st_mode))
890 rsync (command_root, new_root_path, 0, 1);
892 new_objdir_path = xstrdup (concat (new_root_path,
893 support_objdir_root, NULL));
894 new_srcdir_path = xstrdup (concat (new_root_path,
895 support_srcdir_root, NULL));
897 /* new_cwd_path starts with '/' so no "/" needed between the two. */
898 xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
899 xmkdirp (new_srcdir_path, 0755);
900 xmkdirp (new_objdir_path, 0755);
902 original_uid = getuid ();
903 original_gid = getgid ();
905 /* Handle the cp/mv/rm "script" here. */
907 char *the_line = NULL;
908 size_t line_len = 0;
909 char *fname = concat (command_root, "/",
910 command_basename, ".script", NULL);
911 char *the_words[3];
912 FILE *f = fopen (fname, "r");
914 if (verbose && f)
915 fprintf (stderr, "running %s\n", fname);
917 if (f == NULL)
919 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
920 fname = concat (command_base, ".script", NULL);
921 f = fopen (fname, "r");
922 if (verbose && f)
923 fprintf (stderr, "running %s\n", fname);
926 /* Note that we do NOT look for a Makefile-generated foo.script in
927 the build directory. If that is ever needed, this is the place
928 to add it. */
930 /* This is where we "interpret" the mini-script which is <test>.script. */
931 if (f != NULL)
933 while (getline (&the_line, &line_len, f) > 0)
935 int nt = tokenize (the_line, the_words, 3);
936 int i;
938 /* Expand variables. */
939 for (i = 1; i < nt; ++i)
941 if (memcmp (the_words[i], "$B/", 3) == 0)
942 the_words[i] = concat (support_objdir_root,
943 the_words[i] + 2, NULL);
944 else if (memcmp (the_words[i], "$S/", 3) == 0)
945 the_words[i] = concat (support_srcdir_root,
946 the_words[i] + 2, NULL);
947 else if (memcmp (the_words[i], "$I/", 3) == 0)
948 the_words[i] = concat (new_root_path,
949 support_install_prefix,
950 the_words[i] + 2, NULL);
951 else if (memcmp (the_words[i], "$L/", 3) == 0)
952 the_words[i] = concat (new_root_path,
953 support_libdir_prefix,
954 the_words[i] + 2, NULL);
955 else if (memcmp (the_words[i], "$complocaledir/", 15) == 0)
956 the_words[i] = concat (new_root_path,
957 support_complocaledir_prefix,
958 the_words[i] + 14, NULL);
959 /* "exec" and "cwd" use inside-root paths. */
960 else if (strcmp (the_words[0], "exec") != 0
961 && strcmp (the_words[0], "cwd") != 0
962 && the_words[i][0] == '/')
963 the_words[i] = concat (new_root_path,
964 the_words[i], NULL);
967 if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
969 char *r = strrchr (the_words[1], '/');
970 if (r)
971 the_words[2] = concat (the_words[2], r + 1, NULL);
972 else
973 the_words[2] = concat (the_words[2], the_words[1], NULL);
976 /* Run the following commands in the_words[0] with NT number of
977 arguments (including the command). */
979 if (nt == 2 && strcmp (the_words[0], "so") == 0)
981 the_words[2] = concat (new_root_path, support_libdir_prefix,
982 "/", the_words[1], NULL);
983 the_words[1] = concat (so_base, the_words[1], NULL);
984 copy_one_file (the_words[1], the_words[2]);
986 else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
988 copy_one_file (the_words[1], the_words[2]);
990 else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
992 if (rename (the_words[1], the_words[2]) < 0)
993 FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
994 the_words[2], strerror (errno));
996 else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
998 long int m;
999 errno = 0;
1000 m = strtol (the_words[1], NULL, 0);
1001 TEST_COMPARE (errno, 0);
1002 if (chmod (the_words[2], m) < 0)
1003 FAIL_EXIT1 ("chmod %s: %s\n",
1004 the_words[2], strerror (errno));
1007 else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
1009 maybe_xunlink (the_words[1]);
1011 else if (nt >= 2 && strcmp (the_words[0], "exec") == 0)
1013 /* The first argument is the desired location and name
1014 of the test binary as we wish to exec it; we will
1015 copy the binary there. The second (optional)
1016 argument is the value to pass as argv[0], it
1017 defaults to the same as the first argument. */
1018 char *new_exec_path = the_words[1];
1020 /* If the new exec path ends with a slash, that's the
1021 * directory, and use the old test base name. */
1022 if (new_exec_path [strlen(new_exec_path) - 1] == '/')
1023 new_exec_path = concat (new_exec_path,
1024 basename (new_child_proc[0]),
1025 NULL);
1028 /* new_child_proc is in the build tree, so has the
1029 same path inside the chroot as outside. The new
1030 exec path is, by definition, relative to the
1031 chroot. */
1032 copy_one_file (new_child_proc[0], concat (new_root_path,
1033 new_exec_path,
1034 NULL));
1036 new_child_exec = xstrdup (new_exec_path);
1037 if (the_words[2])
1038 new_child_proc[0] = xstrdup (the_words[2]);
1039 else
1040 new_child_proc[0] = new_child_exec;
1042 else if (nt == 2 && strcmp (the_words[0], "cwd") == 0)
1044 change_cwd = xstrdup (the_words[1]);
1046 else if (nt == 1 && strcmp (the_words[0], "su") == 0)
1048 be_su = 1;
1050 else if (nt >= 1 && strcmp (the_words[0], "pidns") == 0)
1052 require_pidns = 1;
1053 #ifdef CLONE_NEWNS
1054 if (nt > 1)
1055 pidns_comment = concat_words (the_words + 1, nt - 1);
1056 #endif
1058 else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0)
1060 long int m;
1061 errno = 0;
1062 m = strtol (the_words[1], NULL, 0);
1063 TEST_COMPARE (errno, 0);
1064 xmkdirp (the_words[2], m);
1066 else if (nt > 0 && the_words[0][0] != '#')
1068 fprintf (stderr, "\033[31minvalid [%s]\033[0m\n", the_words[0]);
1069 exit (1);
1072 fclose (f);
1076 if (do_postclean)
1078 pid_t pc_pid = fork ();
1080 if (pc_pid < 0)
1082 FAIL_EXIT1 ("Can't fork for post-clean");
1084 else if (pc_pid > 0)
1086 /* Parent. */
1087 int status;
1088 waitpid (pc_pid, &status, 0);
1090 /* Child has exited, we can post-clean the test root. */
1091 printf("running post-clean rsync\n");
1092 rsync (pristine_root_path, new_root_path, 1, 0);
1094 if (WIFEXITED (status))
1095 exit (WEXITSTATUS (status));
1097 if (WIFSIGNALED (status))
1099 printf ("%%SIGNALLED%%\n");
1100 exit (77);
1103 printf ("%%EXITERROR%%\n");
1104 exit (78);
1107 /* Child continues. */
1110 /* This is the last point in the program where we're still in the
1111 "normal" namespace. */
1113 #ifdef CLONE_NEWNS
1114 /* The unshare here gives us our own spaces and capabilities. */
1115 if (unshare (CLONE_NEWUSER | CLONE_NEWNS
1116 | (require_pidns ? CLONE_NEWPID : 0)) < 0)
1118 /* Older kernels may not support all the options, or security
1119 policy may block this call. */
1120 if (errno == EINVAL || errno == EPERM)
1122 int saved_errno = errno;
1123 if (errno == EPERM)
1124 check_for_unshare_hints ();
1125 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
1127 /* We're about to exit anyway, it's "safe" to call unshare again
1128 just to see if the CLONE_NEWPID caused the error. */
1129 else if (require_pidns && unshare (CLONE_NEWUSER | CLONE_NEWNS) >= 0)
1130 FAIL_EXIT1 ("unable to unshare pid ns: %s : %s", strerror (errno),
1131 pidns_comment ? pidns_comment : "required by test");
1132 else
1133 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
1135 #else
1136 /* Some targets may not support unshare at all. */
1137 FAIL_UNSUPPORTED ("unshare support missing");
1138 #endif
1140 /* Some systems, by default, all mounts leak out of the namespace. */
1141 if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
1142 FAIL_EXIT1 ("could not create a private mount namespace\n");
1144 trymount (support_srcdir_root, new_srcdir_path);
1145 trymount (support_objdir_root, new_objdir_path);
1147 /* It may not be possible to mount /proc directly. */
1148 if (! require_pidns)
1150 char *new_proc = concat (new_root_path, "/proc", NULL);
1151 xmkdirp (new_proc, 0755);
1152 trymount ("/proc", new_proc);
1153 do_proc_mounts = 1;
1156 xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
1157 devmount (new_root_path, "null");
1158 devmount (new_root_path, "zero");
1159 devmount (new_root_path, "urandom");
1161 /* We're done with the "old" root, switch to the new one. */
1162 if (chroot (new_root_path) < 0)
1163 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
1165 if (chdir (new_cwd_path) < 0)
1166 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
1168 /* This is to pass the "outside" PID to the child, which will be PID
1169 1. */
1170 if (pipe2 (pipes, O_CLOEXEC) < 0)
1171 FAIL_EXIT1 ("Can't create pid pipe");
1173 /* To complete the containerization, we need to fork () at least
1174 once. We can't exec, nor can we somehow link the new child to
1175 our parent. So we run the child and propogate it's exit status
1176 up. */
1177 child = fork ();
1178 if (child < 0)
1179 FAIL_EXIT1 ("Unable to fork");
1180 else if (child > 0)
1182 /* Parent. */
1183 int status;
1185 /* Send the child's "outside" pid to it. */
1186 write (pipes[1], &child, sizeof(child));
1187 close (pipes[0]);
1188 close (pipes[1]);
1190 waitpid (child, &status, 0);
1192 if (WIFEXITED (status))
1193 exit (WEXITSTATUS (status));
1195 if (WIFSIGNALED (status))
1197 printf ("%%SIGNALLED%%\n");
1198 exit (77);
1201 printf ("%%EXITERROR%%\n");
1202 exit (78);
1205 /* The rest is the child process, which is now PID 1 and "in" the
1206 new root. */
1208 if (do_ldconfig)
1210 struct support_capture_subprocess result =
1211 support_capture_subprocess (run_ldconfig, NULL);
1212 support_capture_subprocess_check (&result, "execv", 0, sc_allow_none);
1215 /* Get our "outside" pid from our parent. We use this to help with
1216 debugging from outside the container. */
1217 read (pipes[0], &child, sizeof(child));
1218 close (pipes[0]);
1219 close (pipes[1]);
1220 sprintf (pid_buf, "%lu", (long unsigned)child);
1221 setenv ("PID_OUTSIDE_CONTAINER", pid_buf, 0);
1223 maybe_xmkdir ("/tmp", 0755);
1225 if (require_pidns)
1227 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1228 maybe_xmkdir ("/proc", 0777);
1229 if (mount ("proc", "/proc", "proc", 0, NULL) != 0)
1231 /* This happens if we're trying to create a nested container,
1232 like if the build is running under podman, and we lack
1233 priviledges.
1235 Ideally we would WARN here, but that would just add noise to
1236 *every* test-container test, and the ones that care should
1237 have their own relevent diagnostics.
1239 FAIL_EXIT1 ("Unable to mount /proc: "); */
1241 else
1242 do_proc_mounts = 1;
1245 if (do_proc_mounts)
1247 /* We map our original UID to the same UID in the container so we
1248 can own our own files normally. */
1249 UMAP = open ("/proc/self/uid_map", O_WRONLY);
1250 if (UMAP < 0)
1251 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1253 sprintf (tmp, "%lld %lld 1\n",
1254 (long long) (be_su ? 0 : original_uid), (long long) original_uid);
1255 write (UMAP, tmp, strlen (tmp));
1256 xclose (UMAP);
1258 /* We must disable setgroups () before we can map our groups, else we
1259 get EPERM. */
1260 GMAP = open ("/proc/self/setgroups", O_WRONLY);
1261 if (GMAP >= 0)
1263 /* We support kernels old enough to not have this. */
1264 write (GMAP, "deny\n", 5);
1265 xclose (GMAP);
1268 /* We map our original GID to the same GID in the container so we
1269 can own our own files normally. */
1270 GMAP = open ("/proc/self/gid_map", O_WRONLY);
1271 if (GMAP < 0)
1272 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1274 sprintf (tmp, "%lld %lld 1\n",
1275 (long long) (be_su ? 0 : original_gid), (long long) original_gid);
1276 write (GMAP, tmp, strlen (tmp));
1277 xclose (GMAP);
1280 if (change_cwd)
1282 if (chdir (change_cwd) < 0)
1283 FAIL_EXIT1 ("Can't cd to %s inside container - ", change_cwd);
1286 /* Now run the child. */
1287 execvp (new_child_exec, new_child_proc);
1289 /* Or don't run the child? */
1290 FAIL_EXIT1 ("Unable to exec %s: %s\n", new_child_exec, strerror (errno));
1292 /* Because gcc won't know error () never returns... */
1293 exit (EXIT_UNSUPPORTED);