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

source code of glibc/support/test-container.c