exfat文件系统-基于fuse
简介
exfat文件系统规范:https://learn.microsoft.com/zh-CN/windows/win32/fileio/exfat-specification#6341-allocationpossible-field
exfat的文件系统在github上:https://github.com/relan/exfat.git
现在githug上的exfat文件系统只支持fuse v3.0.0以下版本。
本文主要关注基于fuse的exfat文件系统代码实现。
exfat的文件格式
Exfat文件头
DBR
DBR开始于EXFAT文件系统的第0号扇区。它由6部分组成,分别为跳转指令、OEM代号、保留区、BPB参数、引导程序和结束标志。DBR有一个备份位于12扇区,即0x1800地址
- 00H~02H:跳转指令0xEB 76 90,代表汇编语言的JMP 76。
- 03H~0AH:OEM代号,即明文EXFAT。
- 0BH~3FH:保留区,暂时不用,目前均为00H。
- 40H~77H:BPB参数区。(BIOS Parameter Block,BIOS参数块),其中一些重要的参数描述如下:
- 40H~47H:分区的起始扇区号,也称为隐藏扇区数,是指本分区前的物理扇区数。 图中为0x20,即分区是从物理扇区32开始
- 48H~4FH:分区的总扇区数。 总扇区数为:0x75F7FE0=123699168 对应到字节数*512约等于61个G
- 50H~53H:FAT表的起始扇区号,即从DBR到FAT的扇区数 0x800=2048:即FAT表位于2048逻辑扇区
- 54H~57H:FAT表占用的扇区数,它与分区大小、簇大小有关 0xF00=3840 大小为1.9M
- 58H~5BH:数据区的起始扇区号。一般是指簇位图的起始位置 0x1800=6144 5CH~5FH:分区内的总簇数
- 0x075f67=483175 60H~63H:根目录的起始簇号 0x04 根目录位于4簇 0x64 - 0x67 4 卷序列号 0x68- 0x69 2 卷版本号(固定为0001) 0x6A - 0x6B 2 卷状态 挂载时将会设置为2,卸载时写为0,如果挂载时发现内容已经为2,则表明上一次卸载是一次异常的行为
- 6CH~6CH:每扇区字节数(EXFAT中每扇区字节数用2的N次方的形式表示,一般总是09H,表示512)
- 6DH~6DH:每簇扇区数(2的N次方),EXFAT支持512B到32MB的簇大小。扇区数为8,即每簇有256个扇区,131072字节,128K 6E~6E:FAT表的个数。 exfat默认为1个FAT表根据微软的规范,TexFAT(Transaction-Safe Extended FAT)可以支持两个FAT以确保文件系统不会因为突然插拔导致错误
- 0x6F 1 驱动标记
- 0x70 1 分区使用百分比
- 78H~1FDH:共390字节的引导程序。
- 1FE~1FF:55 AA结束标志。
综上所述,由以上参数可以得到当前的U盘如下地址:
0x0 DBR
0x1800 DBR Backup
0x100000 FAT表
0x300000 簇位图
0x320000 大小写转换表
0x340000 根目录的目录项
FAT
exfat文件系统一般只要一个fat表。
每4个字节表示一个蔟的信息。
不同于fat32文件系统,如果文件连续存储,则蔟对应fat表的项中存储值为0,如果不连续存储,则蔟对应fat表的项中存储值为下一个存储蔟的蔟号。
①0、1号FAT项:为“F8FFFFFF FFFFFFFF”,表示FAT起始位置。
②2号FAT项:分配给元文件“簇位图”使用,“FFFFFFFF”是文件结束标志,表示“簇位图”这个文件占用了一个簇。
③3号FAT项:分配给元文件“大小写转换表”使用,“FFFFFFFF”是文件结束标志,表示“大小写转换表”这个文件占用了一个簇。
④4号FAT项:分配给根目录使用,“FFFFFFFF”是文件结束标志,表示目前根目录占用了一个簇。
簇位图
位于2号蔟。如果蔟使用了,则对应位被置1,否则为0。
每一位对应一个。第一个字节bit0对应2号蔟。bit7对应9号蔟。第二个字节bit0对应10号蔟。
目录项
目录项描述
目录项的属性固定长度为32,一般文件用三个目录项描述,分别为属性1+属性2+文件名,读取目录项的时候check_entries会检查目录项是否按照上述顺序进行排列。
属性1:
0x01附属目录项数表明后续还有几个目录项,一般都是长文件名下目录项会随之增多
0x04的文件属性如下:
属性2:
属性3:
struct exfat_entry_meta1 /* file or directory info (part 1) */
{uint8_t type; /* EXFAT_ENTRY_FILE */uint8_t continuations;le16_t checksum;le16_t attrib; /* combination of EXFAT_ATTRIB_xxx */le16_t __unknown1;le16_t crtime, crdate; /* creation date and time */le16_t mtime, mdate; /* latest modification date and time */le16_t atime, adate; /* latest access date and time */uint8_t crtime_cs; /* creation time in cs (centiseconds) */uint8_t mtime_cs; /* latest modification time in cs */uint8_t crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */uint8_t __unknown2[7];
}
PACKED;struct exfat_entry_meta2 /* file or directory info (part 2) */
{uint8_t type; /* EXFAT_ENTRY_FILE_INFO */uint8_t flags; /* combination of EXFAT_FLAG_xxx */uint8_t __unknown1;uint8_t name_length;le16_t name_hash;le16_t __unknown2;le64_t valid_size; /* in bytes, less or equal to size */uint8_t __unknown3[4];le32_t start_cluster;le64_t size; /* in bytes */
}struct exfat_entry_name /* file or directory name */
{uint8_t type; /* EXFAT_ENTRY_FILE_NAME */uint8_t __unknown;le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */
}
代码结构
主要涉及文件
├── fuse
│ ├── main.c
├── libexfat
│ ├── byteorder.h
│ ├── cluster.c
│ ├── compiler.h
│ ├── exfatfs.h
│ ├── exfat.h
│ ├── io.c
│ ├── log.c
│ ├── lookup.c
│ ├── mount.c
│ ├── node.c
│ ├── platform.h
│ ├── repair.c
│ ├── time.c
│ ├── utf.c
│ └── utils.c
文件 | 描述 |
---|---|
cluster.c | cluster以及bitmap的申请释放,来支持对文件的操作。 |
io.c | 各种IO相关操作的支持。 |
lookup.c | 主要有exfat_lookup函数,以及exfat_readdir函数。 |
main.c | 主要是对ecfat文件格式的block设备的mount; 解析options;注册fuse_exfat_ops,并启动fuse守护进程。 |
mount.c | 主要实现exfat_mount和exfat_unmount函数。 |
node.c | 对文件各种操作的支持。 |
主要结构体
struct exfat
{struct exfat_dev* dev; //指向exfat_devstruct exfat_super_block* sb; //指向super blockuint16_t* upcase; //指向 EXFAT_ENTRY_UPCASE 的entry解析出的 大写表格目录条目struct exfat_node* root; //指向根目录node。struct{cluster_t start_cluster; //bitmap的起始clusteruint32_t size;//cluster count /* in bits */bitmap_t* chunk;//指向malloc的bitmap_tuint32_t chunk_size;//cluster count /* in bits */bool dirty;}cmap; //指向 EXFAT_ENTRY_BITMAP 的entry解析出的 cluster位图char label[EXFAT_UTF8_ENAME_BUFFER_MAX]; //存储 EXFAT_ENTRY_LABEL中解析的labelvoid* zero_cluster;//一个空的cluster用来,erase cluster。int dmask, fmask;//mount option的opt_umask和fmaskuid_t uid;//mount option的uidgid_t gid;//mount option的gidint ro;bool noatime;//mount option的noatimeenum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair;
};
struct exfat_dev
{int fd;//dev block的fdenum exfat_mode mode;//mount option解析可以设置为EXFAT_MODE_RO,EXFAT_MODE_RW,EXFAT_MODE_ANY,决定exfat打开fd的方式是ro,rw。off_t size; //fd的size/* in bytes */
#ifdef USE_UBLIOoff_t pos;ublio_filehandle_t ufh;
#endif
};enum exfat_mode
{EXFAT_MODE_RO,EXFAT_MODE_RW,EXFAT_MODE_ANY,
};
struct exfat_super_block
{uint8_t jump[3]; /* 0x00 jmp and nop instructions */uint8_t oem_name[8]; /* 0x03 "EXFAT " */uint8_t __unused1[53]; /* 0x0B always 0 */le64_t sector_start; /* 0x40 partition first sector */le64_t sector_count; /* 0x48 partition sectors count */le32_t fat_sector_start; /* 0x50 FAT first sector */le32_t fat_sector_count; /* 0x54 FAT sectors count */le32_t cluster_sector_start; /* 0x58 first cluster sector */le32_t cluster_count; /* 0x5C total clusters count */le32_t rootdir_cluster; /* 0x60 first cluster of the root dir */le32_t volume_serial; /* 0x64 volume serial number */struct /* 0x68 FS version */{uint8_t minor;uint8_t major;}version;le16_t volume_state; /* 0x6A volume state flags */uint8_t sector_bits; /* 0x6C sector size as (1 << n) */uint8_t spc_bits; /* 0x6D sectors per cluster as (1 << n) */uint8_t fat_count; /* 0x6E always 1 */uint8_t drive_no; /* 0x6F always 0x80 */uint8_t allocated_percent; /* 0x70 percentage of allocated space */uint8_t __unused2[397]; /* 0x71 always 0 */le16_t boot_signature; /* the value of 0xAA55 */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_super_block) == 512);
struct exfat_node
{struct exfat_node* parent;struct exfat_node* child;struct exfat_node* next;struct exfat_node* prev;int references; //文件被访问++uint32_t fptr_index;//指向文件中能够当前正在访问的cluster indexcluster_t fptr_cluster; //从start_cluster开始,指向文件中能够当前正在访问的clusteroff_t entry_offset; //node 在parent node的clutser中的entry的offsetcluster_t start_cluster; //node对应文件 存在cluster的位置uint16_t attrib;//node的attr EXFAT_ATTRIB_DIR表示文件,uint8_t continuations; //表示node对应文件在FAT表上,cluster是连续的,或者说用连续的cluster描述文件内容bool is_contiguous : 1;bool is_cached : 1;bool is_dirty : 1;bool is_unlinked : 1;uint64_t size;//文件或者文件夹的大小,time_t mtime, atime;//这两个时间 最后mtime修改时间 最后atime访问时间le16_t name[EXFAT_NAME_MAX + 1];//文件名
};
对于node中的parent,child,next,prev的说明如下图:
root中有a,b,c三个文件夹,其中a是root文件夹所在的cluster中的第一个node,a中有a1和a2两个文件,c中有c1文件。
下图是winhex打开的,U盘根目录的文件描述
1.根目录下面有a,b ,c三个文件。
2.已经删除的文件以及文件夹,依旧可以看到。(不懂数据恢复的香港男明星)
3.根目录下同时能看到bitmap和upcase文件。
4.每个文件夹大小都为128K(本人U盘格式化为128K一个cluster),但是如果一个文件夹中的子文件夹或者文件太多,可以申请更多的cluster。
下图是winhex打开的,root node的cluster
1.在本文描述的exfat文件系统中,读取disk中的文件夹以及文件的时候,会找到文件夹对应的cluster,读取32字节的exfat_entry信息来解析。
exfat_entry描述如下,type的种类主要有bitmap(0x81),upcase(0x82)以及file(0x85)。
#define EXFAT_ENTRY_VALID 0x80
#define EXFAT_ENTRY_BITMAP (0x01 | EXFAT_ENTRY_VALID) //bitmap
#define EXFAT_ENTRY_UPCASE (0x02 | EXFAT_ENTRY_VALID) //大写表格目录条目
#define EXFAT_ENTRY_FILE (0x05 | EXFAT_ENTRY_VALID) //文件以及文件夹的描述struct exfat_entry /* common container for all entries */
{uint8_t type; /* any of EXFAT_ENTRY_xxx */uint8_t data[31];
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry) == 32);
2.关于上图中bitmap和upcase的描述如下
struct exfat_entry_bitmap /* allocated clusters bitmap */
{uint8_t type; /* EXFAT_ENTRY_BITMAP */uint8_t __unknown1[19];le32_t start_cluster; //bitmap的存储的clusterle64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);struct exfat_entry_upcase /* upper case translation table */
{uint8_t type; /* EXFAT_ENTRY_UPCASE */uint8_t __unknown1[3];le32_t checksum;uint8_t __unknown2[12];le32_t start_cluster; //upcase存储的clusterle64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32);
3.参考上图中的根目录的cluster。对于文件夹a来说第一个entry是meta1,第二个entry是meta2,第三个entry是name。关于meta1和meta2 的描述如下:
a文件描述的type 0x85(代表文件类型) attrib 0x10(代表文件夹) start_cluster 为0x05(代表a文件的内容在cluster 5) valid_size 0x20000 (文件夹的大小为0x20000-128K) size 0x20000 (文件夹的大小为0x20000-128K)
关于目录项相关的信息参考
#define EXFAT_ATTRIB_DIR 0x10
#define EXFAT_ATTRIB_ARCH 0x20struct exfat_entry_meta1 /* file or directory info (part 1) */
{uint8_t type; /* EXFAT_ENTRY_FILE */uint8_t continuations;le16_t checksum;le16_t attrib; /* combination of EXFAT_ATTRIB_xxx */le16_t __unknown1;le16_t crtime, crdate; /* creation date and time */le16_t mtime, mdate; /* latest modification date and time */le16_t atime, adate; /* latest access date and time */uint8_t crtime_cs; /* creation time in cs (centiseconds) */uint8_t mtime_cs; /* latest modification time in cs */uint8_t crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */uint8_t __unknown2[7];
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);struct exfat_entry_meta2 /* file or directory info (part 2) */
{uint8_t type; /* EXFAT_ENTRY_FILE_INFO */uint8_t flags; /* combination of EXFAT_FLAG_xxx */uint8_t __unknown1;uint8_t name_length;le16_t name_hash;le16_t __unknown2;le64_t valid_size; /* in bytes, less or equal to size */uint8_t __unknown3[4];le32_t start_cluster;le64_t size; /* in bytes */
}
PACKED;
STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32);
a文件夹的cluster内容如下
1.对于a文件夹的cluster为前面描述的cluster 05。
2.对于a1空文件 type 0x85(代表文件类型) , attrib 0x20(代表文件), start_cluster 为0x0 ,valid_size为0x0, size 为0x0。
3.对于logfs文件 mv成为a2文件,可以看到除了各个exfat_entry的type有变化,其他的
4.关于a2文件type 0x85(代表文件类型) , attrib 0x20(代表文件), start_cluster 为0x08 ,valid_size为 0x20f(527字节), size为 0x20f。
关于FAT表
对于一个大文件的情况,以c文件夹中的c1文件为例子。c1文件大小为1MB,start cluster是 9,每个cluster是128KB,c1文件占用cluster 9到clsuter 16, 8个cluster。
我们看一下c1文件的FAT表。可以看到c1文件(cluster 9->16)的FAT表都是0,c1应该是contiguous。
如何让文件对应的FAT表中有数据呢,就是让文件的cluster number不连续。
1.创建了一个文件c1,大小为1M,占用cluster9->16。
2.在创建一个文件c2,大小为1M,占用cluster17->24。
3.增大文件c1到1.5M,如下图。
在打开FAT表发现c1文件的FAT表中有值。这个是后c1文件占用的cluster为cluster 0x9->0x10(1M) 和0x19->0x1c(512k),其间为c2文件的FAT表,因为c2文件的cluster是连续的所以,c2文件的FAT表为0。
文件,文件的cluster,以及FAT表的关系,看下面函数实现exfat_advance_cluster。
cluster_t exfat_advance_cluster(const struct exfat* ef,struct exfat_node* node, uint32_t count)//count为在文件中以cluster为单位的偏移,该值由文件的offset/cluster size来
{uint32_t i;if (node->fptr_index > count){node->fptr_index = 0;node->fptr_cluster = node->start_cluster;}for (i = node->fptr_index; i < count; i++)//根据cluster number在FAT表中逐个遍历文件的cluser。{node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);//找到当前文件的cluster对应FAT表,根据FAT表找到该文件下一个cluster号。if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster))break; /* the caller should handle this and print appropriateerror message */}node->fptr_index = count;return node->fptr_cluster;
}
调用的exfat_next_cluster函数。
cluster_t exfat_next_cluster(const struct exfat* ef,const struct exfat_node* node, cluster_t cluster)
{le32_t next;off_t fat_offset;if (cluster < EXFAT_FIRST_DATA_CLUSTER)exfat_bug("bad cluster 0x%x", cluster);if (node->is_contiguous)//如果node是is_contiguous状态,返回 cluster+1。return cluster + 1;fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))+ cluster * sizeof(cluster_t);//找到cluster对应的FAT表的offsetif (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0)//读取对应的FAT表中的值,找到下一个cluster号。return EXFAT_CLUSTER_BAD; /* the caller should handle this and printappropriate error message */return le32_to_cpu(next);
}
关于BITMAP
格式化之后的U盘。对于bitmap如下图:
对于upcase,如下图:
对于根目录,如下图:
在根目录下创建一个a文件夹占用cluster4,如下图:
在根目录下创建一个a文件夹占用cluster4时,bitmap的使用情况。可见bitmap从0x07变为0x0f,cluster 4 标记为占用。
代码实现
exfat_mount
1.调用exfat_open函数填充全局变量exfat结构体的exfat_dev。
2.读出fd的DBR,用来填充exfat结构体的exfat_super_block。
3.检查DBR中的exfat_super_block,是否符合要求。
4.调用exfat_cache_directory函数,解析root的cluster,依次解析bitmap,upcase,以及根目录的file,并且建立根目录下面文件的层级关系。
exfat_generic_pwrite
对文件的写,通过该函数实现。解释如下
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,const void* buffer, size_t size, off_t offset)//node是要写入的文件,buffer是写入内容,size是写入内容的大小,offset是对于文件写入地址的偏移{uint64_t newsize = offset;int rc;cluster_t cluster;const char* bufp = buffer;off_t lsize, loffset, remainder;if (offset < 0)return -EINVAL;if (newsize > node->size){rc = exfat_truncate(ef, node, newsize, true);if (rc != 0)return rc;}if (newsize + size > node->size)//如果当前node的size覆盖不了offset(文件写入地址的偏移)或者offset+size(是写入内容的大小),就要exfat_truncate{rc = exfat_truncate(ef, node, newsize + size, false);//参数newsize + size是写入之后文件的size,参数false是指不执行eraseif (rc != 0)return rc;}if (size == 0)return 0;cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb));//根据当前文件在FAT表中的描述,找到offset所在的cluster,从offset所在的cluster开始写。if (CLUSTER_INVALID(*ef->sb, cluster)){exfat_error("invalid cluster 0x%x while writing", cluster);return -EIO;}loffset = newsize % CLUSTER_SIZE(*ef->sb);remainder = size;while (remainder > 0){if (CLUSTER_INVALID(*ef->sb, cluster)){exfat_error("invalid cluster 0x%x while writing", cluster);return -EIO;}lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);if (exfat_pwrite(ef->dev, bufp, lsize,//将buffer中的内容写到相应的offsetexfat_c2o(ef, cluster) + loffset) < 0)//exfat_c2o将cluster的编号转换成offset{exfat_error("failed to write cluster %#x", cluster);return -EIO;}bufp += lsize;loffset = 0;remainder -= lsize;cluster = exfat_next_cluster(ef, node, cluster);//根据当前cluster对应的FAT表中的描述,找到下一个cluster。}if (!(node->attrib & EXFAT_ATTRIB_DIR))/* directory's mtime should be updated by the caller only when itcreates or removes something in this directory */exfat_update_mtime(node);//如果不是文件夹,更新修改日期。return size - remainder;
}
再看exfat_truncate函数实现,函数根据传入的size大小跟node的size大小作比较,如果文件增大有需要就申请cluster,文件减小有需要就释放cluster,这两个操作同时会设置相应的bitmap和FAT表。并设置是否是contiguous。
int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,bool erase)
{uint32_t c1 = bytes2clusters(ef, node->size);//写入之前的文件占用的clusteruint32_t c2 = bytes2clusters(ef, size);//写入之后文件占用的clusterint rc = 0;if (node->references == 0 && node->parent)exfat_bug("no references, node changes can be lost");if (node->size == size)return 0;if (c1 < c2)//写入之前的文件占用的cluster 小于 写入之后文件占用的cluster。 就要调用grow_file申请新的cluster。rc = grow_file(ef, node, c1, c2 - c1);else if (c1 > c2)//写入之前的文件占用的cluster 大于 写入之后文件占用的cluster。 就要调用shrink_file删除不用的cluster。rc = shrink_file(ef, node, c1, c1 - c2);if (rc != 0)return rc;if (erase){rc = erase_range(ef, node, node->size, size);//清空文件相应区间if (rc != 0)return rc;}exfat_update_mtime(node);//更新文件修改时间node->size = size;//更新sizenode->is_dirty = true;//node变为is_dirty。return 0;
}
grow_file函数实现如下:
static int grow_file(struct exfat* ef, struct exfat_node* node,uint32_t current, uint32_t difference)
{cluster_t previous;cluster_t next;uint32_t allocated = 0;if (difference == 0)exfat_bug("zero clusters count passed");if (node->start_cluster != EXFAT_CLUSTER_FREE)//对于非空文件或者文件夹的处理方法,空文件的start_cluster为EXFAT_CLUSTER_FREE(0){/* get the last cluster of the file */previous = exfat_advance_cluster(ef, node, current - 1);//找到上一个clusterif (CLUSTER_INVALID(*ef->sb, previous)){exfat_error("invalid cluster 0x%x while growing", previous);return -EIO;}}else{if (node->fptr_index != 0)exfat_bug("non-zero pointer index (%u)", node->fptr_index);/* file does not have clusters (i.e. is empty), allocatethe first one for it */previous = allocate_cluster(ef, 0);//空文件直接申请一个cluster。allocate_cluster的方式是从第二个参数,逐个遍历bitmap找到没有占用的,返回cluster number,并标记相应的bitmap为1。if (CLUSTER_INVALID(*ef->sb, previous))return -ENOSPC;node->fptr_cluster = node->start_cluster = previous;//fptr_cluster 和 start_clusterallocated = 1;/* file consists of only one cluster, so it's contiguous */node->is_contiguous = true;//node的cluster为连续}while (allocated < difference)//allocated是新生申请的cluster个数,difference是需要的cluster个数。{next = allocate_cluster(ef, previous + 1);//从previous + 1对应的bitmap开始申请cluster。if (CLUSTER_INVALID(*ef->sb, next)){if (allocated != 0)shrink_file(ef, node, current + allocated, allocated);return -ENOSPC;}if (next != previous + 1 && node->is_contiguous)//判断申请的cluster是不是连续。{/* it's a pity, but we are not able to keep the file contiguousanymore */if (!make_noncontiguous(ef, node->start_cluster, previous))//如果不连续,需要填充FAT表!!return -EIO;node->is_contiguous = false;//设置为不连续node->is_dirty = true;//标记为drity}if (!set_next_cluster(ef, node->is_contiguous, previous, next))return -EIO;previous = next;allocated++;}if (!set_next_cluster(ef, node->is_contiguous, previous,EXFAT_CLUSTER_END))return -EIO;return 0;
}
fuse_exfat_mkdir
主要调用了exfat_mkdir函数实现
int exfat_mkdir(struct exfat* ef, const char* path)//根据参数path创建文件夹
{int rc;struct exfat_node* node;rc = create(ef, path, EXFAT_ATTRIB_DIR);//创建文件夹的实现if (rc != 0)return rc;rc = exfat_lookup(ef, &node, path);//找到创建的文件夹,返回node。if (rc != 0)return 0;/* directories always have at least one cluster */rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true);//给创建的文件夹申请一个clusterif (rc != 0){delete(ef, node);exfat_put_node(ef, node);return rc;}rc = exfat_flush_node(ef, node);if (rc != 0){delete(ef, node);exfat_put_node(ef, node);return rc;}exfat_put_node(ef, node);return 0;
}
create函数实现如下:
static int create(struct exfat* ef, const char* path, uint16_t attrib)
{struct exfat_node* dir;struct exfat_node* existing;off_t offset = -1;le16_t name[EXFAT_NAME_MAX + 1];int rc;rc = exfat_split(ef, &dir, &existing, name, path);//根据传入的path,逐级找到创建文件夹的parent。 若能找到,建文件夹的parent的node赋值给参数dir,existing标记为1。if (rc != 0)return rc;if (existing != NULL) //path不存在,退出{exfat_put_node(ef, existing);exfat_put_node(ef, dir);return -EEXIST;}rc = find_slot(ef, dir, &offset,2 + DIV_ROUND_UP(exfat_utf16_length(name), EXFAT_ENAME_MAX));//dir为要创建的文件夹的parent文件夹的node。offset为创建的文件entry在parent文件夹的偏移。if (rc != 0){exfat_put_node(ef, dir);return rc;}rc = commit_entry(ef, dir, name, offset, attrib);//将创建文件夹的信息写入对应的offset,并创建文件夹的node。if (rc != 0){exfat_put_node(ef, dir);return rc;}exfat_update_mtime(dir);//更新parent文件夹的修改时间rc = exfat_flush_node(ef, dir);exfat_put_node(ef, dir);return rc;
}
find_slot函数实现如下:
static int find_slot(struct exfat* ef, struct exfat_node* dir,off_t* offset, int n)//参数n代表需要占用多少个连续的exfat_entry,对于文件来说一般是3个exfat_entry。
{bitmap_t* dmap;struct exfat_node* p;size_t i;int contiguous = 0;if (!dir->is_cached)exfat_bug("directory is not cached");/* build a bitmap of valid entries in the directory */dmap = calloc(BMAP_SIZE(dir->size / sizeof(struct exfat_entry)),//对于文件夹的dir->size,一般是以cluster的size为单位的。sizeof(bitmap_t));//对parent文件夹的cluster中的每个exfat_entry做一个bitmap(注意这个bitmap跟上文提到的cluster使用bitmap无关)if (dmap == NULL){exfat_error("failed to allocate directory bitmap (%"PRIu64")",dir->size / sizeof(struct exfat_entry));return -ENOMEM;}for (p = dir->child; p != NULL; p = p->next)//遍历parent文件夹下所有的文件node。for (i = 0; i < 1u + p->continuations; i++)BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i);//根据node的entry_offset标记bitmap,代表该exfat_entry已经被占用。/* find a slot in the directory entries bitmap */for (i = 0; i < dir->size / sizeof(struct exfat_entry); i++)//遍历bitmap中的exfat_entry,找到连续n个空闲的exfat_entry。{if (BMAP_GET(dmap, i) == 0){if (contiguous++ == 0)*offset = (off_t) i * sizeof(struct exfat_entry);if (contiguous == n)//找到连续n个空闲的exfat_entry。{int rc;/* suitable slot is found, check that it's not occupied */rc = check_slot(ef, dir, *offset, n);//check对应的exfat_entryif (rc == -EINVAL){/* slot at (i-n) is occupied, go back and check (i-n+1) */i -= contiguous - 1;contiguous = 0;}else{/* slot is free or an error occurred */free(dmap);return rc;}}}elsecontiguous = 0;}free(dmap);/* no suitable slots found, extend the directory */if (contiguous == 0)//如果找不到合适的exfat_entry存储文件的node*offset = dir->size;//offset设置到parent文件夹的末端。return exfat_truncate(ef, dir,ROUND_UP(dir->size + sizeof(struct exfat_entry[n - contiguous]),CLUSTER_SIZE(*ef->sb)), true);//parent文件夹申请更多的cluster来存储新文件夹的exfat_entry。
}
exfat_lookup主要是根据参数path,遍历对应个层级的node,最终找到path对应的node之后返回。
int exfat_lookup(struct exfat* ef, struct exfat_node** node,const char* path)
{struct exfat_node* parent;const char* p;size_t n;int rc;/* start from the root directory */parent = *node = exfat_get_node(ef->root);for (p = path; (n = get_comp(p, &p)); p += n)//根据path做文件夹的步进查找{if (n == 1 && *p == '.') /* skip "." component */continue;rc = lookup_name(ef, parent, node, p, n);//exfat_readdir参数exfat_iterator找到parent的node,依次读取对应的parent下的node。if (rc != 0){exfat_put_node(ef, parent);return rc;}exfat_put_node(ef, parent);parent = *node;//查找下一级文件}return 0;
}
fuse_exfat_ops
最后介绍基于fuse的exfat的ops,在exfat的main函数中注册了fuse的exfat ops如下,关于fuse暂不介绍。
rc = fuse_exfat_main(fuse_options, mount_point);
fuse_exfat_ops的各个函数的描述如下:
static struct fuse_operations fuse_exfat_ops =
{.getattr = fuse_exfat_getattr, //从root node开始便利查找,对应path下面的文件,找到对应的node并解析,获取status。.truncate = fuse_exfat_truncate,//首先查找文件,文件存在调用exfat_truncate 通过申请cluster调整文件大小。.readdir = fuse_exfat_readdir,//找到path对应的node,找到描述node的cluster,将cluster中的entry读出来解析。.open = fuse_exfat_open,//找到path对应的node,返回fuse_file_info.create = fuse_exfat_create,//返回fuse_file_info.release = fuse_exfat_release,//释放fuse_file_info对应node的references.flush = fuse_exfat_flush,//flash fuse_file_info的node.fsync = fuse_exfat_fsync,//先给dirty的node都flush,再flush bitmap。最后fsync exfat_dev的fd。.fsyncdir = fuse_exfat_fsync,//同上.read = fuse_exfat_read,//调用exfat_generic_pread函数读文件.write = fuse_exfat_write,//调用exfat_generic_pwrite函数写文件.unlink = fuse_exfat_unlink,//删除文件(不包括文件夹),先调用exfat_lookup查找文件,找到之后调用delete删除。.rmdir = fuse_exfat_rmdir,//删除文件夹,先调用exfat_lookup查找文件,在调用exfat_cache_directory看看当前文件夹中是不是有子文件,若果没有调用delete删除。.mknod = fuse_exfat_mknod,//创建文件.mkdir = fuse_exfat_mkdir,//创建文件夹,主要是调用create函数创建文件夹。.rename = fuse_exfat_rename,//调用exfat_rename函数,重命名,或者移动文件。找到源文件的node,将源文件的node,通过rename_entry函数写到新文件的node。.utimens = fuse_exfat_utimens,//修改文件的mtime和atime.chmod = fuse_exfat_chmod,//检查设置的mode是否合理.chown = fuse_exfat_chown,//设置UID和PID.statfs = fuse_exfat_statfs,//??.init = fuse_exfat_init,//设置sb的volume_state 为EXFAT_STATE_MOUNTED.destroy = fuse_exfat_destroy,//调用 exfat_unmount
};
exfat文件系统-基于fuse相关推荐
- 基于fuse文件系统优化方法总结[附带详细说明]
目前很多文件系统基于Fuse( http://fuse.sourceforge.net/ )开发,作者深入钻研Fuse代码后,总结出开发此类文件系统时可考虑的优化方案,拿出来与大家讨论讨论,如有不准确 ...
- 基于fuse文件系统的android sdcard存储方案:之二
续<基于fuse文件系统的android sdcard存储方案:之一>,再聊聊基于fuse文件系统的android sdcard存储方案:之二, 以后有空再谈谈该方案的缺点,及优化方案. ...
- 基于 NVMe 的 exFAT 文件系统 FPGA IP 核简介
应用场景 用于需要对大数据量进行实时高速存储且需要文件管理的场景,如: ·超高速高清相机数据记录 ·航空成像与测量数据记录 ·雷达数据模拟与仿真 ·宽带卫星中频记录系统 ·汽车安全碰撞测试 ·高速AD ...
- Android 7.0支持exfat文件系统
网上看了一圈,关于Android移植exfat支持的文章大多停留在Android 4.x,Android 6.0或7.0以上的基本空缺,所以决定自己写一篇,本文实践环境是MTK的Android 7.0 ...
- Android 11.0 支持exFAT文件系统
Android 11.0 支持exFAT文件系统 U盘常见文件系统类型有FAT32.NTFS.exFAT, Android默认支持FAT32, 一般也有NTFS类型编译选项, 但是exFAT由于版权 ...
- ExFat文件系统DBR受损恢复案例
ExFat文件系统DBR受损恢复案例 ExFat文件系统也FAT32相类似,同时也结合着NTFS中的知识. 下面我们手工分析EXFat文件系统DBR受损手工分析案例 手工恢复受损的DBR需要计算一下参 ...
- linux内核 fat,微软宣布exFAT文件系统规范,希望被Linux内核接纳
对于那些经常需要在 Linux 和 Windows 10 之间切换工作的人们来说,作为 FAT32 继任者的 exFAT 文件系统,能够为用户带来更好的体验.近日,微软宣布了 exFAT 文件系统规范 ...
- Android5.1设备无法识别exFAT文件系统的64G TF卡问题
64G TF卡刚买回来的时候默认exFAT文件系统,在电脑端(XP和WIN7)可以识别,但在我们Android5.1S设备无法识别,采用guiformat工具格式化为FAT32文件系统后才可以正常识别 ...
- FAT和EXFAT文件系统
源:FAT和EXFAT文件系统 转载于:https://www.cnblogs.com/LittleTiger/p/8022450.html
最新文章
- Keepalived双主模型中vrrp_script中权重改变故障排查
- Ceryx —— 基于 OpenResty 的动态反向代理
- 加密工具类 - CryptoUtils.java
- Goodbye, 2010. Hello 2011...
- 慢速HTTP拒绝服务攻击
- SQL 2005数据库函数基本应用
- Linux ACL 权限
- ViewDidLoad运行多次的问题
- chrome 开发工具插件总结
- 苹果cms v10官网源码下载
- python实现自动开机_python自动循环定时开关机(非重启)测试
- 信息化和信息系统-PMP
- 大学计算机应用经典案例,大学计算机基础经典实验案例集
- Jetpack Compose 从入门到入门(七)
- 腾讯笔试题——五笔编码
- 如何优雅的整合定时批量任务(荣耀典藏版)
- JavaScript 中的事件类型1(读书笔记思维导图)
- 恒流LED驱动芯片 SM15133EK SM15133EH 明微电子
- LY3006A手电筒集成IC可充电多功能 LED 驱动控制芯片
- 数据挖掘——机器学习