使用不同的檔案系統,B2G主程式沒跑起來!
前陣子收到一隻手機,需要把 Firefox OS porting 上去。剛好之前有看過這幾篇文章 B2G Porting, 三步驟 build 好 Firefox OS及透過 AOSP build system 來 build Firefox OS,不然還真不知道從那裡開始。先費了一翻功夫把 manifest 弄出來,再經過一陣苦戰之後 system.img 總算也編出來了。既然 system.img 都出來了,當然要鼓起勇氣把它 flash 到手機裡看看。其實早就有心理準備,怎麼可能一次就成功呢?只是還不知道會碰到什麼樣的問題。果然 flash 進手機後,開機畫面一片黑,發生了什麼事了呢?
先用 adb shell 進去看看,結果發現 b2g 主程式根本就沒有在執行。試著用手動去執行看看也失敗,錯誤信息是 Permission Denied。有使用 linux 經驗的朋友,這時應該都會做這件事情,檢查看看相關執檔的屬性。結果發現執行檔的屬性都是 0644。
$ adb shell ls -l /system/b2g/ -rw-r--r-- root root 116116 2014-03-13 03:09 b2g -rw-r--r-- root root 5296 2014-03-12 02:22 plugin-container -rw-r--r-- root root 82380 2014-03-12 02:22 updater
暫時先不管為什麼會變成 0644,把 /system 重新 mount 成 rw,把執行檔全部改成 0755,再手動執行一次,真的有畫面跑出來了。
雖然重新開機後也可以跑起來了,但每次 flash system.img 後,檔案屬性就會跑掉,那還真不方便。這時我突然想到曾經看到過一個 patch, 但在每次開機時 init.rc 都需要去修改檔案屬性好像也不是一個好方法。那就來弄清楚問題出在那裡吧!
--- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -98,6 +98,9 @@ on fs # Mount /system rw first to give the filesystem a chance to save a checkpoint mount yaffs2 mtd@system /system + chmod 0755 /system/b2g/b2g + chmod 0755 /system/b2g/plugin-container + chmod 0755 /system/b2g/updater mount yaffs2 mtd@system /system ro remount
首先來檢查看看編出來的檔案,發現編出來的執行檔屬性都是正確的。
$ ls -l system/b2g/ -rwxr-xr-x root root 116116 2014-03-13 03:09 b2g -rwxr-xr-x root root 5296 2014-03-12 02:22 plugin-container -rwxr-xr-x root root 82380 2014-03-12 02:22 updater
那為什包成 system.img 燒進手機後屬性就變了呢?
在編 system.img 時,相關 build tools 在把一個資料夾包到一個 .img 檔時,可以對檔案的屬性進行調整。我有把 b2g 相關執行檔的屬性對照都有新增到 android_files[]。
在 android_filesystem_config.h 有一個 function fs_config 會使用到 android_files[]。任何 tools 只要來 include 這個 header file 就可以使用這個 function 來調整相關檔案的屬性。
/* system/core/include/private/android_filesystem_config.h */ static const struct fs_path_config android_files[] = { { 00755, AID_ROOT, AID_ROOT, 0, "system/b2g/b2g" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/b2g/updater" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/b2g/plugin-container" }, ... } ... static inline void fs_config(const char *path, int dir, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) { const struct fs_path_config *pc; int plen; if (path[0] == '/') { path++; } pc = dir ? android_dirs : android_files; plen = strlen(path); for(; pc->prefix; pc++){ int len = strlen(pc->prefix); if (dir) { if(plen < len) continue; if(!strncmp(pc->prefix, path, len)) break; continue; } /* If name ends in * then allow partial matches. */ if (pc->prefix[len -1] == '*') { if(!strncmp(pc->prefix, path, len - 1)) break; } else if (plen == len){ if(!strncmp(pc->prefix, path, len)) break; } } *uid = pc->uid; *gid = pc->gid; *mode = (*mode & (~07777)) | pc->mode; *capabilities = pc->capabilities; }
有那些 tools 會呼叫到 fs_config 呢?
使用 ext4fs 檔案系統時會用的 make_ext4fs 有來 include android_filesystem_config.h,也有呼叫到 fs_config。
/* system/extras/ext4_utils/make_ext4fs.c */ fs_config_func = fs_config; ... if (fs_config_func != NULL) { int dir = S_ISDIR(stat.st_mode); fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities); dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; dentries[i].capabilities = capabilities; }
在 yaffs2 檔案系統會使用到的 mkyaffs2image,也有 include android_filesystem_config.h 及呼叫 fs_config。
/* yaffs2/utils/mkyaffs2image.c */ static void fix_stat(const char *path, struct stat *s) { uint64_t capabilities; path += source_path_len; fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode, &capabilities); }
這也太巧合了吧,我 porting 的這隻手機使用的是 ubifs,都沒有使用前面提到的這兩個 tools。相較於 yaffs2,ubifs 支持壓縮及還有一些其他優點,通常在 nand flash 容量較少的手機上就有可能使會用 ubifs。當然 ubifs 也不是完全沒有缺點,如果想要了解 nand flash 上不同檔案系統的特性可以參考這份 測試報告。
回到正題,這隻手機上的 ubifs 真的出了什麼問題嗎?檢查後發現 ubifs 使用的 mkfs.ubifs 沒有 include android_filesystem_config.h 當然也不會呼叫到 fs_config!
看來問題就出在這裡,就來試試看吧。先到 ubifs_utils/mkfs.ubifs/mkfs.ubifs.c 裡新增 fix_stat,然後在 add_directory 及 write_data 這兩個 function 裡來使用 fix_stat。重編 mkfs.ubifs 後,再把 system.img 編出來,flash 到手機一試,結果一切正常,看來這才是正解。
/* system/extras/ubifs_utils/mkfs.jffs2.c */ static void fix_stat(const char *path, struct stat *s) { uint64_t capabilities; path += source_path_len; fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode,&capabilities); } static int write_data(void) { ... if (fixstats) fix_stat(root, &root_st); ... } static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, int non_existing) { ... if (fixstats) fix_stat(root, &root_st); ... }