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