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