1 /*
2  * Copyright (c) 2023 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef __EXT2_IMPL_H__
8 #define __EXT2_IMPL_H__
9 
10 #include <zephyr/fs/fs.h>
11 #include <zephyr/fs/ext2.h>
12 
13 #include "ext2_struct.h"
14 
15 extern struct k_heap direntry_heap;
16 
17 void error_behavior(struct ext2_data *fs, const char *msg);
18 
19 /* Memory allocation for ext2 implementation */
20 void *ext2_heap_alloc(size_t size);
21 void  ext2_heap_free(void *ptr);
22 
23 /* Initialization of disk storage. */
24 int ext2_init_disk_access_backend(struct ext2_data *fs, const void *storage_dev, int flags);
25 
26 /**
27  * @brief Get block from the disk.
28  */
29 struct ext2_block *ext2_get_block(struct ext2_data *fs, uint32_t block);
30 
31 struct ext2_block *ext2_get_empty_block(struct ext2_data *fs);
32 
33 /**
34  * @brief Free the block structure.
35  */
36 void ext2_drop_block(struct ext2_block *b);
37 
38 /**
39  * @brief Write block to the disk.
40  *
41  * NOTICE: to ensure that all writes has ended the sync of disk must be triggered
42  * (fs::sync function).
43  */
44 int ext2_write_block(struct ext2_data *fs, struct ext2_block *b);
45 
46 void ext2_init_blocks_slab(struct ext2_data *fs);
47 
48 /**
49  * @brief Write block to the disk.
50  *
51  * NOTICE: to ensure that all writes has ended the sync of disk must be triggered
52  * (fs::sync function).
53  */
54 int ext2_write_block(struct ext2_data *fs, struct ext2_block *b);
55 
56 int ext2_assign_block_num(struct ext2_data *fs, struct ext2_block *b);
57 
58 /* FS operations */
59 
60 /**
61  * @brief Initialize structure with data needed to access the storage device
62  *
63  * @param fs File system data structure to initialize
64  * @param storage_dev Pointer to storage
65  * @param flags Additional flags (e.g. RO flag)
66  *
67  * @retval 0 on success
68  * @retval -EINVAL when superblock of ext2 was not detected
69  * @retval -ENOTSUP when described file system is not supported
70  * @retval <0 other error
71  */
72 int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags);
73 
74 /**
75  * @brief Verify superblock of file system
76  *
77  * Checks if file system is supported by the implementation.
78  * @retval 0 when superblock is valid
79  * @retval -EROFS when superblock is not valid but file system may be mounted read only
80  * @retval -EINVAL when superblock is not valid and file system cannot be mounted at all
81  * @retval -ENOTSUP when superblock has some field set to value that we don't support
82  */
83 int ext2_verify_disk_superblock(struct ext2_disk_superblock *sb);
84 
85 /**
86  * @brief Initialize all data needed to perform operations on file system
87  *
88  * Fetches the superblock. Initializes structure fields.
89  */
90 int ext2_init_fs(struct ext2_data *fs);
91 
92 /**
93  * @brief Clear the data used by file system implementation
94  *
95  */
96 int ext2_close_fs(struct ext2_data *fs);
97 
98 /**
99  * @brief Clear the data used to communicate with storage device
100  *
101  */
102 int ext2_close_struct(struct ext2_data *fs);
103 
104 /**
105  * @brief Create the ext2 file system
106  *
107  * This function uses functions stored in `ext2_data` structure to create new
108  * file system on storage device.
109  *
110  * NOTICE: fs structure must be first initialized with `ext2_init_fs` function.
111  *
112  * After this function succeeds the `ext2_clean` function must be called.
113  *
114  * @param fs File system data (must be initialized before)
115  *
116  * @retval 0 on success
117  * @retval -ENOSPC when storage device is too small for ext2 file system
118  * @retval -ENOTSUP when storage device is too big (file systems with more than
119  *         8192 blocks are not supported)
120  */
121 int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg);
122 
123 /* Lookup flags */
124 #define LOOKUP_ARG_OPEN BIT(0)
125 #define LOOKUP_ARG_CREATE BIT(1)
126 #define LOOKUP_ARG_STAT BIT(2)
127 #define LOOKUP_ARG_UNLINK BIT(3)
128 
129 /* Structure for lookup arguments and results.
130  *
131  * Required fields (must be filled when lookup function is invoked):
132  *  - path
133  *  - flags
134  *
135  * Fields that hold the result:
136  *  - inode
137  *  - parent
138  *  - offset
139  *  - name_pos
140  *  - name_len
141  *
142  * Some of these fields have a meaning only for a specific function.
143  * (E.g. during stat only the fields parent and offset are used)
144  *
145  * Field is marked with these labels when lookup is used from other function:
146  *  OP -- open
147  *  CR -- create
148  *  ST -- stat
149  *  UN -- unlink
150  */
151 struct ext2_lookup_args {
152 	const char *path;  /* path of inode */
153 	struct ext2_inode *inode;  /* (OP, CR, ST, UN) found inode */
154 	struct ext2_inode *parent; /* (CR, ST, UN) parent of found inode */
155 	uint32_t offset;   /* (CR, ST, UN) offset of entry in directory */
156 	uint32_t name_pos; /* (CR) position of name in input path */
157 	uint32_t name_len; /* (CR) length of name */
158 	uint8_t flags;     /* indicates from which function lookup is invoked */
159 };
160 
161 /**
162  * @brief Look for an inode.
163  *
164  * @param fs File system data
165  * @param args All needed arguments for lookup
166  *
167  * @retval 0 on success
168  * @retval -ENOENT inode or path component not found
169  * @retval -ENOTDIR path component not a directory
170  * @retval <0 other error
171  */
172 int ext2_lookup_inode(struct ext2_data *fs, struct ext2_lookup_args *args);
173 
174 /* Inode operations */
175 
176 /**
177  * @brief Read from inode at given offset
178  *
179  * @param inode Inode
180  * @param buf Buffer to hold read data
181  * @param offset Offset in inode
182  * @param nbytes Number of bytes to read
183  *
184  * @retval >=0 number of bytes read on success
185  * @retval <0 error code
186  */
187 ssize_t ext2_inode_read(struct ext2_inode *inode, void *buf, uint32_t offset,
188 			size_t nbytes);
189 
190 /**
191  * @brief Write to inode at given offset
192  *
193  * @param inode Inode
194  * @param buf Buffer with data to write
195  * @param offset Offset in inode
196  * @param nbytes Number of bytes to write
197  *
198  * @retval >=0 number of bytes read on success
199  * @retval <0 error code
200  */
201 ssize_t ext2_inode_write(struct ext2_inode *inode, const void *buf,
202 			 uint32_t offset, size_t nbytes);
203 
204 /**
205  * @brief Truncate the inode
206  *
207  * @param inode Inode
208  * @param size New size for inode
209  *
210  * @retval 0 on success
211  * @retval -ENOTSUP when requested size is too big
212  * @retval <0 other error
213  */
214 int ext2_inode_trunc(struct ext2_inode *inode, off_t size);
215 
216 /**
217  * @brief Sync currently fetched blocks
218  *
219  * @param inode Inode
220  *
221  */
222 int ext2_inode_sync(struct ext2_inode *inode);
223 
224 /* Directory operations */
225 
226 /**
227  * @brief Get directory entry
228  *
229  * Reads directory entry that is at offset specified in `ext2_file` structure.
230  *
231  * @param dir Read directory
232  * @param ent Directory entry to fill in
233  *
234  * @retval 0 on success
235  * @retval <0 on error
236  */
237 int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent);
238 
239 /**
240  * @brief Create a directory entry with given attributes
241  *
242  * Automatically calculates and sets de_rec_len field.
243  *
244  * NOTE: if you need to adjust the size (e.g. when this entry is the last one in the block)
245  *       then just update the size after this function returns.
246  *
247  * @param name Name of direntry
248  * @param namelen Length of name
249  * @param ino Inode associated with that entry
250  * @param filetype File type of that entry
251  *
252  * @returns structure allocated on direntry_heap filled with given data
253  */
254 struct ext2_direntry *ext2_create_direntry(const char *name, uint8_t namelen, uint32_t ino,
255 		uint8_t filetype);
256 
257 /**
258  * @brief Create a file
259  *
260  * @param parent Parent directory
261  * @param inode Pointer to inode structure that will be filled with new inode
262  * @param args Lookup arguments that describe file to create
263  *
264  * @retval 0 on success
265  * @retval -ENOSPC there is not enough memory on storage device to create a file
266  * @retval <0 on error
267  */
268 int ext2_create_file(struct ext2_inode *parent, struct ext2_inode *inode,
269 		     struct ext2_lookup_args *args);
270 
271 
272 /**
273  * @brief Create a directory
274  *
275  * @param parent Parent directory
276  * @param inode Pointer to inode structure that will be filled with new inode
277  * @param args Lookup arguments that describe directory to create
278  *
279  * @retval 0 on success
280  * @retval -ENOSPC there is not enough memory on storage device to create a file
281  * @retval <0 on error
282  */
283 int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *inode,
284 		    struct ext2_lookup_args *args);
285 
286 /**
287  * @brief Unlink the directory entry at given offset in parent directory
288  *
289  * @param parent Parent directory
290  * @param inode File to unlink
291  * @param offset Offset of unlinked file in the parent directory
292  *
293  * @retval 0 on success
294  * @retval -ENOTEMPTY when directory to unlink is not empty
295  * @retval <0 other error
296  */
297 int ext2_inode_unlink(struct ext2_inode *parent, struct ext2_inode *inode,
298 		      uint32_t offset);
299 
300 /**
301  * @brief Move a file
302  *
303  * Invoked when rename destination entry doesn't exist.
304  *
305  * @param args_from Describe source file
306  * @param args_to Describe destination
307  *
308  * @retval 0 on success
309  * @retval <0 on error
310  */
311 int ext2_move_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *args_to);
312 
313 /**
314  * @brief Replace the file with another
315  *
316  * Invoked when rename destination entry does exist
317  *
318  * @param args_from Describe source file
319  * @param args_to Describe destination file
320  *
321  * @retval 0 on success
322  * @retval <0 on error
323  */
324 int ext2_replace_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *args_to);
325 
326 /* Inode pool operations */
327 
328 /**
329  * @brief Get the inode
330  *
331  * Retrieves inode structure and stores it in the inode pool. The inode is
332  * filled with data of requested inode.
333  *
334  * @param fs File system data
335  * @param ino Inode number
336  * @param ret Pointer to place where to store new inode pointer
337  *
338  * @retval 0 on success
339  * @retval -ENOMEM when there is no memory to hold the requested inode
340  * @retval <0 on error
341  */
342 int ext2_inode_get(struct ext2_data *fs, uint32_t ino, struct ext2_inode **ret);
343 
344 /**
345  * @brief Remove reference to the inode structure
346  *
347  * When removed reference is the last reference to that inode then it is freed.
348  *
349  * @param inode Dropped inode
350  *
351  * @retval 0 on success
352  * @retval -EINVAL the dropped inode is not stored in the inode pool
353  * @retval <0 on error
354  */
355 int ext2_inode_drop(struct ext2_inode *inode);
356 
357 /* Drop blocks fetched in inode structure. */
358 void ext2_inode_drop_blocks(struct ext2_inode *inode);
359 
360 /**
361  * @brief Remove all blocks starting with some block
362  *
363  * @param first First block to remove
364  *
365  * @retval >=0 number of removed blocks
366  * @retval <0 error code
367  */
368 int64_t ext2_inode_remove_blocks(struct ext2_inode *inode, uint32_t first);
369 
370 #endif /* __EXT2_IMPL_H__ */
371