Android
Linux
Debian
DebianNoroot

Debian noroot 環境において link2symlink 機能を実装した proot を導入する

はじめに

Debian noroot とは、 Android OS 上において root 権限を取ることなく Debian 環境を構築するためのアプリケーションです。
CPU の性能とメモリ容量が潤沢にある Android 端末であれば、 Debian noroot の導入によって Android 端末上で非常に軽快な Debian 環境を実現することができます。

Android OS 6.0 上において、 Debian noroot 環境を起動すると、 apt-get 等の Debian 環境のパッケージ管理コマンド及び git 等がコマンド内部で使用するファイルに対するハードリンクの実行の失敗により正常に起動せず、 Debian noroot 環境への各種パッケージの導入に支障を来す問題が発生しています。

apt-get 及び git 等の起動が失敗する原因は Android OS 6.0 における SELinux の権限に関する設定において、システムコール link(2) の実行が禁止されている為と考えられます。

以上に述べた問題を回避するには、 Debian noroot 環境上で起動する全てのプロセスにおいて発生する全てのシステムコールをフックする機能を持つプロセスである proot に、システムコール link(2) の実行の代わりに symlink(2) の実行を行うことによりハードリンクをエミュレートする機能 (以下、 link2symlink 機能と呼びます) を追加する必要が有ります。そこで、 termux の開発コミュニティによって link2symlink の機能が実装された proot が公開されています

この link2symlink 機能を実装した prootUbuntu 14.04 をインストールした PC 端末でクロスコンパイルし、 Android OS 4.4.2, Android OS 6.0 及び Android OS 7.0 上で動作する Debian noroot 環境proot と入れ換えたところ、 Debian noroot 環境において proot の link2symlink 機能が正常に動作することを確認し、また、これに伴って apt-get コマンドも正常に動作することも確認ました。

本稿では、 Ubuntu 14.04 を搭載した PC 端末を用いて link2symlink の機能が実装された proot をクロスコンパイルして Debian noroot 環境に導入し、動作を確認するまでの過程について述べます。

最初に、 "link2symlink 機能を実装した proot のビルド" の章で、 termux の開発コミュニティによる link2symlink 機能を実装した proot をクロスコンパイルする一連の過程について述べます。

次に、 "proot の導入" の章で、前章にてビルドした prootDebian noroot 環境に導入する過程について述べ、 "proot の動作確認" の章で、 link2symlink 機能を実装した proot を導入した Debian noroot 環境において、ハードリンクの実行の正常な動作を確認する過程について述べます。

最後に、 "結論" の章で本稿の結論について述べます。

なお、以降においては特に断りの無い限り、クロスコンパイルを行う端末は Ubuntu 14.04 を導入した PC 端末であり、クロスコンパイルのターゲットとなるアーキテクチャは ARM 32bit アーキテクチャとして本稿を記述します。

link2symlink 機能を実装した proot のビルド

本章では、 link2symlink 機能を実装した proot 及び proot の依存ライブラリである talloc 2.1.14 を PC 端末でクロスコンパイルする過程について述べます。

最初に、 "クロスコンパイル環境の構築" の節において、 proot のビルドに先立って、 PC 端末に ARM 32bit アーキテクチャ用のクロスコンパイル環境を構築する手法について述べます。

次に、 "talloc 2.1.14 のビルド" の節において、 proot が依存するライブラリである talloc 2.1.14 をビルドする手法について述べ、 "proot のビルド" の節において、 link2symlink 機能を実装した proot をビルドする手法について述べます。

最後に、 "proot のビルドの自動化 Ruby スクリプト" の節において、当方にて作成した talloc 2.1.14 及び proot のビルドを一括で行う Makefile の使用法について述べます。

クロスコンパイル環境の構築

本節では、 PC 端末に ARM 32bit アーキテクチャ用のクロスコンパイル環境及び簡単な動作確認用の qemu のユーザモード環境の構築について述べます。

ここで、 PC 端末にクロスコンパイル環境に導入するには、下記の2つの手法があります。

  1. PC 端末に、 ARM 32bit アーキテクチャ用のクロスコンパイル環境を導入するための Debian パッケージ g++-arm-linux-gnueabihf をインストールする手法。
  2. PC 端末に、 Android NDK を導入し、 ARM 32bit アーキテクチャ用のクロスコンパイル環境として、 Android NDK stand alone toolchain をインストールする手法。

本節では、 "Debian パッケージによるクロスコンパイル環境をインストールする手法" の項において、前者による手法について述べ、 "Android NDK によるクロスコンパイル環境をインストールする手法" の項において、後者による手法について述べます。

Debian パッケージによるクロスコンパイル環境をインストールする手法

Debian パッケージによるクロスコンパイル環境をインストールするには、まず最初に PC 端末上で apt-get コマンドを用いて、セルフコンパイル環境と github 及び Web 上で必要なリポジトリ及びアーカイブの取得に必要な gitwget を下記のようにインストールします。

また、 ARM 32bit アーキテクチャ用のクロスコンパイル環境の導入に必要な g++-arm-linux-gnueabihfqemu のユーザモード環境を下記のようにインストールします。

 $ sudo apt-get install build-essensial git wget
 $ sudo apt-get install g++-arm-linux-gnueabihf qemu-user qemu-user-static

ここで、もしディレクトリ /usr/include/asm が存在しない場合は下記のようにして、ディレクトリ /usr/include にカレントディレクトリを移動し、ディレクトリ asm-generic から asm にシンボリックリンクを張ります。

 $ cd /usr/include
 $ sudo ln -sf asm-generic asm

次に、クロスコンパイル環境に関連する CC, LD, CFLAGS 等の環境変数を以下のように設定します。

 $ export BUILD_PREFIX=${PWD}/opt
 $ export BUILD_HOST=arm-linux-gnueabihf
 $ export CROSS_COMPILE=/usr/bin/${BUILD_HOST}-
 $ export CC=${CROSS_COMPILE}gcc
 $ export CPP=${CROSS_COMPILE}cpp
 $ export AR=${CROSS_COMPILE}ar
 $ export RANLIB=${CROSS_COMPILE}ranlib
 $ export STRIP=${CROSS_COMPILE}strip
 $ export LD=${CROSS_COMPILE}ld
 $ export OBJCOPY=${CROSS_COMPILE}objcopy
 $ export OBJDUMP=${CROSS_COMPILE}objdump
 $ export CFLAGS="-mthumb -I${BUILD_PREFIX}/include -I/usr/${BUILD_HOST}/include"
 $ export CPPFLAGS="-mthumb -I${BUILD_PREFIX}/include -I/usr/${BUILD_HOST}/include"
 $ export LDFLAGS="-L${BUILD_PREFIX}/lib -L/usr/${BUILD_HOST}/lib"

以上で、 PC 端末において、 Debian パッケージ g++-arm-linux-gnueabihf による ARM 32bit アーキテクチャ用のクロスコンパイル及び簡単な動作確認の環境が構築されました。

Android NDK によるクロスコンパイル環境をインストールする手法

