1. 程式人生 > >android sdcard儲存方案一(基於fuse檔案系統)

android sdcard儲存方案一(基於fuse檔案系統)



一、 啟動三個相關service

按啟動順序,如下:

service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount

service installd /system/bin/installd
    class main
    socket installd stream 600 system system

service sdcard /system/bin/sdcard-u 1023 -g 1023 -l /data/media /mnt/shell/emulated
    class late_start

上面三個service啟動後,內建sdcard狀態如下圖:


注:

android_filesystem_config.h

#define AID_MEDIA_RW      1023  /* internal media storage write access */

#define AID_SDCARD_RW     1015  /* external storage write access */

#define AID_SDCARD_R      1028  /* external storage read access */

二、insalld service

frameworks/native/cmds/installd/installd.c 
installd 啟動時將/data/media 目錄建立好,並將預置資源遷移到/data/media/0目錄

int initialize_directories() {
    int res = -1;

    // Read current filesystem layout version to handle upgrade paths
    char version_path[PATH_MAX];
    snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);

    int oldVersion;
    if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
        oldVersion = 0;
    }
    int version = oldVersion;
    if (version == 0) {
        // Introducing multi-user, so migrate /data/media contents into /data/media/0
        ALOGD("Upgrading /data/media for multi-user");
        // Ensure /data/media
        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
            goto fail;
        }
        // /data/media.tmp
        char media_tmp_dir[PATH_MAX];
        snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
        // Only copy when upgrade not already in progress
        if (access(media_tmp_dir, F_OK) == -1) {
            if (rename(android_media_dir.path, media_tmp_dir) == -1) {
                ALOGE("Failed to move legacy media path: %s", strerror(errno));
                goto fail;
            }
        }
        // Create /data/media again
        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
            goto fail;
        }
        // /data/media/0
        char owner_media_dir[PATH_MAX];
        snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);

        // Move any owner data into place
        if (access(media_tmp_dir, F_OK) == 0) {
            if (rename(media_tmp_dir, owner_media_dir) == -1) {
                ALOGE("Failed to move owner media path: %s", strerror(errno));
                goto fail;
            }
        }

  。。。。。。

 }

三、sdcard service

1、初始化fuse


static void fuse_init(struct fuse *fuse, int fd, const char *source_path,

        gid_t write_gid, derive_t derive, bool split_perms) {

 //此函式初始化重要的全域性資料結構fuse,供後面建立的多執行緒使用

    這個全域性結構體變數在run()函式中定義,因為run()函式永不退出,所以雖然fuse是函式內的區域性變數,但它的記憶體其實永不釋放。達相當於全域性變數的效果啦。

/*********=============================================

提前將run函式註釋說明fuse資料結構相關程式碼:

static int run(const char* source_path, const char* dest_path, uid_t uid,
        gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
        bool split_perms) {
    int fd;
    char opts[256];
    int res;
   
struct fuse fuse;


    /* cleanup from previous instance, if necessary */
    umount2(dest_path, 2);

    ...................

        res = ignite_fuse(&fuse, num_threads);


    /* we do not attempt to umount the file system here because we are no longer
     * running as the root user */

*********================================================/

    pthread_mutex_init(&fuse->lock, NULL);        //初始化多執行緒互斥量
   
    fuse->fd = fd;     // dev/fuse 作為user space /kernel space 互動的裝置節點
    fuse->next_generation = 0;

 // 系統啟動時帶-d / -l 或者不速這兩個引數,會對 fuse->derive賦不同的值:  service sdcard /system/bin/sdcard -u 1023 -g 1023-l /data/media /mnt/shell/emulated

typedef enum {
    DERIVE_NONE,
    DERIVE_LEGACY,
    DERIVE_UNIFIED,
} derive_t;  

 // -l :derive初始化為DERIVE_LEGACY , -d :derive初始化為DERIVE_UNIFIED ,無這兩引數:derive初始化為DERIVE_NONE/

// derive 初始化這幾值有什麼不同呢????
    fuse->derive = derive;                  
    fuse->split_perms = split_perms;
    fuse->write_gid = write_gid;        //w 

    memset(&fuse->root, 0, sizeof(fuse->root));
    fuse->root.nid = FUSE_ROOT_ID; /* 1 */   
    fuse->root.refcount = 2;
    fuse->root.namelen = strlen(source_path);
    fuse->root.name = strdup(source_path);        //這裡記錄根目錄路徑為:/data/media ,後面操作/mnt/shell/emulated會轉換到/data/media
    fuse->root.userid = 0;
    fuse->root.uid = AID_ROOT;

    /* Set up root node for various modes of operation */
    switch (derive) {
        。。。。。。。。。。。。//derive 初始化為DERIVE_LEGACY
    case DERIVE_LEGACY:
        /* Legacy behavior used to support internal multiuser layout which
         * places user_id at the top directory level, with the actual roots
         * just below that. Shared OBB path is also at top level. */
        fuse->root.perm = PERM_LEGACY_PRE_ROOT;                //初始化根目錄node,sdcard.c也類似kernel fs為每個目錄和檔案維護了一個node結構體
        fuse->root.mode = 0771;
        fuse->root.gid = AID_SDCARD_R;
        fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
        fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
        snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
        fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
        break;

    }

//容量控制
#ifdef LIMIT_SDCARD_SIZE
    struct statfs stat;
    if (statfs(fuse->root.name, &stat) < 0) {
        ERROR("get %s fs status fail \n",fuse->root.name);
fuse->free_size =0;
    }else{
fuse->free_size = stat.f_bfree*stat.f_bsize;
LOG("[fuse_debug]fuse.free_size =%lld \n",fuse->free_size);
    }
#endif
}

兩個重要資料結構:fuse和node

/* Global data structure shared by all fuse handlers. */
struct fuse {
    pthread_mutex_t lock;

    __u64 next_generation;
   int fd;
   derive_t derive;
    bool split_perms;
    gid_t write_gid;
   struct node root;
    char obbpath[PATH_MAX];

    Hashmap* package_to_appid;           //hash map
    Hashmap* appid_with_rw;

#ifdef LIMIT_SDCARD_SIZE
__u64 free_size; //add by mtk for limit internal sdcard size
#endif
};

struct node {
    __u32 refcount;
   __u64 nid;                 // node id
    __u64 gen;

    /* State derived based on current position in hierarchy. */
    perm_t perm;
    userid_t userid;
    uid_t uid;
    gid_t gid;
    mode_t mode;

    struct node *next;          /* per-dir sibling list */
    struct node *child;         /* first contained file by this dir */
    struct node *parent;        /* containing directory */


    size_t namelen;
    char *name;
    /* If non-null, this is the real name of the file in the underlying storage.
     * This may differ from the field "name" only by case.
     * strlen(actual_name) will always equal strlen(name), so it is safe to use
     * namelen for both fields.
     */
    char *actual_name;

    /* If non-null, an exact underlying path that should be grafted into this
     * position. Used to support things like OBB. */
    char* graft_path;
    size_t graft_pathlen;
};

2、啟動fuse

static int run(const char* source_path, const char* dest_path, uid_t uid,
        gid_t gid, gid_t write_gid, int num_threads, derive_t derive,
        bool split_perms) {
    int fd;
    char opts[256];
    int res;
    struct fuse fuse;   //這個區域性變數,卻起到全域性變數的效果。

    /* cleanup from previous instance, if necessary */
    umount2(dest_path, 2);     //先做一次強制umount動作


    fd = open("/dev/fuse", O_RDWR);  //開啟/dev/fuse字元型裝置,這個fd將要傳遞到kernel fuse
    if (fd < 0){
        ERROR("cannot open fuse device: %s\n", strerror(errno));
        return -1;
    }
    snprintf(opts, sizeof(opts),
            "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
            fd, uid, gid);

    res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts);      //將 /mnt/shell/emulated 掛載到/dev/fuse裝置
    if (res < 0) {
        ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
        goto error;
    }
    //將sdcard的許可權由root降為media_rw
    res = setgid(gid);
    if (res < 0) {
        ERROR("cannot setgid: %s\n", strerror(errno));
        goto error;
    }
    res = setuid(uid);
    if (res < 0) {
        ERROR("cannot setuid: %s\n", strerror(errno));
        goto error;
    }

    // /初始化重要的fuse資料結構  ,
    fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms);
    umask(0);
  //建立 hander thread, 啟動fuse
    res = ignite_fuse(&fuse, num_threads);      
    /* we do not attempt to umount the file system here because we are no longer
     * running as the root user */

error:
    close(fd);
    return res;
}
下面再看handler thread 建立,及啟動。 關於pthread請看我轉的一篇blog,寫得非常好。

static int ignite_fuse(struct fuse* fuse, int num_threads)
{
    struct fuse_handler* handlers;
    int i;

    handlers = malloc(num_threads * sizeof(struct fuse_handler));
    if (!handlers) {
        ERROR("cannot allocate storage for threads\n");
        return -ENOMEM;
    }

    for (i = 0; i < num_threads; i++) {        // 預設num_threads==2  // #define DEFAULT_NUM_THREADS 2
        handlers[i].fuse = fuse;
       handlers[i].token = i;                         // 以執行緒號作為 token,標識handler 。 
    }

    /* When deriving permissions, this thread is used to process inotify events,
     * otherwise it becomes one of the FUSE handlers. */
    i = (fuse->derive == DERIVE_NONE) ? 1 : 0;
    for (; i < num_threads; i++) {
        ERROR("to start thread #%d \n", i);
        pthread_t thread;

      //service sdcard /system/bin/sdcard -u 1023 -g 1023-l /data/media /mnt/shell/emulated  

      // 啟動sdcard service時帶 -d / -l 引數,derive 為DERIVE_LEGACY或DERIVE_UNIFIED時。

  //為什麼要兩個執行緒?這兩個執行緒要怎麼同步嗎?

       int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
        if (res) {
            ERROR("failed to start thread #%d, error=%d\n", i, res);
            goto quit;
        }
    }
    ERROR("fuse->derive #%d \n", fuse->derive);
    if (fuse->derive == DERIVE_NONE) {             handle_fuse_requests(&handlers[0]);       //derive 為DERIVE_NONE,主程序處理handlers[0] 
    } else {
       watch_package_list(fuse);    // 主程序watch檔案:"/data/system/packages.list" 的delete通知事件! 

                                     // 可是watch到delete事件後,並沒有做什麼?可以android後續版本會有什麼改進!!??
    }
    ERROR("terminated prematurely\n");
    /* don't bother killing all of the other threads or freeing anything,
     * should never get here anyhow */
quit:
    exit(1);
}

接上個的函式

static void handle_fuse_requests(struct fuse_handler* handler)
{
    struct fuse* fuse = handler->fuse;
    for (;;) {
        ssize_t len = read(fuse->fd,       //從字元型裝置/dev/fuse中讀取kernel fuse檔案系統傳送出來的處理請求包
                handler->request_buffer, sizeof(handler->request_buffer));   
  。。。。。。。
        const struct fuse_in_header *hdr = (void*)handler->request_buffer;       //請求包中解析出fuse request header
        if (hdr->len != (size_t)len) {
            ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
                    handler->token, (size_t)len, hdr->len);
            continue;
        }
        const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
        size_t data_len = len - sizeof(struct fuse_in_header);
        __u64 unique = hdr->unique;
        int res = handle_fuse_request(fuse, handler, hdr, data, data_len);        //處理kernel fuse requst請求包
        /* We do not access the request again after this point because the underlying
         * buffer storage may have been reused while processing the request. */
        if (res != NO_STATUS) {
            if (res) {
                TRACE("[%d] ERROR %d\n", handler->token, res);
            }
            fuse_status(fuse, unique, res);                //返回fuse request請求的處理結果。
        }
    }
}

3、fuse請求包處理

先看幾個相關的巨集定義,及重要的結構體:struct fuse_handler  /* Maximum number of bytes to write in one request. */
#define MAX_WRITE (256 * 1024)


/* Maximum number of bytes to read in one request. */
#define MAX_READ (128 * 1024)


/* Largest possible request.
 * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
 * the largest possible data payload. */
#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)

/* Private data used by a single fuse handler. */
struct fuse_handler {
    struct fuse* fuse;               //指向上面重點介紹過的全域性變數fuse 
    int token;                          // 以執行緒號作為 token,標識handler 

    /* To save memory, we never use the contents of the request buffer and the read
     * buffer at the same time.  This allows us to share the underlying storage. */
    union {
        __u8 request_buffer[MAX_REQUEST_SIZE];    // 從這個陣列大小的巨集定義MAX_REQUEST_SIZE,可以看出它有兩個作用:   //1、儲存從字元型裝置/dev/fuse中讀取kernel fuse檔案系統傳送出來的處理請求包。   //2、儲存從核心fuse write請求copy而來的需要通過ext4寫入到儲存裝置的資料。
        __u8 read_buffer[MAX_READ];   //儲存通過ext4從儲存讀取到的,需要通過核心fuse read請求,傳上userspace app 的資料。
    };
};

sdcard service 與 kernel 檔案系統的互動請求:
enum fuse_opcode {
FUSE_LOOKUP   = 1,
FUSE_FORGET   = 2,  /* no reply */
FUSE_GETATTR   = 3,
FUSE_SETATTR   = 4,
FUSE_READLINK   = 5,
FUSE_SYMLINK   = 6,
FUSE_MKNOD   = 8,
FUSE_MKDIR   = 9,
FUSE_UNLINK   = 10,
FUSE_RMDIR   = 11,
FUSE_RENAME   = 12,
FUSE_LINK   = 13,
FUSE_OPEN   = 14,
FUSE_READ   = 15,
FUSE_WRITE   = 16,
FUSE_STATFS   = 17,
FUSE_RELEASE       = 18,
FUSE_FSYNC         = 20,
FUSE_SETXATTR      = 21,
FUSE_GETXATTR      = 22,
FUSE_LISTXATTR     = 23,
FUSE_REMOVEXATTR   = 24,
FUSE_FLUSH         = 25,
FUSE_INIT          = 26,
FUSE_OPENDIR       = 27,
FUSE_READDIR       = 28,
FUSE_RELEASEDIR    = 29,
FUSE_FSYNCDIR      = 30,
FUSE_GETLK         = 31,
FUSE_SETLK         = 32,
FUSE_SETLKW        = 33,
FUSE_ACCESS        = 34,
FUSE_CREATE        = 35,
FUSE_INTERRUPT     = 36,
FUSE_BMAP          = 37,
FUSE_DESTROY       = 38,
FUSE_IOCTL         = 39,
FUSE_POLL          = 40,


/* CUSE specific operations */
CUSE_INIT          = 4096,
};
好了,瞭解上面這些後,看下面兩fuse請求包處理函式就很容易了! static void handle_fuse_requests(struct fuse_handler* handler)
{
    struct fuse* fuse = handler->fuse;
    for (;;) {
        ssize_t len = read(fuse->fd,      //從字元型裝置/dev/fuse中讀取kernel fuse檔案系統傳送出來的處理請求包,
                handler->request_buffer, sizeof(handler->request_buffer));   //如果是write請求命令,此時包裡還有要寫的資料。
  。。。。。。。
        const struct fuse_in_header *hdr = (void*)handler->request_buffer;        //請求包中解析出fuse request header
        if (hdr->len != (size_t)len) {
            ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
                    handler->token, (size_t)len, hdr->len);
            continue;
        }
        const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
        size_t data_len = len - sizeof(struct fuse_in_header);
        __u64 unique = hdr->unique;
        int res = handle_fuse_request(fuse, handler, hdr, data, data_len);         //處理kernel fuse requst請求包
        /* We do not access the request again after this point because the underlying
         * buffer storage may have been reused while processing the request. */
        if (res != NO_STATUS) {
            if (res) {
                TRACE("[%d] ERROR %d\n", handler->token, res);
            }
            fuse_status(fuse, unique, res);                //返回fuse request請求的處理結果。
        }
    }
}
static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
        const struct fuse_in_header *hdr, const void *data, size_t data_len)
{
    switch (hdr->opcode) {

    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

    case FUSE_OPEN: { /* open_in -> open_out */                              //開啟要操作的檔案, 記錄檔案描述符fd
        const struct fuse_open_in *req = data;
        return handle_open(fuse, handler, hdr, req);                     
    }


    //  READ / WRITE 操作都涉及到sdcard usrspace 與kernel fuse kernel spcace之間的讀、寫資料的記憶體copy互動。
    case FUSE_READ: { /* read_in -> byte[] */
        const struct fuse_read_in *req = data;
        return handle_read(fuse, handler, hdr, req);                         
    }


    case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
        const struct fuse_write_in *req = data;
        const void* buffer = (const __u8*)data + sizeof(*req);
        return handle_write(fuse, handler, hdr, req, buffer);
    }

 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    
    }
}

4、derive permission授權

前面提到sdcard.c也類似kernel fs為每個目錄和檔案維護了一個node結構體, 下面圍繞這個node來看整個許可權控限過程就很明白了。 首先看看幾個跟許可權相關的enum : 定義的檔案幾種許可權選則: typedef enum {
/* Nothing special; this node should just inherit from its parent. */
PERM_INHERIT,
/* This node is one level above a normal root; used for legacy layouts
* which use the first level to represent user_id. */
PERM_LEGACY_PRE_ROOT,
/* This node is "/" */
PERM_ROOT,
/* This node is "/Android" */
PERM_ANDROID,
/* This node is "/Android/data" */
PERM_ANDROID_DATA,
/* This node is "/Android/obb" */
PERM_ANDROID_OBB,
/* This node is "/Android/user" */
PERM_ANDROID_USER,
} perm_t;  

