]>
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 <https://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
);
364 FAIL_EXIT1 ("exec rm: %m");
367 waitpid (child
, &status
, 0);
368 /* "rm" would have already printed a suitable error message. */
369 if (! WIFEXITED (status
)
370 || WEXITSTATUS (status
) != 0)
377 /* Used for both rsync and the mytest.script "cp" command. */
379 copy_one_file (const char *sname
, const char *dname
)
383 struct utimbuf times
;
385 sfd
= open (sname
, O_RDONLY
);
387 FAIL_EXIT1 ("unable to open %s for reading\n", sname
);
389 if (fstat (sfd
, &st
) < 0)
390 FAIL_EXIT1 ("unable to fstat %s\n", sname
);
392 dfd
= open (dname
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0600);
394 FAIL_EXIT1 ("unable to open %s for writing\n", dname
);
396 xcopy_file_range (sfd
, 0, dfd
, 0, st
.st_size
, 0);
401 if (chmod (dname
, st
.st_mode
& 0777) < 0)
402 FAIL_EXIT1 ("chmod %s: %s\n", dname
, strerror (errno
));
404 times
.actime
= st
.st_atime
;
405 times
.modtime
= st
.st_mtime
;
406 if (utime (dname
, ×
) < 0)
407 FAIL_EXIT1 ("utime %s: %s\n", dname
, strerror (errno
));
410 /* We don't check *everything* about the two files to see if a copy is
411 needed, just the minimum to make sure we get the latest copy. */
413 need_sync (char *ap
, char *bp
, struct stat
*a
, struct stat
*b
)
415 if ((a
->st_mode
& S_IFMT
) != (b
->st_mode
& S_IFMT
))
418 if (S_ISLNK (a
->st_mode
))
423 if (a
->st_size
!= b
->st_size
)
428 rv
= strcmp (al
, bl
);
432 return 0; /* links are same */
433 return 1; /* links differ */
438 if (a
->st_size
!= b
->st_size
)
440 if ((a
->st_mode
& 0777) != (b
->st_mode
& 0777))
442 if (a
->st_mtime
!= b
->st_mtime
)
446 if (a
->st_size
== b
->st_size
447 && ((a
->st_mode
& 0777) == (b
->st_mode
& 0777))
448 && a
->st_mtime
== b
->st_mtime
)
455 rsync_1 (path_buf
* src
, path_buf
* dest
, int and_delete
)
462 r_append ("/", dest
);
465 printf ("sync %s to %s %s\n", src
->buf
, dest
->buf
,
466 and_delete
? "and delete" : "");
468 size_t staillen
= src
->len
;
470 size_t dtaillen
= dest
->len
;
472 dir
= opendir (src
->buf
);
474 while ((de
= readdir (dir
)) != NULL
)
476 if (strcmp (de
->d_name
, ".") == 0
477 || strcmp (de
->d_name
, "..") == 0)
481 r_append (de
->d_name
, src
);
482 dest
->len
= dtaillen
;
483 r_append (de
->d_name
, dest
);
488 if (lstat (src
->buf
, &s
) != 0)
489 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src
->buf
);
491 /* It's OK if this one fails, since we know the file might be
493 lstat (dest
->buf
, &d
);
495 if (! need_sync (src
->buf
, dest
->buf
, &s
, &d
))
497 if (S_ISDIR (s
.st_mode
))
498 rsync_1 (src
, dest
, and_delete
);
503 switch (d
.st_mode
& S_IFMT
)
506 if (!S_ISDIR (s
.st_mode
))
509 printf ("-D %s\n", dest
->buf
);
510 recursive_remove (dest
->buf
);
516 printf ("-F %s\n", dest
->buf
);
517 maybe_xunlink (dest
->buf
);
521 switch (s
.st_mode
& S_IFMT
)
525 printf ("+F %s\n", dest
->buf
);
526 copy_one_file (src
->buf
, dest
->buf
);
531 printf ("+D %s\n", dest
->buf
);
532 maybe_xmkdir (dest
->buf
, (s
.st_mode
& 0777) | 0700);
533 rsync_1 (src
, dest
, and_delete
);
540 printf ("+L %s\n", dest
->buf
);
541 lp
= xreadlink (src
->buf
);
542 xsymlink (lp
, dest
->buf
);
554 src
->buf
[staillen
] = 0;
555 dest
->len
= dtaillen
;
556 dest
->buf
[dtaillen
] = 0;
561 /* The rest of this function removes any files/directories in DEST
562 that do not exist in SRC. This is triggered as part of a
563 preclean or postsclean step. */
565 dir
= opendir (dest
->buf
);
567 while ((de
= readdir (dir
)) != NULL
)
569 if (strcmp (de
->d_name
, ".") == 0
570 || strcmp (de
->d_name
, "..") == 0)
574 r_append (de
->d_name
, src
);
575 dest
->len
= dtaillen
;
576 r_append (de
->d_name
, dest
);
581 lstat (src
->buf
, &s
);
583 if (lstat (dest
->buf
, &d
) != 0)
584 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest
->buf
);
588 /* dest exists and src doesn't, clean it. */
589 switch (d
.st_mode
& S_IFMT
)
592 if (!S_ISDIR (s
.st_mode
))
595 printf ("-D %s\n", dest
->buf
);
596 recursive_remove (dest
->buf
);
602 printf ("-F %s\n", dest
->buf
);
603 maybe_xunlink (dest
->buf
);
613 rsync (char *src
, char *dest
, int and_delete
)
615 r_setup (src
, &spath
);
616 r_setup (dest
, &dpath
);
618 rsync_1 (&spath
, &dpath
, and_delete
);
623 /* See if we can detect what the user needs to do to get unshare
624 support working for us. */
626 check_for_unshare_hints (void)
631 /* Default Debian Linux disables user namespaces, but allows a way
633 f
= fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
636 i
= 99; /* Sentinel. */
637 fscanf (f
, "%d", &i
);
640 printf ("To enable test-container, please run this as root:\n");
641 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
647 /* ALT Linux has an alternate way of doing the same. */
648 f
= fopen ("/proc/sys/kernel/userns_restrict", "r");
651 i
= 99; /* Sentinel. */
652 fscanf (f
, "%d", &i
);
655 printf ("To enable test-container, please run this as root:\n");
656 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
664 main (int argc
, char **argv
)
667 char *pristine_root_path
;
670 char *new_objdir_path
;
671 char *new_srcdir_path
;
672 char **new_child_proc
;
675 char *command_basename
;
677 int do_postclean
= 0;
681 /* If set, the test runs as root instead of the user running the testsuite. */
685 /* Used for "%lld %lld 1" so need not be large. */
690 setbuf (stdout
, NULL
);
692 /* The command line we're expecting looks like this:
693 env <set some vars> ld.so <library path> test-binary
695 We need to peel off any "env" or "ld.so" portion of the command
696 line, and keep track of which env vars we should preserve and
701 fprintf (stderr
, "Usage: containerize <program to run> <args...>\n");
705 if (strcmp (argv
[1], "-v") == 0)
712 if (strcmp (argv
[1], "env") == 0)
716 while (is_env_setting (argv
[1]))
718 /* If there are variables we do NOT want to propogate, this
719 is where the test for them goes. */
721 /* Need to keep these. Note that putenv stores a
722 pointer to our argv. */
730 if (strcmp (argv
[1], support_objdir_elf_ldso
) == 0)
734 while (argv
[1][0] == '-')
736 if (strcmp (argv
[1], "--library-path") == 0)
746 pristine_root_path
= strdup (concat (support_objdir_root
,
747 "/testroot.pristine", NULL
));
748 new_root_path
= strdup (concat (support_objdir_root
,
749 "/testroot.root", NULL
));
750 new_cwd_path
= get_current_dir_name ();
751 new_child_proc
= argv
+ 1;
753 lock_fd
= open (concat (pristine_root_path
, "/lock.fd", NULL
),
754 O_CREAT
| O_TRUNC
| O_RDWR
, 0666);
756 FAIL_EXIT1 ("Cannot create testroot lock.\n");
758 while (flock (lock_fd
, LOCK_EX
) != 0)
761 FAIL_EXIT1 ("Cannot lock testroot.\n");
764 xmkdirp (new_root_path
, 0755);
766 /* We look for extra setup info in a subdir in the same spot as the
767 test, with the same name but a ".root" extension. This is that
768 directory. We try to look in the source tree if the path we're
769 given refers to the build tree, but we rely on the path to be
770 absolute. This is what the glibc makefiles do. */
771 command_root
= concat (argv
[1], ".root", NULL
);
772 if (strncmp (command_root
, support_objdir_root
,
773 strlen (support_objdir_root
)) == 0
774 && command_root
[strlen (support_objdir_root
)] == '/')
775 command_root
= concat (support_srcdir_root
,
776 argv
[1] + strlen (support_objdir_root
),
778 command_root
= strdup (command_root
);
780 /* This cuts off the ".root" we appended above. */
781 command_base
= strdup (command_root
);
782 command_base
[strlen (command_base
) - 5] = 0;
784 /* This is the basename of the test we're running. */
785 command_basename
= strrchr (command_base
, '/');
786 if (command_basename
== NULL
)
787 command_basename
= command_base
;
791 /* Shared object base directory. */
792 so_base
= strdup (argv
[1]);
793 if (strrchr (so_base
, '/') != NULL
)
794 strrchr (so_base
, '/')[1] = 0;
796 if (file_exists (concat (command_root
, "/postclean.req", NULL
)))
799 rsync (pristine_root_path
, new_root_path
,
800 file_exists (concat (command_root
, "/preclean.req", NULL
)));
802 if (stat (command_root
, &st
) >= 0
803 && S_ISDIR (st
.st_mode
))
804 rsync (command_root
, new_root_path
, 0);
806 new_objdir_path
= strdup (concat (new_root_path
,
807 support_objdir_root
, NULL
));
808 new_srcdir_path
= strdup (concat (new_root_path
,
809 support_srcdir_root
, NULL
));
811 /* new_cwd_path starts with '/' so no "/" needed between the two. */
812 xmkdirp (concat (new_root_path
, new_cwd_path
, NULL
), 0755);
813 xmkdirp (new_srcdir_path
, 0755);
814 xmkdirp (new_objdir_path
, 0755);
816 original_uid
= getuid ();
817 original_gid
= getgid ();
819 /* Handle the cp/mv/rm "script" here. */
821 char *the_line
= NULL
;
823 char *fname
= concat (command_root
, "/",
824 command_basename
, ".script", NULL
);
826 FILE *f
= fopen (fname
, "r");
829 fprintf (stderr
, "running %s\n", fname
);
833 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
834 fname
= concat (command_base
, ".script", NULL
);
835 f
= fopen (fname
, "r");
837 fprintf (stderr
, "running %s\n", fname
);
840 /* Note that we do NOT look for a Makefile-generated foo.script in
841 the build directory. If that is ever needed, this is the place
844 /* This is where we "interpret" the mini-script which is <test>.script. */
847 while (getline (&the_line
, &line_len
, f
) > 0)
849 int nt
= tokenize (the_line
, the_words
, 3);
852 for (i
= 1; i
< nt
; ++i
)
854 if (memcmp (the_words
[i
], "$B/", 3) == 0)
855 the_words
[i
] = concat (support_objdir_root
,
856 the_words
[i
] + 2, NULL
);
857 else if (memcmp (the_words
[i
], "$S/", 3) == 0)
858 the_words
[i
] = concat (support_srcdir_root
,
859 the_words
[i
] + 2, NULL
);
860 else if (memcmp (the_words
[i
], "$I/", 3) == 0)
861 the_words
[i
] = concat (new_root_path
,
862 support_install_prefix
,
863 the_words
[i
] + 2, NULL
);
864 else if (memcmp (the_words
[i
], "$L/", 3) == 0)
865 the_words
[i
] = concat (new_root_path
,
866 support_libdir_prefix
,
867 the_words
[i
] + 2, NULL
);
868 else if (the_words
[i
][0] == '/')
869 the_words
[i
] = concat (new_root_path
,
873 if (nt
== 3 && the_words
[2][strlen (the_words
[2]) - 1] == '/')
875 char *r
= strrchr (the_words
[1], '/');
877 the_words
[2] = concat (the_words
[2], r
+ 1, NULL
);
879 the_words
[2] = concat (the_words
[2], the_words
[1], NULL
);
882 if (nt
== 2 && strcmp (the_words
[0], "so") == 0)
884 the_words
[2] = concat (new_root_path
, support_libdir_prefix
,
885 "/", the_words
[1], NULL
);
886 the_words
[1] = concat (so_base
, the_words
[1], NULL
);
887 copy_one_file (the_words
[1], the_words
[2]);
889 else if (nt
== 3 && strcmp (the_words
[0], "cp") == 0)
891 copy_one_file (the_words
[1], the_words
[2]);
893 else if (nt
== 3 && strcmp (the_words
[0], "mv") == 0)
895 if (rename (the_words
[1], the_words
[2]) < 0)
896 FAIL_EXIT1 ("rename %s -> %s: %s", the_words
[1],
897 the_words
[2], strerror (errno
));
899 else if (nt
== 3 && strcmp (the_words
[0], "chmod") == 0)
902 m
= strtol (the_words
[1], NULL
, 0);
903 if (chmod (the_words
[2], m
) < 0)
904 FAIL_EXIT1 ("chmod %s: %s\n",
905 the_words
[2], strerror (errno
));
908 else if (nt
== 2 && strcmp (the_words
[0], "rm") == 0)
910 maybe_xunlink (the_words
[1]);
912 else if (nt
== 1 && strcmp (the_words
[0], "su") == 0)
916 else if (nt
> 0 && the_words
[0][0] != '#')
918 printf ("\033[31minvalid [%s]\033[0m\n", the_words
[0]);
927 pid_t pc_pid
= fork ();
931 FAIL_EXIT1 ("Can't fork for post-clean");
937 waitpid (pc_pid
, &status
, 0);
939 /* Child has exited, we can post-clean the test root. */
940 printf("running post-clean rsync\n");
941 rsync (pristine_root_path
, new_root_path
, 1);
943 if (WIFEXITED (status
))
944 exit (WEXITSTATUS (status
));
946 if (WIFSIGNALED (status
))
948 printf ("%%SIGNALLED%%\n");
952 printf ("%%EXITERROR%%\n");
956 /* Child continues. */
959 /* This is the last point in the program where we're still in the
960 "normal" namespace. */
963 /* The unshare here gives us our own spaces and capabilities. */
964 if (unshare (CLONE_NEWUSER
| CLONE_NEWPID
| CLONE_NEWNS
) < 0)
966 /* Older kernels may not support all the options, or security
967 policy may block this call. */
968 if (errno
== EINVAL
|| errno
== EPERM
)
970 int saved_errno
= errno
;
972 check_for_unshare_hints ();
973 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno
));
976 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno
));
979 /* Some targets may not support unshare at all. */
980 FAIL_UNSUPPORTED ("unshare support missing");
983 /* Some systems, by default, all mounts leak out of the namespace. */
984 if (mount ("none", "/", NULL
, MS_REC
| MS_PRIVATE
, NULL
) != 0)
985 FAIL_EXIT1 ("could not create a private mount namespace\n");
987 trymount (support_srcdir_root
, new_srcdir_path
);
988 trymount (support_objdir_root
, new_objdir_path
);
990 xmkdirp (concat (new_root_path
, "/dev", NULL
), 0755);
991 devmount (new_root_path
, "null");
992 devmount (new_root_path
, "zero");
993 devmount (new_root_path
, "urandom");
995 /* We're done with the "old" root, switch to the new one. */
996 if (chroot (new_root_path
) < 0)
997 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path
);
999 if (chdir (new_cwd_path
) < 0)
1000 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path
);
1002 /* To complete the containerization, we need to fork () at least
1003 once. We can't exec, nor can we somehow link the new child to
1004 our parent. So we run the child and propogate it's exit status
1008 FAIL_EXIT1 ("Unable to fork");
1013 waitpid (child
, &status
, 0);
1015 if (WIFEXITED (status
))
1016 exit (WEXITSTATUS (status
));
1018 if (WIFSIGNALED (status
))
1020 printf ("%%SIGNALLED%%\n");
1024 printf ("%%EXITERROR%%\n");
1028 /* The rest is the child process, which is now PID 1 and "in" the
1031 maybe_xmkdir ("/tmp", 0755);
1033 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1034 maybe_xmkdir ("/proc", 0777);
1035 if (mount ("proc", "/proc", "proc", 0, NULL
) < 0)
1036 FAIL_EXIT1 ("Unable to mount /proc: ");
1038 /* We map our original UID to the same UID in the container so we
1039 can own our own files normally. */
1040 UMAP
= open ("/proc/self/uid_map", O_WRONLY
);
1042 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1044 sprintf (tmp
, "%lld %lld 1\n",
1045 (long long) (be_su
? 0 : original_uid
), (long long) original_uid
);
1046 write (UMAP
, tmp
, strlen (tmp
));
1049 /* We must disable setgroups () before we can map our groups, else we
1051 GMAP
= open ("/proc/self/setgroups", O_WRONLY
);
1054 /* We support kernels old enough to not have this. */
1055 write (GMAP
, "deny\n", 5);
1059 /* We map our original GID to the same GID in the container so we
1060 can own our own files normally. */
1061 GMAP
= open ("/proc/self/gid_map", O_WRONLY
);
1063 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1065 sprintf (tmp
, "%lld %lld 1\n",
1066 (long long) (be_su
? 0 : original_gid
), (long long) original_gid
);
1067 write (GMAP
, tmp
, strlen (tmp
));
1070 /* Now run the child. */
1071 execvp (new_child_proc
[0], new_child_proc
);
1073 /* Or don't run the child? */
1074 FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc
[0]);
1076 /* Because gcc won't know error () never returns... */
1077 exit (EXIT_UNSUPPORTED
);