前項で述べた ARM 32bit アーキテクチャ用のクロスコンパイル環境の Debian パッケージ g++-arm-linux-gnueabihf に代えて、 Android NDK stand alone toolchain を用いたクロスコンパイル環境をインストールするには、まず Debian パッケージ wget, git とセルフコンパイル環境及び qemu のユーザモード環境を下記のようにインストールします。

 $ sudo apt-get install build-essensial git wget qemu-user qemu-user-static

次に、 proot をビルドする端末に Android NDK をインストールします。具体的なインストール手法については、 tanjo 氏による "Android の Linux 環境をターミナルから構築する" の投稿の他、 Android NDK に関する各種資料を参考にして下さい。

なお、 Linuxbrew が導入されている端末に Android NDK のインストールする場合は、 2018 年 3 月現在で android-sdk, android-ndk の両 Formula が Caskroom に移行しているため、下記のようにしてインストールする必要があります。

 $ brew install -dv https://raw.githubusercontent.com/Linuxbrew/homebrew-core/a0f7020167cec6ee73c7d99ca89b1bd433ee6536/Formula/android-sdk.rb
 $ brew install -dv https://raw.githubusercontent.com/Linuxbrew/homebrew-core/b0eae852a26b09e17111caa75b6c8e9d636b9055/Formula/android-ndk.rb

そして、クロスコンパイル環境に関連する CC, LD, CFLAGS 等の環境変数を以下のように設定します。

 $ export BUILD_PREFIX=${PWD}/opt
 $ export BUILD_HOST=arm-linux-androideabi
 $ export CROSS_COMPILE=${BUILD_PREFIX}/toolchain-arm/bin/${BUILD_HOST}-
 $ export SYSROOT=${BUILD_PREFIX}/toolchain-arm/sysroot
 $ export CC="${CROSS_COMPILE}gcc --sysroot=${SYSROOT}"
 $ export CPP="${CROSS_COMPILE}cpp --sysroot=${SYSROOT}"
 $ export AR=${CROSS_COMPILE}ar
 $ export RANLIB=${CROSS_COMPILE}ranlib
 $ export STRIP=${CROSS_COMPILE}strip
 $ export LD=${CROSS_COMPILE}ld
 $ export OBJCOPY=${CROSS_COMPILE}objcopy
 $ export OBJDUMP=${CROSS_COMPILE}objdump
 $ export CFLAGS="-mthumb -I${BUILD_PREFIX}/include"
 $ export CPPFLAGS="-mthumb -I${BUILD_PREFIX}/include"
 $ export LDFLAGS="-L${BUILD_PREFIX}/lib"

最後に、下記のコマンドを起動して、 proot をビルドするための ARM 32bit アーキテクチャ用のクロスコンパイル環境となる Android NDK から Android NDK stand alone toolchain を生成します。

ここで、 PC 端末への Android NDK のインストール先のディレクトリは、環境変数 ANDROID_NDK_HOME に代入されているものとします。

 $ ${ANDROID_NDK_HOME}/build/tools/make_standalone_toolchain.py --arch arm --api 24 --force --verbose --install-dir ${BUILD_PREFIX}/toolchain-arm

以上で、 PC 端末において、 Android NDK stand alone toolchain による ARM 32bit アーキテクチャ用のクロスコンパイル及び簡単な動作確認の環境が構築されました。

talloc 2.1.14 のビルド

本節では、 proot の依存ライブラリである talloc 2.1.14 を前節で構築したクロスコンパイル環境でビルドする手法について述べます。

先ず最初に、下記の通りに talloc 2.1.14 のソースコードの tarball をダウンロードして展開します。展開後は、カレントディレクトリを ./talloc-2.1.14 に移動します。

 $ wget https://download.samba.org/pub/talloc/talloc-2.1.14.tar.gz
 $ tar -zxvf talloc-2.1.14.tar.gz
 $ cd ./talloc-2.1.14

次に、 talloc 2.1.14 のクロスコンパイルの際に必要となる設定が書かれたテキストファイル talloc-cross-answer.txt を以下の内容で作成します。
テキストファイル talloc-cross-answer.txt は、ディレクトリ ./talloc-2.1.14 上に置いておきます。

talloc-cross-answer.txt
Checking uname sysname type: "Linux"
Checking uname machine type: "do not care"
Checking uname release type: "do not care"
Checking uname version type: "do not care"
Checking simple C program: OK
building library support: OK
Checking for large file support: OK
Checking for -D_FILE_OFFSET_BITS=64: OK
Checking for WORDS_BIGENDIAN: OK
Checking for C99 vsnprintf: OK
Checking for HAVE_SECURE_MKSTEMP: OK
rpath library support: OK
-Wl,--version-script support: FAIL
Checking correct behavior of strtoll: OK
Checking correct behavior of strptime: OK
Checking for HAVE_IFACE_GETIFADDRS: OK
Checking for HAVE_IFACE_IFCONF: OK
Checking for HAVE_IFACE_IFREQ: OK
Checking getconf LFS_CFLAGS: OK
Checking for large file support without additional flags: OK
Checking for working strptime: OK
Checking for HAVE_SHARED_MMAP: OK
Checking for HAVE_MREMAP: OK
Checking for HAVE_INCOHERENT_MMAP: OK
Checking getconf large file support flags work: OK

続いて、下記の通りに ./configure スクリプト及び make コマンドを起動して、 talloc 2.1.14 の動的ライブラリ及びヘッダファイルをディレクトリ ${BUILD_PREFIX}/{lib,include} 上にインストールします。

 $ ./configure --prefix=${BUILD_PREFIX} --cross-compile --cross-answers=./talloc-cross-answer.txt --disable-python --without-gettext --disable-rpath
 $ make install V=1

ここで、次節で述べる proot のビルドにおいて、ライブラリ tallocproot と静的にリンクさせるため、下記のようにして talloc の静的ライブラリを生成し、動的ライブラリと同じくディレクトリ ${BUILD_PREFIX}/lib 上にインストールします。

 $ (cd bin/default; ${AR} rsv ./libtalloc.a ./talloc_5.o ./lib/replace/replace_2.o ./lib/replace/cwrap_2.o ./lib/replace/closefrom_2.o)
 $ install -v -m 0644 bin/default/libtalloc.a ${BUILD_PREFIX}/lib
 $ cd ..

以上で、 proot の依存ライブラリである talloc 2.1.14 がインストールされました。

proot のビルド

本節では、 link2symlink 機能を実装した proot前々節で構築したクロスコンパイル環境でビルドする手法について述べます。

まず最初に、下記のとおりにして Termux の開発コミュニティによる link2symlink 機能を実装した prootgithub リポジトリを git コマンドを用いて取得します。

 $ git clone https://github.com/termux/proot.git ./proot-termux-git

