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