Купил недавно себе планшет Samsung Galaxy Tab Pro 10.1 LTE SM-T525 и захотелось дома смотреть на нём фильмы и другие файлы с моего сетевого диска, расшаренного через samba. Для этого в ядре должна быть поддержка CIFS и не только… в андроиде начиная с версии 4.2 это оказалось нетривиальной задачей.
1. Суть проблемы с пользовательскими монтированиями
Дело в том, что с версии 4.2 в Adnroid ввели многопользовательский режим и вместе с ним все файловые системы теперь монтируются как Slave, это приводит к тому, что файловая система смонтированная одним пользователем, не видна другим пользователям.
В виртуальной машине Dalvik андроида с версии 4.2 в файле vm/Init.cpp есть такой код:
Этот код монтирует рутовую файловую систему с флагами MS_SLAVE и MS_REC, это означает что файловые системы, смонтированные внутри / будут также монтироваться по умолчанию с флагом MS_SLAVE и доступны только пользователям их смонтировавшим и их дочерним процессам. Для решения этой проблемы, пользователем mkasick с xda-developers был предложен следующий патч для ядра в функции do_mount:
Суть патча в том, что мы перехватываем вызов do_mount для / с флагами MS_SVALE и MS_REC и не даём их установить. Но для работы андроида эти флаги необходимы для пользовательских файловых систем в /storage, иначе система у меня просто не грузится. Поэтому мы вызываем do_mount(NULL, (char *)storage, NULL, (MS_SLAVE | MS_REC), NULL)) == 0). Чтобы последний вызов работал, /storage должна быть точкой монтирования, а не просто директорией, поэтому в файле init.rc в рамдиске, где происходит создание директории /storage необходимо туда смонтировать tmpfs
После этих манипуляций, точки монтирования за пределами /storage будут видны всем пользователям.
2. Сборка ядра
Для начала необходимо добавить поддержку CIFS в Linux ядро. Это можно сделать двумя способами: скомпилировать модуль для ядра cifs, либо добавить поддержку cifs в само ядро. Первый способ лучше, т.к. не надо будет прошивать новое ядро и терять гарантию на некоторых аппаратах.
Ядро моего планшета оказалось собрано без поддержки модулей, так что мне пришлось пересобирать и прошивать новое ядро:
Для сборки ядра необходимо:
а) Машина на Linux
б) Исходники ядра для вашего девайса, для Самсунга берём здесь opensource.samsung.com
в) Toolchain для сборки, берём тут developer.android.com/tools/sdk/ndk/index.html
Для сборки ядра создаём директорию ~/android, и распаковываем внутри неё исходники ядра в директорию kernel, android-ndk в android-ndk-r9
Теперь применяем патч для исправления проблемы с пустыми точками монтирования
Далее необходимо прописать переменные окружения и подготовить конфигурационный файл ядра, для моего самсунга это делается так:
Данная команда создаст файл .config, далее необходимо выполнить команду make menuconfig и перейти в раздел File Systems -> Network File Systems и отметить пункт CIFS support (в случае сборки модуля, необходимо отметить буквой M) и отмечаем RESTRICT_ROOTFS_SLAVE в разделе File Systems
Далее выходим клавишей Esc и сохраняем конфигурацию. Теперь собираем ядро командой make -j3 (вместо цифры 3 указываем количество ядер процессора+1)
На этом сборка ядра окончена.
3. Сборка boot.img для прошивки
Теперь необходимо прошить новое ядро в планшет. Для этого необходимо собрать образ boot.img в который входит ядро, ramdisk и device tree для чипов Qualcomm.
Для начала нам надо получить родной ramdisk, для этого распаковываем оригинальную прошивку (это обычный tar) и достаём оттуда файл boot.img
Для распаковки boot.img понадобится пакет bootimg_tools
Скачиваем его в ~/android, и распаковываем командой split_boot файл boot.img
и получаем распакованный ramdisk в директории boot/ramdisk
Теперь необходимо поправить пару файлов в рамдиске для перевода Selinux в permissive mode и для монтирования /storage как tmpfs
В файле init.rc ищем строчку setsebool debugfs 1 и заменяем её на
Далее ищем строку setprop selinux.reload_policy 1 и меняем её на setprop selinux.reload_policy 0
Для монтирования /storage как tmpfs ищем строчку mkdir /storage 0751 root sdcard_r и после неё добавляем
Также в файле defaul.prop меняем ro.secure=1 на ro.secure=0 и ro.adb.secure=1 на ro.adb.secure=0
Теперь собираем ramdisk назад
Далее для сборки нам ещё необходим файл dt.img, он генерируется утилитой dtbTool которая идёт в комплекте с ядром
Теперь, когда есть всё необходимое для сборки boot.img, начинаем саму сборку при помощи утилиты mkbootimg (поставляется так же с ядром)
Всё, файл boot.img готов. Для прошивки его через Odin, делаем из него boot.tar.md5
Теперь прошиваем его через Odin
На Samsung есть проблема, что с катомным ядром перестаёт работать wifi, лечится это правкой файла build.prop на рабочей системе. Необходимо заменить строчку ro.securestorage.support=true на ro.securestorage.support=false
4. Настройка ПО для монтирования
Для монтирования шар будем использовать программу CifsManager
Тут есть несколько нюансов в настройке:
а) В cifs на ядре 3.4 что-то поломали, и теперь он не обрабатывает параметр device команды mount, т.е. конструкция вида
б) Теперь про точку монтирования. Как писалось выше, если мы будем монтировать внутри /storage, то такая шара будет пустая для других приложений, если же монтировать куда-то вне /storage, то большинство приложений её не смогут открыть, т.к. позволяют открывать файлы только внутри карты памяти. Выход есть. Внутренняя память /storage/emulated/0 на самом деле лежит в /data/media/0, а /storage/emulated/0 — это эмуляция fat32 для этой директории. Так что мы смело можем можем монтировать например в /data/media/0/cifs и увидим нашу шару в фйловом менеджере в internal storage.
в) Есть ещё нигде не описанный нюанс, команда mount -t cifs почему то игнорирует параметры, идущие после password, например в команде
параметр sec=ntlmssp не будет обработан. Поэтому, если для подключения к шаре необходим пароль, то в CifsManager поле Password оставляем пустым, а пароль пишем в поле Options в самый конец, как на скриншоте выше.
г) Для корректной работы с русскими буквами в именах файлов, необходимо добавить опцию iocharset=utf8
Теперь монтируем шару в CifsManager и наслаждаемся!
Использовались материалы из следующих источников:
www.ibm.com/developerworks/ru/library/l-mount-namespaces
forum.xda-developers.com/showthread.php?p=36889027
www.netzgewitter.com/2013/10/troubleshoot-cifs-on-android
1. Суть проблемы с пользовательскими монтированиями
Дело в том, что с версии 4.2 в Adnroid ввели многопользовательский режим и вместе с ним все файловые системы теперь монтируются как Slave, это приводит к тому, что файловая система смонтированная одним пользователем, не видна другим пользователям.
В виртуальной машине Dalvik андроида с версии 4.2 в файле vm/Init.cpp есть такой код:
if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
return -1;
}
Этот код монтирует рутовую файловую систему с флагами MS_SLAVE и MS_REC, это означает что файловые системы, смонтированные внутри / будут также монтироваться по умолчанию с флагом MS_SLAVE и доступны только пользователям их смонтировавшим и их дочерним процессам. Для решения этой проблемы, пользователем mkasick с xda-developers был предложен следующий патч для ядра в функции do_mount:
--- kernel_orig/fs/namespace.c 2014-01-23 15:34:18.000000000 +1100
+++ kernel/fs/namespace.c 2014-04-11 15:18:22.258114000 +1100
@@ -2141,6 +2141,21 @@
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
+#ifdef CONFIG_RESTRICT_ROOTFS_SLAVE
+ /* Check if this is an attempt to mark "/" as recursive-slave. */
+ if (strcmp(dir_name, "/") == 0 && flags == (MS_SLAVE | MS_REC)) {
+ static const char storage[] = "/storage";
+ long res;
+ /* Mark /storage as recursive-slave instead. */
+ if ((res = do_mount(NULL, (char *)storage, NULL, (MS_SLAVE | MS_REC), NULL)) == 0) {
+ return 0;
+ } else {
+ pr_warn("Failed to mount %s as MS_SLAVE: %ld\n", storage, res);
+ }
+ /* Fallback: Mark rootfs as recursive-slave as requested. */
+ }
+#endif
+
/* ... and get the mountpoint */
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
if (retval)
Суть патча в том, что мы перехватываем вызов do_mount для / с флагами MS_SVALE и MS_REC и не даём их установить. Но для работы андроида эти флаги необходимы для пользовательских файловых систем в /storage, иначе система у меня просто не грузится. Поэтому мы вызываем do_mount(NULL, (char *)storage, NULL, (MS_SLAVE | MS_REC), NULL)) == 0). Чтобы последний вызов работал, /storage должна быть точкой монтирования, а не просто директорией, поэтому в файле init.rc в рамдиске, где происходит создание директории /storage необходимо туда смонтировать tmpfs
mkdir /storage 0050 root sdcard_r
mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028
После этих манипуляций, точки монтирования за пределами /storage будут видны всем пользователям.
2. Сборка ядра
Для начала необходимо добавить поддержку CIFS в Linux ядро. Это можно сделать двумя способами: скомпилировать модуль для ядра cifs, либо добавить поддержку cifs в само ядро. Первый способ лучше, т.к. не надо будет прошивать новое ядро и терять гарантию на некоторых аппаратах.
Ядро моего планшета оказалось собрано без поддержки модулей, так что мне пришлось пересобирать и прошивать новое ядро:
Для сборки ядра необходимо:
а) Машина на Linux
б) Исходники ядра для вашего девайса, для Самсунга берём здесь opensource.samsung.com
в) Toolchain для сборки, берём тут developer.android.com/tools/sdk/ndk/index.html
Для сборки ядра создаём директорию ~/android, и распаковываем внутри неё исходники ядра в директорию kernel, android-ndk в android-ndk-r9
mkdir ~/android
cd ~/android
tar xjf android-ndk-r9d-linux-x86_64.tar.bz2
unzip SM-T525_SEA_KK_Opensource.zip
mkdir kernel
cd kernel
tar xzf ../Kernel.tar.gz
Теперь применяем патч для исправления проблемы с пустыми точками монтирования
shared_rootfs.patch
--- kernel_orig/fs/namespace.c 2014-01-23 15:34:18.000000000 +1100
+++ kernel/fs/namespace.c 2014-04-11 15:18:22.258114000 +1100
@@ -2141,6 +2141,21 @@
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
+#ifdef CONFIG_RESTRICT_ROOTFS_SLAVE
+ /* Check if this is an attempt to mark "/" as recursive-slave. */
+ if (strcmp(dir_name, "/") == 0 && flags == (MS_SLAVE | MS_REC)) {
+ static const char storage[] = "/storage";
+ long res;
+ /* Mark /storage as recursive-slave instead. */
+ if ((res = do_mount(NULL, (char *)storage, NULL, (MS_SLAVE | MS_REC), NULL)) == 0) {
+ return 0;
+ } else {
+ pr_warn("Failed to mount %s as MS_SLAVE: %ld\n", storage, res);
+ }
+ /* Fallback: Mark rootfs as recursive-slave as requested. */
+ }
+#endif
+
/* ... and get the mountpoint */
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
if (retval)
--- kernel_orig/fs/Kconfig 2014-01-23 15:34:17.000000000 +1100
+++ kernel/fs/Kconfig 2014-04-10 19:29:30.990114000 +1100
@@ -292,4 +292,29 @@
source "fs/nls/Kconfig"
source "fs/dlm/Kconfig"
+config RESTRICT_ROOTFS_SLAVE
+ bool "Android: Restrict rootfs slave mountspace to /storage"
+ help
+ Restrict slave mountspace so Dalvik apps can mount system-wide volumes
+
+ Android 4.2 implements multi-user storage using per-process mount
+ namespaces. Everything under "/" (the entire filesystem hierarchy) is
+ marked as a recursive-slave mountspace for all zygote instances. This is
+ done so that user-storage sandbox mounts under /storage/emulated are hidden
+ from other apps and users. Unfortunately this means that any Dalvik app
+ (actually, any program whose clone/fork ancestry includes a Dalvik zygote,
+ which is everything except services spawned directly from init) cannot
+ mount system-wide volumes.
+
+ This option restricts rootfs-slave calls to /storage (and
+ /mnt/shell/emulated) so that Dalvik apps can mount system-wide volumes
+ elsewhere (with appropriate permission, as in earlier versions of Android),
+ while retaining full multi-user storage compatibility. It is made
+ available as a kernel-based workaround for instances where libdvm can not
+ be modified.
+
+ This option requires that a tmpfs volume is mounted as /storage in
+ Android init.rc. If this volume is unavailable, then we fall back to the
+ previous behavior of marking the entire filesystem hierarchy as slave.
+
endmenu
patch -p1 < shared_rootfs.patch
Далее необходимо прописать переменные окружения и подготовить конфигурационный файл ядра, для моего самсунга это делается так:
export CROSS_COMPILE="~/android/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-"
export ARCH="arm"
make mrproper
make VARIANT_DEFCONFIG=msm8974_sec_picassoeur_defconfig msm8974_sec_defconfig SELINUX_DEFCONFIG=selinux_defconfig
Данная команда создаст файл .config, далее необходимо выполнить команду make menuconfig и перейти в раздел File Systems -> Network File Systems и отметить пункт CIFS support (в случае сборки модуля, необходимо отметить буквой M) и отмечаем RESTRICT_ROOTFS_SLAVE в разделе File Systems
Далее выходим клавишей Esc и сохраняем конфигурацию. Теперь собираем ядро командой make -j3 (вместо цифры 3 указываем количество ядер процессора+1)
На этом сборка ядра окончена.
3. Сборка boot.img для прошивки
Теперь необходимо прошить новое ядро в планшет. Для этого необходимо собрать образ boot.img в который входит ядро, ramdisk и device tree для чипов Qualcomm.
Для начала нам надо получить родной ramdisk, для этого распаковываем оригинальную прошивку (это обычный tar) и достаём оттуда файл boot.img
tar xf T525XXUANB2_T525SERANA6_T525XXUANA7_HOME.tar.md5 boot.img
Для распаковки boot.img понадобится пакет bootimg_tools
Скачиваем его в ~/android, и распаковываем командой split_boot файл boot.img
mkdir ~/adndroid/bootimg_tools
cd ~/adnroid/bootimg_tools
unzip ../bootimg_tools_7.8.13.zip
./split_boot ../boot.img
и получаем распакованный ramdisk в директории boot/ramdisk
Теперь необходимо поправить пару файлов в рамдиске для перевода Selinux в permissive mode и для монтирования /storage как tmpfs
В файле init.rc ищем строчку setsebool debugfs 1 и заменяем её на
setsebool debugfs 0
setenforce 0
Далее ищем строку setprop selinux.reload_policy 1 и меняем её на setprop selinux.reload_policy 0
Для монтирования /storage как tmpfs ищем строчку mkdir /storage 0751 root sdcard_r и после неё добавляем
mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028
Также в файле defaul.prop меняем ro.secure=1 на ro.secure=0 и ro.adb.secure=1 на ro.adb.secure=0
Теперь собираем ramdisk назад
./repack_ramdisk boot/ramdisk ramdisk.cpio.gz
cp boot/ramdisk.cpio.gz ~/android/kernel
Далее для сборки нам ещё необходим файл dt.img, он генерируется утилитой dtbTool которая идёт в комплекте с ядром
cd ~/android/kernel
./tools/dtbTool -o dt.img -s 2048 -p ./scripts/dtc/ ./arch/arm/boot/
Теперь, когда есть всё необходимое для сборки boot.img, начинаем саму сборку при помощи утилиты mkbootimg (поставляется так же с ядром)
./tools/mkbootimg --kernel ./arch/arm/boot/zImage --ramdisk ramdisk.cpio.gz --cmdline 'console=null androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3' --base 0x00000000 --pagesize 2048 --ramdisk_offset 0x02000000 --tags_offset 0x01E00000 --dt dt.img -o boot.img
Всё, файл boot.img готов. Для прошивки его через Odin, делаем из него boot.tar.md5
tar -H ustar -c boot.img > boot.tar
md5sum -t boot.tar >> boot.tar
mv boot.tar boot.tar.md5
Теперь прошиваем его через Odin
На Samsung есть проблема, что с катомным ядром перестаёт работать wifi, лечится это правкой файла build.prop на рабочей системе. Необходимо заменить строчку ro.securestorage.support=true на ro.securestorage.support=false
4. Настройка ПО для монтирования
Для монтирования шар будем использовать программу CifsManager
Тут есть несколько нюансов в настройке:
а) В cifs на ядре 3.4 что-то поломали, и теперь он не обрабатывает параметр device команды mount, т.е. конструкция вида
mount -t cifs //pc/share /mnt
больше не работает, монтируемую шару необходимо указывать в параметре unc, т.е. предыдущая команда будет выглядеть так:mount -t cifs -o unc=\\\\pc\\share none /mnt
б) Теперь про точку монтирования. Как писалось выше, если мы будем монтировать внутри /storage, то такая шара будет пустая для других приложений, если же монтировать куда-то вне /storage, то большинство приложений её не смогут открыть, т.к. позволяют открывать файлы только внутри карты памяти. Выход есть. Внутренняя память /storage/emulated/0 на самом деле лежит в /data/media/0, а /storage/emulated/0 — это эмуляция fat32 для этой директории. Так что мы смело можем можем монтировать например в /data/media/0/cifs и увидим нашу шару в фйловом менеджере в internal storage.
в) Есть ещё нигде не описанный нюанс, команда mount -t cifs почему то игнорирует параметры, идущие после password, например в команде
mount -t cifs -o unc=\\\\pc\\share,username=user,password=123,sec=ntlmssp none /data/media/0/cifs
параметр sec=ntlmssp не будет обработан. Поэтому, если для подключения к шаре необходим пароль, то в CifsManager поле Password оставляем пустым, а пароль пишем в поле Options в самый конец, как на скриншоте выше.
г) Для корректной работы с русскими буквами в именах файлов, необходимо добавить опцию iocharset=utf8
Теперь монтируем шару в CifsManager и наслаждаемся!
Использовались материалы из следующих источников:
www.ibm.com/developerworks/ru/library/l-mount-namespaces
forum.xda-developers.com/showthread.php?p=36889027
www.netzgewitter.com/2013/10/troubleshoot-cifs-on-android