moztw.org

使用不同的檔案系統,Firefox OS 主程式沒跑起來!

前陣子收到一隻手機,需要把 Firefox OS porting 上去。剛好之前有看過這幾篇文章 B2G Porting, 三步驟 build 好 Firefox OS透過 AOSP build system 來 build Firefox OS,不然還真不知道從那裡開始。先費了一翻功夫把 manifest 弄出來,再經過一陣苦戰之後 system.img 總算也編出來了。既然 system.img 都出來了,當然要鼓起勇氣把它 flash 到手機裡看看。其實早就有心理準備,怎麼可能一次就成功呢?只是還不知道會碰到什麼樣的問題。果然 flash 進手機後,開機畫面一片黑,發生了什麼事了呢?

先用 adb shell 進去看看,結果發現 Firefox OS 主程式根本就沒有在執行。試著用手動去執行看看也失敗,錯誤信息是 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

1234

$ 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,再手動執行一次,真的有畫面跑出來了。

based_on_mozilla_tech

雖然重新開機後也可以跑起來了,但每次 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

123456789

--- 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

1234

$ 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 檔時,可以對檔案的屬性進行調整。我有把 Firefox OS 相關執行檔的屬性對照都有新增到 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;
}

123456789101112131415161718192021222324252627282930313233343536373839404142

/ 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;
}

123456789101112

/ 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);
}

12345678

/ 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);
...
}

123456789101112131415161718192021222324

/ 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);...}