From 11589e1037372c5ad719e1e46d7462fd196caa56 Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Thu, 24 Jun 2021 10:38:28 +0200
Subject: [PATCH 01/13] src/lckpwdf.c: create an empty /etc/.pwd.lock

---
 src/lckpwdf.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/lckpwdf.c b/src/lckpwdf.c
index dc0e68b5..66a058de 100644
--- a/src/lckpwdf.c
+++ b/src/lckpwdf.c
@@ -22,12 +22,37 @@
 
 #ifdef HAVE_LCKPWDF
 
+#include <unistd.h>
+#include <fcntl.h>
 #include "libfakechroot.h"
+#include "open.h"
 
 
 wrapper(lckpwdf, int, (void))
 {
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+
+    int file;
     debug("lckpwdf()");
+    // lckpwdf will create an empty /etc/.pwd.lock
+    // if that file doesn't exist yet, we create it here as well
+    char* pwdlockfile = "/etc/.pwd.lock";
+    expand_chroot_path(pwdlockfile);
+
+    if ((file = nextcall(open)(pwdlockfile, O_RDONLY)) == 0) {
+        // if the file already exists, don't touch it
+        close(file);
+        return 0;
+    }
+
+    if ((file = nextcall(open)(pwdlockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
+        // we ignore any errors (maybe /etc doesn't exist or we don't have the
+        // necessary permissions)
+        return 0;
+    }
+    // the file remains empty
+    close(file);
     return 0;
 }
 

From 3cdb9b5426ef508c9220b4b0316954e3b7dff9ac Mon Sep 17 00:00:00 2001
From: Hajime Yoshimori <lugia.kun@gmail.com>
Date: Fri, 1 May 2020 21:14:32 +0900
Subject: [PATCH 02/13] check return value of dladdr

https://github.com/dex4er/fakechroot/pull/70
---
 src/dladdr.c           | 12 +++++++-----
 test/Makefile.am       |  1 +
 test/src/Makefile.am   |  1 +
 test/src/test-dladdr.c | 14 ++++++++++++++
 test/t/dladdr.t        | 14 ++++++++++++++
 5 files changed, 37 insertions(+), 5 deletions(-)
 create mode 100644 test/src/test-dladdr.c
 create mode 100755 test/t/dladdr.t

diff --git a/src/dladdr.c b/src/dladdr.c
index fef32579..3dffdb3f 100644
--- a/src/dladdr.c
+++ b/src/dladdr.c
@@ -36,11 +36,13 @@ wrapper(dladdr, int, (const void * addr, Dl_info * info))
 
     ret = nextcall(dladdr)(addr, info);
 
-    if (info->dli_fname) {
-        narrow_chroot_path(info->dli_fname);
-    }
-    if (info->dli_sname) {
-        narrow_chroot_path(info->dli_sname);
+    if (ret != 0) {
+        if (info->dli_fname) {
+            narrow_chroot_path(info->dli_fname);
+        }
+        if (info->dli_sname) {
+            narrow_chroot_path(info->dli_sname);
+        }
     }
 
     return ret;
diff --git a/test/Makefile.am b/test/Makefile.am
index aba29538..0021b0a1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,6 +9,7 @@ TESTS = \
     t/cmd-subst.t \
     t/cp.t \
     t/dedotdot.t \
+    t/dladdr.t \
     t/execlp.t \
     t/execve-elfloader.t \
     t/execve-null-envp.t \
diff --git a/test/src/Makefile.am b/test/src/Makefile.am
index 7fb3075b..5f5fde8d 100644
--- a/test/src/Makefile.am
+++ b/test/src/Makefile.am
@@ -3,6 +3,7 @@ check_PROGRAMS = \
     test-chroot \
     test-clearenv \
     test-dedotdot \
+    test-dladdr \
     test-execlp \
     test-execve-null-envp \
     test-fts \
diff --git a/test/src/test-dladdr.c b/test/src/test-dladdr.c
new file mode 100644
index 00000000..5ec8d248
--- /dev/null
+++ b/test/src/test-dladdr.c
@@ -0,0 +1,14 @@
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char** argv)
+{
+  Dl_info info;
+  memset(&info, 0xfe, sizeof(info)); /* fill with inaccessible address */
+  int ret = dladdr(NULL, &info);
+  printf("%ld\n", ret);
+  return 0;
+}
diff --git a/test/t/dladdr.t b/test/t/dladdr.t
new file mode 100755
index 00000000..fc7f9397
--- /dev/null
+++ b/test/t/dladdr.t
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+srcdir=${srcdir:-.}
+. $srcdir/common.inc.sh
+
+prepare 1
+
+PATH=$srcdir/bin:$PATH
+
+t=`$srcdir/fakechroot.sh $testtree /bin/test-dladdr`
+[ "$t" != "0" ] && not
+ok "dladdr returns" $t
+
+cleanup

From 63c2cbed6dca6196940b439736ca2c069cb9358b Mon Sep 17 00:00:00 2001
From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
Date: Mon, 22 Feb 2021 21:44:07 -0800
Subject: [PATCH 03/13] tmpnam.c: fix heap overflow

https://github.com/dex4er/fakechroot/pull/85

Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
---
 src/tmpnam.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tmpnam.c b/src/tmpnam.c
index ce60817a..917ee6b7 100644
--- a/src/tmpnam.c
+++ b/src/tmpnam.c
@@ -42,7 +42,7 @@ wrapper(tmpnam, char *, (char * s))
 
     expand_chroot_path(ptr);
 
-    ptr2 = malloc(strlen(ptr));
+    ptr2 = malloc(strlen(ptr) + 1);
     if (ptr2 == NULL) return NULL;
 
     strcpy(ptr2, ptr);

From be3a291ef37ace606ec2845f6c1b645b981805cb Mon Sep 17 00:00:00 2001
From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
Date: Mon, 22 Feb 2021 21:46:36 -0800
Subject: [PATCH 04/13] declare missing bufs, remove ver from lstat

https://github.com/dex4er/fakechroot/pull/85

Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
---
 src/lstat.c  | 8 +++++---
 src/lstat.h  | 2 +-
 src/mknod.c  | 2 ++
 src/stat.c   | 2 ++
 src/stat64.c | 2 ++
 5 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/lstat.c b/src/lstat.c
index 3f6d819f..54e3263f 100644
--- a/src/lstat.c
+++ b/src/lstat.c
@@ -28,9 +28,11 @@
 #include "lstat.h"
 
 
-wrapper(lstat, int, (int ver, const char * filename, struct stat * buf))
+wrapper(lstat, int, (const char * filename, struct stat * buf))
 {
-    debug("lstat(%d, \"%s\", &buf)", ver, filename);
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("lstat(\"%s\", &buf)", filename);
 
     if (!fakechroot_localdir(filename)) {
         if (filename != NULL) {
@@ -40,7 +42,7 @@ wrapper(lstat, int, (int ver, const char * filename, struct stat * buf))
         }
     }
 
-    return lstat_rel(ver, filename, buf);
+    return lstat_rel(filename, buf);
 }
 
 
diff --git a/src/lstat.h b/src/lstat.h
index 751c1ead..ee483033 100644
--- a/src/lstat.h
+++ b/src/lstat.h
@@ -26,7 +26,7 @@
 
 #ifndef HAVE___LXSTAT
 
-wrapper_proto(lstat, int, (int, const char *, struct stat *));
+wrapper_proto(lstat, int, (const char *, struct stat *));
 
 int lstat_rel(const char *, struct stat *);
 
diff --git a/src/mknod.c b/src/mknod.c
index 52fd33b2..27710372 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -28,6 +28,8 @@
 
 wrapper(mknod, int, (const char * pathname, mode_t mode, dev_t dev))
 {
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
     debug("mknod(\"%s\", 0%o, %ld)", pathname, mode, dev);
     expand_chroot_path(pathname);
     return nextcall(mknod)(pathname, mode, dev);
diff --git a/src/stat.c b/src/stat.c
index 78456620..7b377933 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -33,6 +33,8 @@
 
 wrapper(stat, int, (const char * file_name, struct stat * buf))
 {
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
     debug("stat(\"%s\", &buf)", file_name);
     expand_chroot_path(file_name);
     return nextcall(stat)(file_name, buf);
diff --git a/src/stat64.c b/src/stat64.c
index aac9c75f..a360f66f 100644
--- a/src/stat64.c
+++ b/src/stat64.c
@@ -34,6 +34,8 @@
 
 wrapper(stat64, int, (const char * file_name, struct stat64 * buf))
 {
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
     debug("stat64(\"%s\", &buf)", file_name);
     expand_chroot_path(file_name);
     return nextcall(stat64)(file_name, buf);

From 26f69c2c3120b9b059209c7566850ef5187de56a Mon Sep 17 00:00:00 2001
From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
Date: Mon, 22 Feb 2021 21:47:09 -0800
Subject: [PATCH 05/13] fix glibc 2.33+ compatibility

https://github.com/dex4er/fakechroot/pull/85

Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
---
 configure.ac        | 20 ++++++++++++++++++++
 src/ftw.c           |  2 +-
 src/ftw64.c         | 14 +++++++++++---
 src/libfakechroot.h | 15 +++++++++++++++
 src/lstat.c         |  2 +-
 src/lstat.h         |  2 +-
 src/lstat64.c       |  2 +-
 src/mknod.c         |  2 +-
 src/mknodat.c       |  2 +-
 src/stat.c          |  2 +-
 src/stat64.c        |  2 +-
 11 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/configure.ac b/configure.ac
index f8cdb323..9cc2e779 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,6 +75,26 @@ ACX_CHECK_C_ATTRIBUTE_VISIBILITY
 # Checks for libraries.
 AC_CHECK_LIB([dl], [dlsym])
 
+AH_TEMPLATE([NEW_GLIBC], [glibc >= 2.33])
+AC_MSG_CHECKING([for glibc 2.33+])
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+       #include <sys/stat.h>
+  ]], [[
+#ifdef __GLIBC__
+#if !__GLIBC_PREREQ(2,33)
+#error glibc<2.33
+#endif
+#else
+#error not glibc
+#endif
+  ]])],[
+   AC_DEFINE(NEW_GLIBC,1)
+   AC_MSG_RESULT([yes])
+  ],[
+   AC_DEFINE(NEW_GLIBC,0)
+   AC_MSG_RESULT([no])
+  ])
+
 # Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STDC
