1 /*
2  * Copyright (c) 2019 Peter Bigot Consulting, LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /* Constants, data types, and functions that support testing the Zephyr FS API.
8  *
9  * Includes:
10  *
11  * * A data type that supports building and modifying absolute paths
12  *   without worrying about snprintf or buffer overflow;
13  * * Functions to write known content into files, and to verify that
14  *   content when reading the file.
15  * * A data type that is used to describe file system contents, with
16  *   functions to create the file system and verify file system
17  *   content against the build command description.
18  */
19 
20 #ifndef _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_
21 #define _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_
22 
23 #include <stdarg.h>
24 #include <string.h>
25 #include <zephyr/types.h>
26 #include <zephyr/fs/fs.h>
27 
28 /** Maximum length of a path supported by the test infrastructure. */
29 #define TESTFS_PATH_MAX 127
30 
31 /** Structure holding an absolute file system path. */
32 struct testfs_path {
33 	/* Storage for a maximal path plus NUL */
34 	char path[TESTFS_PATH_MAX + 1];
35 
36 	/* Pointer to the NUL character marking end of string. */
37 	char *eos;
38 };
39 
40 /** A properly cast terminator for variadic arguments to testfs_path
41  * functions.
42  */
43 #define TESTFS_PATH_END ((const char *)NULL)
44 
45 #define TESTFS_BUFFER_SIZE 64
46 
47 /** Initialize the file system path within a mount point.
48  *
49  * Creates an absolute path that begins with the mount point, then
50  * extends it with an arbitrary number of path elements as with
51  * testfs_path_extend().
52  *
53  * @param pp a pointer to the path structure.
54  *
55  * @param mp pointer to the mount point.  A null pointer may be passed
56  * to create the root path without a mount point.
57  *
58  * @param... as with testfs_path_extend()
59  *
60  * @return a pointer to the start of the path.
61  */
62 const char *testfs_path_init(struct testfs_path *pp,
63 			     const struct fs_mount_t *mp,
64 			     ...);
65 
66 /** Extend/modify an existing file system path.
67  *
68  * Given an absolute path this function extends it with additional
69  * path elements.  A forward slash is added between each element.
70  *
71  * If ".." is passed the last element of the path is removed.
72  *
73  * The final argument must be a const char * null pointer such as @ref
74  * TESTFS_PATH_END.
75  *
76  * If adding an element would exceed the maximum allowed path length
77  * extension stops, and the path existing to that point is returned.
78  *
79  * @param pp a pointer to the path structure.
80  *
81  * @param... a sequence of const char * pointers terminating with a
82  * null pointer.
83  *
84  * @return a pointer to the start of the path.
85  */
86 const char *testfs_path_extend(struct testfs_path *pp,
87 			       ...);
88 
testfs_path_copy(struct testfs_path * dp,const struct testfs_path * sp)89 static inline struct testfs_path *testfs_path_copy(struct testfs_path *dp,
90 						   const struct testfs_path *sp)
91 {
92 	size_t len = sp->eos - sp->path;
93 
94 	memcpy(dp->path, sp->path, len + 1);
95 	dp->eos = dp->path + len;
96 
97 	return dp;
98 }
99 
100 /** Write a sequence of constant data to the file.
101  *
102  * Writes are generated in blocks up to TESTFS_BUFFER_SIZE in length.
103  *
104  * @param fp pointer to the file to write
105  *
106  * @param value value of the bytes to write
107  *
108  * @param len number of octets to write
109  *
110  * @return number of octets written, or a negative error code.
111  */
112 int testfs_write_constant(struct fs_file_t *fp,
113 			  uint8_t value,
114 			  unsigned int len);
115 
116 /** Verify that the file contains a sequence of constant data.
117  *
118  * Reads are performed in blocks up to TESTFS_BUFFER_SIZE in length.
119  *
120  * @param fp pointer to the file to read
121  *
122  * @param value the expected value of the constant data.
123  *
124  * @param len the number of times the constant value should appear
125  *
126  * @return the number of times the constant value did appear before
127  * len was reached or a mismatch occurred, or a negative error on file
128  * read failure.
129  */
130 int testfs_verify_constant(struct fs_file_t *fp,
131 			   uint8_t value,
132 			   unsigned int len);
133 
134 /** Write an increasing sequence of bytes to the file.
135  *
136  * Writes are generated in blocks up to TESTFS_BUFFER_SIZE in length.
137  *
138  * @param fp pointer to the file to write
139  *
140  * @param value value of the first byte to write
141  *
142  * @param len number of octets to write
143  *
144  * @return number of octets written, or a negative error code.
145  */
146 int testfs_write_incrementing(struct fs_file_t *fp,
147 			      uint8_t value,
148 			      unsigned int len);
149 
150 /** Verify that the file contains a sequence of increasing data.
151  *
152  * Reads are performed in blocks up to TESTFS_BUFFER_SIZE in length.
153  *
154  * @param fp pointer to the file to read
155  *
156  * @param value the expected value of the first byte of data.
157  *
158  * @param len the number of successive increasing values expected
159  *
160  * @return the number of times the expected value did appear before
161  * len was reached or a mismatch occurred, or a negative error on file
162  * read failure.
163  */
164 int testfs_verify_incrementing(struct fs_file_t *fp,
165 			       uint8_t value,
166 			       unsigned int len);
167 
168 /** Structure used to describe a filesystem layout. */
169 struct testfs_bcmd {
170 	enum fs_dir_entry_type type;
171 	const char *name;
172 	uint32_t size;
173 	uint8_t value;
174 	bool matched;
175 };
176 
177 /* Specify that a directory named _n is to be created, and all entries
178  * up to the next matching EXIT_DIR command are to be created within
179  * that directory.
180  */
181 #define TESTFS_BCMD_ENTER_DIR(_n) {	  \
182 		.type = FS_DIR_ENTRY_DIR, \
183 		.name = _n,		  \
184 }
185 
186 /* Specify that a file named _n is to be created, with _sz bytes of
187  * content that starts with _val and increments with each byte.
188  */
189 #define TESTFS_BCMD_FILE(_n, _val, _sz) {  \
190 		.type = FS_DIR_ENTRY_FILE, \
191 		.name = _n,		   \
192 		.size = _sz,		   \
193 		.value = _val,		   \
194 }
195 
196 /* Specify that the content of the previous matching ENTER_DIR is
197  * complete and subsequent entries should be created in the parent
198  * directory.
199  */
200 #define TESTFS_BCMD_EXIT_DIR(_n) {	  \
201 		.type = FS_DIR_ENTRY_DIR, \
202 }
203 
204 /* Specify that all build commands have been provided. */
205 #define TESTFS_BCMD_END() { \
206 }
207 
208 #define TESTFS_BCMD_IS_ENTER_DIR(cp) (((cp)->type == FS_DIR_ENTRY_DIR) \
209 				      && ((cp)->name != NULL))
210 #define TESTFS_BCMD_IS_EXIT_DIR(cp) (((cp)->type == FS_DIR_ENTRY_DIR) \
211 				     && ((cp)->name == NULL))
212 #define TESTFS_BCMD_IS_FILE(cp) (((cp)->type == FS_DIR_ENTRY_FILE) \
213 				 && ((cp)->name != NULL))
214 #define TESTFS_BCMD_IS_END(cp) (((cp)->type == FS_DIR_ENTRY_FILE) \
215 				&& ((cp)->name == NULL))
216 
217 /** Create a file system hierarchy.
218  *
219  * If an error is returned the
220  *
221  * @param root a path to the directory in which the hierarchy will be
222  * created.  If an error is returned the content of this may identify
223  * where the problem occurred.
224  *
225  * @param cp pointer to a sequence of commands that specify the
226  * directory layout.
227  *
228  * @return Zero after successfully building the hierarchy, or a
229  * negative errno code.
230  */
231 int testfs_build(struct testfs_path *root,
232 		 const struct testfs_bcmd *cp);
233 
234 /** Get the end range pointer for a sequence of build commands.
235  *
236  * @param cp a command within the range
237  *
238  * @return a pointer to the END command in the range.
239  */
testfs_bcmd_end(struct testfs_bcmd * cp)240 static inline struct testfs_bcmd *testfs_bcmd_end(struct testfs_bcmd *cp)
241 {
242 	while (!TESTFS_BCMD_IS_END(cp)) {
243 		++cp;
244 	}
245 	return cp;
246 }
247 
248 /** Verify file system contents against build commands.
249  *
250  * The specified directory is opened and its contents recursively
251  * compared against the build commands for name, size, and content.
252  * Build command record entries that are that are matched will have
253  * their matched flag set; unmatched entries will have a cleared
254  * matched flag.
255  *
256  * @param pp the path to a directory in the file system.
257  *
258  * @param scp the first build command relevant to the directory.
259  *
260  * @param ecp the exclusive last entry in the sequence of build
261  * commands relevant to the directory.
262  *
263  * @return the number of file system entries found that did not match
264  * build command content, or a negative error code on compare or file
265  * system failures.
266  */
267 int testfs_bcmd_verify_layout(struct testfs_path *pp,
268 			      struct testfs_bcmd *scp,
269 			      struct testfs_bcmd *ecp);
270 
271 /** Search a build command range for a match to an entry.
272  *
273  * A match is identified if the directory entry type, name, and size
274  * match between the build command and the file status.  The content
275  * of the file is not verified.
276  *
277  * @param statp pointer to a file system entry
278  *
279  * @param scp pointer to the first build command associated with the
280  * hierarchy level of the entry.
281  *
282  * @param ecp pointer to the exclusive last entry in the build command
283  * range.
284  *
285  * @return a pointer to a build command that matches in type and name,
286  * or a null pointer if no match is found at the top level.
287  */
288 struct testfs_bcmd *testfs_bcmd_find(struct fs_dirent *statp,
289 				     struct testfs_bcmd *scp,
290 				     struct testfs_bcmd *ecp);
291 
292 /** Find the exit dir command that balances the enter dir command.
293  *
294  * @param cp pointer to an ENTER_DIR command.
295  *
296  * @param ecp pointer to the exclusive range end of this level of the
297  * hierarchy.
298  *
299  * @return a pointer to the paired EXIT_DIR command.
300  */
301 struct testfs_bcmd *testfs_bcmd_exitdir(struct testfs_bcmd *cp,
302 					struct testfs_bcmd *ecp);
303 
304 #endif /* _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_ */
305