• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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