diff --git a/src/ftw.c b/src/ftw.c
index 92fc126c..a9abc853 100644
--- a/src/ftw.c
+++ b/src/ftw.c
@@ -185,7 +185,7 @@ int rpl_lstat (const char *, struct stat *);
 # define NFTW_NEW_NAME __new_nftw
 # define INO_T ino_t
 # define STAT stat
-# ifdef _LIBC
+# if defined(_LIBC) && !NEW_GLIBC
 #  define LXSTAT __lxstat
 #  define XSTAT __xstat
 #  define FXSTATAT __fxstatat
diff --git a/src/ftw64.c b/src/ftw64.c
index 7cc8cdfd..cee1f2bc 100644
--- a/src/ftw64.c
+++ b/src/ftw64.c
@@ -18,6 +18,8 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include "config.h"
+
 #define __FTW64_C
 #define FTW_NAME ftw64
 #define NFTW_NAME nftw64
@@ -25,9 +27,15 @@
 #define NFTW_NEW_NAME __new_nftw64
 #define INO_T ino64_t
 #define STAT stat64
-#define LXSTAT __lxstat64
-#define XSTAT __xstat64
-#define FXSTATAT __fxstatat64
+#if NEW_GLIBC
+#  define LXSTAT(V,f,sb) lstat64 (f,sb)
+#  define XSTAT(V,f,sb) stat64 (f,sb)
+#  define FXSTATAT(V,d,f,sb,m) fstatat64 (d, f, sb, m)
+#else
+#  define LXSTAT __lxstat64
+#  define XSTAT __xstat64
+#  define FXSTATAT __fxstatat64
+#endif
 #define FTW_FUNC_T __ftw64_func_t
 #define NFTW_FUNC_T __nftw64_func_t
 
diff --git a/src/libfakechroot.h b/src/libfakechroot.h
index 4cf199ff..64ff15fb 100644
--- a/src/libfakechroot.h
+++ b/src/libfakechroot.h
@@ -200,6 +200,21 @@
 # endif
 #endif
 
+#ifndef _STAT_VER
+ #if defined (__aarch64__)
+  #define _STAT_VER 0
+ #elif defined (__powerpc__) && __WORDSIZE == 64
+  #define _STAT_VER 1
+ #elif defined (__riscv) && __riscv_xlen==64
+  #define _STAT_VER 0
+ #elif defined (__s390x__)
+  #define _STAT_VER 1
+ #elif defined (__x86_64__)
+  #define _STAT_VER 1
+ #else
+  #define _STAT_VER 3
+ #endif
+#endif
 
 typedef void (*fakechroot_wrapperfn_t)(void);
 
