• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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