1 /****************************************************************************
2 * fs/romfs/fs_romfsutil.c
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership. The
7 * ASF licenses this file to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance with the
9 * License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 *
19 ****************************************************************************/
20
21 /****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25 #include <sys/types.h>
26
27 #include <stdint.h>
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <assert.h>
34
35 #ifdef LOSCFG_FS_ROMFS
36 #include "fs_romfs.h"
37
38 /****************************************************************************
39 * Private Functions
40 ****************************************************************************/
41
42 /****************************************************************************
43 * Name: romfs_swap32
44 *
45 * Description:
46 * Convert the 32-bit big-endian value to little endian
47 *
48 ****************************************************************************/
49
romfs_swap32(uint32_t value)50 inline uint32_t romfs_swap32(uint32_t value)
51 {
52 return ((((value) & 0x000000ff) << 24) | (((value) & 0x0000ff00) << 8) |
53 (((value) & 0x00ff0000) >> 8) | (((value) & 0xff000000) >> 24));
54 }
55
56 /****************************************************************************
57 * Name: romfs_devread32
58 *
59 * Description:
60 * Read the big-endian 32-bit value from the mount device buffer
61 *
62 * Assumption:
63 * All values are aligned to 32-bit boundaries
64 *
65 ****************************************************************************/
66
romfs_devread32(struct romfs_mountpt_s * rm,int ndx)67 static uint32_t romfs_devread32(struct romfs_mountpt_s *rm, int ndx)
68 {
69 /* Extract the value */
70
71 uint32_t value = *(uint32_t *)&rm->rm_buffer[ndx];
72
73 /* Convert the big-endian value to native host endianness. */
74
75 return romfs_swap32(value);
76 }
77
78 /****************************************************************************
79 * Name: romfs_checkentry
80 *
81 * Description:
82 * Check if the entry at offset is a directory or file path segment
83 *
84 ****************************************************************************/
85
romfs_checkentry(struct romfs_mountpt_s * rm,uint32_t offset,const char * entryname,int entrylen,struct romfs_dirinfo_s * dirinfo)86 static inline int romfs_checkentry(struct romfs_mountpt_s *rm,
87 uint32_t offset, const char *entryname,
88 int entrylen,
89 struct romfs_dirinfo_s *dirinfo)
90 {
91 char name[NAME_MAX + 1];
92 uint32_t linkoffset;
93 uint32_t next;
94 uint32_t info;
95 uint32_t size;
96 int ret;
97
98 /* Parse the directory entry at this offset (which may be re-directed
99 * to some other entry if HARLINKED).
100 */
101
102 ret = romfs_parsedirentry(rm, offset, &linkoffset, &next, &info, &size);
103 if (ret < 0)
104 {
105 return ret;
106 }
107
108 /* Now we are pointing to the real entry of interest. Is it a
109 * directory? Or a file?
110 */
111
112 if (IS_DIRECTORY(next) || IS_FILE(next))
113 {
114 /* Get the name of the directory entry. */
115
116 ret = romfs_parsefilename(rm, offset, name);
117 if (ret < 0)
118 {
119 return ret;
120 }
121
122 /* Then check if this the name segment we are looking for. The
123 * string comparison is awkward because there is no terminator
124 * on entryname (there is a terminator on name, however)
125 */
126
127 if (memcmp(entryname, name, entrylen) == 0 &&
128 strlen(name) == entrylen)
129 {
130 /* Found it -- save the component info and return success */
131
132 if (IS_DIRECTORY(next))
133 {
134 dirinfo->rd_dir.fr_firstoffset = info;
135 dirinfo->rd_dir.fr_curroffset = info;
136 dirinfo->rd_size = 0;
137 }
138 else
139 {
140 dirinfo->rd_dir.fr_curroffset = offset;
141 dirinfo->rd_size = size;
142 }
143
144 dirinfo->rd_next = next;
145 return OK;
146 }
147 }
148
149 /* The entry is not a directory or it does not have the matching name */
150
151 return -ENOENT;
152 }
153
154 /****************************************************************************
155 * Name: romfs_devcacheread
156 *
157 * Description:
158 * Read the specified sector for specified offset into the sector cache.
159 * Return the index into the sector corresponding to the offset
160 *
161 ****************************************************************************/
162
romfs_devcacheread(struct romfs_mountpt_s * rm,uint32_t offset)163 uint32_t romfs_devcacheread(struct romfs_mountpt_s *rm, uint32_t offset)
164 {
165 return offset;
166 }
167
168 /****************************************************************************
169 * Name: romfs_followhardlinks
170 *
171 * Description:
172 * Given the offset to a file header, check if the file is a hardlink.
173 * If so, traverse the hard links until the terminal, non-linked header
174 * so found and return that offset.
175 *
176 ****************************************************************************/
177
romfs_followhardlinks(struct romfs_mountpt_s * rm,uint32_t offset,uint32_t * poffset)178 static int romfs_followhardlinks(struct romfs_mountpt_s *rm, uint32_t offset,
179 uint32_t *poffset)
180 {
181 uint32_t next;
182 uint32_t ndx;
183 int i;
184
185 /* Loop while we are redirected by hardlinks */
186
187 for (i = 0; i < ROMF_MAX_LINKS; i++)
188 {
189 /* Read the sector containing the offset into memory */
190
191 ndx = romfs_devcacheread(rm, offset);
192 if (ndx < 0)
193 {
194 return ndx;
195 }
196
197 /* Check if this is a hard link */
198
199 next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
200 if (!IS_HARDLINK(next))
201 {
202 *poffset = offset;
203 return OK;
204 }
205
206 /* Follow the hard-link */
207
208 offset = romfs_devread32(rm, ndx + ROMFS_FHDR_INFO);
209 }
210
211 return -ELOOP;
212 }
213
214 /****************************************************************************
215 * Name: romfs_searchdir
216 *
217 * Description:
218 * This is part of the romfs_finddirentry log. Search the directory
219 * beginning at dirinfo->fr_firstoffset for entryname.
220 *
221 ****************************************************************************/
222
romfs_searchdir(struct romfs_mountpt_s * rm,const char * entryname,int entrylen,uint32_t firstoffset,struct romfs_dirinfo_s * dirinfo)223 int romfs_searchdir(struct romfs_mountpt_s *rm,
224 const char *entryname, int entrylen, uint32_t firstoffset,
225 struct romfs_dirinfo_s *dirinfo)
226 {
227 uint32_t offset;
228 uint32_t next;
229 uint32_t ndx;
230 int ret;
231
232 /* Then loop through the current directory until the directory
233 * with the matching name is found. Or until all of the entries
234 * the directory have been examined.
235 */
236
237 offset = firstoffset;
238 do
239 {
240 /* Read the sector into memory (do this before calling
241 * romfs_checkentry() so we won't have to read the sector
242 * twice in the event that the offset refers to a hardlink).
243 */
244
245 ndx = romfs_devcacheread(rm, offset);
246 if (ndx < 0)
247 {
248 return ndx;
249 }
250
251 /* Because everything is chunked and aligned to 16-bit boundaries,
252 * we know that most the basic node info fits into the sector.
253 */
254
255 next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT) & RFNEXT_OFFSETMASK;
256
257 /* Check if the name this entry is a directory with the matching
258 * name
259 */
260
261 ret = romfs_checkentry(rm, offset, entryname, entrylen, dirinfo);
262 if (ret == OK)
263 {
264 /* Its a match! Return success */
265
266 return OK;
267 }
268
269 /* No match... select the offset to the next entry */
270
271 offset = next;
272 }
273 while (next != 0);
274
275 /* There is nothing in this directory with that name */
276
277 return -ENOENT;
278 }
279
280 /****************************************************************************
281 * Public Functions
282 ****************************************************************************/
283
284 /****************************************************************************
285 * Name: romfs_semtake
286 ****************************************************************************/
287
romfs_semtake(struct romfs_mountpt_s * rm)288 void romfs_semtake(struct romfs_mountpt_s *rm)
289 {
290 int ret;
291
292 do
293 {
294 /* Take the semaphore (perhaps waiting) */
295
296 ret = sem_wait(&rm->rm_sem);
297
298 /* The only case that an error should occur here is if the wait was
299 * awakened by a signal.
300 */
301
302 DEBUGASSERT(ret == OK || ret == -EINTR);
303 }
304 while (ret == -EINTR);
305 }
306
307 /****************************************************************************
308 * Name: romfs_semgive
309 ****************************************************************************/
310
romfs_semgive(struct romfs_mountpt_s * rm)311 void romfs_semgive(struct romfs_mountpt_s *rm)
312 {
313 (void)sem_post(&rm->rm_sem);
314 }
315
316 /****************************************************************************
317 * Name: romfs_hwconfigure
318 *
319 * Description:
320 * This function is called as part of the ROMFS mount operation It
321 * configures the ROMFS filestem for use on this block driver. This includes
322 * the accounting for the geometry of the device, setting up any XIP modes
323 * of operation, and/or allocating any cache buffers.
324 *
325 ****************************************************************************/
326
romfs_hwconfigure(struct romfs_mountpt_s * rm)327 int romfs_hwconfigure(struct romfs_mountpt_s *rm)
328 {
329 uint32_t total_size;
330
331 if (!rm->rm_buffer)
332 {
333 return -ENOMEM;
334 }
335
336
337 total_size = romfs_devread32(rm, ROMFS_VHDR_SIZE);
338
339 rm->rm_hwnsectors = total_size;
340 rm->rm_hwsectorsize = 1;
341 rm->rm_cachesector = (uint32_t)-1;
342 rm->rm_volsize = total_size;
343
344 return OK;
345 }
346
347 /****************************************************************************
348 * Name: romfs_fsconfigure
349 *
350 * Description:
351 * This function is called as part of the ROMFS mount operation It
352 * sets up the mount structure to include configuration information contained
353 * in the ROMFS header. This is the place where we actually determine if
354 * the media contains a ROMFS filesystem.
355 *
356 ****************************************************************************/
357
romfs_fsconfigure(struct romfs_mountpt_s * rm)358 int romfs_fsconfigure(struct romfs_mountpt_s *rm)
359 {
360 const char *name;
361 uint32_t ndx;
362
363 /* Then get information about the ROMFS filesystem on the devices managed
364 * by this block driver. Read sector zero which contains the volume header.
365 */
366
367 ndx = romfs_devcacheread(rm, 0);
368 if (ndx < 0)
369 {
370 return ndx;
371 }
372
373 /* Verify the magic number at that identifies this as a ROMFS filesystem */
374
375 if (memcmp(rm->rm_buffer, ROMFS_VHDR_MAGIC, 8) != 0)
376 {
377 return -EINVAL;
378 }
379
380 /* The root directory entry begins right after the header */
381
382 name = (const char *)&rm->rm_buffer[ROMFS_VHDR_VOLNAME];
383 rm->rm_rootoffset = ROMFS_ALIGNUP(ROMFS_VHDR_VOLNAME + strlen(name) + 1);
384
385 /* and return success */
386
387 rm->rm_mounted = true;
388 return OK;
389 }
390
391 /****************************************************************************
392 * Name: romfs_checkmount
393 *
394 * Description: Check if the mountpoint is still valid.
395 *
396 * The caller should hold the mountpoint semaphore
397 *
398 ****************************************************************************/
399
romfs_checkmount(struct romfs_mountpt_s * rm)400 int romfs_checkmount(struct romfs_mountpt_s *rm)
401 {
402 return OK;
403 }
404
405 /****************************************************************************
406 * Name: romfs_parsedirentry
407 *
408 * Description:
409 * Return the directory entry at this offset. If rf is NULL, then the
410 * mount device resources are used. Otherwise, file resources are used.
411 *
412 ****************************************************************************/
413
romfs_parsedirentry(struct romfs_mountpt_s * rm,uint32_t offset,uint32_t * poffset,uint32_t * pnext,uint32_t * pinfo,uint32_t * psize)414 int romfs_parsedirentry(struct romfs_mountpt_s *rm, uint32_t offset,
415 uint32_t *poffset, uint32_t *pnext, uint32_t *pinfo,
416 uint32_t *psize)
417 {
418 uint32_t save;
419 uint32_t next;
420 uint32_t ndx;
421 int ret;
422
423 /* Read the sector into memory */
424
425 ndx = romfs_devcacheread(rm, offset);
426 if (ndx < 0)
427 {
428 return ndx;
429 }
430
431 /* Yes.. Save the first 'next' value. That has the offset needed to
432 * traverse the parent directory. But we may need to change the type
433 * after we follow the hard links.
434 */
435
436 save = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
437
438 /* Traverse hardlinks as necessary to get to the real file header */
439
440 ret = romfs_followhardlinks(rm, offset, poffset);
441 if (ret < 0)
442 {
443 return ret;
444 }
445
446 if (*poffset != offset)
447 {
448 ndx = romfs_devcacheread(rm, *poffset);
449 if (ndx < 0)
450 {
451 return ndx;
452 }
453 }
454
455 /* Because everything is chunked and aligned to 16-bit boundaries,
456 * we know that most the basic node info fits into the sector. The
457 * associated name may not, however.
458 */
459
460 next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
461 *pnext = (save & RFNEXT_OFFSETMASK) | (next & RFNEXT_ALLMODEMASK);
462 *pinfo = romfs_devread32(rm, ndx + ROMFS_FHDR_INFO);
463 *psize = romfs_devread32(rm, ndx + ROMFS_FHDR_SIZE);
464
465 return OK;
466 }
467
468 /****************************************************************************
469 * Name: romfs_parsefilename
470 *
471 * Description:
472 * Return the filename from directory entry at this offset
473 *
474 ****************************************************************************/
475
romfs_parsefilename(struct romfs_mountpt_s * rm,uint32_t offset,char * pname)476 int romfs_parsefilename(struct romfs_mountpt_s *rm, uint32_t offset,
477 char *pname)
478 {
479 uint32_t ndx;
480 uint16_t namelen;
481 uint16_t chunklen;
482 bool done;
483
484 /* Loop until the whole name is obtained or until NAME_MAX characters
485 * of the name have been parsed.
486 */
487
488 offset += ROMFS_FHDR_NAME;
489 for (namelen = 0, done = false; namelen < NAME_MAX && !done; )
490 {
491 /* Read the sector into memory */
492
493 ndx = romfs_devcacheread(rm, offset + namelen);
494 if (ndx < 0)
495 {
496 return ndx;
497 }
498
499 /* Is the name terminated in this 16-byte block */
500
501 if (rm->rm_buffer[ndx + 15] == '\0')
502 {
503 /* Yes.. then this chunk is less than 16 */
504
505 chunklen = strlen((char *)&rm->rm_buffer[ndx]);
506 done = true;
507 }
508 else
509 {
510 /* No.. then this chunk is 16 bytes in length */
511
512 chunklen = 16;
513 }
514
515 /* Check if we would exceed the NAME_MAX */
516
517 if (namelen + chunklen > NAME_MAX)
518 {
519 chunklen = NAME_MAX - namelen;
520 done = true;
521 }
522
523 /* Copy the chunk */
524
525 memcpy(&pname[namelen], &rm->rm_buffer[ndx], chunklen);
526 namelen += chunklen;
527 }
528
529 /* Terminate the name (NAME_MAX+1 chars total) and return success */
530
531 pname[namelen] = '\0';
532
533 return OK;
534 }
535
536 /****************************************************************************
537 * Name: romfs_datastart
538 *
539 * Description:
540 * Given the offset to a file header, return the offset to the start of
541 * the file data
542 *
543 ****************************************************************************/
544
romfs_datastart(struct romfs_mountpt_s * rm,uint32_t offset,uint32_t * start)545 int romfs_datastart(struct romfs_mountpt_s *rm, uint32_t offset,
546 uint32_t *start)
547 {
548 uint32_t ndx;
549 int ret;
550
551 /* Traverse hardlinks as necessary to get to the real file header */
552
553 ret = romfs_followhardlinks(rm, offset, &offset);
554 if (ret < 0)
555 {
556 return ret;
557 }
558
559 /* Loop until the header size is obtained. */
560
561 offset += ROMFS_FHDR_NAME;
562 for (; ; )
563 {
564 /* Read the sector into memory */
565
566 ndx = romfs_devcacheread(rm, offset);
567 if (ndx < 0)
568 {
569 return ndx;
570 }
571
572 /* Get the offset to the next chunk */
573
574 offset += 16;
575 if (offset >= rm->rm_volsize)
576 {
577 return -EIO;
578 }
579
580 /* Is the name terminated in this 16-byte block */
581
582 if (rm->rm_buffer[ndx + 15] == '\0')
583 {
584 /* Yes.. then the data starts at the next chunk */
585
586 *start = offset;
587 return OK;
588 }
589 }
590
591 return -EINVAL; /* Won't get here */
592 }
593
594 #endif
595