なお、 Termux の開発コミュニティによる link2symlink 機能を実装した proot において、現行の github リポジトリのソースコードをコンパイルする際には、下記に述べる問題を修正する必要があります。

  • Debian パッケージ g++-arm-linux-gnueabihf による ARM 32bit アーキテクチャ及び ARM 64bit アーキテクチャ用のクロスコンパイル環境を用いてコンパイルを行った場合に、コンパイルエラーが発生する問題を修正。
  • VFAT 領域等、シンボリックリンクに対応していないファイルシステムの領域において、システムコール link(2) を実行した時に、リンク元のファイルが別名に変更されたままとなる問題を修正。
  • proot コマンドにおいて、オプション --link2symlink が指定され、かつ、環境変数 PROOT_L2S_DIR が設定されない場合に、自動的にオプション -H が指定されて proot コマンドが起動されるように修正。
    • また、オプション -H が設定された場合に、 proot によって不可視化されるファイル及びシンボリックリンクのプレフィックスを ".l2s." とするように修正。
    • link(2)symlink(2) によってエミュレートする機能を使用時に proot の内部で作成される ".l2s." をプレフィックスとするファイル及びシンボリックリンクが外部から直接読み書きが出来るためにこれらのファイルを削除すると、ハードリンクが機能しなくなるために行われる修正です。
  • ソースコード src/syscall/socket.c において、 obsolete である glibc の標準ライブラリ関数 mktemp(3) に代えて独自の実装による mktemp(3) 関数である proot_mktemp 関数を使用するように修正。
  • ソースコードのうち、幾つかのファイル等において、 proot のコンパイル時に警告を出力する問題を修正。
  • proot の一時ファイルを置くためのディレクトリのパス名の設定について、環境変数 PROOT_TMP_DIR の他に、環境変数 PROOT_TMPDIR を参照するように修正。

上記で述べた問題を修正するためには、ソースコードをコンパイルする前に、以下に示す差分ファイルをソースコードに適用する必要があります。

proot-termux-fix.diff
diff --git a/src/arch.h b/src/arch.h
index f554338..272bcbe 100644
--- a/src/arch.h
+++ b/src/arch.h
@@ -111,6 +111,22 @@ typedef unsigned char byte_t;
     #undef SYSCALL_AVOIDER
     #define SYSCALL_AVOIDER ((word_t) 222)

+    #if !defined(__ANDROID__) && defined(__thumb__)
+    /* Fix a compile error of proot when edc869d -> 58d2161. */
+    /* These defines come from /usr/arm-linux-gnueabihf/include/asm/ptrace.h */
+    #ifndef ARM_cpsr
+    #define ARM_cpsr        uregs[16]
+    #endif /* ARM_cpsr */
+
+    #ifndef V4_PSR_T_BIT
+    #define V4_PSR_T_BIT    0x00000020      /* >= V4T, but not V7M */
+    #endif /* V4_PSR_T_BIT */
+
+    #ifndef PSR_T_BIT
+    #define PSR_T_BIT       V4_PSR_T_BIT
+    #endif /* PSR_T_BIT */
+    #endif /* !__ANDROID__ && __thumb__ */
+
 #elif defined(ARCH_ARM64)

     #define SYSNUMS_HEADER1 "syscall/sysnums-arm64.h"
@@ -139,6 +155,35 @@ typedef unsigned char byte_t;
     #undef SYSCALL_AVOIDER
     #define SYSCALL_AVOIDER ((word_t) -1)

+    #ifndef __ANDROID__
+    /* Fix a compile error of proot when using aarch64-linux-gnu-gcc. */
+    /* These define come from glibc/sysdeps/unix/sysv/linux/aarch64/sys/user.h */
+    /* See https://code.woboq.org/userspace/glibc//sysdeps/unix/sysv/linux/aarch64/sys/user.h.html */
+    #include <stdint.h>
+
+    struct user_regs_struct {
+       uint64_t regs[31];
+       uint64_t sp;
+       uint64_t pc;
+       uint64_t pstate;
+    };
+
+    struct user_fpsimd_struct {
+       __uint128_t vregs[32];
+       uint32_t fpsr;
+       uint32_t fpcr;
+    };
+    #endif /* __ANDROID__ */
+
+    /* This define come from /usr/src/include/uapi/linux/ptrace.h */
+    /* See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ptrace.h */
+    #ifndef PTRACE_GETSIGMASK
+    #define PTRACE_GETSIGMASK  0x420a
+    #endif
+    #ifndef PTRACE_SETSIGMASK
+    #define PTRACE_SETSIGMASK  0x420b
+    #endif
+
 #elif defined(ARCH_X86)

     #define SYSNUMS_HEADER1 "syscall/sysnums-i386.h"
diff --git a/src/cli/cli.c b/src/cli/cli.c
index c523f2e..e3949ee 100644
--- a/src/cli/cli.c
+++ b/src/cli/cli.c
@@ -557,10 +557,17 @@ const char *expand_front_variable(TALLOC_CTX *context, const char *string)

 static int indent_level = 0;

+#ifdef __ANDROID__
+void __cyg_profile_func_enter(void *this_function UNUSED, void *call_site UNUSED) DONT_INSTRUMENT;
+void __cyg_profile_func_enter(void *this_function UNUSED, void *call_site UNUSED)
+#else
 void __cyg_profile_func_enter(void *this_function, void *call_site) DONT_INSTRUMENT;
 void __cyg_profile_func_enter(void *this_function, void *call_site)
