1
/* fshelp.c -- Filesystem helper functions */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2004, 2005 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 2 of the License, or
9
* (at your option) any later version.
11
* This program 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, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
#include <grub/misc.h>
24
#include <grub/disk.h>
25
#include <grub/fshelp.h>
28
/* Lookup the node PATH. The node ROOTNODE describes the root of the
29
directory tree. The node found is returned in FOUNDNODE, which is
30
either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to
31
iterate over all directory entries in the current node.
32
READ_SYMLINK is used to read the symlink if a node is a symlink.
33
EXPECTTYPE is the type node that is expected by the called, an
34
error is generated if the node is not of the expected type. Make
35
sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
36
because GCC has a nasty bug when using regparm=3. */
38
grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
39
grub_fshelp_node_t *foundnode,
40
int (*iterate_dir) (grub_fshelp_node_t dir,
41
int NESTED_FUNC_ATTR (*hook)
42
(const char *filename,
43
enum grub_fshelp_filetype filetype,
44
grub_fshelp_node_t node)),
45
char *(*read_symlink) (grub_fshelp_node_t node),
46
enum grub_fshelp_filetype expecttype)
49
enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR;
52
auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
53
grub_fshelp_node_t currroot,
54
grub_fshelp_node_t *currfound);
56
grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
57
grub_fshelp_node_t currroot,
58
grub_fshelp_node_t *currfound)
60
char fpath[grub_strlen (currpath) + 1];
63
// unsigned int pos = 0;
64
enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
65
grub_fshelp_node_t currnode = currroot;
66
grub_fshelp_node_t oldnode = currroot;
68
auto int NESTED_FUNC_ATTR iterate (const char *filename,
69
enum grub_fshelp_filetype filetype,
70
grub_fshelp_node_t node);
72
auto void free_node (grub_fshelp_node_t node);
74
void free_node (grub_fshelp_node_t node)
76
if (node != rootnode && node != currroot)
80
int NESTED_FUNC_ATTR iterate (const char *filename,
81
enum grub_fshelp_filetype filetype,
82
grub_fshelp_node_t node)
84
if (type == GRUB_FSHELP_UNKNOWN || grub_strcmp (name, filename))
90
/* The node is found, stop iterating over the nodes. */
98
grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
100
/* Remove all leading slashes. */
106
*currfound = currnode;
114
/* Extract the actual part from the pathname. */
115
next = grub_strchr (name, '/');
118
/* Remove all leading slashes. */
123
/* At this point it is expected that the current node is a
124
directory, check if this is true. */
125
if (type != GRUB_FSHELP_DIR)
127
free_node (currnode);
128
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
131
/* Iterate over the directory. */
132
found = iterate_dir (currnode, iterate);
141
/* Read in the symlink and follow it. */
142
if (type == GRUB_FSHELP_SYMLINK)
146
/* Test if the symlink does not loop. */
147
if (++symlinknest == 8)
149
free_node (currnode);
151
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
154
symlink = read_symlink (currnode);
155
free_node (currnode);
163
/* The symlink is an absolute path, go back to the root inode. */
164
if (symlink[0] == '/')
170
/* Lookup the node the symlink points to. */
171
find_file (symlink, oldnode, &currnode);
184
/* Found the node! */
185
if (!next || *next == '\0')
187
*currfound = currnode;
195
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
198
if (!path || path[0] != '/')
200
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
204
err = find_file (path, rootnode, foundnode);
208
/* Check if the node that was found was of the expected type. */
209
if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
210
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
211
else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
212
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
218
/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
219
beginning with the block POS. READ_HOOK should be set before
220
reading a block from the file. GET_BLOCK is used to translate file
221
blocks to disk blocks. The file is FILESIZE bytes big and the
222
blocks have a size of LOG2BLOCKSIZE (in log2). */
224
grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
225
void (*read_hook) (unsigned long sector,
226
unsigned offset, unsigned length),
227
int pos, unsigned int len, char *buf,
228
int (*get_block) (grub_fshelp_node_t node, int block),
229
unsigned int filesize, int log2blocksize)
233
int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
235
/* Adjust len so it we can't read past the end of the file. */
239
blockcnt = ((len + pos)
240
+ blocksize - 1) / blocksize;
242
for (i = pos / blocksize; i < blockcnt; i++)
245
int blockoff = pos % blocksize;
246
int blockend = blocksize;
250
blknr = get_block (node, i);
254
blknr = blknr << log2blocksize;
257
if (i == blockcnt - 1)
259
blockend = (len + pos) % blocksize;
261
/* The last portion is exactly blocksize. */
263
blockend = blocksize;
267
if (i == pos / blocksize)
269
skipfirst = blockoff;
270
blockend -= skipfirst;
273
/* If the block number is 0 this block is not stored on disk but
274
is zero filled instead. */
277
disk->read_hook = read_hook;
279
grub_disk_read (disk, blknr, skipfirst,
286
grub_memset (buf, blocksize - skipfirst, 0);
288
buf += blocksize - skipfirst;
295
grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow)
302
mod = blksize - ((blksize >> 1) << 1);
305
/* Check if it really is a power of two. */
307
return grub_error (GRUB_ERR_BAD_NUMBER,
308
"the blocksize is not a power of two");
312
return GRUB_ERR_NONE;