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