+#endif
 {
+#ifndef __ANDROID__
    void *const pointers[] = { this_function, call_site };
+#endif
    char **symbols = NULL;

 #ifndef __ANDROID__
diff --git a/src/cli/proot.c b/src/cli/proot.c
index 6a37c79..1f29a44 100644
--- a/src/cli/proot.c
+++ b/src/cli/proot.c
@@ -221,8 +221,10 @@ static int handle_option_V(Tracee *tracee UNUSED, const Cli *cli, const char *va
    fflush(stdout);

    size = &_binary_licenses_end - &_binary_licenses_start;
-   if (size > 0)
-       write(1, &_binary_licenses_start, size);
+   if (size > 0) {
+       if (write(1, &_binary_licenses_start, size) < 0) { /* noop */
+       }
+   }

    exit_failure = false;
    return -1;
@@ -283,12 +285,20 @@ static int handle_option_S(Tracee *tracee, const Cli *cli, const char *value)
 static int handle_option_link2symlink(Tracee *tracee, const Cli *cli UNUSED, const char *value UNUSED)
 {
    int status;
+   char *l2s_directory;

    /* Initialize the link2symlink extension.  */
    status = initialize_extension(tracee, link2symlink_callback, NULL);
    if (status < 0)
        note(tracee, WARNING, INTERNAL, "link2symlink not initialized");

+   /* If environment variable PROOT_L2S_DIR is not set, the option "-H" is forced to be set. */
+   l2s_directory = getenv("PROOT_L2S_DIR");
+   if (l2s_directory == NULL || l2s_directory[0] == '\0') {
+       if(handle_option_H(tracee, cli, value) < 0) { /* noop */
+       }
+   }
+
    return 0;
 }

diff --git a/src/cli/proot.h b/src/cli/proot.h
index 31ce9ac..bccaafb 100644
--- a/src/cli/proot.h
+++ b/src/cli/proot.h
@@ -245,7 +245,12 @@ Copyright (C) 2015 STMicroelectronics, licensed under GPL v2 or later.",
                 { .name = "-H", .separator = '\0', .value = NULL },
                 { .name = NULL, .separator = '\0', .value = NULL } },
           .handler = handle_option_H,
+#ifdef USERLAND
           .description = "Hide files and directories starting with '.proot.' .",
+#endif
+#ifndef USERLAND
+          .description = "Hide files and directories starting with '.l2s.' . (If environment variable 'PROOT_L2S_DIR' is not set, this option is forced to be set.)",
+#endif
           .detail = "",
         },
         { .class = "Extension options",
diff --git a/src/extension/fake_id0/chroot.c b/src/extension/fake_id0/chroot.c
index 757a602..1876d13 100644
--- a/src/extension/fake_id0/chroot.c
+++ b/src/extension/fake_id0/chroot.c
@@ -32,12 +32,14 @@ int handle_chroot_exit_end(Tracee *tracee, Config *config) {
    status = translate_path(tracee, path_translated, AT_FDCWD, path, false);
    if (status < 0)
        return status;
-   realpath(path_translated, path_translated_absolute);
+   if (realpath(path_translated, path_translated_absolute) == (char *)NULL) { /* noop */
+   }

    status = translate_path(tracee, root_translated, AT_FDCWD, get_root(tracee), false);
    if (status < 0)
        return status;
-   realpath(root_translated, root_translated_absolute);
+   if (realpath(root_translated, root_translated_absolute) == (char *)NULL) { /* noop */
+   }

    /* Only "new rootfs == current rootfs" is supported yet.  */
    status = compare_paths(root_translated_absolute, path_translated_absolute);
diff --git a/src/extension/fake_id0/fake_id0.c b/src/extension/fake_id0/fake_id0.c
index 0e248e3..886c483 100644
--- a/src/extension/fake_id0/fake_id0.c
+++ b/src/extension/fake_id0/fake_id0.c
@@ -1082,6 +1082,7 @@ int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t data1
            uid = getuid();

        gid_string = strchr(uid_string, ':');
+       gid = 0;
        if (gid_string == NULL) {
            errno = EINVAL;
        }
diff --git a/src/extension/fake_id0/helper_functions.c b/src/extension/fake_id0/helper_functions.c
index 3a16ce5..48c4e97 100644
--- a/src/extension/fake_id0/helper_functions.c
+++ b/src/extension/fake_id0/helper_functions.c
@@ -319,7 +319,8 @@ int read_meta_file(char path[PATH_MAX], mode_t *mode, uid_t *owner, gid_t *group
        return 0;

    }
-   fscanf(fp, "%d %d %d ", &lcl_mode, owner, group);
+   if (fscanf(fp, "%d %d %d ", &lcl_mode, owner, group) == EOF) { /* noop */
+   }
    lcl_mode = otod(lcl_mode);
    *mode = (mode_t)lcl_mode;
    fclose(fp);
diff --git a/src/extension/fake_id0/sendmsg.c b/src/extension/fake_id0/sendmsg.c
index 228fcdc..ea3cb8d 100644
--- a/src/extension/fake_id0/sendmsg.c
+++ b/src/extension/fake_id0/sendmsg.c
@@ -13,7 +13,7 @@ int handle_sendmsg_enter_end(Tracee *tracee, word_t sysnum)
    /* Read sendmsg header.  */
    int status;
    unsigned long socketcall_args[3];
-   struct msghdr msg = {};
+   struct msghdr msg = { .msg_name = (void *)NULL };
    bool is_socketcall = sysnum == PR_socketcall;

    if (!is_socketcall)
diff --git a/src/extension/hidden_files/hidden_files.c b/src/extension/hidden_files/hidden_files.c
index 54e4abc..a27a42b 100644
--- a/src/extension/hidden_files/hidden_files.c
+++ b/src/extension/hidden_files/hidden_files.c
@@ -13,7 +13,12 @@
 #include "path/path.h"

 /* Change the HIDDEN_PREFIX to change which files are hidden */
+#ifdef USERLAND
 #define HIDDEN_PREFIX ".proot"
+#endif 
+#ifndef USERLAND
+#define HIDDEN_PREFIX ".l2s."
+#endif 

 struct linux_dirent {
     unsigned long d_ino;
diff --git a/src/extension/link2symlink/link2symlink.c b/src/extension/link2symlink/link2symlink.c
index 3b63382..511cc22 100644
--- a/src/extension/link2symlink/link2symlink.c
+++ b/src/extension/link2symlink/link2symlink.c
@@ -56,6 +56,8 @@ static int move_and_symlink_path(Tracee *tracee, Reg sysarg)
    char new_intermediate[PATH_MAX];
    char final[PATH_MAX];
    char new_final[PATH_MAX];
+   char final_bak[PATH_MAX];
+   char intermediate_bak[PATH_MAX];
    char * name;
    const char * l2s_directory;
    struct stat statl;
@@ -141,13 +143,24 @@ static int move_and_symlink_path(Tracee *tracee, Reg sysarg)

        /* Symlink the intermediate to the final file.  */
        status = symlink(final, intermediate);
-       if (status < 0)
+       if (status < 0) {
+           /* ensure rename final -> original */
+           rename(final, original);
+
            return status;
+       }

        /* Symlink the original path to the intermediate one.  */
        status = symlink(intermediate, original);
-       if (status < 0)
+       if (status < 0) {
+           /* ensure rename final -> original */
+           rename(final, original);
+
+           /* ensure unlink intermediate */
+           unlink(intermediate);
+
            return status;
+       }
    } else {
        /*Move the original content to new location, by incrementing count at end of path. */
        size = my_readlink(intermediate, final);
@@ -166,12 +179,32 @@ static int move_and_symlink_path(Tracee *tracee, Reg sysarg)
        status = notify_extensions(tracee, LINK2SYMLINK_RENAME, (intptr_t) final, (intptr_t) new_final);
        if (status < 0)
            return status;
+
+       strcpy(final_bak, final);
        strcpy(final, new_final);
+       snprintf(intermediate_bak, (size_t)PATH_MAX, "%s.bak", intermediate);
+
        /* Symlink the intermediate to the final file.  */
-       status = unlink(intermediate);
-       if (status < 0)
+       status = rename(intermediate, intermediate_bak);
+       if (status < 0) {
+           /* ensure rename final_bak (original final) -> final */
+           rename(final, final_bak);
+
            return status;
+       }
+
        status = symlink(final, intermediate);
+       if (status < 0) {
+           /* ensure rename intermediate_bak (original intermediate) -> intermediate */
+           rename(intermediate_bak, intermediate);
+
+           /* ensure rename final_bak (original final) -> final */
+           rename(final, final_bak);
+
+           return status;
+       }
+
+       status = unlink(intermediate_bak);
        if (status < 0)
            return status;
    }
@@ -195,6 +228,8 @@ static int decrement_link_count(Tracee *tracee, Reg sysarg)
    char intermediate[PATH_MAX];
    char final[PATH_MAX];
    char new_final[PATH_MAX];
+   char final_bak[PATH_MAX];
+   char intermediate_bak[PATH_MAX];
    char * name;
    struct stat statl;
    ssize_t size;
@@ -254,15 +289,32 @@ static int decrement_link_count(Tracee *tracee, Reg sysarg)
        if (status < 0)
            return status;

+       strcpy(final_bak, final);
        strcpy(final, new_final);
+       snprintf(intermediate_bak, (size_t)PATH_MAX, "%s.bak", intermediate);

        /* Symlink the intermediate to the final file.  */
-       status = unlink(intermediate);
-       if (status < 0)
+       status = rename(intermediate, intermediate_bak);
+       if (status < 0) {
+           /* ensure final_bak (original final) -> final */
+           rename(final_bak, final);
+
            return status;
+       }

        status = symlink(final, intermediate);
-       if (status < 0)
+       if (status < 0) {
+           /* ensure final_bak (original final) -> final */
+           rename(final_bak, final);
+
+           /* ensure intermediate_bak (original intermediate) -> intermediate */
+           rename(intermediate_bak, intermediate);
+
+           return status;
+       }
+
+       status = unlink(intermediate_bak);
+       if(status < 0)
            return status;
    } else {
        /* If it is the last, delete the intermediate and final */
diff --git a/src/path/temp.c b/src/path/temp.c
index 8d8aa63..829168a 100644
--- a/src/path/temp.c
+++ b/src/path/temp.c
@@ -24,7 +24,11 @@ const char *get_temp_directory()

    temp_directory = getenv("PROOT_TMP_DIR");
    if (temp_directory == NULL) {
-       temp_directory = P_tmpdir;
+       temp_directory = getenv("PROOT_TMPDIR");
+       if (temp_directory == NULL) {
+           temp_directory = P_tmpdir;
+           return temp_directory;
+       }
    }

    tmp = realpath(temp_directory, NULL);
diff --git a/src/syscall/rlimit.c b/src/syscall/rlimit.c
index d167843..27efd2a 100644
--- a/src/syscall/rlimit.c
+++ b/src/syscall/rlimit.c
@@ -88,7 +88,7 @@ int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit)
        /* Convert this special value from 32-bit to 64-bit,
         * if needed.  */
        if (is_32on64_mode(tracee) && tracee_stack_limit == (uint32_t) -1)
-           tracee_stack_limit = RLIM_INFINITY;
+           tracee_stack_limit = (uint32_t)RLIM_INFINITY;
    }
    if (errno != 0)
        return -errno;
