简介

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相关推荐

  1. 基于fuse文件系统优化方法总结[附带详细说明]

    目前很多文件系统基于Fuse( http://fuse.sourceforge.net/ )开发,作者深入钻研Fuse代码后,总结出开发此类文件系统时可考虑的优化方案,拿出来与大家讨论讨论,如有不准确 ...

  2. 基于fuse文件系统的android sdcard存储方案:之二

    续<基于fuse文件系统的android sdcard存储方案:之一>,再聊聊基于fuse文件系统的android sdcard存储方案:之二, 以后有空再谈谈该方案的缺点,及优化方案. ...

  3. 基于 NVMe 的 exFAT 文件系统 FPGA IP 核简介

    应用场景 用于需要对大数据量进行实时高速存储且需要文件管理的场景,如: ·超高速高清相机数据记录 ·航空成像与测量数据记录 ·雷达数据模拟与仿真 ·宽带卫星中频记录系统 ·汽车安全碰撞测试 ·高速AD ...

  4. Android 7.0支持exfat文件系统

    网上看了一圈,关于Android移植exfat支持的文章大多停留在Android 4.x,Android 6.0或7.0以上的基本空缺,所以决定自己写一篇,本文实践环境是MTK的Android 7.0 ...

  5. Android 11.0 支持exFAT文件系统

    Android 11.0 支持exFAT文件系统 U盘常见文件系统类型有FAT32.NTFS.exFAT, Android默认支持FAT32,  一般也有NTFS类型编译选项, 但是exFAT由于版权 ...

  6. ExFat文件系统DBR受损恢复案例

    ExFat文件系统DBR受损恢复案例 ExFat文件系统也FAT32相类似,同时也结合着NTFS中的知识. 下面我们手工分析EXFat文件系统DBR受损手工分析案例 手工恢复受损的DBR需要计算一下参 ...

  7. linux内核 fat,微软宣布exFAT文件系统规范,希望被Linux内核接纳

    对于那些经常需要在 Linux 和 Windows 10 之间切换工作的人们来说,作为 FAT32 继任者的 exFAT 文件系统,能够为用户带来更好的体验.近日,微软宣布了 exFAT 文件系统规范 ...

  8. Android5.1设备无法识别exFAT文件系统的64G TF卡问题

    64G TF卡刚买回来的时候默认exFAT文件系统,在电脑端(XP和WIN7)可以识别,但在我们Android5.1S设备无法识别,采用guiformat工具格式化为FAT32文件系统后才可以正常识别 ...

  9. FAT和EXFAT文件系统

    源:FAT和EXFAT文件系统 转载于:https://www.cnblogs.com/LittleTiger/p/8022450.html

最新文章

  1. Keepalived双主模型中vrrp_script中权重改变故障排查
  2. Ceryx —— 基于 OpenResty 的动态反向代理
  3. 加密工具类 - CryptoUtils.java
  4. Goodbye, 2010. Hello 2011...
  5. 慢速HTTP拒绝服务攻击
  6. SQL 2005数据库函数基本应用
  7. Linux ACL 权限
  8. ViewDidLoad运行多次的问题
  9. chrome 开发工具插件总结
  10. 苹果cms v10官网源码下载
  11. python实现自动开机_python自动循环定时开关机(非重启)测试
  12. 信息化和信息系统-PMP
  13. 大学计算机应用经典案例,大学计算机基础经典实验案例集
  14. Jetpack Compose 从入门到入门(七)
  15. 腾讯笔试题——五笔编码
  16. 如何优雅的整合定时批量任务(荣耀典藏版)
  17. JavaScript 中的事件类型1(读书笔记思维导图)
  18. 恒流LED驱动芯片 SM15133EK SM15133EH 明微电子
  19. LY3006A手电筒集成IC可充电多功能 LED 驱动控制芯片
  20. 数据挖掘——机器学习

热门文章

  1. java实现物流查询(使用阿里云物流查询接口)
  2. 2020年全国硕士研究生入学统一考试管理类专业学位联考数学试题——纯题目版
  3. 在QT中使用JSON
  4. python降维之时间类型数据的处理_python数据预处理方式 :数据降维
  5. ps -ef |grep cqs
  6. 公司随便开除人能够解决问题吗?(转载)
  7. Kafka原理以及介绍
  8. 谭浩强C++(第三版)(3)-9-11章
  9. Fedora31开机启动自己的图形界面
  10. 后端SpringBoot和前端vue