2016/10/29

Android エミュレーターで Read-only file system に書き込めるようにする

エミュレーター、もしくは root をとったデバイスでも、Read-only file system としてマウントされているディレクトリは、書き込み・編集をすることが出来ません。

しかし、以下のように書き込み権限をつけてリマウントすれば、編集可能になります。

### マウント状態の確認
`adb shell` でエミュレーターにログインし、以下のようにマウント状態を確認。
`ro` がついているのが Read-only file system です。

```console
`highlight: [3, 14]; gutter: false;
$ adb shell
# mount
rootfs / rootfs ro,relatime 0 0
tmpfs /dev tmpfs rw,nosuid,relatime,mode=755 0 0
devpts /dev/pts devpts rw,relatime,mode=600 0 0
proc /proc proc rw,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
none /acct cgroup rw,relatime,cpuacct 0 0
none /sys/fs/cgroup tmpfs rw,relatime,mode=750,gid=1000 0 0
tmpfs /mnt/asec tmpfs rw,relatime,mode=755,gid=1000 0 0
tmpfs /mnt/obb tmpfs rw,relatime,mode=755,gid=1000 0 0
none /dev/cpuctl cgroup rw,relatime,cpu 0 0
/dev/block/vda /system ext4 ro,relatime,data=ordered 0 0
/dev/block/vdb /cache ext4 rw,nosuid,nodev,noatime,errors=panic,data=ordered 0 0
/dev/block/vdc /data ext4 rw,nosuid,nodev,noatime,errors=panic,data=ordered 0 0
/dev/block/vold/253:48 /mnt/media_rw/sdcard vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1023,gid=1023,fmask=0007,dmask=0007,allow_utime=0020,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
/dev/block/vold/253:48 /mnt/secure/asec vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1023,gid=1023,fmask=0007,dmask=0007,allow_utime=0020,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
/dev/fuse /storage/sdcard fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0
```



### 書き込み権限をつけてリマウント
以下のようにリマウントすると、書き込み・編集が可能になります。

```console
`gutter: false;
# mount -o rw,remount /system
```

### エミュレーターはディスクイメージを共有しているので注意
`~/.android/avd/AVD_NAME/hardware-qemu.ini` の中に以下のような項目があります。
```
disk.systemPartition.initPath = ANDROID-SDK_DIR/system-images/android-21/default/x86_64//system.img
```

パスを確認すると、img ファイルは Android のバージョンとアーキテクチャ毎に共通です。
つまり、同じ `system.img` を複数の AVD で共有しているため、一つに変更を加えると、他のエミュレータ全てに影響が及んでしまいます。

上記の例だと、Android version 21 の x86_64 アーキを使っている AVD 全てに影響が及ぶということです。


### rootfs は揮発性
また、上記の方法を rootfs (/) に行う場合も注意が必要です。
以下のように `/` をリマウントすると、一見成功するように見えるのですが、エミュレーターを再起動すると加えた変更が取り消されてしまいます。

```console
`gutter: false;
# mount -o rw,remount /
# mkdir /test
```

このケースだと、`/test` ディレクトリは作成されて使用可能ですが、エミュレーターの再起動後には消えてしまうということです。

これは、`/` が [Initramfs](https://ja.wikipedia.org/w/index.php?title=Initramfs&redirect=no) という RAM 上に展開されるファイルシステムのためです。

RAM 上のファイルに変更を加えることは出来ても、実体具体的にはgzipで圧縮されたcpioアーカイブイメージには変更が反映されないので、再起動すると消えてしまうというわけです。

> 参考
> 
> [第384回 Initramfsのしくみ:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社](http://gihyo.jp/admin/serial/01/ubuntu-recipe/0384)

Initiramfs を再起動後も維持したい場合、
実機の場合は、OSをビルドし直すしか方法はありませんが多分。裏ワザ的な方法はあるかも?、
エミュレータの場合は ramdisk.img を編集することで、比較的容易に実現することができます。

> 追記
>
> [穀風: Android エミュレーターの rootfs に永続的な編集を加える方法](http://kokufu.blogspot.jp/2016/10/android-rootfs.html)

0 件のコメント: