]>
git.ipfire.org Git - thirdparty/glibc.git/blob - support/test-container.c
1 /* Run a test case in an isolated namespace.
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
19 #define _FILE_OFFSET_BITS 64
25 #include <sys/syscall.h>
27 #include <sys/types.h>
31 #include <sys/fcntl.h>
35 #include <sys/sysmacros.h>
40 #include <libc-pointer-arith.h>
43 #include <sys/mount.h>
46 #include <support/support.h>
47 #include <support/xunistd.h>
49 #include "test-driver.h"
52 #define mount(s,t,fs,f,d) no_mount()
55 FAIL_UNSUPPORTED("mount not supported; port needed");
61 /* Running a test in a container is tricky. There are two main
62 categories of things to do:
64 1. "Once" actions, like setting up the container and doing an
67 2. "Per-test" actions, like copying in support files and
68 configuring the container.
73 * mkdir $buildroot/testroot.pristine/
75 * rsync to $buildroot/testroot.root/
78 * maybe rsync to $buildroot/testroot.root/
79 * copy support files and test binary
81 * set up any mounts (like /proc)
85 For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
88 * mytest.root/ is rsync'd into container
89 * mytest.root/preclean.req causes fresh rsync (with delete) before
91 * mytest.root/mytest.script has a list of "commands" to run:
98 FILE must start with $B/, $S/, $I/, $L/, or /
99 (expands to build dir, source dir, install dir, library dir
100 (in container), or container's root)
103 - 'su': Enables running test as root in the container.
104 - 'mv': A minimal move files command.
105 - 'cp': A minimal copy files command.
106 - 'rm': A minimal remove files command.
107 * mytest.root/postclean.req causes fresh rsync (with delete) after
110 Note that $srcdir/foo/mytest.script may be used instead of a
111 $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
112 there is no other reason for a sysroot.
116 * independent of other packages which may not be installed (like
117 rsync or Docker, or even "cp")
119 * Simple, easy to review code (i.e. prefer simple naive code over
120 complex efficient code)
122 * The current implementation ist parallel-make-safe, but only in
123 that it uses a lock to prevent parallel access to the testroot. */
126 /* Utility Functions */
128 /* Like xunlink, but it's OK if the file already doesn't exist. */
130 maybe_xunlink (const char *path
)
132 int rv
= unlink (path
);
133 if (rv
< 0 && errno
!= ENOENT
)
134 FAIL_EXIT1 ("unlink (\"%s\"): %m", path
);
137 /* Like xmkdir, but it's OK if the directory already exists. */
139 maybe_xmkdir (const char *path
, mode_t mode
)
143 if (stat (path
, &st
) == 0
144 && S_ISDIR (st
.st_mode
))
149 /* Temporarily concatenate multiple strings into one. Allows up to 10
150 temporary results; use strdup () if you need them to be
153 concat (const char *str
, ...)
155 /* Assume initialized to NULL/zero. */
156 static char *bufs
[10];
157 static size_t buflens
[10];
169 bufn
= (bufn
+ 1) % 10;
172 while ((next
= va_arg (ap
, char *)) != NULL
)
173 len
= len
+ strlen (next
);
179 bufs
[n
] = xmalloc (len
+ 1); /* NUL */
180 buflens
[n
] = len
+ 1;
182 else if (buflens
[n
] < len
+ 1)
184 bufs
[n
] = xrealloc (bufs
[n
], len
+ 1); /* NUL */
185 buflens
[n
] = len
+ 1;
188 strcpy (bufs
[n
], str
);
189 cp
= strchr (bufs
[n
], '\0');
190 while ((next
= va_arg (ap2
, char *)) != NULL
)
193 cp
= strchr (cp
, '\0');
201 /* Try to mount SRC onto DEST. */
203 trymount (const char *src
, const char *dest
)
205 if (mount (src
, dest
, "", MS_BIND
, NULL
) < 0)
206 FAIL_EXIT1 ("can't mount %s onto %s\n", src
, dest
);
209 /* Special case of above for devices like /dev/zero where we have to
210 mount a device over a device, not a directory over a directory. */
212 devmount (const char *new_root_path
, const char *which
)
215 fd
= open (concat (new_root_path
, "/dev/", which
, NULL
),
216 O_CREAT
| O_TRUNC
| O_RDWR
, 0777);
219 trymount (concat ("/dev/", which
, NULL
),
220 concat (new_root_path
, "/dev/", which
, NULL
));
223 /* Returns true if the string "looks like" an environement variable
226 is_env_setting (const char *a
)
232 if (isalnum (*a
) || *a
== '_')
234 else if (*a
== '=' && count_name
> 0)
243 /* Break the_line into words and store in the_words. Max nwords,
244 returns actual count. */
246 tokenize (char *the_line
, char **the_words
, int nwords
)
252 /* Skip leading whitespace, if any. */
253 while (*the_line
&& isspace (*the_line
))
260 /* THE_LINE points to a non-whitespace character, so we have a
262 *the_words
= the_line
;
267 /* Skip leading whitespace, if any. */
268 while (*the_line
&& ! isspace (*the_line
))
271 /* We now point at the trailing NUL *or* some whitespace. */
275 /* It was whitespace, skip and keep tokenizing. */
279 /* We get here if we filled the words buffer. */
284 /* Mini-RSYNC implementation. Optimize later. */
286 /* A few routines for an "rsync buffer" which stores the paths we're
287 working on. We continuously grow and shrink the paths in each
288 buffer so there's lot of re-use. */
290 /* We rely on "initialized to zero" to set these up. */
298 static path_buf spath
, dpath
;
301 r_setup (char *path
, path_buf
* pb
)
303 size_t len
= strlen (path
);
304 if (pb
->buf
== NULL
|| pb
->size
< len
+ 1)
306 /* Round up. This is an arbitrary number, just to keep from
307 reallocing too often. */
308 size_t sz
= ALIGN_UP (len
+ 1, 512);
310 pb
->buf
= (char *) xmalloc (sz
);
312 pb
->buf
= (char *) xrealloc (pb
->buf
, sz
);
314 FAIL_EXIT1 ("Out of memory while rsyncing\n");
318 strcpy (pb
->buf
, path
);
323 r_append (const char *path
, path_buf
* pb
)
325 size_t len
= strlen (path
) + pb
->len
;
326 if (pb
->size
< len
+ 1)
329 size_t sz
= ALIGN_UP (len
+ 1, 512);
330 pb
->buf
= (char *) xrealloc (pb
->buf
, sz
);
332 FAIL_EXIT1 ("Out of memory while rsyncing\n");
336 strcpy (pb
->buf
+ pb
->len
, path
);
341 file_exists (char *path
)
344 if (lstat (path
, &st
) == 0)
350 recursive_remove (char *path
)
360 FAIL_EXIT1 ("Unable to fork");
363 execlp ("rm", "rm", "-rf", path
, NULL
);
366 waitpid (child
, &status
, 0);
367 /* "rm" would have already printed a suitable error message. */
368 if (! WIFEXITED (status
)
369 || WEXITSTATUS (status
) != 0)
376 /* Used for both rsync and the mytest.script "cp" command. */
378 copy_one_file (const char *sname
, const char *dname
)
382 struct utimbuf times
;
384 sfd
= open (sname
, O_RDONLY
);
386 FAIL_EXIT1 ("unable to open %s for reading\n", sname
);
388 if (fstat (sfd
, &st
) < 0)
389 FAIL_EXIT1 ("unable to fstat %s\n", sname
);
391 dfd
= open (dname
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0600);
393 FAIL_EXIT1 ("unable to open %s for writing\n", dname
);
395 xcopy_file_range (sfd
, 0, dfd
, 0, st
.st_size
, 0);
400 if (chmod (dname
, st
.st_mode
& 0777) < 0)
401 FAIL_EXIT1 ("chmod %s: %s\n", dname
, strerror (errno
));
403 times
.actime
= st
.st_atime
;
404 times
.modtime
= st
.st_mtime
;
405 if (utime (dname
, ×
) < 0)
406 FAIL_EXIT1 ("utime %s: %s\n", dname
, strerror (errno
));
409 /* We don't check *everything* about the two files to see if a copy is
410 needed, just the minimum to make sure we get the latest copy. */
412 need_sync (char *ap
, char *bp
, struct stat
*a
, struct stat
*b
)
414 if ((a
->st_mode
& S_IFMT
) != (b
->st_mode
& S_IFMT
))
417 if (S_ISLNK (a
->st_mode
))
422 if (a
->st_size
!= b
->st_size
)
427 rv
= strcmp (al
, bl
);
431 return 0; /* links are same */
432 return 1; /* links differ */
437 if (a
->st_size
!= b
->st_size
)
439 if ((a
->st_mode
& 0777) != (b
->st_mode
& 0777))
441 if (a
->st_mtime
!= b
->st_mtime
)
445 if (a
->st_size
== b
->st_size
446 && ((a
->st_mode
& 0777) == (b
->st_mode
& 0777))
447 && a
->st_mtime
== b
->st_mtime
)
454 rsync_1 (path_buf
* src
, path_buf
* dest
, int and_delete
)
461 r_append ("/", dest
);
464 printf ("sync %s to %s %s\n", src
->buf
, dest
->buf
,
465 and_delete
? "and delete" : "");
467 size_t staillen
= src
->len
;
469 size_t dtaillen
= dest
->len
;
471 dir
= opendir (src
->buf
);
473 while ((de
= readdir (dir
)) != NULL
)
475 if (strcmp (de
->d_name
, ".") == 0
476 || strcmp (de
->d_name
, "..") == 0)
480 r_append (de
->d_name
, src
);
481 dest
->len
= dtaillen
;
482 r_append (de
->d_name
, dest
);
487 if (lstat (src
->buf
, &s
) != 0)
488 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src
->buf
);
490 /* It's OK if this one fails, since we know the file might be
492 lstat (dest
->buf
, &d
);
494 if (! need_sync (src
->buf
, dest
->buf
, &s
, &d
))
496 if (S_ISDIR (s
.st_mode
))
497 rsync_1 (src
, dest
, and_delete
);
502 switch (d
.st_mode
& S_IFMT
)
505 if (!S_ISDIR (s
.st_mode
))
508 printf ("-D %s\n", dest
->buf
);
509 recursive_remove (dest
->buf
);
515 printf ("-F %s\n", dest
->buf
);
516 maybe_xunlink (dest
->buf
);
520 switch (s
.st_mode
& S_IFMT
)
524 printf ("+F %s\n", dest
->buf
);
525 copy_one_file (src
->buf
, dest
->buf
);
530 printf ("+D %s\n", dest
->buf
);
531 maybe_xmkdir (dest
->buf
, (s
.st_mode
& 0777) | 0700);
532 rsync_1 (src
, dest
, and_delete
);
539 printf ("+L %s\n", dest
->buf
);
540 lp
= xreadlink (src
->buf
);
541 xsymlink (lp
, dest
->buf
);
553 src
->buf
[staillen
] = 0;
554 dest
->len
= dtaillen
;
555 dest
->buf
[dtaillen
] = 0;
560 /* The rest of this function removes any files/directories in DEST
561 that do not exist in SRC. This is triggered as part of a
562 preclean or postsclean step. */
564 dir
= opendir (dest
->buf
);
566 while ((de
= readdir (dir
)) != NULL
)
568 if (strcmp (de
->d_name
, ".") == 0
569 || strcmp (de
->d_name
, "..") == 0)
573 r_append (de
->d_name
, src
);
574 dest
->len
= dtaillen
;
575 r_append (de
->d_name
, dest
);
580 lstat (src
->buf
, &s
);
582 if (lstat (dest
->buf
, &d
) != 0)
583 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest
->buf
);
587 /* dest exists and src doesn't, clean it. */
588 switch (d
.st_mode
& S_IFMT
)
591 if (!S_ISDIR (s
.st_mode
))
594 printf ("-D %s\n", dest
->buf
);
595 recursive_remove (dest
->buf
);
601 printf ("-F %s\n", dest
->buf
);
602 maybe_xunlink (dest
->buf
);
612 rsync (char *src
, char *dest
, int and_delete
)
614 r_setup (src
, &spath
);
615 r_setup (dest
, &dpath
);
617 rsync_1 (&spath
, &dpath
, and_delete
);
622 /* See if we can detect what the user needs to do to get unshare
623 support working for us. */
625 check_for_unshare_hints (void)
630 /* Default Debian Linux disables user namespaces, but allows a way
632 f
= fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
635 i
= 99; /* Sentinel. */
636 fscanf (f
, "%d", &i
);
639 printf ("To enable test-container, please run this as root:\n");
640 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
646 /* ALT Linux has an alternate way of doing the same. */
647 f
= fopen ("/proc/sys/kernel/userns_restrict", "r");
650 i
= 99; /* Sentinel. */
651 fscanf (f
, "%d", &i
);
654 printf ("To enable test-container, please run this as root:\n");
655 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
663 main (int argc
, char **argv
)
666 char *pristine_root_path
;
669 char *new_objdir_path
;
670 char *new_srcdir_path
;
671 char **new_child_proc
;
674 char *command_basename
;
676 int do_postclean
= 0;
680 /* If set, the test runs as root instead of the user running the testsuite. */
684 /* Used for "%lld %lld 1" so need not be large. */
689 setbuf (stdout
, NULL
);
691 /* The command line we're expecting looks like this:
692 env <set some vars> ld.so <library path> test-binary
694 We need to peel off any "env" or "ld.so" portion of the command
695 line, and keep track of which env vars we should preserve and
700 fprintf (stderr
, "Usage: containerize <program to run> <args...>\n");
704 if (strcmp (argv
[1], "-v") == 0)
711 if (strcmp (argv
[1], "env") == 0)
715 while (is_env_setting (argv
[1]))
717 /* If there are variables we do NOT want to propogate, this
718 is where the test for them goes. */
720 /* Need to keep these. Note that putenv stores a
721 pointer to our argv. */
729 if (strcmp (argv
[1], support_objdir_elf_ldso
) == 0)
733 while (argv
[1][0] == '-')
735 if (strcmp (argv
[1], "--library-path") == 0)
745 pristine_root_path
= strdup (concat (support_objdir_root
,
746 "/testroot.pristine", NULL
));
747 new_root_path
= strdup (concat (support_objdir_root
,
748 "/testroot.root", NULL
));
749 new_cwd_path
= get_current_dir_name ();
750 new_child_proc
= argv
+ 1;
752 lock_fd
= open (concat (pristine_root_path
, "/lock.fd", NULL
),
753 O_CREAT
| O_TRUNC
| O_RDWR
, 0666);
755 FAIL_EXIT1 ("Cannot create testroot lock.\n");
757 while (flock (lock_fd
, LOCK_EX
) != 0)
760 FAIL_EXIT1 ("Cannot lock testroot.\n");
763 xmkdirp (new_root_path
, 0755);
765 /* We look for extra setup info in a subdir in the same spot as the
766 test, with the same name but a ".root" extension. This is that
767 directory. We try to look in the source tree if the path we're
768 given refers to the build tree, but we rely on the path to be
769 absolute. This is what the glibc makefiles do. */
770 command_root
= concat (argv
[1], ".root", NULL
);
771 if (strncmp (command_root
, support_objdir_root
,
772 strlen (support_objdir_root
)) == 0
773 && command_root
[strlen (support_objdir_root
)] == '/')
774 command_root
= concat (support_srcdir_root
,
775 argv
[1] + strlen (support_objdir_root
),
777 command_root
= strdup (command_root
);
779 /* This cuts off the ".root" we appended above. */
780 command_base
= strdup (command_root
);
781 command_base
[strlen (command_base
) - 5] = 0;
783 /* This is the basename of the test we're running. */
784 command_basename
= strrchr (command_base
, '/');
785 if (command_basename
== NULL
)
786 command_basename
= command_base
;
790 /* Shared object base directory. */
791 so_base
= strdup (argv
[1]);
792 if (strrchr (so_base
, '/') != NULL
)
793 strrchr (so_base
, '/')[1] = 0;
795 if (file_exists (concat (command_root
, "/postclean.req", NULL
)))
798 rsync (pristine_root_path
, new_root_path
,
799 file_exists (concat (command_root
, "/preclean.req", NULL
)));
801 if (stat (command_root
, &st
) >= 0
802 && S_ISDIR (st
.st_mode
))
803 rsync (command_root
, new_root_path
, 0);
805 new_objdir_path
= strdup (concat (new_root_path
,
806 support_objdir_root
, NULL
));
807 new_srcdir_path
= strdup (concat (new_root_path
,
808 support_srcdir_root
, NULL
));
810 /* new_cwd_path starts with '/' so no "/" needed between the two. */
811 xmkdirp (concat (new_root_path
, new_cwd_path
, NULL
), 0755);
812 xmkdirp (new_srcdir_path
, 0755);
813 xmkdirp (new_objdir_path
, 0755);
815 original_uid
= getuid ();
816 original_gid
= getgid ();
818 /* Handle the cp/mv/rm "script" here. */
820 char *the_line
= NULL
;
822 char *fname
= concat (command_root
, "/",
823 command_basename
, ".script", NULL
);
825 FILE *f
= fopen (fname
, "r");
828 fprintf (stderr
, "running %s\n", fname
);
832 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
833 fname
= concat (command_base
, ".script", NULL
);
834 f
= fopen (fname
, "r");
836 fprintf (stderr
, "running %s\n", fname
);
839 /* Note that we do NOT look for a Makefile-generated foo.script in
840 the build directory. If that is ever needed, this is the place
843 /* This is where we "interpret" the mini-script which is <test>.script. */
846 while (getline (&the_line
, &line_len
, f
) > 0)
848 int nt
= tokenize (the_line
, the_words
, 3);
851 for (i
= 1; i
< nt
; ++i
)
853 if (memcmp (the_words
[i
], "$B/", 3) == 0)
854 the_words
[i
] = concat (support_objdir_root
,
855 the_words
[i
] + 2, NULL
);
856 else if (memcmp (the_words
[i
], "$S/", 3) == 0)
857 the_words
[i
] = concat (support_srcdir_root
,
858 the_words
[i
] + 2, NULL
);
859 else if (memcmp (the_words
[i
], "$I/", 3) == 0)
860 the_words
[i
] = concat (new_root_path
,
861 support_install_prefix
,
862 the_words
[i
] + 2, NULL
);
863 else if (memcmp (the_words
[i
], "$L/", 3) == 0)
864 the_words
[i
] = concat (new_root_path
,
865 support_libdir_prefix
,
866 the_words
[i
] + 2, NULL
);
867 else if (the_words
[i
][0] == '/')
868 the_words
[i
] = concat (new_root_path
,
872 if (nt
== 3 && the_words
[2][strlen (the_words
[2]) - 1] == '/')
874 char *r
= strrchr (the_words
[1], '/');
876 the_words
[2] = concat (the_words
[2], r
+ 1, NULL
);
878 the_words
[2] = concat (the_words
[2], the_words
[1], NULL
);
881 if (nt
== 2 && strcmp (the_words
[0], "so") == 0)
883 the_words
[2] = concat (new_root_path
, support_libdir_prefix
,
884 "/", the_words
[1], NULL
);
885 the_words
[1] = concat (so_base
, the_words
[1], NULL
);
886 copy_one_file (the_words
[1], the_words
[2]);
888 else if (nt
== 3 && strcmp (the_words
[0], "cp") == 0)
890 copy_one_file (the_words
[1], the_words
[2]);
892 else if (nt
== 3 && strcmp (the_words
[0], "mv") == 0)
894 if (rename (the_words
[1], the_words
[2]) < 0)
895 FAIL_EXIT1 ("rename %s -> %s: %s", the_words
[1],
896 the_words
[2], strerror (errno
));
898 else if (nt
== 3 && strcmp (the_words
[0], "chmod") == 0)
901 m
= strtol (the_words
[1], NULL
, 0);
902 if (chmod (the_words
[2], m
) < 0)
903 FAIL_EXIT1 ("chmod %s: %s\n",
904 the_words
[2], strerror (errno
));
907 else if (nt
== 2 && strcmp (the_words
[0], "rm") == 0)
909 maybe_xunlink (the_words
[1]);
911 else if (nt
== 1 && strcmp (the_words
[0], "su") == 0)
915 else if (nt
> 0 && the_words
[0][0] != '#')
917 printf ("\033[31minvalid [%s]\033[0m\n", the_words
[0]);
926 pid_t pc_pid
= fork ();
930 FAIL_EXIT1 ("Can't fork for post-clean");
936 waitpid (pc_pid
, &status
, 0);
938 /* Child has exited, we can post-clean the test root. */
939 printf("running post-clean rsync\n");
940 rsync (pristine_root_path
, new_root_path
, 1);
942 if (WIFEXITED (status
))
943 exit (WEXITSTATUS (status
));
945 if (WIFSIGNALED (status
))
947 printf ("%%SIGNALLED%%\n");
951 printf ("%%EXITERROR%%\n");
955 /* Child continues. */
958 /* This is the last point in the program where we're still in the
959 "normal" namespace. */
962 /* The unshare here gives us our own spaces and capabilities. */
963 if (unshare (CLONE_NEWUSER
| CLONE_NEWPID
| CLONE_NEWNS
) < 0)
965 /* Older kernels may not support all the options, or security
966 policy may block this call. */
967 if (errno
== EINVAL
|| errno
== EPERM
)
969 int saved_errno
= errno
;
971 check_for_unshare_hints ();
972 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno
));
975 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno
));
978 /* Some targets may not support unshare at all. */
979 FAIL_UNSUPPORTED ("unshare support missing");
982 /* Some systems, by default, all mounts leak out of the namespace. */
983 if (mount ("none", "/", NULL
, MS_REC
| MS_PRIVATE
, NULL
) != 0)
984 FAIL_EXIT1 ("could not create a private mount namespace\n");
986 trymount (support_srcdir_root
, new_srcdir_path
);
987 trymount (support_objdir_root
, new_objdir_path
);
989 xmkdirp (concat (new_root_path
, "/dev", NULL
), 0755);
990 devmount (new_root_path
, "null");
991 devmount (new_root_path
, "zero");
992 devmount (new_root_path
, "urandom");
994 /* We're done with the "old" root, switch to the new one. */
995 if (chroot (new_root_path
) < 0)
996 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path
);
998 if (chdir (new_cwd_path
) < 0)
999 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path
);
1001 /* To complete the containerization, we need to fork () at least
1002 once. We can't exec, nor can we somehow link the new child to
1003 our parent. So we run the child and propogate it's exit status
1007 FAIL_EXIT1 ("Unable to fork");
1012 waitpid (child
, &status
, 0);
1014 if (WIFEXITED (status
))
1015 exit (WEXITSTATUS (status
));
1017 if (WIFSIGNALED (status
))
1019 printf ("%%SIGNALLED%%\n");
1023 printf ("%%EXITERROR%%\n");
1027 /* The rest is the child process, which is now PID 1 and "in" the
1030 maybe_xmkdir ("/tmp", 0755);
1032 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1033 maybe_xmkdir ("/proc", 0777);
1034 if (mount ("proc", "/proc", "proc", 0, NULL
) < 0)
1035 FAIL_EXIT1 ("Unable to mount /proc: ");
1037 /* We map our original UID to the same UID in the container so we
1038 can own our own files normally. */
1039 UMAP
= open ("/proc/self/uid_map", O_WRONLY
);
1041 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1043 sprintf (tmp
, "%lld %lld 1\n",
1044 (long long) (be_su
? 0 : original_uid
), (long long) original_uid
);
1045 write (UMAP
, tmp
, strlen (tmp
));
1048 /* We must disable setgroups () before we can map our groups, else we
1050 GMAP
= open ("/proc/self/setgroups", O_WRONLY
);
1053 /* We support kernels old enough to not have this. */
1054 write (GMAP
, "deny\n", 5);
1058 /* We map our original GID to the same GID in the container so we
1059 can own our own files normally. */
1060 GMAP
= open ("/proc/self/gid_map", O_WRONLY
);
1062 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1064 sprintf (tmp
, "%lld %lld 1\n",
1065 (long long) (be_su
? 0 : original_gid
), (long long) original_gid
);
1066 write (GMAP
, tmp
, strlen (tmp
));
1069 /* Now run the child. */
1070 execvp (new_child_proc
[0], new_child_proc
);
1072 /* Or don't run the child? */
1073 FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc
[0]);
1075 /* Because gcc won't know error () never returns... */
1076 exit (EXIT_UNSUPPORTED
);