@@ -112,6 +112,6 @@ int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit)
        VERBOSE(tracee, 1, "can't set stack limit.");
    return 0; /* Not fatal.  */

-   VERBOSE(tracee, 1, "stack soft limit increased to %llu bytes", proot_stack.rlim_cur);
+   VERBOSE(tracee, 1, "stack soft limit increased to %llu bytes", (long long unsigned int)proot_stack.rlim_cur);
    return 0;
 }
diff --git a/src/syscall/socket.c b/src/syscall/socket.c
index ec74950..6780318 100644
--- a/src/syscall/socket.c
+++ b/src/syscall/socket.c
@@ -28,6 +28,10 @@
 #include <sys/socket.h>  /* struct sockaddr_un, AF_UNIX, */
 #include <sys/un.h>      /* struct sockaddr_un, */
 #include <sys/param.h>   /* MIN(), MAX(), */
+#include <stdlib.h>      /* For proot_mktemp() */
+#include <time.h>        /* For proot_mktemp() */
+#include <sys/types.h>   /* For proot_mktemp() */
+#include <sys/stat.h>    /* For proot_mktemp() */

 #include "syscall/socket.h"
 #include "tracee/tracee.h"
@@ -39,12 +43,47 @@

 #include "compat.h"

+/* For proot_mktemp() */
+#define ALPHABET "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define XXXXXX "XXXXXX"
+
 /* The sockaddr_un structure has exactly the same layout on all
  * architectures.  */
 static const off_t offsetof_path = offsetof(struct sockaddr_un, sun_path);
 extern struct sockaddr_un sockaddr_un__;
 static const size_t sizeof_path  = sizeof(sockaddr_un__.sun_path);

