1@page page_component_vfs Virtual File System
2
3In early days, the amount of data to be stored in embedded systems was relatively small and data types were relatively simple.
4The data were stored by directly writing to a specific address in storage devices. However, with today modern technology, embedded device's functions are getting complicated and required more data storage. Therefore, we need new data management methods to simplify and organize the data storage.
5
6A file system is made up of abstract data types and also a mechanism for providing data access, retrieve, implements, and store them in hierarchical structure. A folder contains multiple files and a file contains multiple organized data on the file system. This chapter explains about the RT-Thread file system, architecture, features and usage of virtual file system in RT-Thread OS.
7
8# An Introduction to DFS
9
10Device File System (DFS) is a virtual file system component and name structure is similar to UNIX files and folders. Following is the files and folders structure:
11
12The root directory is represented by "/". For example, if users want to access to f1.bin file under root directory, it can be accessed by "/f1.bin". If users want to access to f1.bin file under /2019 folder, it can be accessed by "/data/2019/f1.bin" according to their folder paths as in UNIX/Linux unlike Windows System.
13
14## The Architecture of DFS
15
16The main features of the RT-Thread DFS component are:
17
18- Provides a unified POSIX file and directory operations interface for applications: read, write, poll/select, and more.
19- Supports multiple types of file systems, such as FatFS, RomFS, DevFS, etc., and provides management of common files, device files, and network file descriptors.
20- Supports multiple types of storage devices such as SD Card, SPI Flash, Nand Flash, etc.
21
22The hierarchical structure of DFS is shown in the following figure, which is mainly divided into POSIX interface layer, virtual file system layer and device abstraction layer.
23
24![The hierarchical structure of DFS](figures/fs-layer.png)
25
26## POSIX Interface Layer
27
28POSIX stands for Portable Operating System Interface of UNIX (POSIX). The POSIX standard defines the interface standard that the operating system should provide for applications. It is a general term for a series of API standards defined by IEEE for software to run on various UNIX operating systems.
29
30The POSIX standard is intended to achieve software portability at the source code level. In other words, a program written for a POSIX-compatible operating system should be able to compile and execute on any other POSIX operating system (even from another vendor). RT-Thread supports the POSIX standard interface, so it is easy to port Linux/Unix programs to the RT-Thread operating system.
31
32On UNIX-like systems, normal files, device files, and network file descriptors are the same. In the RT-Thread operating system, DFS is used to achieve this uniformity. With the uniformity of such file descriptors, we can use the `poll/select` interface to uniformly poll these descriptors and bring convenience to the implement of the  program functions.
33
34Using the `poll/select` interface to block and simultaneously detect whether a group of  I/O devices which support non-blocking have events (such as readable, writable, high-priority error output, errors, etc.) until a device trigger the event was or exceed the specified wait time. This mechanism can help callers find devices that are currently ready, reducing the complexity of programming.
35
36## Virtual File System Layer
37
38Users can register specific file systems to DFS, such as FatFS, RomFS, DevFS, etc. Here are some common file system types:
39
40* FatFS is a Microsoft FAT format compatible file system developed for small embedded devices. It is written in ANSI C and has good hardware independence and portability. It is the most commonly used file system type in RT-Thread.
41* The traditional RomFS file system is a simple, compact, read-only file system that does not support dynamic erasing and saving or storing data in order, thus it supports applications to run in XIP (execute In Place) method and save RAM space while the system is running.
42* The Jffs2 file system is a log flash file system. It is mainly used for NOR flash memory, based on MTD driver layer, featuring: readable and writable, supporting data compression, Hash table based log file system, and providing crash/power failure security protection, write balance support, etc..
43* DevFS is the device file system. After the function is enabled in the RT-Thread operating system, the devices in the system can be virtualized into files in the `/dev` folder, so that the device can use the interfaces such as `read` and `write` according to the operation mode of the file to operate.
44* NFS (Network File System) is a technology for sharing files over a network between different machines and different operating systems. In the development and debugging phase of the operating system, this technology can be used to build an NFS-based root file system on the host and mount it on the embedded device, which can easily modify the contents of the root file system.
45* UFFS is short for Ultra-low-cost Flash File System. It is an open source file system developed by Chinese people and used for running Nand Flash in small memory environments such as embedded devices. Compared with the Yaffs file system which often used in embedded devices, it has the advantages of less resource consumption, faster startup speed and free.
46
47## Device Abstraction Layer
48
49The device abstraction layer abstracts physical devices such as SD Card, SPI Flash, and Nand Flash into devices that are accessible to the file system. For example, the FAT file system requires that the storage device be a block device type.
50
51Different file system types are implemented independently of the storage device driver, so the file system function can be correctly used after the drive interface of the underlying storage device is docked with the file system.
52
53# Mount Management
54
55The initialization process of the file system is generally divided into the following steps:
56
571. Initialize the DFS component.
582. Initialize a specific type of file system.
593. Create a block device on the memory.
604. Format the block device.
615. Mount the block device to the DFS directory.
626. When the file system is no longer in use, you can unmount it.
63
64## Initialize the DFS Component
65
66The initialization of the DFS component is done by the dfs_init() function. The dfs_init() function initializes the relevant resources required by DFS and creates key data structures that allow DFS to find a specific file system in the system and get a way to manipulate files within a particular storage device. This function will be called automatically if auto-initialization is turned on (enabled by default).
67
68## Registered File System
69
70After the DFS component is initialized, you also need to initialize the specific type of file system used, that is, register a specific type of file system into DFS. The interface to register the file system is as follows:
71
72```c
73int dfs_register(const struct dfs_filesystem_ops *ops);
74```
75
76|**Parameter**|**Description**                          |
77|----------|------------------------------------|
78| ops      | a collection of operation functions of the file system |
79|**return**|**——**                            |
80| 0        | file registered successfully |
81| -1      | file fail to register |
82
83This function does not require user calls, it will be called by the initialization function of different file systems, such as the elm-FAT file system's initialization function `elm_init()`. After the corresponding file system is enabled, if automatic initialization is enabled (enabled by default), the file system initialization function will also be called automatically.
84
85The `elm_init()` function initializes the elm-FAT file system, which calls the `dfs_register(`) function to register the elm-FAT file system with DFS. The file system registration process is shown below:
86
87![Register file system](figures/fs-reg.png)
88
89## Register a Storage Device as a Block Device
90
91Only block devices can be mounted to the file system,  so you need to create the required block devices on the storage device. If the storage device is SPI Flash, you can use the "Serial Flash Universal Driver Library SFUD" component, which provides various SPI Flash drivers, and abstracts the SPI Flash into a block device for mounting. The  process of registering block device is shown as follows:
92
93![The timing diagram of registering block device](figures/fs-reg-block.png)
94
95## Format the file system
96
97After registering a block device, you also need to create a file system of the specified type on the block device, that is, format the file system. You can use the `dfs_mkfs()` function to format the specified storage device and create a file system. The interface to format the file system is as follows:
98
99```c
100int dfs_mkfs(const char * fs_name, const char * device_name);
101```
102
103|**Parameter**   |**Description**                  |
104|-------------|----------------------------|
105| fs_name     | type of the file system |
106| device_name | name of the block device |
107|**return**   |**——**                    |
108| 0           | file system formatted successfully |
109| -1         | fail to format the file system |
110
111The file system type (fs_name) possible values and the corresponding file system is shown in the following table:
112
113|**Value**   |**File System Type**                  |
114|-------------|----------------------------|
115| elm         | elm-FAT file system |
116| jffs2       | jffs2 journaling flash file system |
117| nfs         | NFS network file system |
118| ram         | RamFS file system |
119| rom         | RomFS read-only file system |
120| uffs        | uffs file system |
121
122Take the elm-FAT file system format block device as an example. The formatting process is as follows:
123
124![Formatted file system](figures/elm-fat-mkfs.png)
125
126You can also format the file system using the `mkfs` command. The result of formatting the block device sd0 is as follows:
127
128```shell
129msh />mkfs sd0                    # Sd0 is the name of the block device, the command will format by default
130sd0 is elm-FAT file system
131msh />
132msh />mkfs -t elm sd0             # Use the -t parameter to specify the file system type as elm-FAT file system
133```
134
135## Mount file system
136
137In RT-Thread, mounting refers to attaching a storage device to an existing path. To access a file on a storage device, we must mount the partition where the file is located to an existing path and then access the storage device through this path. The interface to mount the file system is as follows:
138
139```c
140int dfs_mount(const char   *device_name,
141              const char   *path,
142              const char   *filesystemtype,
143              unsigned long rwflag,
144              const void   *data);
145```
146
147|**Parameter**      |**Description**                    |
148|----------------|------------------------------|
149| device_name    | the name of the block device that has been formatted |
150| path           | the mount path |
151| filesystemtype | The type of the mounted file system. Possible values can refer to the dfs_mkfs() function description. |
152| rwflag         | read and write flag bit |
153| data           | private data for a specific file system |
154|**return**      | **——**                     |
155| 0              | file system mounted successfully |
156| -1            | file system mount fail to be mounted |
157
158If there is only one storage device, it can be mounted directly to the root directory `/`.
159
160## Unmount a file system
161
162When a file system does not need to be used anymore, it can be unmounted. The interface to unmount the file system is as follows:
163
164```c
165int dfs_unmount(const char *specialfile);
166```
167
168|**Parameter**   |**Description**                |
169|-------------|--------------------------|
170| specialfile | mount path |
171|**return**   |**——**                  |
172| 0           | unmount the file system successfully |
173| -1         | fail to unmount the file system |
174
175# Document Management
176
177This section introduces the functions that are related to the operation of the file. The operation of the file is generally based on the file descriptor fd, as shown in the following figure:
178
179![common function of file management](figures/fs-mg.png)
180
181## Open and Close Files
182
183To open or create a file, you can call the following open() function:
184
185```c
186int open(const char *file, int flags, ...);
187```
188
189|**Parameter**  |**Description**                            |
190|------------|--------------------------------------|
191| file       | file names that are opened or created |
192| flags      | Specify the way to open the file, and values can refer to the following table. |
193|**return**  |**——**                                 |
194| file descriptor | file opened successfully |
195| -1        | fail to open the file    |
196
197A file can be opened in a variety of ways, and multiple open methods can be specified at the same time. For example, if a file is opened by O_WRONLY and O_CREAT, then when the specified file which need to be open does not exist, it will create the file first and then open it as write-only. The file opening method is as follows:
198
199|**Parameter**|**Description**                     |
200|----------|-----------------------|
201| O_RDONLY | open file in read-only mode |
202| O_WRONLY | open file in write-only mode |
203| O_RDWR   | open file in read-write mode |
204| O_CREAT  | if the file to be open does not exist, then you can create the file |
205| O_APPEND | When the file is read or written, it will start from the end of the file, that is, the data written will be added to the end of the file in an additional way. |
206| O_TRUNC  | empty the contents of the file if it already exists |
207
208If you no longer need to use the file, you can use the `close()` function to close the file, and `close()` will write the data back to disk and release the resources occupied by the file.
209
210```
211int close(int fd);
212```
213
214|**Parameter**|**Description**    |
215|----------|--------------|
216| fd       | file descriptor |
217|**return**|**——**      |
218| 0        | file closed successfully |
219| -1      | fail to close the file |
220
221## Read and Write Data
222
223To read the contents of a file, use the `read()` function:
224
225```c
226int read(int fd, void *buf, size_t len);
227```
228
229|**Parameter**|**Description**                                |
230|----------|------------------------------------------|
231| fd       | file descriptor                |
232| buf      | buffer pointer                |
233| len      | read number of bytes of the files |
234|**return**|**——**                                  |
235| int      | the number of bytes actually read |
236| 0        | read data has reached the end of the file or there is no readable data |
237| -1      | read error, error code to view the current thread's errno |
238
239This function reads the `len` bytes of the file pointed to by the parameter `fd` into the memory pointed to by the `buf pointer`. In addition, the read/write position pointer of the file moves with the byte read.
240
241To write data into a file, use the `write()` function:
242
243```c
244int write(int fd, const void *buf, size_t len);
245```
246
247|**Parameter**|**Description**                             |
248|----------|---------------------------------------|
249| fd      | file descriptor             |
250| buf      | buffer pointer              |
251| len      | the number of bytes written to the file |
252|**return**|**——**                               |
253| int      | the number of bytes actually written |
254| -1      | write error, error code to view the current thread's errno |
255
256This function writes `len` bytes in the memory pointed out by the `buf pointer` into the file pointed out by the parameter `fd`. In addition, the read and write location pointer of the file moves with the bytes written.
257
258## Rename
259
260To rename a file, use the `rename()` function:
261
262```
263int rename(const char *old, const char *new);
264```
265
266|**Parameter**|**Description**    |
267|----------|--------------|
268| old      | file's old name |
269| new      | new name |
270|**return**|**——**      |
271| 0        | change the name successfully |
272| -1      | fail to change the name |
273
274This function changes the file name specified by the parameter `old` to the file name pointed to by the parameter `new`. If the file specified by `new` already exists, the file will be overwritten.
275
276## Get Status
277
278To get the file status, use the following `stat()` function:
279
280```c
281int stat(const char *file, struct stat *buf);
282```
283
284|**Parameter**|**Description**                                  |
285|----------|--------------------------------------------|
286| file | file name                                                    |
287| buf      | structure pointer to a structure that stores file status information |
288|**return**|**——**                                    |
289| 0        | access status successfully    |
290| -1      | fail to access to status       |
291
292## Delete Files
293
294Delete a file in the specified directory using the `unlink()` function:
295
296```
297int unlink(const char *pathname);
298```
299
300|**Parameter**|**Description**              |
301|----------|------------------------|
302| pathname | specify the absolute path to delete the file |
303|**return**|**——**                |
304| 0        | deleted the file successfully |
305| -1      | fail to deleted the file |
306
307## Synchronize File Data to Storage Devices
308
309Synchronize all modified file data in memory to the storage device using the `fsync()` function:
310
311```c
312int fsync(int fildes);
313```
314
315|**Parameter**|**Description**    |
316|----------|--------------|
317| fildes   | file descriptor |
318|**Return**|**——**      |
319| 0        | synchronize files successfully |
320| -1      | fail to synchronize files |
321
322## Query file system related information
323
324Use the `statfs()` function to query file system related information.
325
326```c
327int statfs(const char *path, struct statfs *buf);
328```
329
330|**Parameter**|**Description**                        |
331|----------|----------------------------------|
332| path     | file system mount path |
333| buf      | structure pointer for storing file system information |
334|**Return**|**——**                          |
335| 0        | query file system information successfully |
336| -1      | fail to query file system information |
337
338## Monitor I/O device status
339
340To monitor the I/O device for events, use the `select()` function:
341
342```c
343int select( int nfds,
344            fd_set *readfds,
345            fd_set *writefds,
346            fd_set *exceptfds,
347            struct timeval *timeout);
348```
349
350|**Parameter** |**Description**                                               |
351|-----------|---------------------------------------------------------|
352| nfds      | The range of all file descriptors in the collection, that is, the maximum value of all file descriptors plus 1 |
353| readfds   | Collection of file descriptors that need to monitor read changes |
354| writefds  | Collection of file descriptors that need to monitor write changes |
355| exceptfds | Collection of file descriptors that need to be monitored for exceptions |
356| timeout   | timeout of **select**           |
357|**return** |**——**                                                 |
358| positive value | a read/write event or error occurred in the monitored file collection |
359| 0         | waiting timeout, no readable or writable or erroneous files |
360| negative value | error                                               |
361
362Use the `select()` interface to block and simultaneously detect whether a group of non-blocking I/O devices have events (such as readable, writable, high-priority error output, errors, etc.) until a device triggered an event or exceeded a specified wait time.
363
364# Directory management
365
366This section describes functions that directory management often uses, and operations on directories are generally based on directory addresses, as shown in the following image:
367
368![functions that directory management often uses](figures/fs-dir-mg.png)
369
370## Create and Delete Directories
371
372To create a directory, you can use the mkdir() function:
373
374```c
375int mkdir(const char *path, mode_t mode);
376```
377
378|**Parameter**|**Description**      |
379|----------|----------------|
380| path     | the absolute address of the directory |
381| mode     | create a pattern |
382|**Return**|**——**        |
383| 0        | create directory successfully |
384| -1      | fail to create directory |
385
386This function is used to create a directory as a folder, the parameter path is the absolute path of the directory, the parameter mode is not enabled in the current version, so just fill in the default parameter 0x777.
387
388Delete a directory using the rmdir() function:
389
390```c
391int rmdir(const char *pathname);
392```
393
394|**Parameter**|**Description**              |
395|----------|------------------------|
396| pathname | absolute path to delete the directory |
397|**Return**|**——**                |
398| 0        | delete the directory successfully |
399| -1      | fail to delete the directory |
400
401## Open and Close the Directory
402
403Open the directory to use the `opendir()` function:
404
405```c
406DIR* opendir(const char* name);
407```
408
409|**Parameter**|**Description**                               |
410|----------|-----------------------------------------|
411| name     | absolute address of the directory |
412|**Return**|**——**                                 |
413| DIR      | open the directory successfully, and return to a pointer to the directory stream |
414| NULL     | fail to open                    |
415
416To close the directory, use the `closedir()` function:
417
418```c
419int closedir(DIR* d);
420```
421
422|**Parameter**|**Description**    |
423|----------|--------------|
424| d        | directory stream pointer |
425|**Return**|**——**      |
426| 0        | directory closed successfully |
427| -1     | directory closing error       |
428
429This function is used to close a directory and must be used with the `opendir()` function.
430
431## Read Directory
432
433To read the directory, use the `readdir()` function:
434
435```c
436struct dirent* readdir(DIR *d);
437```
438
439|**Parameter**|**Description**                             |
440|----------|---------------------------------------|
441| d        | directory stream pointer    |
442|**Return**|**——**                               |
443| dirent   | read successfully and return to a structure pointer to a directory entry |
444| NULL     | read to the end of the directory |
445
446This function is used to read the directory, and the parameter d is the directory stream pointer. In addition, each time a directory is read, the pointer position of the directory stream is automatically recursed by 1 position backward.
447
448## Get the Read Position of the Directory Stream
449
450To get the read location of the directory stream, use the `telldir()` function:
451
452```
453long telldir(DIR *d);
454```
455
456|**Parameter**|**Description**        |
457|----------|------------------|
458| d        | directory stream pointer |
459|**Return**|**——**          |
460| long     | read the offset of the position |
461
462The return value of this function records the current position of a directory stream. This return value represents the offset from the beginning of the directory file. You can use this value in the following  `seekdir()` to reset the directory to the current position. In other words, the `telldir()` function can be used with the `seekdir()` function to reset the read position of the directory stream to the specified offset.
463
464## Set the Location to Read the Directory Next Time
465
466Set the location to read the directory next time using the `seekdir()` function:
467
468```
469void seekdir(DIR *d, off_t offset);
470```
471
472|**Parameter**|**Description**                  |
473|----------|----------------------------|
474| d        | directory stream pointer |
475| offset   | the offset value, displacement from this directory |
476
477This is used to set the read position of the parameter d directory stream, and starts reading from this new position when readdir() is called.
478
479## Reset the Position of Reading Directory to the Beginning
480
481To reset the directory stream's read position to the beginning, use the `rewinddir()` function:
482
483```
484void rewinddir(DIR *d);
485```
486
487|**Parameter**|**Description**  |
488|----------|------------|
489| d        | directory stream pointer |
490
491This function can be used to set the current read position of the `d` directory stream to the initial position of the directory stream.
492
493# DFS Configuration Options
494
495The specific configuration path of the file system in menuconfig is as follows:
496
497```c
498RT-Thread Components  --->
499    Device virtual file system  --->
500```
501
502The configuration menu description and corresponding macro definitions are shown in the following table:
503
504|**Configuration Options** |**Corresponding Macro Definition**|**Description** |
505|-------------------------------|-------------------------------|----------------------|
506|[*] Using device virtual file system |RT_USING_DFS |Open DFS virtual file system |
507|[*]   Using working directory |DFS_USING_WORKDIR |open a relative path |
508|(2)   The maximal number of mounted file system |DFS_FILESYSTEMS_MAX |maximum number of mounted file systems |
509|(2)   The maximal number of file system type |DFS_FILESYSTEM_TYPES_MAX |maximum number of supported file systems |
510|(4)   The maximal number of opened files | DFS_FD_MAX|maximum number of open files |
511|[ ]   Using mount table for file system|RT_USING_DFS_MNTTABLE |open the automatic mount table |
512|[*]   Enable elm-chan fatfs |RT_USING_DFS_ELMFAT |open the elm-FatFs file system |
513|[*]   Using devfs for device objects |RT_USING_DFS_DEVFS | open the DevFS device file system |
514|[ ]   Enable ReadOnly file system on flash |RT_USING_DFS_ROMFS |open the RomFS file system |
515|[ ]   Enable RAM file system |RT_USING_DFS_RAMFS |open the RamFS file system |
516|[ ]   Enable UFFS file system: Ultra-low-cost Flash File System |RT_USING_DFS_UFFS |open the UFFS file system |
517|[ ]   Enable JFFS2 file system |RT_USING_DFS_JFFS2 |open the JFFS2 file system |
518|[ ]   Using NFS v3 client file system |RT_USING_DFS_NFS |open the NFS file system |
519
520By default, the RT-Thread operating system does not turn on the relative path function in order to obtain a small memory footprint. When the Support Relative Paths option is not turned on, you should use an absolute directory when working with files and directory interfaces (because there is no currently working directory in the system). If you need to use the current working directory and the relative directory, you can enable the relative path function in the configuration item of the file system.
521
522When the option `[*] Use mount table for file system` is selected, the corresponding macro `RT_USING_DFS_MNTTABLE` will be enabled to turn on the automatic mount table function. The automatic `mount_table[]` is provided by the user in the application code. The user needs to specify the device name, mount path, file system type, read and write flag and private data in the table. After that, the system will traverse the mount table to execute the mount. It should be noted that the mount table must end with `{0}` to judge the end of the table.
523
524The automatic mount table `mount_table []` is shown below, where the five members of `mount_table [0]` are the five parameters of function `dfs_mount ()`. This means that the elm file system is mounted `/` path on the flash 0 device, rwflag is 0, data is 0, `mount_table [1]` is `{0}` as the end to judge the end of the table.
525
526```c
527const struct dfs_mount_tbl mount_table[] =
528{
529    {"flash0", "/", "elm", 0, 0},
530    {0}
531};
532```
533
534## elm-FatFs File System Configuration Option
535
536Elm-FatFs can be further configured after opening the elm-FatFs file system in menuconfig. The configuration menu description and corresponding macro definitions are as follows:
537
538|**Configuration Options** |**Corresponding Macro Definition**|**Description** |
539|---------------------------------|-----------------------------------|-------------------|
540|(437) OEM code page |RT_DFS_ELM_CODE_PAGE |encoding mode |
541|[*] Using RT_DFS_ELM_WORD_ACCESS |RT_DFS_ELM_WORD_ACCESS | |
542|Support long file name (0: LFN disable)  ---> |RT_DFS_ELM_USE_LFN |open long file name submenu |
543|(255) Maximal size of file name length |RT_DFS_ELM_MAX_LFN |maximum file name length |
544|(2) Number of volumes (logical drives) to be used. |RT_DFS_ELM_DRIVES |number of devices mounting FatFs  |
545|(4096) Maximum sector size to be handled. |RT_DFS_ELM_MAX_SECTOR_SIZE |the sector size of the file system|
546|[ ] Enable sector erase feature |RT_DFS_ELM_USE_ERASE | |
547|[*] Enable the reentrancy (thread safe) of the FatFs module |RT_DFS_ELM_REENTRANT |open reentrant|
548
549### Long File Name
550
551By default, FatFs file naming has the following disadvantages:
552
553- The file name (without suffix) can be up to 8 characters long and the suffix can be up to 3 characters long. The file name and suffix will be truncated when the limit is exceeded.
554- File name does not support case sensitivity (displayed in uppercase).
555
556If you need to support long filenames, you need to turn on the option to support long filenames. The  submenu of the long file name is described as follows:
557
558|**Configuration Options** |**Corresponding Macro Definition**|**Description** |
559|----------------------------------|-------------------------|---------------------|
560|( ) 0: LFN disable |RT_DFS_ELM_USE_LFN_0 |close the long file name |
561|( ) 1: LFN with static LFN working buffer|RT_DFS_ELM_USE_LFN_1 |use static buffers to support long file names, and multi-threaded operation of file names will bring re-entry problems |
562|( ) 2: LFN with dynamic LFN working buffer on the stack |RT_DFS_ELM_USE_LFN_2 |long file names are supported by temporary buffers in the stack. Larger demand for stack space. |
563|(X) 3: LFN with dynamic LFN working buffer on the heap |RT_DFS_ELM_USE_LFN_3 |use the heap (malloc request) buffer to store long filenames, it is the safest (default) |
564
565### Encoding Mode
566
567When long file name support is turned on, you can set the encoding mode for the file name. RT-Thread/FatFs uses 437 encoding (American English) by default. If you need to store the Chinese file name, you can use 936 encoding (GBK encoding). The 936 encoding requires a font library of approximately 180KB. If you only use English characters as a file, we recommend using 437 encoding (American English), this will save this 180KB of Flash space.
568
569The file encodings supported by FatFs are as follows:
570
571```c
572/* This option specifies the OEM code page to be used on the target system.
573/  Incorrect setting of the code page can cause a file open failure.
574/
575/   1   - ASCII (No extended character. Non-LFN cfg. only)
576/   437 - U.S.
577/   720 - Arabic
578/   737 - Greek
579/   771 - KBL
580/   775 - Baltic
581/   850 - Latin 1
582/   852 - Latin 2
583/   855 - Cyrillic
584/   857 - Turkish
585/   860 - Portuguese
586/   861 - Icelandic
587/   862 - Hebrew
588/   863 - Canadian French
589/   864 - Arabic
590/   865 - Nordic
591/   866 - Russian
592/   869 - Greek 2
593/   932 - Japanese (DBCS)
594/   936 - Simplified Chinese (DBCS)
595/   949 - Korean (DBCS)
596/   950 - Traditional Chinese (DBCS)
597*/
598```
599
600### File System Sector Size
601
602Specify the internal sector size of FatFs, which needs to be greater than or equal to the sector size of the actual hardware driver. For example, if a spi flash chip sector is 4096 bytes, the above macro needs to be changed to 4096. Otherwise, when the FatFs reads data from the driver, the array will be out of bounds and the system will crash (the new version gives a warning message when the system is executed) .
603
604Usually Flash device can be set to 4096, and the common TF card and SD card have a sector size of 512.
605
606### Reentrant
607
608FatFs fully considers the situation of multi-threaded safe read and write security. When reading and writing FafFs in multi-threading, in order to avoid the problems caused by re-entry, you need to open the macro above. If the system has only one thread to operate the file system and there is no reentrancy problem, you can turn it off to save resources.
609
610### More Configuration
611
612FatFs itself supports a lot of configuration options and the configuration is very flexible. The following file is a FatFs configuration file that can be modified to customize FatFs.
613
614```c
615components/dfs/filesystems/elmfat/ffconf.h
616```
617
618# DFS Application Example
619
620## FinSH Command
621
622After the file system is successfully mounted, the files and directories can be operated. The commonly used FinSH commands for file system operations are shown in the following table:
623
624|**FinSH Command**   |**Description**                      |
625|--------|----------------------------------|
626| ls            | display information about files and directories |
627| cd            | enter the specified directory   |
628| cp            | copy file                         |
629| rm            | delete the file or the directory  |
630| mv            | move the file or rename it     |
631| echo          | write the specified content to the specified file, write the file when it exists, and create a new file and write when the file does not exist. |
632| cat           | display the contents of the file |
633| pwd           | print out the current directory address |
634| mkdir         | create a folder                   |
635| mkfs         | formatted the file system       |
636
637Use the `ls` command to view the current directory information, and the results are as follows:
638
639```c
640msh />ls                          # use the `ls` command to view the current directory information
641Directory /:                      # you can see that the root directory already exists /
642```
643
644Use the `mkdir` command to create a folder, and the results are as follows:
645
646```c
647msh />mkdir rt-thread             # create an rt-thread folder
648msh />ls                          # view directory information as follows
649Directory /:
650rt-thread           <DIR>
651```
652
653Use the `echo` command to output the input string to the specified output location. The result is as follows:
654
655```c
656msh />echo "hello rt-thread!!!"                # outputs the string to standard output
657hello rt-thread!!!
658msh />echo "hello rt-thread!!!" hello.txt      # output the string output to the hello.txt file
659msh />ls
660Directory /:
661rt-thread           <DIR>
662hello.txt           18
663msh />
664```
665
666Use the `cat` command to view the contents of the file. The result is as follows:
667
668```c
669msh />cat hello.txt                     # view the contents of the hello.txt file and output
670hello rt-thread!!!
671```
672
673Use the `rm` command to delete a folder or file. The result is as follows:
674
675```c
676msh />ls                                # view the information of current directory
677Directory /:
678rt-thread           <DIR>
679hello.txt           18
680msh />rm rt-thread                      # delete the rt-thread folder
681msh />ls
682Directory /:
683hello.txt           18
684msh />rm hello.txt                      # delete the hello.txt file
685msh />ls
686Directory /:
687msh />
688```
689
690## Read and Write File Examples
691
692Once the file system is working, you can run the application example. In the sample code, you first create a file `text.txt` using the `open()` function and write the string `"RT -Thread Programmer!\n"` in the file using the `write()` function, and then close the file. Use the ` open()` function again to open the `text.txt` file, read the contents and print it out, and close the file finally.
693
694The sample code is as follows:
695
696```c
697#include <rtthread.h>
698#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
699
700static void readwrite_sample(void)
701{
702    int fd, size;
703    char s[] = "RT-Thread Programmer!", buffer[80];
704
705    rt_kprintf("Write string %s to test.txt.\n", s);
706
707    /* open the ‘/text.txt’ file in create and read-write mode and create the file if it does not exist*/
708    fd = open("/text.txt", O_WRONLY | O_CREAT);
709    if (fd>= 0)
710    {
711        write(fd, s, sizeof(s));
712        close(fd);
713        rt_kprintf("Write done.\n");
714    }
715
716      /* open the ‘/text.txt’ file in read-only mode */
717    fd = open("/text.txt", O_RDONLY);
718    if (fd>= 0)
719    {
720        size = read(fd, buffer, sizeof(buffer));
721        close(fd);
722        rt_kprintf("Read from file test.txt : %s \n", buffer);
723        if (size < 0)
724            return ;
725    }
726  }
727/* export to the msh command list */
728MSH_CMD_EXPORT(readwrite_sample, readwrite sample);
729
730```
731
732## An Example of Changing the File Name
733
734The sample code in this section shows how to modify the file name. The program creates a function `rename_sample()` that manipulates the file and exports it to the msh command list. This function calls the `rename()` function to rename the file named `text.txt` to `text1.txt`. The sample code is as follows:
735
736```c
737#include <rtthread.h>
738#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
739
740static void rename_sample(void)
741{
742    rt_kprintf("%s => %s", "/text.txt", "/text1.txt");
743
744    if (rename("/text.txt", "/text1.txt") < 0)
745        rt_kprintf("[error!]\n");
746    else
747        rt_kprintf("[ok!]\n");
748}
749/* export to the msh command list */
750MSH_CMD_EXPORT(rename_sample, rename sample);
751```
752
753Run the example in the FinSH console and the results are as follows:
754
755```shell
756msh />echo "hello" text.txt
757msh />ls
758Directory /:
759text.txt           5
760msh />rename_sample
761/text.txt => /text1.txt [ok!]
762msh />ls
763Directory /:
764text1.txt           5
765```
766
767In the example demonstration, we first create a file named `text.txt` using the echo command, and then run the sample code to change the file name of the file `text.txt` to `text1.txt`.
768
769## Get File Status Example
770
771The sample code shows how to get the file status. The program creates a function `stat_sample()` that manipulates the file and exports it to the msh command list. This function calls the `stat()` function to get the file size information of the text.txt file. The sample code is as follows:
772
773```c
774#include <rtthread.h>
775#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
776
777static void stat_sample(void)
778{
779    int ret;
780     struct stat buf;
781     ret = stat("/text.txt", &buf);
782    if(ret == 0)
783    rt_kprintf("text.txt file size = %d\n", buf.st_size);
784    else
785    rt_kprintf("text.txt file not fonud\n");
786}
787/* export to the msh command list */
788MSH_CMD_EXPORT(stat_sample, show text.txt stat sample);
789```
790
791Run the example in the FinSH console and the results are as follows:
792
793```c
794msh />echo "hello" text.txt
795msh />stat_sample
796text.txt file size = 5
797```
798
799During the example run, the file `text.txt` is first created with the `echo` command, then the sample code is run, and the file size information for the file `text.txt` is printed.
800
801## Create a Directory Example
802
803The sample code in this section shows how to create a directory. The program creates a function file `mkdir_sample()` that manipulates the file and exports it to the msh command list, which calls the `mkdir()` function to create a folder called `dir_test`. The sample code is as follows:
804
805```c
806#include <rtthread.h>
807#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
808
809static void mkdir_sample(void)
810{
811    int ret;
812
813    /* create a directory */
814    ret = mkdir("/dir_test", 0x777);
815    if (ret < 0)
816    {
817        /* fail to create a directory */
818        rt_kprintf("dir error!\n");
819    }
820    else
821    {
822        /* create a directory successfully */
823        rt_kprintf("mkdir ok!\n");
824    }
825}
826/* export to the msh command list */
827MSH_CMD_EXPORT(mkdir_sample, mkdir sample);
828```
829
830Run the example in the FinSH console and the result is as follows:
831
832```shell
833msh />mkdir_sample
834mkdir ok!
835msh />ls
836Directory /:
837dir_test                 <DIR>    # <DIR> it indicates that the type of the directory is a folder
838```
839
840This example demonstrates creating a folder named `dir_test` in the root directory.
841
842## Read directory Example
843
844The sample code shows how to read the directory. The program creates a function `readdir_sample()` that manipulates the file and exports it to the msh command list. This function calls the `readdir()` function to get the contents of the `dir_test` folder and print it out. The sample code is as follows:
845
846```c
847#include <rtthread.h>
848#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
849
850static void readdir_sample(void)
851{
852    DIR *dirp;
853    struct dirent *d;
854
855    /* open the / dir_test directory */
856    dirp = opendir("/dir_test");
857    if (dirp == RT_NULL)
858    {
859        rt_kprintf("open directory error!\n");
860    }
861    else
862    {
863        /* read the directory */
864        while ((d = readdir(dirp)) != RT_NULL)
865        {
866            rt_kprintf("found %s\n", d->d_name);
867        }
868
869        /* close the directory */
870        closedir(dirp);
871    }
872}
873/* exports to the msh command list */
874MSH_CMD_EXPORT(readdir_sample, readdir sample);
875```
876
877Run the example in the FinSH console and the result is as follows:
878
879```shell
880msh />ls
881Directory /:
882dir_test                 <DIR>
883msh />cd dir_test
884msh /dir_test>echo "hello" hello.txt       # create a hello.txt file
885msh /dir_test>cd ..                        # switch to the parent folder
886msh />readdir_sample
887found hello.txt
888```
889
890In this example, first create a hello.txt file under the dir_test folder and exit the dir_test folder. At this point, run the sample program to print out the contents of the dir_test folder.
891
892## An Example of Setting the location of the read directory
893
894The sample code in this section shows how to set the location to read the directory next time. The program creates a function `telldir_sample()` that manipulates the file and exports it to the msh command list. This function first opens the root directory, then reads all the directory information in the root directory and prints the directory information. Meanwhile, use the `telldir()` function to record the location information of the third directory entry. Before reading the directory information in the root directory for the second time, use the `seekdir()` function to set the read location to the address of the third directory entry previously recorded. At this point, read the information in the root directory again, and the directory information is printed out. The sample code is as follows:
895
896```c
897#include <rtthread.h>
898#include <dfs_posix.h> /* this header file need to be included when you need to operate the file */
899
900/* assume that the file operation is done in one thread */
901static void telldir_sample(void)
902{
903    DIR *dirp;
904    int save3 = 0;
905    int cur;
906    int i = 0;
907    struct dirent *dp;
908
909    /* open the root directory */
910    rt_kprintf("the directory is:\n");
911    dirp = opendir("/");
912
913    for (dp = readdir(dirp); dp != RT_NULL; dp = readdir(dirp))
914    {
915        /* save the directory pointer for the third directory entry */
916        i++;
917        if (i == 3)
918            save3 = telldir(dirp);
919
920        rt_kprintf("%s\n", dp->d_name);
921    }
922
923    /* go back to the directory pointer of the third directory entry you just saved */
924    seekdir(dirp, save3);
925
926    /* Check if the current directory pointer is equal to the pointer to the third directory entry that was saved. */
927    cur = telldir(dirp);
928    if (cur != save3)
929    {
930        rt_kprintf("seekdir (d, %ld); telldir (d) == %ld\n", save3, cur);
931    }
932
933    /* start printing from the third directory entry */
934    rt_kprintf("the result of tell_seek_dir is:\n");
935    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
936    {
937         rt_kprintf("%s\n", dp->d_name);
938    }
939
940    /* close the directory */
941    closedir(dirp);
942}
943/* exports to the msh command list */
944MSH_CMD_EXPORT(telldir_sample, telldir sample);
945```
946
947In this demo example, you need to manually create the five folders from `hello_1` to `hello_5` in the root directory with the `mkdir` command, making sure that there is a folder directory under the root directory for running the sample.
948
949Run the example in the FinSH console and the results are as follows:
950
951```shell
952msh />ls
953Directory /:
954hello_1             <DIR>
955hello_2             <DIR>
956hello_3             <DIR>
957hello_4             <DIR>
958hello_5             <DIR>
959msh />telldir_sample
960the directory is:
961hello_1
962hello_2
963hello_3
964hello_4
965hello_5
966the result of tell_seek_dir is:
967hello_3
968hello_4
969hello_5
970```
971
972After running the sample, you can see that the first time you read the root directory information, it starts from the first folder and prints out all the directory information in the root directory. When the directory information is printed for the second time, since the starting position of the reading is set to the position of the third folder by using the `seekdir()` function, the second time when reading the root directory is from the third folder. Start reading until the last folder, only the directory information from `hello_3` to `hello_5` is printed.
973
974# FAQ
975
976## Q: What should I do if I find that the file name or folder name is not displayed properly?
977
978  **A:** Check if long file name support is enabled, DFS feature configuration section.
979
980## Q: What should I do if the file system fails to initialize?
981
982  **A:** Check if the type and number of file systems allowed to be mounted in the file system configuration project are sufficient.
983
984## Q: What should I do if the file system *mkfs* command fails?
985
986  **A:** Check if the storage device exists. If it exists, check to see if the device driver can pass the function test, if it fails, check the driver error. Check if the libc function is enabled.
987
988## Q: What should I do if the file system fails to mount?
989
990**A:**
991
992- Check if the specified mount path exists. The file system can be mounted directly to the root directory ("/"), but if you want to mount it on another path, such as ("/sdcard"). You need to make sure that the ("/sdcard") path exists. Otherwise, you need to create the `sdcard` folder in the root directory before the mount is successful.
993- Check if the file system is created on the storage device. If there is no file system on the storage device, you need to create a file system on the storage using the `mkfs` command.
994
995## Q: What should I do if SFUD cannot detect the Flash specific model?
996
997  **A:**
998
999- Check if the hardware pin settings are wrong.
1000- Whether the SPI device is already registered.
1001- Whether the SPI device is mounted to the bus.
1002- Check the `Using auto probe flash JEDEC SFDP parameter` and the `Using defined supported flash chip information table' under the 'RT-Thread Components → Device Drivers -> Using SPI Bus/Device device drivers -> Using Serial Flash Universal Driver` menu, to see whether the configuration item is selected, if it is not selected then you need to enable these two options.
1003- If the storage device is still not recognized with the above option turned on, then issues can be raised in the [SFUD](https://github.com/armink/SFUD) project.
1004
1005## Q: Why does the benchmark test of the storage device take too long?
1006
1007**A:**
1008
1009  - Compare the [benchmark test data](https://github.com/armink/SFUD/blob/master/docs/zh/benchmark.txt) when the `system tick` is 1000 and the length of time required for this test. If the time lag is too large, you can think that the test work is not working properly.
1010  - Check the settings of the system tick, because some delay operations will be determined according to the tick time, so you need to set the appropriate `system tick` value according to the system conditions. If the system's `system tick` value is no less than 1000, you will need to use a logic analyzer to check the waveform to determine that the communication rate is normal.
1011
1012## Q: SPI Flash implements elmfat file system, and how to keep some sectors not used by file system?
1013
1014  **A:** You can create multiple block devices for the entire storage device using the [partition](https://github.com/RT-Thread-packages/partition) tool package provided by RT-Thread. And block devices can be assigned different functions.
1015
1016## Q: What should I do if the program gets stuck during the test file system?
1017
1018  **A:** Try using the debugger or print some necessary debugging information to determine where the program is stuck and ask questions.
1019
1020## Q: How can I check the problem of the file system step by step?
1021
1022  **A:** You can step through the problem from the bottom to the top.
1023
1024- First check if the storage device is successfully registered and the function is normal.
1025- Check if a file system is created in the storage device.
1026- Check if the specified file system type is registered to the DFS framework, and often check if the allowed file system types and quantities are sufficient.
1027- Check if DFS is successfully initialized. The initialization of this step is pure software, so the possibility of error is not high. It should be noted that if component auto-initialization is turned on, there is no need to manually initialize it again.
1028