1# File Systems 2 3## VFS 4 5 6### Basic Concepts 7 8The Virtual File System (VFS) is not a real file system. It is an abstract layer on top of a heterogeneous file system and provides you with a unified Unix-like interface for file operations. Different types of file systems use different file operation interfaces. If there are multiple types of file systems in a system, different and non-standard interfaces are required for accessing these file systems. The VFS is introduced as an abstract layer to shield the differences between these heterogeneous file systems. With the VFS, you do not need to care about the underlying storage medium and file system type. 9 10The OpenHarmony LiteOS-M kernel supports the File Allocation Table (FAT) and LittleFS file systems. It provides the Portable Operating System Interface (POSIX) over the VFS to ensure interface consistency. However, the VFS of the LiteOS-M kernel is light and does not provide advanced functions (such as pagecache) due to insufficient resources. Therefore, the VFS of the LiteOS-M kernel implements only API standardization and adaptation. The file systems handle specific transactions. The following tables describe the APIs supported by the file systems of the LiteOS-M kernel. 11 12### Available APIs 13 14**Table 1** APIs for file operations 15 16| API| Description| FAT | LittleFS | 17| -------- | -------- | -------- | -------- | 18| open | Opens a file.| Supported| Supported| 19| close | Closes a file.| Supported| Supported| 20| read | Reads the file content. | Supported | Supported | 21| write | Writes data to a file. | Supported | Supported | 22| lseek | Sets the file offset. | Supported | Supported | 23| stat | Obtains file information based on the file path name.| Supported | Supported | 24| unlink | Deletes a file.| Supported| Supported| 25| rename | Renames the file.| Supported| Supported| 26| fstat | Obtains file information based on the file handle. | Supported | Supported | 27| fsync | Saves a file to a storage device. | Supported | Supported | 28 29**Table 2** APIs for directory operations 30 31| API| Description| FATFS | LITTLEFS | 32| -------- | -------- | -------- | -------- | 33| mkdir | Creates a directory.| Supported| Supported| 34| opendir | Opens a directory.| Supported| Supported| 35| readdir | Reads the content of a directory.| Supported| Supported| 36| closedir | Closes a directory.| Supported| Supported| 37| rmdir | Deletes a directory.| Supported| Supported| 38 39**Table 3** APIs for partition operations 40 41| API| Description| FATFS | LITTLEFS | 42| -------- | -------- | -------- | -------- | 43| mount | Mounts a partition.| Supported| Supported| 44| umount | Unmounts a partition.| Supported| Supported| 45| umount2 | Forcibly unmounts a partition using the **MNT_FORCE** parameter.| Supported| Not supported| 46| statfs | Obtains partition information.| Supported| Not supported| 47 48Interfaces, such as **ioctl** and **fcntl**, are supported by different libraries and are irrelevant to the underlying file system. 49 50## FAT 51 52 53### Basic Concepts 54 55As a file system designed for personal computers, the FAT file system consists of the DOS Boot Record (DBR) region, FAT region, and Data region. Each entry in the FAT region records information about the corresponding cluster in the storage device. The cluster information includes whether the cluster is used, number of the next cluster of the file, whether the file ends with the cluster. 56 57The FAT file system supports a variety of formats, including FAT12, FAT16, and FAT32. The numbers 12, 16, and 32 indicate the number of bits per cluster within the FAT, respectively. The FAT file system also supports diversified storage media, especially removable media (such as USB flash drives, SD cards, and removable hard drives). It features good compatibility between embedded devices and desktop systems (such as Windows and Linux) and facilitates file management. 58 59The OpenHarmony kernel supports FAT12, FAT16, and FAT32 file systems. These file systems require a tiny amount of code to implement, use less resources, support a variety of physical media, and are tailorable and compatible with Windows and Linux systems. They also support identification of multiple devices and partitions. The kernel supports multiple partitions on hard drives and allows creation of the FAT file system on the primary and logical partitions. 60 61 62### Development Guidelines 63 64 65#### Driver Adaptation 66 67The use of a FAT file system requires support from the underlying MultiMediaCard (MMC) driver. Before using a FAT file system on a board with an MMC, you must perform the following operations: 68 691. Implement the **disk_status**, **disk_initialize**, **disk_read**, **disk_write**, and **disk_ioctl** APIs to adapt to the embedded MMC (eMMC) driver on the board. 70 712. Add the **fs_config.h** file with information such as **FS_MAX_SS** (maximum sector size of the storage device) and **FF_VOLUME_STRS** (partition names) configured. 72 73 The following is an example: 74 75 76``` 77#define FF_VOLUME_STRS "system", "inner", "update", "user" 78#define FS_MAX_SS 512 79#define FAT_MAX_OPEN_FILES 50 80``` 81 82#### Mounting Partitions 83 84Before using a FAT file system on a device, you need to initialize the flash drive and partition the device storage. 85 86API for partitioning the storage: 87 88**int LOS_DiskPartition(const char \*dev, const char \*fsType, int \*lengthArray, int \*addrArray, int partNum);** 89 90- **dev**: pointer to the device name, for example, **spinorblk0**. 91- **fsType**: pointer to the file system type, which is **vfat** for the FAT file system. 92- **lengthArray**: pointer to a list of partition lengths (in percentage for a FAT file system) of the device. 93- **addrArray**: pointer to a list of partition start addresses of the device. 94- **partNum**: number of partitions. 95 96API for formatting a partition: 97 98**int LOS_PartitionFormat(const char \*partName, char \*fsType, void \*data);** 99 100- **partName**: pointer to the partition name, in the *Device_name*+**p**+*Partition_ number* format. For example, **spinorblk0p0**. 101- **fsType**: pointer to the file system type, which is **vfat** for the FAT file system. 102- **data**: pointer to the private data that passes in **(VOID \*) formatType**, for example, **FMT_FAT** or **FMT_FAT32**. 103 104API for mounting a partition: 105 106**int mount(const char \*source, const char \*target, const char \*filesystemtype, unsigned long mountflags, const void \*data);** 107 108- **source**: pointer to the partition name, in the *Device_name*+**p**+*Partition_ number* format. For example, **spinorblk0p0**. 109- **target**: pointer to the target path to mount. 110- **filesystemtype**: pointer to the file system type, which is **vfat** for the FAT file system. 111- **mountflags**: parameters used for the mount operation. 112- **data**: pointer to the private data that passes in **(VOID \*) formatType**, for example, **FMT_FAT** or **FMT_FAT32**. 113 114The sample code is implemented in **./device/qemu/arm_mps2_an386/liteos_m/board/fs/fs_init.c** and can be directly used on the Quick EMUlator (QEMU) that uses the LiteOS-M kernel. You can modify the code based on the hardware you use. 115 116 #include "fatfs_conf.h" 117 #include "fs_config.h" 118 #include "los_config.h" 119 #include "ram_virt_flash.h" 120 #include "los_fs.h" 121 122 struct fs_cfg { 123 CHAR *mount_point; 124 struct PartitionCfg partCfg; 125 }; 126 127 INT32 FatfsLowLevelInit() 128 { 129 INT32 ret; 130 INT32 i; 131 UINT32 addr; 132 int data = FMT_FAT32; 133 134 const char * const pathName[FF_VOLUMES] = {FF_VOLUME_STRS}; 135 HalLogicPartition *halPartitionsInfo = getPartitionInfo(); /* Function for obtaining the partition lengths and start addresses. Modify it as required. */ 136 INT32 lengthArray[FF_VOLUMES] = {25, 25, 25, 25}; 137 INT32 addrArray[FF_VOLUMES]; 138 139 /* Set the address and length for each partition. */ 140 for (i = 0; i < FF_VOLUMES; i++) { 141 addr = halPartitionsInfo[FLASH_PARTITION_DATA1].partitionStartAddr + i * 0x10000; 142 addrArray[i] = addr; 143 FlashInfoInit(i, addr); 144 } 145 146 /* Set partition information. */ 147 SetupDefaultVolToPartTable(); 148 149 ret = LOS_DiskPartition("spinorblk0", "vfat", lengthArray, addrArray, FF_VOLUMES); 150 printf("%s: DiskPartition %s\n", __func__, (ret == 0) ? "succeed" : "failed"); 151 if (ret != 0) { 152 return -1; 153 } 154 155 ret = LOS_PartitionFormat("spinorblk0p0", "vfat", &data); 156 printf("%s: PartitionFormat %s\n", __func__, (ret == 0) ? "succeed" : "failed"); 157 if (ret != 0) { 158 return -1; 159 } 160 161 ret = mount("spinorblk0p0", "/system", "vfat", 0, &data); 162 printf("%s: mount fs on '%s' %s\n", __func__, pathName[0], (ret == 0) ? "succeed" : "failed"); 163 if (ret != 0) { 164 return -1; 165 } 166 return 0; 167 } 168#### How to Develop 169 170Observe the following when managing files and directories in a FAT file system: 171 172- A file cannot exceed 4 GB. 173- **FAT_MAX_OPEN_FILES** specifies the maximum number files you can open at a time, and **FAT_MAX_OPEN_DIRS** specifies the maximum number of folders you can open at a time. 174- Root directory management is not supported. File and directory names start with the partition name. For example, **user/testfile** indicates the **testfile** file or directory in the **user** partition. 175- To open a file multiple times at the same time, use **O_RDONLY** (read-only mode). **O_RDWR** or **O_WRONLY** (writable mode) can open a file only once at a time. 176- The read and write pointers are not separated. If a file is open in **O_APPEND** mode, the read pointer is also at the end of the file. If you want to read the file from the beginning, you must manually set the position of the read pointer. 177- File and directory permission management is not supported. 178- The **stat** and **fstat** APIs do not support query of the modification time, creation time, and last access time. The Microsoft FAT protocol does not support time before A.D. 1980. 179 180Observe the following when managing files and directories in a FAT file system: 181 182- Partitions can be mounted with the read-only attribute. If the input parameter of **mount()** is **MS_RDONLY**, all APIs with the write attribute, such as **write()**, **mkdir()**, **unlink()**, and **open()** with **non-O_RDONLY** attributes, will be rejected. 183- You can use the **MS_REMOUNT** flag in **mount()** to modify the permissions for a mounted partition. 184- Before unmounting a partition, ensure that all directories and files in the partition are closed. 185- You can use **umount2** with the **MNT_FORCE** parameter to forcibly close all files and folders and unmount the partition. However, this may cause data loss. Therefore, exercise caution when using **umount2**. 186 187You can use **fatfs_fdisk()** and **fatfs_format()** to re-partition the device storage and format the partitions. Observe the following: 188 189- Before using **fatfs_format()**, ensure that the target partition is unmounted and all directories and files in the partition are closed. 190- Before using **fatfs_fdisk**, ensure that all partitions in the device are unmounted. 191- Using **fatfs_fdisk** and **fatfs_format** may cause data loss. Exercise caution when using them. 192 193 194### Development Example 195 196 197#### Example Description 198 199This example implements the following: 200 2011. Create a **system/test** directory. 202 2032. Create a **file.txt** file in the **system/test** directory. 204 2053. Write **Hello OpenHarmony!** at the beginning of the file. 206 2074. Save the file to a device. 208 2095. Set the offset to the start position of the file. 210 2116. Reads the file content. 212 2137. Close the file. 214 2158. Delete the file. 216 2179. Delete the directory. 218 219 220#### Sample Code 221 222**Prerequisites** 223 224- The **system** partition is mounted to the QEMU. 225- FAT is enabled. 226 1. In the **kernel/liteos_m** directory, run the **make menuconfig** command and choose **FileSystem->Enable FS VFS** to enable VFS. 227 2. Select **Enable FAT** to enable the FAT file system. 228 229**Implementation** 230 231The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **ExampleFatfs** function is called in **TestTaskEntry**. 232 233 ``` 234#include <stdio.h> 235#include <string.h> 236#include "sys/stat.h" 237#include "fcntl.h" 238#include "unistd.h" 239 240#define BUF_SIZE 20 241#define TEST_ROOT "system" /* Set the test root directory. */ 242VOID ExampleFatfs(VOID) 243{ 244 int ret; 245 int fd; 246 ssize_t len; 247 off_t off; 248 char dirName[BUF_SIZE] = TEST_ROOT"/test"; 249 char fileName[BUF_SIZE] = TEST_ROOT"/test/file.txt"; 250 char writeBuf[BUF_SIZE] = "Hello OpenHarmony!"; 251 char readBuf[BUF_SIZE] = {0}; 252 253 /* Create a test directory. */ 254 ret = mkdir(dirName, 0777); 255 if (ret != LOS_OK) { 256 printf("mkdir failed.\n"); 257 return; 258 } 259 260 /* Create a file that is readable and writable. */ 261 fd = open(fileName, O_RDWR | O_CREAT, 0777); 262 if (fd < 0) { 263 printf("open file failed.\n"); 264 return; 265 } 266 267 /* Write the content from writeBuf to the file. */ 268 len = write(fd, writeBuf, strlen(writeBuf)); 269 if (len != strlen(writeBuf)) { 270 printf("write file failed.\n"); 271 return; 272 } 273 274 /* Save the file to a storage device. */ 275 ret = fsync(fd); 276 if (ret != LOS_OK) { 277 printf("fsync failed.\n"); 278 return; 279 } 280 281 /* Move the read/write pointer to the beginning of the file. */ 282 off = lseek(fd, 0, SEEK_SET); 283 if (off != 0) { 284 printf("lseek failed.\n"); 285 return; 286 } 287 288 /* Read the file content with the length of readBuf to readBuf. */ 289 len = read(fd, readBuf, sizeof(readBuf)); 290 if (len != strlen(writeBuf)) { 291 printf("read file failed.\n"); 292 return; 293 } 294 printf("%s\n", readBuf); 295 296 /* Close the test file. */ 297 ret = close(fd); 298 if (ret != LOS_OK) { 299 printf("close failed.\n"); 300 return; 301 } 302 303 /* Delete the test file. */ 304 ret = unlink(fileName); 305 if (ret != LOS_OK) { 306 printf("unlink failed.\n"); 307 return; 308 } 309 310 /* Delete the test directory. */ 311 ret = rmdir(dirName); 312 if (ret != LOS_OK) { 313 printf("rmdir failed.\n"); 314 return; 315 } 316 317 return; 318} 319 ``` 320 321 322#### Verification 323 324The development is successful if the return result is as follows: 325 326 327``` 328Hello OpenHarmony! 329``` 330## LittleFS 331 332 333### Basic Concepts 334 335LittleFS is a small file system designed for the flash drive. It stores metadata in log structure and data in the copy-on-write (COW) structure. This feature empowers LittleFS high power-loss resilience. LittleFS uses the statistical wear leveling algorithm when allocating COW data blocks, effectively prolonging the service life of flash devices. LittleFS is designed for small-sized devices with limited resources, such as ROM and RAM. All RAM resources are allocated through a buffer with the fixed size (configurable). That is, the RAM usage does not grow with the file system. 336 337LittleFS is a good choice when you look for a flash file system that is power-cut resilient and has wear leveling support on a small device with limited resources. 338 339 340### Development Guidelines 341 342Before using a LittleFS to a device, you need to initialize the flash drive and partition the device storage 343 344API for partitioning the storage: 345 346**int LOS_DiskPartition(const char \*dev, const char \*fsType, int \*lengthArray, int \*addrArray, int partNum);** 347 348- **dev**: pointer to the device name. 349- **fsType**: pointer to the file system type, which is **littlefs** for LittleFS. 350- **lengthArray**: pointer to a list of partition lengths of the device. 351- **addrArray**: pointer to a list of partition start addresses of the device. 352- **partNum**: number of partitions. 353 354API for formatting a partition: 355 356**int LOS_PartitionFormat(const char \*partName, char \*fsType, void \*data);** 357 358- **partName**: pointer to the partition name. 359- **fsType**: pointer to the file system type, which is **littlefs** for LittleFS. 360- **data**: pointer to the private data that passes in **void pass (VOID \*) struct fs_cfg**. 361 362API for mounting a partition: 363 364**int mount(const char \*source, const char \*target, const char \*filesystemtype, unsigned long mountflags, const void \*data);** 365 366- **source**: pointer to the partition name. 367- **target**: pointer to the target path to mount. 368- **filesystemtype**: pointer to the file system type, which is **littlefs** for LittleFS. 369- **mountflags**: parameters used for the mount operation. 370- **data**: pointer to the private data that passes in **void pass (VOID \*) struct fs_cfg**. 371 372The sample code is implemented in **./device/qemu/arm_mps2_an386/liteos_m/board/fs/fs_init.c** and can be directly used on the QEMU that uses the LiteOS-M kernel. You can modify the code based on the hardware you use. 373 374 375``` 376#include "los_config.h" 377#include "ram_virt_flash.h" 378#include "los_fs.h" 379 380struct fs_cfg { 381 CHAR *mount_point; 382 struct PartitionCfg partCfg; 383}; 384 385INT32 LfsLowLevelInit() 386{ 387 INT32 ret; 388 struct fs_cfg fs[LOSCFG_LFS_MAX_MOUNT_SIZE] = {0}; 389 HalLogicPartition *halPartitionsInfo = getPartitionInfo(); /* Function for obtaining the partition lengths and start addresses. You can modify the function to match your development. */ 390 391 INT32 lengthArray[2]; 392 lengthArray[0]= halPartitionsInfo[FLASH_PARTITION_DATA0].partitionLength; 393 394 INT32 addrArray[2]; 395 addrArray[0] = halPartitionsInfo[FLASH_PARTITION_DATA0].partitionStartAddr; 396 397 ret = LOS_DiskPartition("flash0", "littlefs", lengthArray, addrArray, 2); 398 printf("%s: DiskPartition %s\n", __func__, (ret == 0) ? "succeed" : "failed"); 399 if (ret != 0) { 400 return -1; 401 } 402 fs[0].mount_point = "/littlefs"; 403 fs[0].partCfg.partNo = 0; 404 fs[0].partCfg.blockSize = 4096; /* 4096, lfs block size */ 405 fs[0].partCfg.blockCount = 1024; /* 2048, lfs block count */ 406 fs[0].partCfg.readFunc = virt_flash_read; /* Function for reading data from the flash drive. You can modify it to match your development. */ 407 fs[0].partCfg.writeFunc = virt_flash_write; /* Function for writing data to the flash drive. You can modify it to match your development. */ 408 fs[0].partCfg.eraseFunc = virt_flash_erase; /* Function for erasing the flash driver. You can modify it to match your development. */ 409 410 fs[0].partCfg.readSize = 256; /* 256, lfs read size */ 411 fs[0].partCfg.writeSize = 256; /* 256, lfs prog size */ 412 fs[0].partCfg.cacheSize = 256; /* 256, lfs cache size */ 413 fs[0].partCfg.lookaheadSize = 16; /* 16, lfs lookahead size */ 414 fs[0].partCfg.blockCycles = 1000; /* 1000, lfs block cycles */ 415 416 ret = LOS_PartitionFormat("flash0", "littlefs", &fs[0].partCfg); 417 printf("%s: PartitionFormat %s\n", __func__, (ret == 0) ? "succeed" : "failed"); 418 if (ret != 0) { 419 return -1; 420 } 421 ret = mount(NULL, fs[0].mount_point, "littlefs", 0, &fs[0].partCfg); 422 printf("%s: mount fs on '%s' %s\n", __func__, fs[0].mount_point, (ret == 0) ? "succeed" : "failed"); 423 if (ret != 0) { 424 return -1; 425 } 426 return 0; 427} 428``` 429 430The **.readFunc**, **.writeFunc**, and **.eraseFunc** functions correspond to **read()**, **write()**, and **erase()** of the underlying hardware platform. 431 432**readSize** indicates the number of bytes read each time. You can set it to a value greater than the physical read unit to improve performance. This value determines the size of the read cache. However, if the value is too large, more memory is consumed. 433 434**writeSize** indicates the number of bytes written each time. You can set it to a value greater than the physical write unit to improve performance. This value determines the size of the write cache and must be an integral multiple of **readSize**. However, if the value is too large, more memory is consumed. 435 436**blockSize** indicates the number of bytes in each erase block. The value can be greater than that of the physical erase unit. However, a smaller value is recommended because each file occupies at least one block. The value must be an integral multiple of **writeSize**. 437 438**blockCount** indicates the number of blocks that can be erased, which depends on the capacity of the block device and the size of the block to be erased (**blockSize**). 439 440 441### Sample Code 442 443**Prerequisites** 444 445- **/littlefs** is mounted to the QEMU. 446- LittleFS is enabled. 447 1. In the **kernel/liteos_m** directory, run the **make menuconfig** command and choose **FileSystem->Enable FS VFS** to enable VFS. 448 2. Select **Enable Little FS** to enable the LittleFS. 449 450The sample code is as follows: 451 452The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **ExampleLittlefs** function is called in **TestTaskEntry**. 453 454``` 455#include <stdio.h> 456#include <string.h> 457#include "sys/stat.h" 458#include "fcntl.h" 459#include "unistd.h" 460 461#define BUF_SIZE 20 462#define TEST_ROOT "/littlefs" /* Set the test root directory. */ 463VOID ExampleLittlefs(VOID) 464{ 465 int ret; 466 int fd; 467 ssize_t len; 468 off_t off; 469 char dirName[BUF_SIZE] = TEST_ROOT"/test"; 470 char fileName[BUF_SIZE] = TEST_ROOT"/test/file.txt"; 471 char writeBuf[BUF_SIZE] = "Hello OpenHarmony!"; 472 char readBuf[BUF_SIZE] = {0}; 473 474 /* Create a test directory. */ 475 ret = mkdir(dirName, 0777); 476 if (ret != LOS_OK) { 477 printf("mkdir failed.\n"); 478 return; 479 } 480 481 /* Create a file that is readable and writable. */ 482 fd = open(fileName, O_RDWR | O_CREAT, 0777); 483 if (fd < 0) { 484 printf("open file failed.\n"); 485 return; 486 } 487 488 /* Write the content from writeBuf to the file. */ 489 len = write(fd, writeBuf, strlen(writeBuf)); 490 if (len != strlen(writeBuf)) { 491 printf("write file failed.\n"); 492 return; 493 } 494 495 /* Save the file to a storage device. */ 496 ret = fsync(fd); 497 if (ret != LOS_OK) { 498 printf("fsync failed.\n"); 499 return; 500 } 501 502 /* Move the read/write pointer to the beginning of the file. */ 503 off = lseek(fd, 0, SEEK_SET); 504 if (off != 0) { 505 printf("lseek failed.\n"); 506 return; 507 } 508 509 /* Read the file content with the length of readBuf to readBuf. */ 510 len = read(fd, readBuf, sizeof(readBuf)); 511 if (len != strlen(writeBuf)) { 512 printf("read file failed.\n"); 513 return; 514 } 515 printf("%s\n", readBuf); 516 517 /* Close the test file. */ 518 ret = close(fd); 519 if (ret != LOS_OK) { 520 printf("close failed.\n"); 521 return; 522 } 523 524 /* Delete the test file. */ 525 ret = unlink(fileName); 526 if (ret != LOS_OK) { 527 printf("unlink failed.\n"); 528 return; 529 } 530 531 /* Delete the directory. */ 532 ret = rmdir(dirName); 533 if (ret != LOS_OK) { 534 printf("rmdir failed.\n"); 535 return; 536 } 537 538 return LOS_OK; 539} 540``` 541 542**Verification** 543 544The development is successful if the return result is as follows: 545 546 547``` 548Hello OpenHarmony! 549``` 550 551