diff --git a/src/lstat.c b/src/lstat.c
index 54e3263f..fa383234 100644
--- a/src/lstat.c
+++ b/src/lstat.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#ifndef HAVE___LXSTAT
+#if !defined(HAVE___LXSTAT) || NEW_GLIBC
 
 #include <sys/stat.h>
 #include <unistd.h>
diff --git a/src/lstat.h b/src/lstat.h
index ee483033..c46a2b9b 100644
--- a/src/lstat.h
+++ b/src/lstat.h
@@ -24,7 +24,7 @@
 #include <config.h>
 #include "libfakechroot.h"
 
-#ifndef HAVE___LXSTAT
+#if !defined(HAVE___LXSTAT) || NEW_GLIBC
 
 wrapper_proto(lstat, int, (const char *, struct stat *));
 
diff --git a/src/lstat64.c b/src/lstat64.c
index b6212fc8..a332d7c3 100644
--- a/src/lstat64.c
+++ b/src/lstat64.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#if defined(HAVE_LSTAT64) && !defined(HAVE___LXSTAT64)
+#if defined(HAVE_LSTAT64) && (!defined(HAVE___LXSTAT64) || NEW_GLIBC)
 
 #define _LARGEFILE64_SOURCE
 #define _BSD_SOURCE
diff --git a/src/mknod.c b/src/mknod.c
index 27710372..aeb750b0 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#ifndef HAVE___XMKNOD
+#if !defined(HAVE___XMKNOD) || NEW_GLIBC
 
 #include <sys/stat.h>
 #include "libfakechroot.h"
diff --git a/src/mknodat.c b/src/mknodat.c
index 732a22bc..3239b357 100644
--- a/src/mknodat.c
+++ b/src/mknodat.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#if defined(HAVE_MKNODAT) && !defined(HAVE___XMKNODAT)
+#if defined(HAVE_MKNODAT) && (!defined(HAVE___XMKNODAT) || NEW_GLIBC)
 
 #define _ATFILE_SOURCE
 #include <sys/stat.h>
diff --git a/src/stat.c b/src/stat.c
index 7b377933..5ef57bab 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#ifndef HAVE___XSTAT
+#if !defined(HAVE___XSTAT) || NEW_GLIBC
 
 #define _BSD_SOURCE
 #define _DEFAULT_SOURCE
diff --git a/src/stat64.c b/src/stat64.c
index a360f66f..993ce808 100644
--- a/src/stat64.c
+++ b/src/stat64.c
@@ -20,7 +20,7 @@
 
 #include <config.h>
 
-#if defined(HAVE_STAT64) && !defined(HAVE___XSTAT64)
+#if defined(HAVE_STAT64) && (!defined(HAVE___XSTAT64) || NEW_GLIBC)
 
 #define _BSD_SOURCE
 #define _LARGEFILE64_SOURCE

From 5366e9a366b213b879abf0f0a3aeb3409d3b57ed Mon Sep 17 00:00:00 2001
From: neok-m4700 <neok-m4700@users.noreply.github.com>
Date: Wed, 24 Feb 2021 17:36:57 +0100
Subject: [PATCH 06/13] wrap fstatat and fstatat64

https://github.com/dex4er/fakechroot/pull/86
---
 configure.ac    |  2 ++
 src/Makefile.am |  2 ++
 src/fstatat.c   | 42 ++++++++++++++++++++++++++++++++++++++++++
 src/fstatat64.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+)
 create mode 100644 src/fstatat.c
 create mode 100644 src/fstatat64.c

