FAT32 File Operations

load_dentry

與 tmpfs 不同的是,在 traversal 過程中,vnode 有可能還在 disk 上,還沒被 load 進來建立 vnode structure,因此需要在 traversal 找不到的時候去 fat32 的 directory entry 尋找,建立成功後再重新 traversal 一次

src/vfs.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void traversal_recursive(struct dentry* node, const char* path, struct vnode** target_node, char* target_path) {
    // ... skip for simplicity

    // find in node's child
    struct list_head* p;
    int found = 0;
    list_for_each(p, &node->childs) {
        struct dentry* dent = list_entry(p, struct dentry, list);
        if (!strcmp(dent->name, target_path)) {
            if (dent->mountpoint != NULL) {
                traversal_recursive(dent->mountpoint->root, path + i, target_node, target_path);
            }
            else if (dent->type == DIRECTORY) {
                traversal_recursive(dent, path + i, target_node, target_path);
            }
            found = 1;
            break;
        }
    }
    // not found in vnode tree, then try to load dentry in real hard disk
    if (!found) {
        int ret = node->vnode->v_ops->load_dentry(node, target_path);
        if (ret == 0) { // load success, traversal again
            traversal_recursive(node, path, target_node, target_path);
        }
    }
}

include/fat32.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
struct fat32_dirent {
    uint8_t name[8];            // 0x0-0x7
    uint8_t ext[3];             // 0x8-0xA
    uint8_t attr;               // 0xB
    uint8_t reserved;           // 0xC
    uint8_t create_time[3];     // 0xD-0xF
    uint16_t create_date;       // 0x10-0x11
    uint16_t last_access_date;  // 0x12-0x13
    uint16_t cluster_high;      // 0x14-0x15
    uint32_t ext_attr;          // 0x16-0x19
    uint16_t cluster_low;       // 0x1A-0x1B
    uint32_t size;              // 0x1C-0x1F
} __attribute__((packed));

src/fat32.c

在這邊是使用 TA 提供的 sfn_nctuos.img,處理檔名的時候是 SFN 的格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
static uint32_t get_cluster_blk_idx(uint32_t cluster_idx) {
    return fat32_metadata.data_region_blk_idx +
           (cluster_idx - fat32_metadata.first_cluster) * fat32_metadata.sector_per_cluster;
}

int fat32_load_dentry(struct dentry* dir, char* component_name) {
    // read first block of cluster
    struct fat32_internal* dir_internal = (struct fat32_internal*)dir->vnode->internal;
    uint8_t sector[BLOCK_SIZE];
    uint32_t dirent_cluster = get_cluster_blk_idx(dir_internal->first_cluster);
    readblock(dirent_cluster, sector);

    // parse
    struct fat32_dirent* sector_dirent = (struct fat32_dirent*)sector;

    // load all children under dentry
    int found = -1;
    for (int i = 0; sector_dirent[i].name[0] != '\0'; i++) {
        // special value
        if (sector_dirent[i].name[0] == 0xE5) {
            continue;
        }
        // get filename
        char filename[13];
        int len = 0;
        for (int j = 0; j < 8; j++) {
            char c = sector_dirent[i].name[j];
            if (c == ' ') {
                break;
            }
            filename[len++] = c;
        }
        filename[len++] = '.';
        for (int j = 0; j < 3; j++) {
            char c = sector_dirent[i].ext[j];
            if (c == ' ') {
                break;
            }
            filename[len++] = c;
        }
        filename[len++] = 0;
        if (!strcmp(filename, component_name)) {
            found = 0;
        }
        // create dirent
        struct dentry* dentry;
        if (sector_dirent[i].attr == 0x10) {  // directory
            dentry = fat32_create_dentry(dir, filename, DIRECTORY);
        }
        else {  // file
            dentry = fat32_create_dentry(dir, filename, REGULAR_FILE);
        }
        // create fat32 internal
        struct fat32_internal* child_internal = (struct fat32_internal*)kmalloc(sizeof(struct fat32_internal));
        child_internal->first_cluster = ((sector_dirent[i].cluster_high) << 16) | (sector_dirent[i].cluster_low);
        child_internal->dirent_cluster = dirent_cluster;
        child_internal->size = sector_dirent[i].size;
        dentry->vnode->internal = child_internal;
    }
    return found;
}

lookup

src/fat32.c

跟 tmpfs 一致

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int fat32_lookup(struct vnode* dir, struct vnode** target, const char* component_name) {
    // component_name is empty, return dir vnode
    if (!strcmp(component_name, "")) {
        *target = dir;
        return 0;
    }
    // search component_name in dir
    struct list_head* p = &dir->dentry->childs;
    list_for_each(p, &dir->dentry->childs) {
        struct dentry* dentry = list_entry(p, struct dentry, list);
        if (!strcmp(dentry->name, component_name)) {
            *target = dentry->vnode;
            return 0;
        }
    }
    *target = NULL;
    return -1;
}

read

include/fat32.h

1
2
3
#define BLOCK_SIZE 512
#define FAT_ENTRY_PER_BLOCK (BLOCK_SIZE / sizeof(int))
#define EOC 0xFFFFFFF

src/fat32.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static uint32_t get_fat_blk_idx(uint32_t cluster_idx) {
    return fat32_metadata.fat_region_blk_idx + (cluster_idx / FAT_ENTRY_PER_BLOCK);
}

int fat32_read(struct file* file, void* buf, uint64_t len) {
    struct fat32_internal* file_node = (struct fat32_internal*)file->vnode->internal;
    uint64_t f_pos_ori = file->f_pos;
    uint32_t current_cluster = file_node->first_cluster;
    int remain_len = len;
    int fat[FAT_ENTRY_PER_BLOCK];

    while (remain_len > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) {
        readblock(get_cluster_blk_idx(current_cluster), buf + file->f_pos); // NEED FIX: buf
        file->f_pos += (remain_len < BLOCK_SIZE) ? remain_len : BLOCK_SIZE;
        remain_len -= BLOCK_SIZE;

        // update cluster number from FAT
        if (remain_len > 0) {
            readblock(get_fat_blk_idx(current_cluster), fat);
            current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK];
        }
    }

    return file->f_pos - f_pos_ori;
}

write

src/fat32.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int fat32_write(struct file* file, const void* buf, uint64_t len) {
    struct fat32_internal* file_node = (struct fat32_internal*)file->vnode->internal;
    uint64_t f_pos_ori = file->f_pos;
    int fat[FAT_ENTRY_PER_BLOCK];
    char write_buf[BLOCK_SIZE];

    // traversal to target cluster using f_pos
    uint32_t current_cluster = file_node->first_cluster;
    int remain_offset = file->f_pos;
    while (remain_offset > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) {
        remain_offset -= BLOCK_SIZE;
        if (remain_offset > 0) {
            readblock(get_fat_blk_idx(current_cluster), fat);
            current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK];
        }
    }

    // write first block, handle f_pos
    int buf_idx, f_pos_offset = file->f_pos % BLOCK_SIZE;
    readblock(get_cluster_blk_idx(current_cluster), write_buf);
    for (buf_idx = 0; buf_idx < BLOCK_SIZE - f_pos_offset && buf_idx < len; buf_idx++) {
        write_buf[buf_idx + f_pos_offset] = ((char*)buf)[buf_idx];
    }
    writeblock(get_cluster_blk_idx(current_cluster), write_buf);
    file->f_pos += buf_idx;

    // write complete block
    int remain_len = len - buf_idx;
    while (remain_len > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) {
        // write block
        writeblock(get_cluster_blk_idx(current_cluster), buf + buf_idx);
        file->f_pos += (remain_len < BLOCK_SIZE) ? remain_len : BLOCK_SIZE;
        remain_len -= BLOCK_SIZE;
        buf_idx += BLOCK_SIZE;

        // update cluster number from FAT
        if (remain_len > 0) {
            readblock(get_fat_blk_idx(current_cluster), fat);
            current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK];
        }
    }

    // TODO: last block also need to handle remainder

    // update file size
    if (file->f_pos > file_node->size) {
        file_node->size = file->f_pos;

        // update directory entry
        uint8_t sector[BLOCK_SIZE];
        readblock(file_node->dirent_cluster, sector);
        struct fat32_dirent* sector_dirent = (struct fat32_dirent*)sector;
        for (int i = 0; sector_dirent[i].name[0] != '\0'; i++) {
            // special value
            if (sector_dirent[i].name[0] == 0xE5) {
                continue;
            }
            // find target file directory entry
            if (((sector_dirent[i].cluster_high) << 16) | (sector_dirent[i].cluster_low) == file_node->first_cluster) {
                sector_dirent[i].size = (uint32_t)file->f_pos;
            }
        }
        writeblock(file_node->dirent_cluster, sector);
    }

    return file->f_pos - f_pos_ori;
}
comments powered by Disqus