許可權控制的三種方法,
/* Permissions structure to derive */
typedef enum {
DERIVE_NONE,
DERIVE_LEGACY,    ====》用於內建sdcard ,主要用於多使用者的許可權控制
DERIVE_UNIFIED,    ====》用於外接sdcard

} derive_t;
========================================== DERIVE_LEGACY
DERIVE_UNIFIED
這兩個列舉fuck地讓我迷糊很久啊,到底有什麼不一樣啊???分析下面的程式碼很久才搞明白。 ========================================== 即下來看重要的node結構體 :  struct node {
    __u32 refcount;     // node 的引用計數
    __u64 nid;      //node id ,用於唯一標識node,太聰明瞭,除root node 使用該node申請的記憶體指標地址作為id,肯定可以達到唯一性。


    /* State derived based on current position in hierarchy. */
    perm_t perm;                //許可權控制方式
    userid_t userid;     //使用者id,即,0、 1、 2、 3、 。。。。
    uid_t uid;         //uid
    gid_t gid;                                 //gid
    mode_t mode;                       //訪問模式

    //node鏈就和vfs類似了……
    struct node *next;          /* per-dir sibling list */
    struct node *child;         /* first contained file by this dir */
    struct node *parent;        /* containing directory */


    size_t namelen;          //node 名長度
    char *name;          //node 名

};
下面fuse_init函式初始化fuse結構體前面已經說過的。 這裡重點看fuse結構體裡的root node結構的初始化。 static voidfuse_init(struct fuse *fuse, int fd, const char *source_path,
        gid_t write_gid, derive_t derive, bool split_perms) {
    pthread_mutex_init(&fuse->lock, NULL);


    fuse->fd = fd;
    fuse->next_generation = 0; 
    fuse->derive = derive;
    fuse->split_perms = split_perms;
    fuse->write_gid = write_gid;                                     //初始設定有sdcard讀寫許可權的gid

    memset(&fuse->root, 0, sizeof(fuse->root));                 // 建立第一個node,後面的node都往這裡插入
    fuse->root.nid = FUSE_ROOT_ID; /* 1 */                       //  root node id  = 1 ,
    fuse->root.refcount = 2;
    fuse->root.namelen = strlen(source_path);
    fuse->root.name = strdup(source_path);                         // root node 對應的是什麼呢?? 即根目錄:'/data/media' , 不要以為是為是'/data/media/0 ' 啊 !
    fuse->root.userid = 0;
    fuse->root.uid = AID_ROOT;            // uid  :  root


    /* Set up root node for various modes of operation */
    switch (derive) {
    case DERIVE_NONE:        //無許可權控制的方式,現在一般不用。無法selinux的要求吧!
        /* Traditional behavior that treats entire device as being accessible
         * to sdcard_rw, and no permissions are derived. */
        fuse->root.perm = PERM_ROOT;
        fuse->root.mode = 0775;
        fuse->root.gid = AID_SDCARD_RW;
        break;
    case DERIVE_LEGACY:            //內建sdcard使用,支援多使用者的訪問獨立sdcard資料許可權控制方式, //內建sdcard目錄頂層為使用者id為名的0、1、2、3等使用者目錄, data/media/0, data/media/1, data/media/2 ............... 等 //,目錄頂層也包括/data/media/obb 目錄 // data/media/userid 目錄下/data/media/userid/Android/data/ 子目錄才是app的麼有資料 。
        /* Legacy behavior used to support internal multiuser layout which
         * places user_id at the top directory level, with the actual roots
         * just below that. Shared OBB path is also at top level. */
        fuse->root.perm = PERM_LEGACY_PRE_ROOT;                      //root node 的許可權,後面根目錄下根據useid建立的的0 , 1 ,,2 , 3 .....目錄許可權為:PERM_ROOT
        fuse->root.mode = 0771;
        fuse->root.gid = AID_SDCARD_R;                                                                                           // gid =  AID_SDCARD_R
        fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);   // 初始hash Map ,用於做什麼? 下面會詳細說明,它跟許可權有關就是。
        fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
        snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
        fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());              //  建立/data/media/obb 目錄,用於apk獨立資料訪問許可權控制的?

        LOG("[fuse_debug]obbpath =%s \n",fuse->obbpath);
        break;
    case DERIVE_UNIFIED:
        /* Unified multiuser layout which places secondary user_id under
         * /Android/user and shared OBB path under /Android/obb. */ // 不支援多使用者的的許可權控制方式。 //  外接sdcard目錄頂層即為user 0的使用者資料,及Android/obb // 而user 1開始的第二個使用者數所都放在Android/user目錄下 。 fuse->root.perm = PERM_ROOT;                          // root node 的許可權就為PERM_ROOT
        fuse->root.mode = 0771;
        fuse->root.gid = AID_SDCARD_R;
        fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
        fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
        snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
        break;
    }

}
再看下面的這個函式的作用:根據parent node的許可權為新的child node 分配許可權。 static voidderive_permissions_locked(struct fuse* fuse, struct node *parent,
        struct node *node) {
    appid_t appid;


    /* By default, each node inherits from its parent */
    node->perm = PERM_INHERIT;
    node->userid = parent->userid;
    node->uid = parent->uid;                               //新的node預設 繼承父node uid / gid 
    node->gid = parent->gid;

    node->mode = parent->mode;


    if (fuse->derive == DERIVE_NONE) {
        return;
    }  //這裡新增一條重要的log列印 
    TRACE("derive_permissions_locked %s 0%d  (%s)\n",
            node->name, parent->perm, parent->name);


    /* Derive custom permissions based on parent and current node */
    switch (parent->perm) {
    case PERM_INHERIT:
        /* Already inherited above */
        break;
    case PERM_LEGACY_PRE_ROOT:       // 內建sdcard的根目錄許可權為PERM_LEGACY_PRE_ROOT
        /* Legacy internal layout places users at top level */
        node->perm = PERM_ROOT;                               /內建sdcard的/根目錄下,根據使用者id建立的0、1、2、3等使用者子目錄,授權為: PERM_ROOT
        node->userid = strtoul(node->name, NULL, 10);  // userid設為0、1、2、3.......
        break;
    case PERM_ROOT:                                                    //外接sdcard,及內建sdcard根目錄下的0、1、2、3等使用者子目錄的許可權為:PERM_ROOT
        /* Assume masked off by default. */
        node->mode = 0770;   // 下面為外接sdcard根目錄下的子目錄,及內建sdcard根目錄下的0、1、2、3等使用者子目錄 授予不同許可權 :  
        if (!strcasecmp(node->name, "Android")) {             //root/Android目錄授權 
            /* App-specific directories inside; let anyone traverse */ 
            node->perm = PERM_ANDROID;                  
            node->mode = 0771; 
        } 
        break;
    case PERM_ANDROID:
        if (!strcasecmp(node->name, "data")) {                                                      // root/Android/data目錄授權
            /* App-specific directories inside; let anyone traverse */
            node->perm = PERM_ANDROID_DATA;                               
            node->mode = 0771;
        } else if (!strcasecmp(node->name, "obb")) {           // root/Android/obb目錄授權 
            /* App-specific directories inside; let anyone traverse */
            node->perm = PERM_ANDROID_OBB;
            node->mode = 0771;
            /* Single OBB directory is always shared */
            node->graft_path = fuse->obbpath;
            node->graft_pathlen = strlen(fuse->obbpath);
        } else if (!strcasecmp(node->name, "user")) {                                            //root/Android/user目錄授權 , 同時修改gid
            /* User directories must only be accessible to system, protected
             * by sdcard_all. Zygote will bind mount the appropriate user-
             * specific path. */
            node->perm = PERM_ANDROID_USER;
            node->gid = AID_SDCARD_ALL;
            node->mode = 0770;
        }
        break;
    case PERM_ANDROID_DATA:                                   // root/Android/data 與 root/Android/obb 目錄下的各子目錄授權,appid作為uid,讓該app獨佔該目錄                       
    case PERM_ANDROID_OBB:
        appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name);   //從haspMap裡獲取appid 
        if (appid != 0) {
            node->uid = multiuser_get_uid(parent->userid, appid);     // appid 作為uid,讓該app才能有許可權訪問
        }
        node->mode = 0770;
        break;
    case PERM_ANDROID_USER:                 //root/Android/user目錄授權目錄下的的子錄目授權,比如外接多使用者:sdcard2/user/2 授權為根目錄 PERM_ROOT
        /* Root of a secondary user */
        node->perm = PERM_ROOT;
        node->userid = strtoul(node->name, NULL, 10);
        node->gid = AID_SDCARD_R;
        node->mode = 0771;
        break;
    }
}
通過在derive_permissions_locked新增的一句列印log分析看看: #define FUSE_TRACE 1  //打出sdcard service debug log 可以看到下面這些sdcard service 針對內建sdcard的log資訊:
06-04 17:49:41.993   232   232 I sdcard  : source_path='/data/media', dest_path='/mnt/shell/emulated', derive=1, write_gid=1015 這裡為根目錄/data/media/0  授權02: PERM_ROOT
06-04 17:50:03.904   232   251 D sdcard  : [1] LOOKUP 0 @ 1 (/data/media)   
06-04 17:50:03.904   232   251 D sdcard  : derive_permissions_locked 0 01  (/data/media)