diff --git a/configure.ac b/configure.ac
index 9cc2e779..5b3053e1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -218,6 +218,8 @@ AC_CHECK_FUNCS(m4_normalize([
     freopen64
     fstat
     fstat64
+    fstatat
+    fstatat64
     fts_children
     fts_open
     fts_read
diff --git a/src/Makefile.am b/src/Makefile.am
index 60663452..eb311c0a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,6 +61,8 @@ libfakechroot_la_SOURCES = \
     fopen64.c \
     freopen.c \
     freopen64.c \
+    fstatat.c \
+    fstatat64.c \
     fts.c \
     fts64.c \
     ftw.c \
diff --git a/src/fstatat.c b/src/fstatat.c
new file mode 100644
index 00000000..ca7578b3
--- /dev/null
+++ b/src/fstatat.c
@@ -0,0 +1,42 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2021 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE_FSTATAT
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <sys/stat.h>
+#include <limits.h>
+#include "libfakechroot.h"
+
+wrapper(fstatat, int, (int dirfd, const char *pathname, struct stat *buf, int flags))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("fstatat(%d, \"%s\", &buf, %d)", dirfd, pathname, flags);
+    expand_chroot_path_at(dirfd, pathname);
+    return nextcall(fstatat)(dirfd, pathname, buf, flags);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/fstatat64.c b/src/fstatat64.c
new file mode 100644
index 00000000..18633725
--- /dev/null
+++ b/src/fstatat64.c
@@ -0,0 +1,43 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2021 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE_FSTATAT64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#define _LARGEFILE64_SOURCE
+#include <sys/stat.h>
+#include <limits.h>
+#include "libfakechroot.h"
+
+wrapper(fstatat64, int, (int dirfd, const char *pathname, struct stat64 *buf, int flags))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("fstatat64(%d, \"%s\", &buf, %d)", dirfd, pathname, flags);
+    expand_chroot_path_at(dirfd, pathname);
+    return nextcall(fstatat64)(dirfd, pathname, buf, flags);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif

From bc7ef087c17a475ec03768053fa22c2193ae7fc2 Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Wed, 24 Aug 2022 08:26:04 +0200
Subject: [PATCH 07/13] Wrap all functions accessing /etc/passwd, /etc/group
 and /etc/shadow

Starting with glibc 2.32 the compat nss module for getpwnam calls
__nss_files_fopen (which is a GLIBC_PRIVATE symbol provided by glibc)
instead of fopen (see 299210c1fa67e2dfb564475986fce11cd33db9ad). This
leads to getpwnam calls accessing /etc/passwd from *outside* the chroot
and as a result programs like adduser do not work correctly anymore
under fakechroot.

Starting with glibc 2.34 the __nss_files_fopen was moved from nss to
libc.so and thus wrapping it with LD_PRELOAD has no affect anymore
(see 6212bb67f4695962748a5981e1b9fea105af74f6).

So now we also wrap all the functions accessing /etc/passwd, /etc/group
and /etc/shadow. This solution will ignore NIS, LDAP or other local files
as potentially configured in /etc/nsswitch.conf.

https://github.com/dex4er/fakechroot/pull/98
---
 src/Makefile.am        |   1 +
 src/passwd.c           | 296 +++++++++++++++++++++++++++++++++++++++++
 test/Makefile.am       |   1 +
 test/src/Makefile.am   |   1 +
 test/src/test-passwd.c |  28 ++++
 test/t/passwd.t        |  23 ++++
 test/testtree.sh       |   6 +
 7 files changed, 356 insertions(+)
 create mode 100644 src/passwd.c
 create mode 100644 test/src/test-passwd.c
 create mode 100755 test/t/passwd.t

diff --git a/src/Makefile.am b/src/Makefile.am
index eb311c0a..6e9d9ae1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -120,6 +120,7 @@ libfakechroot_la_SOURCES = \
     openat64.c \
     opendir.c \
     opendir.h \
+    passwd.c \
     pathconf.c \
     popen.c \
     posix_spawn.c \
diff --git a/src/passwd.c b/src/passwd.c
new file mode 100644
index 00000000..d4cee86c
--- /dev/null
+++ b/src/passwd.c
@@ -0,0 +1,296 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+/*
+ * Starting with glibc 2.32 the compat nss module for getpwnam calls
+ * __nss_files_fopen (which is a GLIBC_PRIVATE symbol provided by glibc)
+ * instead of fopen (see 299210c1fa67e2dfb564475986fce11cd33db9ad). This
+ * leads to getpwnam calls accessing /etc/passwd from *outside* the chroot
+ * and as a result programs like adduser do not work correctly anymore
+ * under fakechroot.
+ *
+ * Starting with glibc 2.34 the __nss_files_fopen was moved from nss to
+ * libc.so and thus wrapping it with LD_PRELOAD has no affect anymore
+ * (see 6212bb67f4695962748a5981e1b9fea105af74f6).
+ *
+ * So now we also wrap all the functions accessing /etc/passwd, /etc/group
+ * and /etc/shadow. This solution will ignore NIS, LDAP or other local files
+ * as potentially configured in /etc/nsswitch.conf.
+ */
+
+#include <gnu/libc-version.h>
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <shadow.h>
+#include "libfakechroot.h"
+
+/* getpwent, setpwent, endpwent, getpwuid, getpwnam */
+
+static FILE *pw_f;
+
+wrapper(getpwent, struct passwd *, (void))
+{
+	if (!pw_f) pw_f = fopen("/etc/passwd", "rbe");
+	if (!pw_f) return 0;
+	return fgetpwent(pw_f);
+}
+
+wrapper (getpwent_r, int, (struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp))
+{
+	if (!pw_f) pw_f = fopen("/etc/passwd", "rbe");
+	if (!pw_f) return 0;
+	return fgetpwent_r(pw_f, pwbuf, buf, buflen, pwbufp);
+}
+
+wrapper(setpwent, void, (void))
+{
+	if (pw_f) fclose(pw_f);
+	pw_f = 0;
+}
+
+wrapper(endpwent, void, (void))
+{
+	if (pw_f) fclose(pw_f);
+	pw_f = 0;
+}
+
+wrapper(getpwuid, struct passwd *, (uid_t uid))
+{
+	debug("getpwuid(\"%ul\")", uid);
+	FILE *f = fopen("/etc/passwd", "rbe");
+	if (!f) {
+		return NULL;
+	}
+	struct passwd *res = NULL;
+	while ((res = fgetpwent(f))) {
+		if (res->pw_uid == uid)
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getpwuid_r, int, (uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result))
+{
+	debug("getpwuid_r(\"%ul\")", uid);
+	FILE *f = fopen("/etc/passwd", "rbe");
+	if (!f) {
+		return errno;
+	}
+	int res;
+	while (!(res = fgetpwent_r(f, pwd, buf, buflen, result))) {
+		if (pwd->pw_uid == uid)
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getpwnam, struct passwd *, (const char *name))
+{
+	debug("getpwnam(\"%s\")", name);
+	FILE *f = fopen("/etc/passwd", "rbe");
+	if (!f) {
+		return NULL;
+	}
+	struct passwd *res = NULL;
+	while ((res = fgetpwent(f))) {
+		if (name && !strcmp(name, res->pw_name))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getpwnam_r, int, (const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result))
+{
+	debug("getpwnam_r(\"%s\")", name);
+	FILE *f = fopen("/etc/passwd", "rbe");
+	if (!f) {
+		return errno;
+	}
+	int res;
+	while (!(res = fgetpwent_r(f, pwd, buf, buflen, result))) {
+		if (name && !strcmp(name, pwd->pw_name))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+/* getgrent, setgrent, endgrent, getgrgid, getgrnam */
+
+static FILE *gr_f;
+
+wrapper(getgrent, struct group *, (void))
+{
+	if (!gr_f) gr_f = fopen("/etc/group", "rbe");
+	if (!gr_f) return 0;
+	return fgetgrent(gr_f);
+}
+
+wrapper (getgrent_r, int, (struct group *gbuf, char *buf, size_t buflen, struct group **gbufp))
+{
+	if (!gr_f) gr_f = fopen("/etc/group", "rbe");
+	if (!gr_f) return 0;
+	return fgetgrent_r(gr_f, gbuf, buf, buflen, gbufp);
+}
+
+wrapper(setgrent, void, (void))
+{
+	if (gr_f) fclose(gr_f);
+	gr_f = 0;
+}
+
+wrapper(endgrent, void, (void))
+{
+	if (gr_f) fclose(gr_f);
+	gr_f = 0;
+}
+
+wrapper(getgrgid, struct group *, (gid_t gid))
+{
+	debug("getgrgid(\"%ul\")", gid);
+	FILE *f = fopen("/etc/group", "rbe");
+	if (!f) {
+		return NULL;
+	}
+	struct group *res = NULL;
+	while ((res = fgetgrent(f))) {
+		if (res->gr_gid == gid)
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getgrgid_r, int, (gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result))
+{
+	debug("getgrgid_r(\"%ul\")", gid);
+	FILE *f = fopen("/etc/group", "rbe");
+	if (!f) {
+		return errno;
+	}
+	int res;
+	while (!(res = fgetgrent_r(f, grp, buf, buflen, result))) {
+		if (grp->gr_gid == gid)
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getgrnam, struct group *, (const char *name))
+{
+	debug("getgrnam(\"%s\")", name);
+	FILE *f = fopen("/etc/group", "rbe");
+	if (!f) {
+		return NULL;
+	}
+	struct group *res = NULL;
+	while ((res = fgetgrent(f))) {
+		if (name && !strcmp(name, res->gr_name))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getgrnam_r, int, (const char *name, struct group *grp, char *buf, size_t buflen, struct group **result))
+{
+	debug("getgrnam_r(\"%s\")", name);
+	FILE *f = fopen("/etc/group", "rbe");
+	if (!f) {
+		return errno;
+	}
+	int res;
+	while (!(res = fgetgrent_r(f, grp, buf, buflen, result))) {
+		if (name && !strcmp(name, grp->gr_name))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+/* getspent, setspent, endspent, getspnam */
+
+static FILE *sp_f;
+
+wrapper(getspent, struct spwd *, (void))
+{
+	if (!sp_f) sp_f = fopen("/etc/shadow", "rbe");
+	if (!sp_f) return 0;
+	return fgetspent(sp_f);
+}
+
+wrapper(setspent, void, (void))
+{
+	if (sp_f) fclose(sp_f);
+	sp_f = 0;
+}
+
+wrapper(endspent, void, (void))
+{
+	if (sp_f) fclose(sp_f);
+	sp_f = 0;
+}
+
+wrapper(getspnam, struct spwd *, (const char *name))
+{
+	debug("getspnam(\"%s\")", name);
+	FILE *f = fopen("/etc/shadow", "rbe");
+	if (!f) {
+		return NULL;
+	}
+	struct spwd *res = NULL;
+	while ((res = fgetspent(f))) {
+		if (name && !strcmp(name, res->sp_namp))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+wrapper(getspnam_r, int, (const char *name, struct spwd *spbuf, char *buf, size_t buflen, struct spwd **spbufp))
+{
+	debug("getspnam_r(\"%s\")", name);
+	FILE *f = fopen("/etc/shadow", "rbe");
+	if (!f) {
+		return errno;
+	}
+	int res;
+	while (!(res = fgetspent_r(f, spbuf, buf, buflen, spbufp))) {
+		if (name && !strcmp(name, spbuf->sp_namp))
+			break;
+	}
+	fclose(f);
+	return res;
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/test/Makefile.am b/test/Makefile.am
index 0021b0a1..a1ec743d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -22,6 +22,7 @@ TESTS = \
     t/mkstemps.t \
     t/mktemp.t \
     t/opendir.t \
+    t/passwd.t \
     t/popen.t \
     t/posix_spawn.t \
     t/posix_spawnp.t \
diff --git a/test/src/Makefile.am b/test/src/Makefile.am
index 5f5fde8d..594a8e0f 100644
--- a/test/src/Makefile.am
+++ b/test/src/Makefile.am
@@ -15,6 +15,7 @@ check_PROGRAMS = \
     test-mkstemps \
     test-mktemp \
     test-opendir \
+    test-passwd \
     test-popen \
     test-posix_spawn \
     test-posix_spawnp \
diff --git a/test/src/test-passwd.c b/test/src/test-passwd.c
new file mode 100644
index 00000000..fb9c8c4c
--- /dev/null
+++ b/test/src/test-passwd.c
@@ -0,0 +1,28 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <unistd.h>
+
+int main (int argc, char *argv[]) {
+	struct passwd *pwd;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s username\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	pwd = getpwnam(argv[1]);
+	if (pwd == NULL) {
+		if (errno == 0) {
+			printf("Not found\n");
+		} else {
+			perror("getpwnam");
+		}
+		exit(EXIT_FAILURE);
+	}
+
+	printf("%jd\n", (intmax_t)(pwd->pw_uid));
+	exit(EXIT_SUCCESS);
+}
diff --git a/test/t/passwd.t b/test/t/passwd.t
new file mode 100755
index 00000000..5c3414e0
--- /dev/null
+++ b/test/t/passwd.t
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+srcdir=${srcdir:-.}
+. $srcdir/common.inc.sh
+
+prepare 4
+
+for chroot in chroot fakechroot; do
+    if [ $chroot = "chroot" ] && ! is_root; then
+        skip $(( $tap_plan / 2 )) "not root"
+    else
+
+        t=`$srcdir/$chroot.sh $testtree /bin/test-passwd user 2>&1`
+        test "$t" = "1337" || not
+        ok "$chroot uid is" $t
+
+        t=`$srcdir/$chroot.sh $testtree getent group user 2>&1`
+        test "$t" = "user:x:1337:" || not
+        ok "$chroot getent group user is" $t
+    fi
+done
+
+cleanup
diff --git a/test/testtree.sh b/test/testtree.sh
index ee35fc26..d857a195 100755
--- a/test/testtree.sh
+++ b/test/testtree.sh
@@ -32,6 +32,10 @@ do
     mkdir -p $destdir/$d
 done
 
+echo "user:x:1337:1337:user:/home/user:/bin/bash" > $destdir/etc/passwd
+echo "root:x:0:" > $destdir/etc/group
+echo "user:x:1337:" >> $destdir/etc/group
+
 for d in \
     /dev \
     /proc
@@ -64,6 +68,7 @@ for p in \
     '/usr/bin/dirname' \
     '/usr/bin/env' \
     '/usr/bin/find' \
+    '/usr/bin/getent' \
     '/usr/bin/id' \
     '/usr/bin/ischroot' \
     '/usr/bin/less' \
@@ -116,6 +121,7 @@ for p in \
     'libm.so.*' \
     'libncurses.so.*' \
     'libncursesw.so.*' \
+    'libnss_*.so.*' \
     'libpcre*.so.*' \
     'libpthread.so.*' \
     'libreadline.so.*' \

From d9a47178203931231987d10117acb5684d12ed4c Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Tue, 1 Nov 2022 00:47:56 +0100
Subject: [PATCH 08/13] add test/t/rm.t and amend test/t/touch.t with
 --no-dereference test

 - test/t/rm.t will fail under glibc 2.34 unless __stat64_time64 functions are
   wrapped
 - test/t/touch.t will fail under glibc 2.34 unless __lstat64_time64 is
   wrapped
---
 test/Makefile.am |  1 +
 test/t/rm.t      | 25 +++++++++++++++++++++++++
 test/t/touch.t   | 17 +++++++++++++++--
 3 files changed, 41 insertions(+), 2 deletions(-)
 create mode 100755 test/t/rm.t

diff --git a/test/Makefile.am b/test/Makefile.am
index a1ec743d..88e740e5 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -29,6 +29,7 @@ TESTS = \
     t/pwd.t \
     t/readlink.t \
     t/realpath.t \
+    t/rm.t \
     t/socket-af_unix.t \
     t/statfs.t \
     t/statvfs.t \
diff --git a/test/t/rm.t b/test/t/rm.t
new file mode 100755
index 00000000..9ecf88c9
--- /dev/null
+++ b/test/t/rm.t
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+srcdir=${srcdir:-.}
+. $srcdir/common.inc.sh
+
+prepare 2
+
+for chroot in chroot fakechroot; do
+
+    if [ $chroot = "chroot" ] && ! is_root; then
+        skip $(( $tap_plan / 2 )) "not root"
+    else
+
+        mkdir -p $testtree/dir-$chroot
+        echo 'something' > $testtree/dir-$chroot/file
+
+        $srcdir/$chroot.sh $testtree /bin/sh -c "rm -r /dir-$chroot"
+        test -e $testtree/dir-$chroot && not
+        ok "$chroot rm -r /dir-$chroot:" $t
+
+    fi
+
+done
+
+cleanup
diff --git a/test/t/touch.t b/test/t/touch.t
index fbea316b..cc05a2ba 100755
--- a/test/t/touch.t
+++ b/test/t/touch.t
@@ -3,12 +3,12 @@
 srcdir=${srcdir:-.}
 . $srcdir/common.inc.sh
 
-prepare 16
+prepare 24
 
 . $srcdir/touch.inc.sh
 
 if [ -z "$touch" ]; then
-    skip 16 "touch not found"
+    skip 24 "touch not found"
 else
 
     for chroot in chroot fakechroot; do
@@ -37,6 +37,19 @@ else
 
             sleep 1
 
+            # with --no-dereference, on 32bit, touch will use __lstat64_time64
+            t=`$srcdir/$chroot.sh $testtree $touch -h -r /tmp/$chroot-touch.txt /tmp/$chroot-touch2.txt 2>&1`
+            test "$t" = "" || not
+            ok "$chroot touch -r" $t
+            test -f $testtree/tmp/$chroot-touch2.txt || not
+            ok "$chroot $chroot-touch2.txt exists"
+            test $testtree/tmp/$chroot-touch2.txt -nt $testtree/tmp/$chroot-touch.txt && not
+            ok "$chroot $chroot-touch2.txt is not newer than touch.txt"
+            test $testtree/tmp/$chroot-touch2.txt -ot $testtree/tmp/$chroot-touch.txt && not
+            ok "$chroot $chroot-touch2.txt is not older than $chroot-touch.txt"
+
+            sleep 1
+
             t=`$srcdir/$chroot.sh $testtree $touch -m /tmp/$chroot-touch.txt 2>&1`
             test "$t" = "" || not
             ok "$chroot touch -m" $t

From dac74cd68cfb6eeaae9cd13bdc48737a44980df9 Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Tue, 1 Nov 2022 00:48:23 +0100
Subject: [PATCH 09/13] support glibc 2.34 by wrapping
 __{f,l,}stat{at,}64_time64 and__utime{nsat,s,}64

These functions are only wrapped on 32 bit platforms like i386, armel or
armhf. On 64 bit platforms, the corresponding HAVE_* macros will not be
defined.

 * __fstatat64_time64
 * __lstat64_time64
 * __stat64_time64
 * __utime64
 * __utimensat64
 * __utimes64
---
 configure.ac             |  6 +++++
 src/Makefile.am          |  6 +++++
 src/__fstatat64_time64.c | 44 ++++++++++++++++++++++++++++++++++++
 src/__lstat64_time64.c   | 49 ++++++++++++++++++++++++++++++++++++++++
 src/__stat64_time64.c    | 47 ++++++++++++++++++++++++++++++++++++++
 src/__utime64.c          | 41 +++++++++++++++++++++++++++++++++
 src/__utimensat64.c      | 42 ++++++++++++++++++++++++++++++++++
 src/__utimes64.c         | 42 ++++++++++++++++++++++++++++++++++
 8 files changed, 277 insertions(+)
 create mode 100644 src/__fstatat64_time64.c
 create mode 100644 src/__lstat64_time64.c
 create mode 100644 src/__stat64_time64.c
 create mode 100644 src/__utime64.c
 create mode 100644 src/__utimensat64.c
 create mode 100644 src/__utimes64.c

diff --git a/configure.ac b/configure.ac
index 5b3053e1..26c06116 100644
--- a/configure.ac
+++ b/configure.ac
@@ -158,11 +158,13 @@ ACX_CHECK_FTS_NAME_TYPE
 # Checks for library functions.
 AC_CHECK_FUNCS(m4_normalize([
     __chk_fail
+    __fstatat64_time64
     __fxstat64
     __fxstatat
     __fxstatat64
     __getcwd_chk
     __getwd_chk
+    __lstat64_time64
     __lxstat
     __lxstat64
     __open
@@ -175,7 +177,11 @@ AC_CHECK_FUNCS(m4_normalize([
     __realpath_chk
     __readlink_chk
     __readlinkat_chk
+    __stat64_time64
     __statfs
+    __utime64
+    __utimensat64
+    __utimes64
     __xmknod
     __xmknodat
     __xstat
diff --git a/src/Makefile.am b/src/Makefile.am
index 6e9d9ae1..55193a96 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,9 +1,11 @@
 pkglib_LTLIBRARIES = libfakechroot.la
 libfakechroot_la_SOURCES = \
+    __fstatat64_time64.c \
     __fxstatat.c \
     __fxstatat64.c \
     __getcwd_chk.c \
     __getwd_chk.c \
+    __lstat64_time64.c \
     __lxstat.c \
     __lxstat64.c \
     __lxstat64.h \
@@ -18,7 +20,11 @@ libfakechroot_la_SOURCES = \
     __readlinkat_chk.c \
     __realpath_chk.c \
     __realpath_chk.h \
+    __stat64_time64.c \
     __statfs.c \
+    __utime64.c \
+    __utimensat64.c \
+    __utimes64.c \
     __xmknod.c \
     __xmknodat.c \
     __xstat.c \
diff --git a/src/__fstatat64_time64.c b/src/__fstatat64_time64.c
new file mode 100644
index 00000000..47a401f2
--- /dev/null
+++ b/src/__fstatat64_time64.c
@@ -0,0 +1,44 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2021 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___FSTATAT64_TIME64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <sys/stat.h>
+#include <limits.h>
+#include "libfakechroot.h"
+
+struct __stat64_t64;
+
+wrapper(__fstatat64_time64, int, (int dirfd, const char *pathname, struct __stat64_t64 *buf, int flags))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("__fstatat64_time64(%d, \"%s\", &buf, %d)", dirfd, pathname, flags);
+    expand_chroot_path_at(dirfd, pathname);
+    return nextcall(__fstatat64_time64)(dirfd, pathname, buf, flags);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/__lstat64_time64.c b/src/__lstat64_time64.c
new file mode 100644
index 00000000..e3e84002
--- /dev/null
+++ b/src/__lstat64_time64.c
@@ -0,0 +1,49 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2021 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___LSTAT64_TIME64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <sys/stat.h>
+#include <limits.h>
+#include "libfakechroot.h"
+
+struct __stat64_t64;
+
+wrapper(__lstat64_time64, int, (const char *filename, struct __stat64_t64 *buf))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    char resolved[FAKECHROOT_PATH_MAX];
+    debug("__lstat64_time64(\"%s\", &buf)", filename);
+    if (rel2abs(filename, resolved) == NULL) {
+        return -1;
+    }
+    filename = resolved;
+    expand_chroot_path(filename);
+    return nextcall(__lstat64_time64)(filename, buf);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/__stat64_time64.c b/src/__stat64_time64.c
new file mode 100644
index 00000000..1b65345e
--- /dev/null
+++ b/src/__stat64_time64.c
@@ -0,0 +1,47 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010-2015 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___STAT64_TIME64
+
+#define _BSD_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _DEFAULT_SOURCE
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "libfakechroot.h"
+
+struct __stat64_t64;
+
+wrapper(__stat64_time64, int, (const char * file_name, struct __stat64_t64 * buf))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("__stat64_time64(\"%s\", &buf)", file_name);
+    expand_chroot_path(file_name);
+    return nextcall(__stat64_time64)(file_name, buf);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/__utime64.c b/src/__utime64.c
new file mode 100644
index 00000000..65d6e831
--- /dev/null
+++ b/src/__utime64.c
@@ -0,0 +1,41 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___UTIME64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <utime.h>
+#include "libfakechroot.h"
+
+wrapper(__utime64, int, (const char * filename, const struct utimbuf * buf))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("__utime64(\"%s\", &buf)", filename);
+    expand_chroot_path(filename);
+    return nextcall(__utime64)(filename, buf);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/__utimensat64.c b/src/__utimensat64.c
new file mode 100644
index 00000000..3973d64c
--- /dev/null
+++ b/src/__utimensat64.c
@@ -0,0 +1,42 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___UTIMENSAT64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <sys/time.h>
+#include "libfakechroot.h"
+
+
+wrapper(__utimensat64, int, (int dirfd, const char * pathname, const struct timespec times [2], int flags))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("utimeat(%d, \"%s\", &buf, %d)", dirfd, pathname, flags);
+    expand_chroot_path_at(dirfd, pathname);
+    return nextcall(__utimensat64)(dirfd, pathname, times, flags);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
diff --git a/src/__utimes64.c b/src/__utimes64.c
new file mode 100644
index 00000000..03e57d16
--- /dev/null
+++ b/src/__utimes64.c
@@ -0,0 +1,42 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___UTIMES64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+
+#include <sys/time.h>
+#include "libfakechroot.h"
+
+wrapper(__utimes64, int, (const char * filename, UTIMES_TYPE_ARG2(tv)))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("__utimes64(\"%s\", &tv)", filename);
+    expand_chroot_path(filename);
+    return nextcall(__utimes64)(filename, tv);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif

From 117d2e6e741bc4ff47e41e6879ca9e9821755ffd Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Tue, 31 Jan 2023 11:43:33 +0100
Subject: [PATCH 10/13] also investigate .interp section for architectures that
 do not list the linker in `objdump -p` like mips64el, ppc64el and s390x

---
 scripts/ldd.fakechroot.pl | 33 +++++++++++++++++++++++++++++++++
 test/Makefile.am          |  1 +
 test/t/ldd_interp.t       | 26 ++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100755 test/t/ldd_interp.t

diff --git a/scripts/ldd.fakechroot.pl b/scripts/ldd.fakechroot.pl
index b4bb2a8c..13a94540 100755
--- a/scripts/ldd.fakechroot.pl
+++ b/scripts/ldd.fakechroot.pl
@@ -124,6 +124,38 @@ sub objdump {
     }
 }
 
+# mips64el, ppc64el and s390x do not list the linker itself
+# if it's missing, obtain it from the .interp section
+#
+# mips64el: /lib64/ld.so.1
+# ppc64el: /lib64/ld64.so.2
+# s390x: /lib/ld64.so.1
+sub elfinterp {
+    my $file = shift;
+    my $res = '';
+    local *PIPE;
+    open PIPE, "objdump -sj .interp '$file' 2>/dev/null |";
+    while (my $line = <PIPE>) {
+        if ( $line !~ /^ [a-f0-9]+ ([a-f0-9][a-f0-9][a-f0-9 ]{6} [a-f0-9 ]{8} [a-f0-9 ]{8} [a-f0-9 ]{8})  /) {
+            next;
+        }
+        $line = $1;
+        $line =~ s/ //g;
+        $line =~ s/(..)/chr(hex($1))/eg;
+        $res .= $line;
+    }
+    close PIPE;
+
+    # remove trailing NUL byte
+    $res =~ s/\000$//;
+
+    # only add if it is missing
+    if ( $res && !exists $Libs{$res} ) {
+        push @Libs, $res;
+        $Libs{$res} = '';
+    }
+}
+
 
 sub load_ldsoconf {
     my ($file) = @_;
@@ -191,6 +223,7 @@ sub load_ldsoconf {
         }
 
         objdump($file);
+        elfinterp($file_in_chroot);
 
         if ($Dynamic == 0) {
             print "\tnot a dynamic executable\n";
diff --git a/test/Makefile.am b/test/Makefile.am
index 88e740e5..d7f98f03 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -19,6 +19,7 @@ TESTS = \
     t/host.t \
     t/java.t \
     t/jemalloc.t \
+    t/ldd_interp.t \
     t/mkstemps.t \
     t/mktemp.t \
     t/opendir.t \
diff --git a/test/t/ldd_interp.t b/test/t/ldd_interp.t
new file mode 100755
index 00000000..7c968bb8
--- /dev/null
+++ b/test/t/ldd_interp.t
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+srcdir=${srcdir:-.}
+. $srcdir/common.inc.sh
+
+plan 1
+
+pwd=`dirname $0`
+abs_top_srcdir=${abs_top_srcdir:-`cd "$pwd/../.." 2>/dev/null && pwd -P`}
+
+interp_file=$(file /bin/true | sed 's/^.*, interpreter \([^,]\+\), .*$/\1/')
+interp_readelf=$(readelf --string-dump=.interp /bin/true | sed -ne 's/^  \[ \+[0-9]\+\]  //p')
+
+# diag "$interp_file" "$interp_readelf"
+
+test "$interp_file" = "$interp_readelf" || not
+
+# ldd /bin/true | diag
+
+ldd /bin/true | grep --quiet "^[[:space:]]$interp_file (" || not
+
+# "$abs_top_srcdir/scripts/ldd.fakechroot" /bin/true | diag
+
+"$abs_top_srcdir/scripts/ldd.fakechroot" /bin/true | grep --quiet "^[[:space:]]$interp_file (" || not
+
+ok "ldd lists interpreter $interp_file"

From 1617833d4bfa415cbe2fc1e961cb751e47697c24 Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Mon, 6 Feb 2023 10:42:40 +0100
Subject: [PATCH 11/13] test/touch.inc.sh: use testtree variable

 - the variable is set in test/common.inc.sh and includes `basename $0 .t`
 - otherwise test/t/touch.t will always get skipped
---
 test/touch.inc.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/touch.inc.sh b/test/touch.inc.sh
index 25e0dc01..76c21321 100644
--- a/test/touch.inc.sh
+++ b/test/touch.inc.sh
@@ -1,6 +1,6 @@
-if [ -x testtree/usr/bin/touch ]; then
+if [ -x $testtree/usr/bin/touch ]; then
     touch=/usr/bin/touch
-elif [ -x testtree/bin/touch ]; then
+elif [ -x $testtree/bin/touch ]; then
     touch=/bin/touch
 else
     touch=

From 5f742bbfda32a1345ec00132216ea0cb56223551 Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Sun, 14 Apr 2024 18:04:20 +0200
Subject: [PATCH 12/13] symlink.t: also test lutimes (if available) as well as
 real files

---
 test/src/test-lstat.c | 15 +++++++++++++++
 test/t/symlink.t      |  6 +++++-
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/test/src/test-lstat.c b/test/src/test-lstat.c
index 640d74c8..37b00691 100644
--- a/test/src/test-lstat.c
+++ b/test/src/test-lstat.c
@@ -1,7 +1,9 @@
 #define _BSD_SOURCE
 #define _DEFAULT_SOURCE
+
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -22,6 +24,19 @@ int main (int argc, char *argv[]) {
         exit(1);
     }
 
+#ifdef HAVE_LUTIMES
+    struct timeval tv[2];
+    if (lutimes(argv[1], tv)) {
+        perror("lutimes");
+        exit(1);
+    }
+#endif
+
+    if ((stab.st_mode & S_IFMT) == S_IFREG) {
+        printf("regular file size: %d\n", (int)stab.st_size);
+        return 0;
+    }
+
     if ((stab.st_mode & S_IFMT) != S_IFLNK) {
         fprintf(stderr, "not a symlink\n");
         exit(1);
diff --git a/test/t/symlink.t b/test/t/symlink.t
index 73ff5353..869ef6f2 100755
--- a/test/t/symlink.t
+++ b/test/t/symlink.t
@@ -6,7 +6,7 @@ srcdir=${srcdir:-.}
 readlink=`command -v readlink 2>/dev/null`
 readlink=${readlink:-/bin/readlink}
 
-prepare 18
+prepare 19
 
 for chroot in chroot fakechroot; do
 
@@ -55,6 +55,10 @@ for chroot in chroot fakechroot; do
         test -n "$rs" -a "$rs" = "$ss" || not
         ok "$chroot abs-symlink readlink size [" $rs "] = stat size [" $ss "]"
 
+        t=`$srcdir/$chroot.sh $testtree /bin/test-lstat /$chroot-file 2>&1`
+        test "$t" = "regular file size: 10" || not
+        ok "$chroot-file is of size 10"
+
     fi
 
 done

From 0481f29757567dc23a8a9ae47456be037bde9e9f Mon Sep 17 00:00:00 2001
From: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Date: Sun, 14 Apr 2024 18:05:50 +0200
Subject: [PATCH 13/13] wrap __lutimes64 for glibc 2.37

---
 configure.ac      |  1 +
 src/Makefile.am   |  1 +
 src/__lutimes64.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 44 insertions(+)
 create mode 100644 src/__lutimes64.c

diff --git a/configure.ac b/configure.ac
index 26c06116..dca4f956 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,6 +165,7 @@ AC_CHECK_FUNCS(m4_normalize([
     __getcwd_chk
     __getwd_chk
     __lstat64_time64
+    __lutimes64
     __lxstat
     __lxstat64
     __open
diff --git a/src/Makefile.am b/src/Makefile.am
index 55193a96..5eec2649 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ libfakechroot_la_SOURCES = \
     __getcwd_chk.c \
     __getwd_chk.c \
     __lstat64_time64.c \
+    __lutimes64.c \
     __lxstat.c \
     __lxstat64.c \
     __lxstat64.h \
diff --git a/src/__lutimes64.c b/src/__lutimes64.c
new file mode 100644
index 00000000..e8bce0cc
--- /dev/null
+++ b/src/__lutimes64.c
@@ -0,0 +1,42 @@
+/*
+    libfakechroot -- fake chroot environment
+    Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+*/
+
+
+#include <config.h>
+
+#ifdef HAVE___LUTIMES64
+
+#define _ATFILE_SOURCE
+#define _POSIX_C_SOURCE 200809L
+
+#include <sys/time.h>
+#include "libfakechroot.h"
+
+wrapper(__lutimes64, int, (const char * filename, UTIMES_TYPE_ARG2(tv)))
+{
+    char fakechroot_abspath[FAKECHROOT_PATH_MAX];
+    char fakechroot_buf[FAKECHROOT_PATH_MAX];
+    debug("__lutimes64(\"%s\", &tv)", filename);
+    expand_chroot_path(filename);
+    return nextcall(__lutimes64)(filename, tv);
+}
+
+#else
+typedef int empty_translation_unit;
+#endif
