1# File System Adaptation<a name="EN-US_TOPIC_0000001078936814"></a> 2 3- [Basic Concepts](#section19480121811422) 4- [Adapting the Mount API](#section147051940104212) 5- [Adapting the Lookup API](#section11930181394317) 6- [Summary and Precautions](#section5617183014319) 7 8## Basic Concepts<a name="section19480121811422"></a> 9 10The 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: 11 12``` 13struct MountOps g_yourFsMountOps = { 14 .Mount = YourMountMethod, 15}; 16 17struct file_operations_vfs g_yourFsFileOps = { 18 .read = YourReadMethod, 19 .write = YourWriteMethod, 20} 21 22struct VnodeOps g_yourFsVnodeOps = { 23 .Create = YourCreateMethod; 24 .Lookup = YourLookupMethod; 25 .Reclaim = YourReclaimMethod; 26}; 27 28FSMAP_ENTRY(yourfs_fsmap, "your fs name", g_yourFsMountOps, TRUE, TRUE); // Register the file system. 29``` 30 31> **NOTE:** 32>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 these APIs only when special operations need to be performed during the open and close operations on the file system. 33>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. 34 35## Adapting the Mount API<a name="section147051940104212"></a> 36 37**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: 38 39``` 40int (*Mount)(struct Mount *mount, struct Vnode *blkDriver, const void *data); 41``` 42 43The parameter **struct Mount \*mount** specifies information about the mount point. The following variables need to be set during adaptation: 44 45``` 46struct Mount { 47 const struct MountOps *ops; /* Mount-related function hooks */ 48 struct Vnode *vnodeCovered; /* root node of the file system generated after the mount*/ 49 void *data; /* Private data of the mount point*/ 50}; 51``` 52 53The parameter **struct Vnode \*blkDriver** specifies the driver node, which can be used to access the driver. 54 55The parameter **const void \*data** specifies the data passed by the **mount** command and can be processed according to the requirements of the file system. 56 57The following uses JFFS2 as an example to describe how the adapt the **mount** API: 58 59``` 60int VfsJffs2Bind(struct Mount *mnt, struct Vnode *blkDriver, const void *data) 61{ 62 int ret; 63 int partNo; 64 mtd_partition *p = NULL; 65 struct MtdDev *mtd = NULL; 66 struct Vnode *pv = NULL; 67 struct jffs2_inode *rootNode = NULL; 68 69 LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER); 70 71 /* Obtain information required by the file system from the driver node, for example, the partition ID for the JFFS2. */ 72 p = (mtd_partition *)((struct drv_data *)blkDriver->data)->priv; 73 mtd = (struct MtdDev *)(p->mtd_info); 74 75 if (mtd == NULL || mtd->type != MTD_NORFLASH) { 76 LOS_MuxUnlock(&g_jffs2FsLock); 77 return -EINVAL; 78 } 79 80 partNo = p->patitionnum; 81 82 /* 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. 83 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. 84 */ 85 ret = jffs2_mount(partNo, &rootNode); 86 if (ret != 0) { 87 LOS_MuxUnlock(&g_jffs2FsLock); 88 return ret; 89 } 90 91 ret = VnodeAlloc(&g_jffs2Vops, &pv); 92 if (ret != 0) { 93 LOS_MuxUnlock(&g_jffs2FsLock); 94 goto ERROR_WITH_VNODE; 95 } 96 97 /* 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.*/ 98 pv->type = VNODE_TYPE_DIR; 99 pv->data = (void *)rootNode; 100 pv->originMount = mnt; 101 pv->fop = &g_jffs2Fops; 102 mnt->data = p; 103 mnt->vnodeCovered = pv; 104 pv->uid = rootNode->i_uid; 105 pv->gid = rootNode->i_gid; 106 pv->mode = rootNode->i_mode; 107 108 /* 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. */ 109 (void)VfsHashInsert(pv, rootNode->i_ino); 110 111 g_jffs2PartList[partNo] = blkDriver; 112 113 LOS_MuxUnlock(&g_jffs2FsLock); 114 115 return 0; 116ERROR_WITH_VNODE: 117 return ret; 118} 119... 120... 121const struct MountOps jffs_operations = { 122 .Mount = VfsJffs2Bind, 123 ... 124 ... 125}; 126``` 127 128Summary: 129 1301. Obtain the required private information from the driver node. 1312. Generate the root node of the file system based on the private information. 132 133## Adapting the **Lookup** API<a name="section11930181394317"></a> 134 135**Lookup** is used to search for files. The function prototype of **Lookup** is: 136 137``` 138int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode); 139``` 140 141This 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. 142 143You 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: 144 145``` 146int VfsJffs2Lookup(struct Vnode *parentVnode, const char *path, int len, struct Vnode **ppVnode) 147{ 148 int ret; 149 struct Vnode *newVnode = NULL; 150 struct jffs2_inode *node = NULL; 151 struct jffs2_inode *parentNode = NULL; 152 153 LOS_MuxLock(&g_jffs2FsLock, (uint32_t)JFFS2_WAITING_FOREVER); 154 155 /* Obtain information about the parent node from the private data.*/ 156 parentNode = (struct jffs2_inode *)parentVnode->data; 157 158 /* Obtain information about the target node. Note that the jffs2_lookup function called is a function of JFFS2. */ 159 node = jffs2_lookup(parentNode, (const unsigned char *)path, len); 160 if (!node) { 161 LOS_MuxUnlock(&g_jffs2FsLock); 162 return -ENOENT; 163 } 164 165 /* Check whether the located target has an existing vnode, which corresponds to VfsHashInsert mentioned earlier. */ 166 (void)VfsHashGet(parentVnode->originMount, node->i_ino, &newVnode, NULL, NULL); 167 LOS_MuxUnlock(&g_jffs2FsLock); 168 if (newVnode) { 169 newVnode->parent = parentVnode; 170 *ppVnode = newVnode; 171 return 0; 172 } 173 174 /* If the Vnode does not exist, create a Vnode and enter related information. */ 175 ret = VnodeAlloc(&g_jffs2Vops, &newVnode); 176 if (ret != 0) { 177 PRINT_ERR("%s-%d, ret: %x\n", __FUNCTION__, __LINE__, ret); 178 (void)jffs2_iput(node); 179 LOS_MuxUnlock(&g_jffs2FsLock); 180 return ret; 181 } 182 183 Jffs2SetVtype(node, newVnode); 184 newVnode->fop = parentVnode->fop; 185 newVnode->data = node; 186 newVnode->parent = parentVnode; 187 newVnode->originMount = parentVnode->originMount; 188 newVnode->uid = node->i_uid; 189 newVnode->gid = node->i_gid; 190 newVnode->mode = node->i_mode; 191 192 /* Insert the newly created Vnode into the hashtable.*/ 193 (void)VfsHashInsert(newVnode, node->i_ino); 194 195 *ppVnode = newVnode; 196 197 LOS_MuxUnlock(&g_jffs2FsLock); 198 return 0; 199} 200``` 201 202Summary: 203 2041. Obtain private data from the parent node. 2052. Obtain the private data of the target file based on the private data. 2063. Create the target Vnode based on the private data of the target file. 207 208## Summary and Precautions<a name="section5617183014319"></a> 209 210The general adaptation procedure is as follows: 211 2121. Obtain the private data required by the file system based on the Vnode input parameters. 2132. Implement the API based on the private data. 2143. Encapsulate the result in the format required by the Vnode or other APIs and return the result to the upper layer. 215 216The 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 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. 217 218> **CAUTION:** 219>1. When a file is accessed, the **Lookup** API of the file system is not necessrily called. The **Lookup** API is called only when the path cache is invalid. 220>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. 221>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. 222>4. The **Reclaim** API is automatically called when a Vnode is released. Release the resources used by the private data in this API. 223 224