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, GRUB_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;