// 為/data/media/0 /Android 授權
06-04 17:50:09.023   232   251 D sdcard  : derive_permissions_locked Android 02  (0)

// 為/data/media/0 /Android/data  授權
06-04 17:50:09.038   232   250 D sdcard  : derive_permissions_locked data 03  (Android)


06-04 17:50:09.052   232   251 D sdcard  : derive_permissions_locked com.amap.android.location 04  (data)

下面通過/sdcard/Android/data目錄的實際的授權結果是什麼樣的: 可以看到uid是隨著app變化而變化的。
root@S850:/sdcard/Android/data # ls -Z   ////這個目錄下建立的都是以app名為的子目錄。
drwxrwx---
u0_a0   sdcard_r          u:object_r:sdcard_external:s0 com.amap.android.location
drwxrwx---
u0_a3   sdcard_r          u:object_r:sdcard_external:s0 com.lenovo.carapplication
drwxrwx---
u0_a67  sdcard_r          u:object_r:sdcard_external:s0 com.len
通過上面的程式碼分析,DERIVE_LEGACY 與DERIVE_UNIFIED 的區別已經很清楚啦!

5、sdcard node訪問許可權控制

上面提到很多對node授權,實際訪問是如何進行許可權控制的呢? 其實就是通過上面偶有提到HashMap,下面仔細再看看。 fuse 結構體中有兩個重要的HashMap變數,如下: struct fuse {
 。。。。。。。
    Hashmap* package_to_appid;
    Hashmap* appid_with_rw;

 。。。。。。
};
fuse_init()函式中對這兩個hashMap變數進行了初始化,請看: static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
        gid_t write_gid, derive_t derive, bool split_perms) {
。。。。。。。。。。。。。。。。     /* Set up root node for various modes of operation */
    switch (derive) {
。。。。。。。。。。。。。。。。
    case DERIVE_LEGACY:
//初始化了兩個haspMap例項指標,具體怎麼初始化的,請仔細看/system/core/libcutils/hashmap.c         fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
        fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
。。。。。。。。。。。。。。 hashMap是如何建立的呢? 繼續 ~…… ignite_fuse()函式啟動兩個fuse handle執行緒後,主程序呼叫watch_package_list()進入死迴圈,watch檔案"/data/system/packages.list"。
注:packages.list這個檔案的內容是系統在啟動時掃描出的已安裝的應用apk列表資訊。內容例子如下: root@:/data/system # cat packages.list                                     
com.lakala.android 10111 0 /data/data/com.lakala.android default 1028,1015,1023,3003
com.android.defcontainer 10004 0 /data/data/com.android.defcontainer platform 1028,1015,1023,2001,1035
下面我們分析一下主程序呼叫 watch_package_list()如何建立hashMap 的? static void watch_package_list(struct fuse* fuse) {
。。。。。。
    boolactive = false;
    while (1) {
        if (!active) {
            int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
。。。。。。。。。
            /* Watch above will tell us about any future changes, so
             * read the current state. */
            if (read_package_list(fuse) == -1) {  //解析檔案packages.list內容,並建立hashMap 
                ERROR("read_package_list failed: %s\n", strerror(errno));
                return;
            }
           active = true;
        }

。。。。。。。。。。。。。。
}
hashMap的建立一切就在這裡了! static int read_package_list(struct fuse *fuse) {
    pthread_mutex_lock(&fuse->lock);
 
 //如果已有hashMap,則清乾淨。(如果watch到packages.list初刪除,就要重新建立hashMap,那就要先清乾淨原來的舊值了)
    hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid);
    hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw);

    FILE* file = fopen(kPackagesListFile, "r");       //開啟檔案:packages.list
    if (!file) {
        ERROR("failed to open package list: %s\n", strerror(errno));
        pthread_mutex_unlock(&fuse->lock);
        return -1;
    }

    char buf[512];
    bool is_found = false;
    while (fgets(buf, sizeof(buf), file) != NULL) {      //讀packages.list一行
        char package_name[512];
        int appid;
        char gids[512];


        is_found = false;        //從packages.list讀取的一行內容,比如:com.lakala.android 10111 0 /data/data/com.lakala.android default 1028,1015,1023,3003    //解析app名,appid,資料所在目錄,app所屬組s(可能屬於多組啊!)
        if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {     // 
            char* package_name_dup = strdup(package_name);
            hashmapPut(fuse->package_to_appid, package_name_dup, (void*) appid);      // 以app name , app id 建立一個新的hashMap新增到package_to_appid, Map 如下:
/* 注:
struct Entry {
    void* key;       //app name
    int hash;          // app name's hash value
    void* value;       // app id
    Entry* next;
};
*/
            char* token = strtok(gids, ",");     
            while (token != NULL) {
                if (strtoul(token, NULL, 10) == fuse->write_gid) {  //從上面獲取到的app所屬組s, 判斷是否該app屬組 fuse->write_gid(AID_SDCARD_RW)。            hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1);            //屬於組AID_SDCARD_RW,則有sdcard rw許可權,則以app id 和1建立一個新的hashMap新增到appid_with_rw ,Map如下;      /* 注:       struct Entry {
          void* key;       //app id
          int hash;          // app ids hash value
          void* value;       // 1
          Entry* next;
     };
     */
                    is_found = true;
                    break;
                }
                token = strtok(NULL, ",");
            }
            if (is_found == false) {
                if (!hashmapContainsKey(fuse->appid_with_rw, (void*) appid)){
                    hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 0);
                }
            }
        }
    }


    TRACE("read_package_list: found %d packages, %d with write_gid\n",
            hashmapSize(fuse->package_to_appid),
            hashmapSize(fuse->appid_with_rw));
    fclose(file);
    pthread_mutex_unlock(&fuse->lock);
    return 0;
}
好的,這樣hashMap package_to_appid 和appid_with_rw就建立好了。 接著,看看derive_permissions_locked()是如何根據hashMap進行授權工作的啦! static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
        struct node *node) {
    appid_t appid;


    /* By default, each node inherits from its parent */
    node->perm = PERM_INHERIT;
   node->userid = parent->userid;
    node->uid = parent->uid;
    node->gid = parent->gid;
    node->mode = parent->mode;
。。。。。。。。。。。。。。。。。。。。。。。。
    TRACE("derive_permissions_locked %s 0%d  (%s)\n",
            node->name, parent->perm, parent->name);
            
    /* Derive custom permissions based on parent and current node */
    switch (parent->perm) {
。。。。。。。。。。。。。。。。。。。。。。。。
    case PERM_ANDROID_DATA:
    case PERM_ANDROID_OBB:
        appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name);    // 通過node name, 即app名,獲取appid。(上面說過,他們有建立過map的)
        if (appid != 0) {
            node->uid = multiuser_get_uid(parent->userid, appid);       // 通過appid 和 userid 計算出一個唯一的uid(演算法:multiuser_get_uid),分配給該app,下面馬上用到它了!!
        }
        node->mode = 0770;
        break;
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。     }
}
下面最後看看如何通過get_caller_has_rw_locked進行訪問許可權控制的吧!
/* Return if the calling UID holds sdcard_rw. */
static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) {
。。。。。。。。。
    appid_t appid = multiuser_get_app_id(hdr->uid);   //uid 反算出appid 
    
    if (appid == AID_SHELL) {
      /* for mtklogger with uid, shell, grant the write permisssion to them */
      TRACE("WARNING: appid is AID_SHELL. Grant the write permission to it\n");
      return true;
    }
    else if (hashmapContainsKey(fuse->appid_with_rw, (void*) appid)) {   //判斷appid對應的app是否在有許可權RW sdcard的hashMap中。
       return (bool)hashmapGet(fuse->appid_with_rw, appid);    //返回derive_permissions_locked所授權value: 1,表未有rw許可權。 //appid_with_rw中的hashMap如下:      struct Entry {
          void* key;       //app id
          int hash;          // app ids hash value
          void* value;       // 1 or 0
          Entry* next;
     };
     */
    }
    else {
       TRACE("WARNING: appid=%d is NOT in packages.list. Grant the write permission to it\n", appid);   //沒有授權RW sdcard 
       return true;
    }
}