1 /* fshelp.c -- Filesystem helper functions */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
22 #include <grub/misc.h>
23 #include <grub/disk.h>
24 #include <grub/fshelp.h>
27 /* Lookup the node PATH. The node ROOTNODE describes the root of the
28 directory tree. The node found is returned in FOUNDNODE, which is
29 either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to
30 iterate over all directory entries in the current node.
31 READ_SYMLINK is used to read the symlink if a node is a symlink.
32 EXPECTTYPE is the type node that is expected by the called, an
33 error is generated if the node is not of the expected type. Make
34 sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
35 because GCC has a nasty bug when using regparm=3. */
37 grub_fshelp_find_file (const char *path
, grub_fshelp_node_t rootnode
,
38 grub_fshelp_node_t
*foundnode
,
39 int (*iterate_dir
) (grub_fshelp_node_t dir
,
40 int NESTED_FUNC_ATTR (*hook
)
41 (const char *filename
,
42 enum grub_fshelp_filetype filetype
,
43 grub_fshelp_node_t node
)),
44 char *(*read_symlink
) (grub_fshelp_node_t node
),
45 enum grub_fshelp_filetype expecttype
)
48 enum grub_fshelp_filetype foundtype
= GRUB_FSHELP_DIR
;
51 auto grub_err_t NESTED_FUNC_ATTR
find_file (const char *currpath
,
52 grub_fshelp_node_t currroot
,
53 grub_fshelp_node_t
*currfound
);
55 grub_err_t NESTED_FUNC_ATTR
find_file (const char *currpath
,
56 grub_fshelp_node_t currroot
,
57 grub_fshelp_node_t
*currfound
)
59 char fpath
[grub_strlen (currpath
) + 1];
62 // unsigned int pos = 0;
63 enum grub_fshelp_filetype type
= GRUB_FSHELP_DIR
;
64 grub_fshelp_node_t currnode
= currroot
;
65 grub_fshelp_node_t oldnode
= currroot
;
67 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
68 enum grub_fshelp_filetype filetype
,
69 grub_fshelp_node_t node
);
71 auto void free_node (grub_fshelp_node_t node
);
73 void free_node (grub_fshelp_node_t node
)
75 if (node
!= rootnode
&& node
!= currroot
)
79 int NESTED_FUNC_ATTR
iterate (const char *filename
,
80 enum grub_fshelp_filetype filetype
,
81 grub_fshelp_node_t node
)
83 if (filetype
== GRUB_FSHELP_UNKNOWN
||
84 (grub_strcmp (name
, filename
) &&
85 (! (filetype
& GRUB_FSHELP_CASE_INSENSITIVE
) ||
86 grub_strncasecmp (name
, filename
, LONG_MAX
))))
92 /* The node is found, stop iterating over the nodes. */
93 type
= filetype
& ~GRUB_FSHELP_CASE_INSENSITIVE
;
100 grub_strncpy (fpath
, currpath
, grub_strlen (currpath
) + 1);
102 /* Remove all leading slashes. */
108 *currfound
= currnode
;
116 /* Extract the actual part from the pathname. */
117 next
= grub_strchr (name
, '/');
120 /* Remove all leading slashes. */
125 /* At this point it is expected that the current node is a
126 directory, check if this is true. */
127 if (type
!= GRUB_FSHELP_DIR
)
129 free_node (currnode
);
130 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
133 /* Iterate over the directory. */
134 found
= iterate_dir (currnode
, iterate
);
143 /* Read in the symlink and follow it. */
144 if (type
== GRUB_FSHELP_SYMLINK
)
148 /* Test if the symlink does not loop. */
149 if (++symlinknest
== 8)
151 free_node (currnode
);
153 return grub_error (GRUB_ERR_SYMLINK_LOOP
,
154 "too deep nesting of symlinks");
157 symlink
= read_symlink (currnode
);
158 free_node (currnode
);
166 /* The symlink is an absolute path, go back to the root inode. */
167 if (symlink
[0] == '/')
173 /* Lookup the node the symlink points to. */
174 find_file (symlink
, oldnode
, &currnode
);
187 /* Found the node! */
188 if (! next
|| *next
== '\0')
190 *currfound
= currnode
;
198 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
201 if (!path
|| path
[0] != '/')
203 grub_error (GRUB_ERR_BAD_FILENAME
, "bad filename");
207 err
= find_file (path
, rootnode
, foundnode
);
211 /* Check if the node that was found was of the expected type. */
212 if (expecttype
== GRUB_FSHELP_REG
&& foundtype
!= expecttype
)
213 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a regular file");
214 else if (expecttype
== GRUB_FSHELP_DIR
&& foundtype
!= expecttype
)
215 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
220 /* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
221 beginning with the block POS. READ_HOOK should be set before
222 reading a block from the file. GET_BLOCK is used to translate file
223 blocks to disk blocks. The file is FILESIZE bytes big and the
224 blocks have a size of LOG2BLOCKSIZE (in log2). */
226 grub_fshelp_read_file (grub_disk_t disk
, grub_fshelp_node_t node
,
227 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
230 grub_off_t pos
, grub_size_t len
, char *buf
,
231 grub_disk_addr_t (*get_block
) (grub_fshelp_node_t node
,
232 grub_disk_addr_t block
),
233 grub_off_t filesize
, int log2blocksize
)
235 grub_disk_addr_t i
, blockcnt
;
236 int blocksize
= 1 << (log2blocksize
+ GRUB_DISK_SECTOR_BITS
);
238 /* Adjust LEN so it we can't read past the end of the file. */
239 if (pos
+ len
> filesize
)
240 len
= filesize
- pos
;
242 blockcnt
= ((len
+ pos
) + blocksize
- 1) >> (log2blocksize
+ GRUB_DISK_SECTOR_BITS
);
244 for (i
= pos
>> (log2blocksize
+ GRUB_DISK_SECTOR_BITS
); i
< blockcnt
; i
++)
246 grub_disk_addr_t blknr
;
247 int blockoff
= pos
& (blocksize
- 1);
248 int blockend
= blocksize
;
252 blknr
= get_block (node
, i
);
256 blknr
= blknr
<< log2blocksize
;
259 if (i
== blockcnt
- 1)
261 blockend
= (len
+ pos
) & (blocksize
- 1);
263 /* The last portion is exactly blocksize. */
265 blockend
= blocksize
;
269 if (i
== (pos
>> (log2blocksize
+ GRUB_DISK_SECTOR_BITS
)))
271 skipfirst
= blockoff
;
272 blockend
-= skipfirst
;
275 /* If the block number is 0 this block is not stored on disk but
276 is zero filled instead. */
279 disk
->read_hook
= read_hook
;
281 grub_disk_read (disk
, blknr
, skipfirst
,
288 grub_memset (buf
, 0, blockend
);
290 buf
+= blocksize
- skipfirst
;
297 grub_fshelp_log2blksize (unsigned int blksize
, unsigned int *pow
)
304 mod
= blksize
- ((blksize
>> 1) << 1);
307 /* Check if it really is a power of two. */
309 return grub_error (GRUB_ERR_BAD_NUMBER
,
310 "the blocksize is not a power of two");
314 return GRUB_ERR_NONE
;