• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# File System Adaptation
2
3
4## Basic Concepts<a name="section19480121811422"></a>
5
6The purpose of interconnecting with the VFS layer is to implement API functions defined by the VFS layer. You can adapt some APIs based on the file system features and service requirements. Basically, file read and write must be supported. The minimum file system adaptation is as follows:
7
8```
9struct MountOps g_yourFsMountOps = {
10    .Mount = YourMountMethod,
11};
12
13struct file_operations_vfs g_yourFsFileOps = {
14  .read = YourReadMethod,
15  .write = YourWriteMethod,
16}
17
18struct VnodeOps g_yourFsVnodeOps = {
19    .Create = YourCreateMethod;
20    .Lookup = YourLookupMethod;
21    .Reclaim = YourReclaimMethod;
22};
23
24FSMAP_ENTRY(yourfs_fsmap, "your fs name", g_yourFsMountOps, TRUE, TRUE); // Register the file system.
25```
26
27>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>
28>1.  The  **open**  and  **close**  APIs are not necessarily implemented because they are used to operate files and are imperceptible to the underlying file system. You need to implement them only when special operations need to be performed during the open and close operations on the file system.
29>2.  Basic file system knowledge is required for file system adaptation. You need to have a deep understanding of the principles and implementation of the target file system. This section does not include the file system basics in detail. If you have any questions during the adaptation process, refer to the code in the  **kernel/liteos\_a/fs**  directory.
30
31## Adapting the Mount API<a name="section147051940104212"></a>
32
33**Mount**  is the first API called by the file system. This API reads the driver parameters, initializes the file system based on the configuration, and generates the root node of the file system. The  **Mount**  API is defined as follows:
34
35```
36int (*Mount)(struct Mount *mount, struct Vnode *blkDriver, const void *data);
37```
38
39The parameter  **struct Mount \*mount**  specifies information about the mount point. The following variables need to be set during adaptation:
40
41```
42struct Mount {
43    const struct MountOps *ops;        /* Mount-related function hooks */
44    struct Vnode *vnodeCovered;        /* root node of the file system generated after the mount*/
45    void *data;                        /* Private data of the mount point*/
46};
47```
48
49The parameter  **struct Vnode \*blkDriver**  specifies the driver node, which can be used to access the driver.
50
51The parameter  **const void \*data**  specifies the data passed by the  **mount**  command and can be processed according to the requirements of the file system.
52
53The following uses JFFS2 as an example to describe how to adapt the  **mount**  API:
54
55```
56int VfsJffs2Bind(struct Mount *mnt, struct Vnode *blkDriver, const void *data)
57{
58    int ret;
59    int partNo;
60    mtd_partition *p = NULL;
61    struct MtdDev *mtd = NULL;
62    struct Vnode *pv = NULL;
63    struct jffs2_inode *rootNode = NULL;
64
65    LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
66
67    /* Obtain information required by the file system from the driver node, for example, the partition ID for JFFS2. */
68    p = (mtd_partition *)((struct drv_data *)blkDriver->data)->priv;
69    mtd = (struct MtdDev *)(p->mtd_info);
70
71    if (mtd == NULL || mtd->type != MTD_NORFLASH) {
72        LOS_MuxUnlock(&g_jffs2FsLock);
73        return -EINVAL;
74    }
75
76    partNo = p->patitionnum;
77
78    /* Generate a root Vnode for the file system. Do not mix rootNode and root Vnode. The rootNode type is inode, which is the private data maintained in JFFS2. Vnode is a common file node of VFS.
79       This step saves the private information in the file system to the Vnode. As a result, you can directly find the corresponding file in the file system through the Vnode.
80     */
81    ret = jffs2_mount(partNo, &rootNode);
82    if (ret != 0) {
83        LOS_MuxUnlock(&g_jffs2FsLock);
84        return ret;
85    }
86
87    ret = VnodeAlloc(&g_jffs2Vops, &pv);
88    if (ret != 0) {
89        LOS_MuxUnlock(&g_jffs2FsLock);
90        goto ERROR_WITH_VNODE;
91    }
92
93    /* The following enters information about the file corresponding to the Vnode. Some file systems may not support uid, gid, and mode, which can be skipped.*/
94    pv->type = VNODE_TYPE_DIR;
95    pv->data = (void *)rootNode;
96    pv->originMount = mnt;
97    pv->fop = &g_jffs2Fops;
98    mnt->data = p;
99    mnt->vnodeCovered = pv;
100    pv->uid = rootNode->i_uid;
101    pv->gid = rootNode->i_gid;
102    pv->mode = rootNode->i_mode;
103
104    /* HashInsert is used to prevent repeated generation of Vnodes. The second parameter is generally set to the unique file identifier in the file system, for example, the inode address in JFFS2. */
105    (void)VfsHashInsert(pv, rootNode->i_ino);
106
107    g_jffs2PartList[partNo] = blkDriver;
108
109    LOS_MuxUnlock(&g_jffs2FsLock);
110
111    return 0;
112ERROR_WITH_VNODE:
113    return ret;
114}
115...
116...
117const struct MountOps jffs_operations = {
118    .Mount = VfsJffs2Bind,
119    ...
120    ...
121};
122```
123
124Summary:
125
1261.  Obtain the required private information from the driver node.
1272.  Generate the root node of the file system based on the private information.
128
129## Adapting the  **Lookup**  API<a name="section11930181394317"></a>
130
131**Lookup**  is used to search for files. The function prototype of  **Lookup**  is as follows:
132
133```
134int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);
135```
136
137This function searches for the Vnode from the parent node based on the file name \(**name**\) and file name length \(**len**\) and returns the Vnode to the upper layer.
138
139You need to specify the parent node information and file name to implement search for the file of the specified name under the parent directory. The following uses JFFS2 as an example:
140
141```
142int VfsJffs2Lookup(struct Vnode *parentVnode, const char *path, int len, struct Vnode **ppVnode)
143{
144    int ret;
145    struct Vnode *newVnode = NULL;
146    struct jffs2_inode *node = NULL;
147    struct jffs2_inode *parentNode = NULL;
148
149    LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER);
150
151    /* Obtain information about the parent node from the private data.*/
152    parentNode = (struct jffs2_inode *)parentVnode->data;
153
154    /* Obtain information about the target node. Note that the jffs2_lookup function called is a function of JFFS2. */
155    node = jffs2_lookup(parentNode, (const unsigned char *)path, len);
156    if (!node) {
157        LOS_MuxUnlock(&g_jffs2FsLock);
158        return -ENOENT;
159    }
160
161    /* Check whether the located target has an existing Vnode, which corresponds to VfsHashInsert mentioned earlier. */
162    (void)VfsHashGet(parentVnode->originMount, node->i_ino, &newVnode, NULL, NULL);
163    LOS_MuxUnlock(&g_jffs2FsLock);
164    if (newVnode) {
165        newVnode->parent = parentVnode;
166        *ppVnode = newVnode;
167        return 0;
168    }
169
170    /* If the Vnode does not exist, create a Vnode and enter related information. */
171    ret = VnodeAlloc(&g_jffs2Vops, &newVnode);
172    if (ret != 0) {
173        PRINT_ERR("%s-%d, ret: %x\n", __FUNCTION__, __LINE__, ret);
174        (void)jffs2_iput(node);
175        LOS_MuxUnlock(&g_jffs2FsLock);
176        return ret;
177    }
178
179    Jffs2SetVtype(node, newVnode);
180    newVnode->fop = parentVnode->fop;
181    newVnode->data = node;
182    newVnode->parent = parentVnode;
183    newVnode->originMount = parentVnode->originMount;
184    newVnode->uid = node->i_uid;
185    newVnode->gid = node->i_gid;
186    newVnode->mode = node->i_mode;
187
188    /* Insert the newly created Vnode into the hashtable.*/
189    (void)VfsHashInsert(newVnode, node->i_ino);
190
191    *ppVnode = newVnode;
192
193    LOS_MuxUnlock(&g_jffs2FsLock);
194    return 0;
195}
196```
197
198Summary:
199
2001.  Obtain private data from the parent node.
2012.  Obtain the private data of the target file based on the private data.
2023.  Create the target Vnode based on the private data of the target file.
203
204## Summary and Precautions<a name="section5617183014319"></a>
205
206The general adaptation procedure is as follows:
207
2081.  Obtain the private data required by the file system based on the Vnode input parameters.
2092.  Implement the API based on the private data.
2103.  Encapsulate the result in the format required by the Vnode or other APIs and return the result to the upper layer.
211
212The core logic is how to use the private data to implement API functions. These APIs implement common functions of the file systems and are generally implemented before the files systems are ported. Therefore, the key is to determine the private data required by the file system and store the data in the Vnode for later use. Generally, the private data is information that can uniquely locate a file on a storage medium. Most file systems have similar data structures, for example, the inode data structure in JFFS2.
213
214>![](../public_sys-resources/icon-caution.gif) **CAUTION:**<br/>
215>1.  When a file is accessed, the  **Lookup**  API of the file system is not necessarily called. The  **Lookup**  API is called only when the PathCache is invalid.
216>2.  Do not directly return the Vnode located by using  **VfsHashGet**  as the result. The information stored in the Vnode may be invalid. Update the fields and return it.
217>3.  Vnodes are automatically released in the background based on memory usage. If data needs to be stored persistently, do not save it only in Vnodes.
218>4.  The  **Reclaim**  API is automatically called when a Vnode is released. Release the resources used by the private data in this API.
219
220