+/**
+ * This function generates a unique temporary filename from @path.
+ * The last six characters of @path must be XXXXXX and these 
+ * are replaced with a string that makes the filename unique. 
+ * The function proot_mktemp() is made instead of mktemp(3) in glibc.
+ */
+static char *proot_mktemp(char *path) {
+   char *xptr, *p; struct stat st;
+
+   xptr = strstr((const char *)path, XXXXXX);
+
+   if ((xptr == (char *)NULL) || (*(xptr + strlen(XXXXXX)) != '\0')) {
+       (void)strncpy(path, "", strlen((const char *)path));
+       return (char *)NULL;
+   }
+
+   srand((unsigned int)time(NULL));
+
+   for ( ; ; ) {
+       for (p = xptr ; *p == 'X'; p++) {
+           *p = ALPHABET[rand() % strlen(ALPHABET)];
+       }
+
+       if (stat(path, &st) == -1) {
+           return path;
+       } else {
+           (void)strncpy(xptr, XXXXXX, strlen(XXXXXX));
+       }
+   }
+}
+
 /**
  * Copy in @sockaddr the struct sockaddr_un stored in the @tracee
  * memory at the given @address.  Also, its pathname is copied to the
@@ -120,7 +159,7 @@ int translate_socketcall_enter(Tracee *tracee, word_t *address, int size)
        if (shorter_host_path == NULL || strlen(shorter_host_path) > sizeof_path)
            return -EINVAL;

-       (void) mktemp(shorter_host_path);
+       (void) proot_mktemp(shorter_host_path);

        if (strlen(shorter_host_path) > sizeof_path)
            return -EINVAL;
diff --git a/src/tracee/event.c b/src/tracee/event.c
index 7310605..501baee 100644
--- a/src/tracee/event.c
+++ b/src/tracee/event.c
@@ -637,7 +637,7 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
            break;

        case SIGSYS: {
-           siginfo_t siginfo = {};
+           siginfo_t siginfo = { .si_signo = 0 };
            ptrace(PTRACE_GETSIGINFO, tracee->pid, NULL, &siginfo);
            if (siginfo.si_code == SYS_SECCOMP) {
                if (tracee->skip_next_seccomp_signal) {
diff --git a/src/tracee/mem.c b/src/tracee/mem.c
index 5e0d1d9..0aadd68 100644
--- a/src/tracee/mem.c
+++ b/src/tracee/mem.c
@@ -192,14 +192,14 @@ static int ptrace_pokedata_or_via_stub(Tracee *tracee, word_t addr, word_t word)
    return status;
 }

-void mem_prepare_after_execve(Tracee *tracee)
+void mem_prepare_after_execve(Tracee *tracee UNUSED)
 {
 #if HAS_POKEDATA_WORKAROUND
    tracee->pokedata_workaround_stub_addr = peek_reg(tracee, CURRENT, INSTR_POINTER) + offset_to_pokedata_workaround;
 #endif
 }

-void mem_prepare_before_first_execve(Tracee *tracee)
+void mem_prepare_before_first_execve(Tracee *tracee UNUSED)
 {
 #if HAS_POKEDATA_WORKAROUND
    tracee->pokedata_workaround_stub_addr = (word_t)&launcher_pokedata_workaround;
diff --git a/src/tracee/reg.c b/src/tracee/reg.c
index 4c8d254..986e6e6 100644
--- a/src/tracee/reg.c
+++ b/src/tracee/reg.c
@@ -262,7 +262,11 @@ int fetch_regs(Tracee *tracee)
    return 0;
 }

+#ifdef ARCH_ARM_EABI
 int push_specific_regs(Tracee *tracee, bool including_sysnum)
+#else
+int push_specific_regs(Tracee *tracee, bool including_sysnum UNUSED)
+#endif
 {
    int status;

diff --git a/src/tracee/seccomp.c b/src/tracee/seccomp.c
index 7a3893e..1fa186a 100644
--- a/src/tracee/seccomp.c
+++ b/src/tracee/seccomp.c
@@ -230,7 +230,6 @@ static int handle_seccomp_event_common(Tracee *tracee)
    case PR_statfs:
    {
        int size;
-       int status;
        char path[PATH_MAX];
        char original[PATH_MAX];
        struct statfs64 my_statfs64;

即ち、 proot のソースコードの github リポジトリが置かれているディレクトリ ./proot-termux-git 上において、下記のように差分ファイル proot-termux-fix.diff を適用します。

 $ patch -p1 < ../proot-termux-fix.diff

最後に、カレントディレクトリを ./proot-termux-git/src に移動した後、 ./GNUmakefile に対して make コマンドを下記のように起動して、 link2symlink 機能を実装した proot をビルドします。
この時、一時的に環境変数 LC_ALLC に設定し、また、前節においてビルドした talloc 2.1.14 を静的にリンクするために、環境変数 LDFLAGS"-static -ltalloc" を追加して make コマンドを起動するのを忘れないようにします。

 $ cd ./proot-termux-git/src
 $ /usr/bin/env LC_ALL=C make -f ./GNUmakefile CC=${CC} LD=${CC} STRIP=${STRIP} OBJCOPY=${OBJCOPY} OBJDUMP=${OBJDUMP} \
     CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I${BUILD_PREFIX}/include ${CPPFLAGS}" \
     CFLAGS="${CFLAGS} -Wall -Wextra -O2" LDFLAGS="${LDFLAGS} -static -ltalloc -Wl,-z,noexecstack" V=1

以上で、 link2symlink 機能が実装された prootARM 32bit アーキテクチャ用実行ファイルがビルドされました。

proot のビルドの自動化 Ruby スクリプト

ここで、前々節において述べた talloc 2.1.14 のビルドと、前節において述べた link2symlink 機能を実装した proot をビルドを一括して行う為の Ruby スクリプトを当方にて作成し、github にて公開しました

本節では、 link2symlink 機能を実装した proot の実行ファイルのビルドを自動化する Ruby スクリプトの使用法について述べます。

まず最初に、下記のようにして、 "クロスコンパイル環境の構築" の節において述べた ARM 32bit アーキテクチャ用クロスコンパイル環境を PC 端末に構築した後、 git コマンドを用いて link2symlink 機能を実装した proot のビルドを自動化する Ruby スクリプトのリポジトリを取得します。

 $ sudo apt-get install build-essensial git wget
 $ sudo apt-get install g++-arm-linux-gnueabihf qemu-user qemu-user-static
 $ git clone https://github.com/z80oolong/proot-termux-build.git

次に、カレントディレクトリを ./proot-termux-build に移動して下記のように ./build-proot.rb コマンドを起動します。

これにより、 talloc 2.1.14 及び link2symlink 機能を実装した proot のソースコードの取得と ARM 32bit アーキテクチャ用のクロスコンパイルによるビルドを自動的に行い、実行形式のファイル proot をディレクトリ ./cross-compile にインストールします。

 $ cd ./proot-termux-build
 $ ./build-proot --arch arm   

また、 link2symlink 機能を実装した proot のビルドに Android NDK を使用する場合は、以下のように ./build-proot.rb コマンドに、オプション --android-ndk-prefix と共に Android NDK のインストール先のディレクトリを渡します。これにより、 Android NDK stand alone toolchain の生成と一連の link2symlink 機能を実装した proot のビルドが自動的に行われます。

 $ cd ./proot-termux-build
 $ ./build-proot --arch arm --android-ndk-prefix ${ANDROID_NDK_HOME}    # (ここに、 ANDROID_NDK_HOME は Android NDK のインストール先のディレクトリを示す環境変数)

Ruby スクリプト ./build-proot.rb の詳細については、link2symlink 機能を実装した proot のビルドを自動化する Ruby スクリプトのリポジトリに同梱されている README.md を御覧下さい。

なお、 ARM 用及び x86 用のセルフコンパイル環境及びクロスコンパイル環境の構築が煩わしい若しくは困難な場合は、次に示す URL から Debian noroot 環境上に実行ファイルを下記のようにダウンロードして使用しても構いません。

 $ wget -O ./proot.arm https://git.io/proot-5.1.0.109.arm       # (ARM 用の実行ファイルのダウンロードの場合)
 $ wget -O ./proot.x86-32 https://git.io/proot-5.1.0.109.x32    # (32 ビット x86 用の実行ファイルのダウンロードの場合)

ここで、上記の URL から取得できる proot の実行形式のファイルは、何れも Android NDK stand alone toolchain によるクロスコンパイル環境を用いてビルドされたものです。

また、上記の URL から取得した proot の実行形式のファイルにおいて、動作に問題が発生する場合は、 Debian パッケージによるクロスコンパイル環境を用いてビルドされた proot の実行形式のファイルを下記の URL から取得して実行を試みて下さい。

proot の導入

本章では、前章でビルドした link2symlink 機能を実装した prootDebian noroot 環境に導入する手法について述べます。

先ず最初に、下記のようにして proot の実行ファイルをルートディレクトリにインストールします。この時、 proot の実行ファイルの名前は /proot と異なる名前にする必要が有ります。

 $ install -v -m 0700 ./proot /proot.link2symlink
 $ install -v -m 0700 ./proot.arm /proot.link2symlink   # (proot の実行ファイルを proot のビルド自動化 Makefile によってビルドしたか、若しくは https://git.io/proot* からダウンロードした場合)

ここで、ルートディレクトリ上の実行ファイル /proot.link2symlink の実行権限がユーザ権限に対して与えられていることと、 /proot.link2symlink --help コマンドが正常に動作することを確認します。

  $ ls -l /proot.link2symlink
  -rwx------. 1 u0_a185 u0_a185 525464 Dec  7 15:02 /proot.link2symlink
  $ /proot.link2symlink --help
  proot v0.1-5-gd06009d1-dirty: chroot, mount --bind, and binfmt_misc without privilege/setup.

  Usage:
    proot [option] ... [command]
  ...
    --link2symlink
        Replace hard links with symlinks, pretending they are really hardlinks

        Emulates hard links with symbolic links when SELinux policies
        do not allow hard links.
  ...
  Visit http://proot.me for help, bug reports, suggestions, patchs, ...
  Copyright (C) 2015 STMicroelectronics, licensed under GPL v2 or later.
  $

次に、Debian noroot 環境の為の chroot 環境の構築を行うシェルスクリプトである /proot.sh を修正します。ここで、修正を行う前に、 /proot.sh のバックアップを下記のように取っておきます。

もし link2symlink 機能を実装した proot の導入に問題が生じた場合は、バックアップファイルを用いて Debian noroot 環境を起動する必要が有ります。障害復旧用の環境に関しての詳細は "Debian noroot 環境に障害復旧用の環境を導入する" の投稿を参照して下さい。

 $ cd /
 $ cp -p ./proot.sh ./proot-safe.sh
 $ chmod u+x ./proot-safe.sh

そして、シェルスクリプト /proot.sh を下記のように修正します。即ち、 /proot.sh の最後の行の ./proot を起動している箇所を link2symlink 機能を実装した proot である ./proot.link2symlink に修正し、オプション --link2symlink を追加します。

/proot.sh
#!/system/bin/sh
...
export HOME=/home/$USER
export SHELL=/bin/bash
export LD_LIBRARY_PATH=
...
export TZ="`getprop persist.sys.timezone`"
# ./proot -r `pwd` -w / -b /dev -b /proc -b /sys -b /system $STORAGE "$@" # proot を実行している行を以下のように修正する。
./proot.link2symlink --link2symlink -r `pwd` -w / -b /dev -b /proc -b /sys -b /system $STORAGE "$@"

シェルスクリプト /proot.sh を修正した後は、 Debian noroot 環境を再起動します。

以上で、 Debian noroot 環境への link2symlink 機能を実装した proot の導入が完了しました。

proot の動作確認

本章では、 Debian noroot 環境へ導入した link2symlink 機能を実装した proot について、正常にシステムコール link(2)symlink(2) によって代替され、 link(2) の機能をエミュレートされていることを確認する一連の過程について述べます。

まず最初に、 link2symlink 機能を実装した proot に基づく Debian noroot 環境において、下記のようにテスト用の簡潔なテキストファイル foo を作成します。

 $ mkdir ./test
 $ cd ./test
 $ echo aaaa > foo
 $ ls -l
 合計 4
 -rw-------. 1 u0_a135 u0_a135 5  6月 12 14:35 foo

次に、テキストファイル foo に向けてシンボリックリンク foo1 を作成します。

 $ ln -sf foo foo1
 $ ls -l
 合計 4
 -rw-------. 1 u0_a135 u0_a135 5  6月 12 14:35 foo
 lrwxrwxrwx. 1 u0_a135 u0_a135 3  6月 12 14:37 foo1 -> foo

続いて、テキストファイル foo に向けてハードリンク foo2 を作成します。 ls -li コマンドによって各ファイル及びリンクを見ると、ハードリンク foo2 はシンボリックリンク foo1 と異なり、ファイル foo と inode 番号を共有し、権限やタイムスタンプ等も同じ物を共有していることが判ります。

 $ ln foo baz
 $ ls -l
 合計 8
 -rw-------. 2 u0_a135 u0_a135 5  6月 12 14:35 foo
 lrwxrwxrwx. 1 u0_a135 u0_a135 3  6月 12 14:37 foo1 -> foo
 -rw-------. 2 u0_a135 u0_a135 5  6月 12 14:35 foo2
 $ ls -li
 合計 8
 1745266 -rw-------. 2 u0_a135 u0_a135 5  6月 12 14:35 foo
 1745267 lrwxrwxrwx. 1 u0_a135 u0_a135 3  6月 12 14:37 foo1 -> foo
 1745266 -rw-------. 2 u0_a135 u0_a135 5  6月 12 14:35 foo2
 $ cat foo foo1 foo2
 aaaa
 aaaa
 aaaa

そして、もう一度、テキストファイル foo に向けてハードリンク foo2 を作成すると、下記のようになります。

 $ ln foo foo3
 $ ls -li
 合計 12
 1745266 -rw-------. 3 u0_a135 u0_a135 5  6月 12 14:35 foo
 1745267 lrwxrwxrwx. 1 u0_a135 u0_a135 3  6月 12 14:37 foo1 -> foo
 1745266 -rw-------. 3 u0_a135 u0_a135 5  6月 12 14:35 foo2
 1745266 -rw-------. 3 u0_a135 u0_a135 5  6月 12 14:35 foo3

ここで、 Debian noroot 環境上のスクリプトファイル /proot.sh を元の状態に戻し、 link2symlink 機能を無効にして再起動した後、再度ファイル及びリンク foo, foo1, foo2, foo3 をコマンド ls -li 等によって表示させると下記の通りになることが判ります。

 $ ls -ali
 合計 28
 drwx------.  2 u0_a135 u0_a135 4096  6月 12 14:52 .
 drwx------. 54 u0_a135 u0_a135 4096  6月 12 14:02 ..
 lrwxrwxrwx.  1 u0_a135 u0_a135  116  6月 12 14:46 .l2s.foo0001 -> /home/u0_a135/test/.l2s.foo0001.0003
 -rw-------.  1 u0_a135 u0_a135    5  6月 12 14:35 .l2s.foo0001.0003
 lrwxrwxrwx.  1 u0_a135 u0_a135  111  6月 12 14:40 foo -> /home/u0_a135/test/.l2s.foo0001
 lrwxrwxrwx.  1 u0_a135 u0_a135    3  6月 12 14:37 foo1 -> foo
 lrwxrwxrwx.  1 u0_a135 u0_a135  111  6月 12 14:40 foo2 -> /home/u0_a135/test/.l2s.foo0001
 lrwxrwxrwx.  1 u0_a135 u0_a135  111  6月 12 14:46 foo3 -> /home/u0_a135/test/.l2s.foo0001

これより、 link2symlink 機能を実装した proot のプロセスの子プロセスよりシステムコール link(2) が実行されると、 proot によってこれがフックされ、以下の処理が行われることが判ります。

  1. 1回目に、ハードリンク元となるファイルからハードリンク先となるファイルにハードリンクを張る時、以下の処理が行われる。
    • ハードリンク元となるファイルの実体のファイル名が .l2s.*.0002 で始まるファイル名に変更される。ここに、ファイル名の末尾の .0002 は、ファイルの i-node を共有するリンクの個数を示す。
    • ハードリンク元となるファイルの実体のファイル .l2s.*.0002 に向けて、中間的なシンボリックリンク .l2s.* を張る。
    • 中間的なシンボリックリンク .l2s.* に向けて、ハードリンク元及びハードリンク先となるファイル名にシンボリックリンクを張る。
  2. 2回目以降に、ハードリンク元となるファイルからハードリンク先となるファイルにハードリンクを張る時、以下の処理が行われる。
    • ハードリンク元となるファイルの実体のファイル名である .l2s.*.0002 の末尾の .0002 の数字を .0003 にインクリメントする。 (ハードリンク元となるファイルの実体のファイル名が、 l2s.*.0003 以降も同様。)
    • ハードリンク元となるファイルの実体のファイル .l2s.*.0003 に向けて、中間的なシンボリックリンク .l2s.* を張り直す。
    • 中間的なシンボリックリンク .l2s.* に向けて、ハードリンク先となるファイル名にシンボリックリンクを張る。
  3. proot による chroot 環境下においては、 1. 及び 2. で示された過程で作成されたシンボリックリンクが擬似的にハードリンクとして扱われる。
  4. 但し、 link2symlink 機能を実装した proot の起動時に環境変数 PROOT_L2S_DIR が設定されている場合には、 .l2s. で始まるファイル及びシンボリックリンクは、全て PROOT_L2S_DIR で示されるディレクトリの下に置かれる。

即ち、 link2symlink 機能を実装した proot を用いた Debian noroot 環境において、システムコール link(2) の実行は問題無く完全に symlink(2) によって代替されることが判りました。

最後に、/proot.sh を再度修正し、 proot の link2symlink 機能を再度有効にして Debian noroot 環境を再起動した後、 apt-get install hello コマンドを実行して、 hello パッケージのインストールが問題なく実行されるか確認します。

  $ sudo apt-get install hello
  Reading package lists... Done
  Building dependency tree       
  Reading state information... Done
  ...
  Preparing to unpack .../hello_2.9-2+deb8u1_armhf.deb ...
  Unpacking hello (2.9-2+deb8u1) ...
  Processing triggers for man-db (2.7.0.2-5) ...
  Setting up hello (2.9-2+deb8u1) ...
  $

以上の動作確認が問題なく実行されることが確認されれば、 link2symlink 機能を実装した proot が正常に動作することが判ります。

結論

本稿では、"link2symlink 機能を実装した proot のビルド" の章及び "proot の導入" の章において、 Debian noroot 環境において、 Termux の開発コミュニティによる link2symlink 機能を実装した proot のビルド及び導入に関する一連の過程について述べました。

そして、"proot の動作確認" の章では、 link2symlink 機能を実装した proot を用いた Debian noroot 環境において、システムコール link(2) が正常に symlink(2) によってエミュレートされることを示し、コマンド apt-get install hello の実行によって、 apt-get によるパッケージのインストールが正常に行われることが確認されました。

本稿にて行った link2symlink 機能を実装した proot の導入により、 Android OS 6.0 以降の Android OS 上において、 Debian noroot 環境を起動すると、 apt-get 等の Debian 環境のパッケージ管理コマンド及び git コマンドが正常に動作し、 Debian noroot 環境への各種パッケージの導入に支障を来す問題等が回避されることが示されました。

謝辞

最初に、本稿の記述に当たって、 Android OS 端末上で非常に軽快な Debian 環境を実現することを可能にした Debian noroot 環境の開発者である pelya 氏に心より感謝致します。

次に、 "クロスコンパイル環境の構築" の節で述べた ARM アーキテクチャのクロスコンパイル環境の構築の詳細に関しては、下記の記事を参考にしました。下記の記事の筆者の皆様に心より感謝致します。

また、 "talloc 2.1.14 のビルド" の節で述べた talloc 2.1.14 のクロスコンパイルに関する設定に関しては、 Termux のプロジェクトにおける libtalloc のインストール用スクリプトを参考にしました。

ここで、 Debian noroot 環境の実現の中心を担う proot を開発した STMicroelectronics 社及び proot に link2symlink の機能を実装した Termux の開発コミュニティの皆様に心より感謝致します。

次に、 link2symlink の機能を実装した proot の各種不具合の修正については、 GNURoot の開発コミュニティによる proot のソースコード及び、その中の Dieter Mueller 氏による特定のプレフィックスを持つファイルを不可視化するコードである hidden_files.c を参考にしました。 Dieter Mueller 氏及び GNURoot Debian の開発コミュニティの各氏に心より感謝致します。

そして、 Android OS 6.0 が動作する実機による link2symlink 機能を実装した proot の動作確認に関しては、 Uhucream 氏による多くの協力を得ました。 Uhucream 氏に心より感謝致します。

最後に、 Debian noroot 環境Android OS 及び Debian 環境の全ての事に関わる全ての皆様に心より感謝致します。

追記及びお断り

2018/01/15 現在の追記

link2symlink 機能を実装した proot に関して、 Android OS 6.0 及び Android OS 7.0 が動作する実機による正常な動作が確認されたことに伴い、本稿の大幅な改稿を行いました。

"結論" の章において述べた通り、実機における動作確認について多大なる協力を頂いた Uhucream 氏に心より感謝致します。

2018/03/16 現在の追記

link2symlink 機能を実装した proot にビルドに関して、 Android NDK によるクロスコンパイル環境の構築について、本稿に追記しました。

また、 link2symlink 機能を実装した proot のビルドを自動化する Makefile のリポジトリにおいて、 Makefile を完全に Ruby スクリプトに書き換えると共に、 talloc 及び proot を最新のバージョンに更新したことに伴い、本稿を改稿致しました。どうか御了承下さい。

2018/04/02 現在の追記

複数の誤字等の訂正を行いました。どうか御了承下さい。

2018/06/05 現在の追記

link2symlink 機能を実装した proot の安定版を最新版に更新した事と、 link2symlink 機能を実装した proot において、 Debian noroot 環境における外部ストレージ領域及び VFAT 領域等の、シンボリックリンクに対応していないファイルシステムの領域において、システムコール link(2) を実行した場合に問題が発生する不具合を修正した事に伴い、本稿を改稿致しました。

どうか御了承下さい。

2018/06/12 現在の追記

link2symlink 機能を実装した proot の安定版を最新版に更新した事と、 prootlink2symlink 機能の動作原理に変更が生じた事に伴い、本稿の改稿を行いました。どうか御了承下さい。

2018/06/30 現在の追記

link2symlink 機能を実装した proot の最新の安定版のリンクに誤りが有りましたので、ここに御詫びして訂正致します。この度は、誠に恐れ入ります。

また、 link2symlink 機能を実装した proot の安定版において、一部端末での動作の不具合が発見されたことに伴い、 Debian パッケージによるクロスコンパイルを用いてビルドした proot の実行形式ファイルを併せて公開致しました。

上記の件に関しまして問題の御報告を頂きました、おやすみ様には、誠にお手数をお掛けしますと共に、心より感謝致します。

2018/07/05 現在の追記

link2symlink 機能を実装した proot の各種不具合を修正し、安定版を最新版に更新した事に伴い、本稿の改稿を行いました。

"謝辞" の章において前述した通り、 link2symlink 機能を実装した proot の各種不具合に修正にあたっては、 GNURoot Debian の開発コミュニティによる proot のソースコードを参考にしました。参考にしたコードの作者である Dieter Mueller 氏を始め、 GNURoot Debian の開発コミュニティの各氏には心より感謝致します。

また、 "proot のビルド" の節でも前述した通り、 link2symlink 機能を実装した proot の最新の安定版では、 link2symlink 機能に関する各種問題を修正していますので、 link2symlink 機能を実装した proot の実行形式ファイルは、最新の安定版を使用することを強く御勧めします。

2018/07/26 現在の追記

proot 及び talloc のソースコードを最新版に更新したことにより、 link2symlink 機能を実装した proot の安定版を最新版に更新した事に伴い、本稿の改稿を行いました。

2018/08/19 現在の追記

proot のソースコードを最新版に更新したことと、 proot のソースコードのコンパイル時に発生する問題の修正を行ったことにより、 link2symlink 機能を実装した proot の安定版を最新版に更新した事に伴い、本稿の改稿を行いました。

2018/09/25 現在の追記

proot のソースコードの最新版への更新と、 proot のソースコードへの各種修正を行ったことにより、 link2symlink 機能を実装した proot の安定版を最新版 v5.1.0.109 に更新した事に伴い、本稿の改稿を行いました。