]>
Commit | Line | Data |
---|---|---|
b2499b29 | 1 | /* xfs.c - XFS. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
da9a6a94 | 4 | * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. |
b2499b29 | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
b2499b29 | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
b2499b29 | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
b2499b29 | 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. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
b2499b29 | 18 | */ |
19 | ||
20 | #include <grub/err.h> | |
21 | #include <grub/file.h> | |
22 | #include <grub/mm.h> | |
23 | #include <grub/misc.h> | |
24 | #include <grub/disk.h> | |
25 | #include <grub/dl.h> | |
26 | #include <grub/types.h> | |
27 | #include <grub/fshelp.h> | |
28 | ||
29 | #define XFS_INODE_EXTENTS 9 | |
30 | ||
31 | #define XFS_INODE_FORMAT_INO 1 | |
32 | #define XFS_INODE_FORMAT_EXT 2 | |
33 | #define XFS_INODE_FORMAT_BTREE 3 | |
34 | ||
35 | ||
36 | struct grub_xfs_sblock | |
37 | { | |
38 | grub_uint8_t magic[4]; | |
39 | grub_uint32_t bsize; | |
00c108a4 | 40 | grub_uint8_t unused1[24]; |
41 | grub_uint16_t uuid[8]; | |
42 | grub_uint8_t unused2[8]; | |
b2499b29 | 43 | grub_uint64_t rootino; |
b2499b29 | 44 | grub_uint8_t unused3[20]; |
00c108a4 | 45 | grub_uint32_t agsize; |
46 | grub_uint8_t unused4[20]; | |
b2499b29 | 47 | grub_uint8_t label[12]; |
48 | grub_uint8_t log2_bsize; | |
00c108a4 | 49 | grub_uint8_t unused5[2]; |
b2499b29 | 50 | grub_uint8_t log2_inop; |
51 | grub_uint8_t log2_agblk; | |
00c108a4 | 52 | grub_uint8_t unused6[67]; |
c004e1b4 | 53 | grub_uint8_t log2_dirblk; |
b2499b29 | 54 | } __attribute__ ((packed)); |
55 | ||
56 | struct grub_xfs_dir_header | |
57 | { | |
d4c9b428 | 58 | grub_uint8_t count; |
59 | grub_uint8_t i8count; | |
60 | union | |
61 | { | |
62 | grub_uint32_t i4; | |
63 | grub_uint64_t i8; | |
64 | } parent __attribute__ ((packed)); | |
b2499b29 | 65 | } __attribute__ ((packed)); |
66 | ||
67 | struct grub_xfs_dir_entry | |
68 | { | |
69 | grub_uint8_t len; | |
70 | grub_uint16_t offset; | |
71 | char name[1]; | |
72 | /* Inode number follows, 32 bits. */ | |
73 | } __attribute__ ((packed)); | |
74 | ||
75 | struct grub_xfs_dir2_entry | |
76 | { | |
77 | grub_uint64_t inode; | |
78 | grub_uint8_t len; | |
79 | } __attribute__ ((packed)); | |
80 | ||
81 | typedef grub_uint32_t grub_xfs_extent[4]; | |
82 | ||
c004e1b4 | 83 | struct grub_xfs_btree_node |
84 | { | |
85 | grub_uint8_t magic[4]; | |
86 | grub_uint16_t level; | |
87 | grub_uint16_t numrecs; | |
88 | grub_uint64_t left; | |
89 | grub_uint64_t right; | |
90 | grub_uint64_t keys[1]; | |
91 | } __attribute__ ((packed)); | |
92 | ||
93 | struct grub_xfs_btree_root | |
94 | { | |
95 | grub_uint16_t level; | |
96 | grub_uint16_t numrecs; | |
97 | grub_uint64_t keys[1]; | |
98 | } __attribute__ ((packed)); | |
99 | ||
b2499b29 | 100 | struct grub_xfs_inode |
101 | { | |
102 | grub_uint8_t magic[2]; | |
103 | grub_uint16_t mode; | |
104 | grub_uint8_t version; | |
105 | grub_uint8_t format; | |
106 | grub_uint8_t unused2[50]; | |
107 | grub_uint64_t size; | |
c004e1b4 | 108 | grub_uint64_t nblocks; |
109 | grub_uint32_t extsize; | |
110 | grub_uint32_t nextents; | |
111 | grub_uint8_t unused3[20]; | |
b2499b29 | 112 | union |
113 | { | |
114 | char raw[156]; | |
115 | struct dir | |
116 | { | |
117 | struct grub_xfs_dir_header dirhead; | |
118 | struct grub_xfs_dir_entry direntry[1]; | |
119 | } dir; | |
120 | grub_xfs_extent extents[XFS_INODE_EXTENTS]; | |
c004e1b4 | 121 | struct grub_xfs_btree_root btree; |
b2499b29 | 122 | } data __attribute__ ((packed)); |
123 | } __attribute__ ((packed)); | |
124 | ||
125 | struct grub_xfs_dirblock_tail | |
126 | { | |
127 | grub_uint32_t leaf_count; | |
128 | grub_uint32_t leaf_stale; | |
129 | } __attribute__ ((packed)); | |
130 | ||
131 | struct grub_fshelp_node | |
132 | { | |
133 | struct grub_xfs_data *data; | |
134 | struct grub_xfs_inode inode; | |
135 | grub_uint64_t ino; | |
136 | int inode_read; | |
137 | }; | |
138 | ||
139 | struct grub_xfs_data | |
140 | { | |
141 | struct grub_xfs_sblock sblock; | |
142 | struct grub_xfs_inode *inode; | |
143 | grub_disk_t disk; | |
144 | int pos; | |
145 | int bsize; | |
146 | int agsize; | |
147 | struct grub_fshelp_node diropen; | |
148 | ||
149 | }; | |
b4093103 | 150 | |
151 | #ifndef GRUB_UTIL | |
152 | static grub_dl_t my_mod; | |
153 | #endif | |
154 | ||
b2499b29 | 155 | \f |
156 | ||
157 | /* Filetype information as used in inodes. */ | |
158 | #define FILETYPE_INO_MASK 0170000 | |
159 | #define FILETYPE_INO_REG 0100000 | |
160 | #define FILETYPE_INO_DIRECTORY 0040000 | |
161 | #define FILETYPE_INO_SYMLINK 0120000 | |
162 | ||
163 | #define GRUB_XFS_INO_AGBITS(data) \ | |
164 | ((data)->sblock.log2_agblk + (data)->sblock.log2_inop) | |
165 | #define GRUB_XFS_INO_INOINAG(data, ino) \ | |
166 | (grub_be_to_cpu64 (ino) & ((1 << GRUB_XFS_INO_AGBITS (data)) - 1)) | |
167 | #define GRUB_XFS_INO_AG(data,ino) \ | |
168 | (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data)) | |
169 | ||
5444088d | 170 | #define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \ |
171 | (((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \ | |
172 | + ((fsb) & ((1 << (data)->sblock.log2_agblk) - 1))) | |
173 | ||
c004e1b4 | 174 | #define GRUB_XFS_EXTENT_OFFSET(exts,ex) \ |
175 | ((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23 \ | |
176 | | grub_be_to_cpu32 (exts[ex][1]) >> 9) | |
b2499b29 | 177 | |
c004e1b4 | 178 | #define GRUB_XFS_EXTENT_BLOCK(exts,ex) \ |
179 | ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1]) \ | |
180 | & (0x1ff)) << 43 \ | |
181 | | (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11 \ | |
182 | | grub_be_to_cpu32 (exts[ex][3]) >> 21) | |
b2499b29 | 183 | |
c004e1b4 | 184 | #define GRUB_XFS_EXTENT_SIZE(exts,ex) \ |
185 | (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1)) | |
b2499b29 | 186 | |
187 | #define GRUB_XFS_ROUND_TO_DIRENT(pos) ((((pos) + 8 - 1) / 8) * 8) | |
188 | #define GRUB_XFS_NEXT_DIRENT(pos,len) \ | |
189 | (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2) | |
190 | \f | |
688e5699 | 191 | static inline int |
192 | grub_xfs_inode_block (struct grub_xfs_data *data, | |
193 | grub_uint64_t ino) | |
b2499b29 | 194 | { |
195 | long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino); | |
196 | long long ag = GRUB_XFS_INO_AG (data, ino); | |
197 | long long block; | |
198 | ||
199 | block = (inoinag >> 4) + ag * data->agsize; | |
200 | block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS); | |
201 | return block; | |
202 | } | |
203 | ||
204 | ||
688e5699 | 205 | static inline int |
206 | grub_xfs_inode_offset (struct grub_xfs_data *data, | |
207 | grub_uint64_t ino) | |
b2499b29 | 208 | { |
209 | int inoag = GRUB_XFS_INO_INOINAG (data, ino); | |
210 | return (inoag & ((1 << 4) - 1)) << 8; | |
211 | } | |
212 | ||
213 | ||
214 | static grub_err_t | |
215 | grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino, | |
216 | struct grub_xfs_inode *inode) | |
217 | { | |
218 | int block = grub_xfs_inode_block (data, ino); | |
219 | int offset = grub_xfs_inode_offset (data, ino); | |
220 | ||
221 | /* Read the inode. */ | |
222 | if (grub_disk_read (data->disk, block, offset, | |
223 | sizeof (struct grub_xfs_inode), (char *) inode)) | |
224 | return grub_errno; | |
225 | ||
7b455f4d | 226 | if (grub_strncmp ((char *) inode->magic, "IN", 2)) |
b2499b29 | 227 | return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode.\n"); |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | ||
887d2619 | 233 | static grub_disk_addr_t |
234 | grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) | |
b2499b29 | 235 | { |
c004e1b4 | 236 | struct grub_xfs_btree_node *leaf = 0; |
237 | int ex, nrec; | |
238 | grub_xfs_extent *exts; | |
239 | grub_uint64_t ret = 0; | |
b2499b29 | 240 | |
c004e1b4 | 241 | if (node->inode.format == XFS_INODE_FORMAT_BTREE) |
242 | { | |
243 | grub_uint64_t *keys; | |
244 | ||
245 | leaf = grub_malloc (node->data->sblock.bsize); | |
246 | if (leaf == 0) | |
247 | return 0; | |
248 | ||
249 | nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs); | |
250 | keys = &node->inode.data.btree.keys[0]; | |
251 | do | |
252 | { | |
253 | int i; | |
254 | ||
255 | for (i = 0; i < nrec; i++) | |
256 | { | |
887d2619 | 257 | if (fileblock < grub_be_to_cpu64 (keys[i])) |
c004e1b4 | 258 | break; |
259 | } | |
260 | ||
261 | /* Sparse block. */ | |
262 | if (i == 0) | |
263 | { | |
264 | grub_free (leaf); | |
265 | return 0; | |
266 | } | |
267 | ||
268 | if (grub_disk_read (node->data->disk, | |
269 | grub_be_to_cpu64 (keys[i - 1 + XFS_INODE_EXTENTS]) | |
270 | << (node->data->sblock.log2_bsize | |
271 | - GRUB_DISK_SECTOR_BITS), | |
272 | 0, node->data->sblock.bsize, (char *) leaf)) | |
273 | return 0; | |
274 | ||
275 | if (grub_strncmp ((char *) leaf->magic, "BMAP", 4)) | |
276 | { | |
277 | grub_free (leaf); | |
278 | grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node.\n"); | |
279 | return 0; | |
280 | } | |
281 | ||
282 | nrec = grub_be_to_cpu16 (leaf->numrecs); | |
283 | keys = &leaf->keys[0]; | |
284 | } while (leaf->level); | |
285 | exts = (grub_xfs_extent *) keys; | |
286 | } | |
287 | else if (node->inode.format == XFS_INODE_FORMAT_EXT) | |
288 | { | |
289 | nrec = grub_be_to_cpu32 (node->inode.nextents); | |
290 | exts = &node->inode.data.extents[0]; | |
291 | } | |
292 | else | |
b2499b29 | 293 | { |
294 | grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
295 | "xfs does not support inode format %d yet", | |
296 | node->inode.format); | |
297 | return 0; | |
298 | } | |
299 | ||
300 | /* Iterate over each extent to figure out which extent has | |
301 | the block we are looking for. */ | |
c004e1b4 | 302 | for (ex = 0; ex < nrec; ex++) |
b2499b29 | 303 | { |
c004e1b4 | 304 | grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex); |
887d2619 | 305 | grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex); |
306 | grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex); | |
c004e1b4 | 307 | |
308 | /* Sparse block. */ | |
309 | if (fileblock < offset) | |
310 | break; | |
311 | else if (fileblock < offset + size) | |
312 | { | |
313 | ret = (fileblock - offset + start); | |
314 | break; | |
315 | } | |
b2499b29 | 316 | } |
317 | ||
c004e1b4 | 318 | if (leaf) |
319 | grub_free (leaf); | |
320 | ||
5444088d | 321 | return GRUB_XFS_FSB_TO_BLOCK(node->data, ret); |
b2499b29 | 322 | } |
323 | ||
324 | ||
325 | /* Read LEN bytes from the file described by DATA starting with byte | |
326 | POS. Return the amount of read bytes in READ. */ | |
327 | static grub_ssize_t | |
328 | grub_xfs_read_file (grub_fshelp_node_t node, | |
9959f7db | 329 | void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, |
b2499b29 | 330 | unsigned offset, unsigned length), |
524a1e6a | 331 | int pos, grub_size_t len, char *buf) |
b2499b29 | 332 | { |
333 | return grub_fshelp_read_file (node->data->disk, node, read_hook, | |
334 | pos, len, buf, grub_xfs_read_block, | |
335 | grub_be_to_cpu64 (node->inode.size), | |
336 | node->data->sblock.log2_bsize | |
337 | - GRUB_DISK_SECTOR_BITS); | |
338 | } | |
339 | ||
340 | ||
341 | static char * | |
342 | grub_xfs_read_symlink (grub_fshelp_node_t node) | |
343 | { | |
344 | int size = grub_be_to_cpu64 (node->inode.size); | |
345 | ||
346 | switch (node->inode.format) | |
347 | { | |
348 | case XFS_INODE_FORMAT_INO: | |
349 | return grub_strndup (node->inode.data.raw, size); | |
350 | ||
351 | case XFS_INODE_FORMAT_EXT: | |
352 | { | |
353 | char *symlink; | |
354 | grub_ssize_t numread; | |
355 | ||
356 | symlink = grub_malloc (size + 1); | |
357 | if (!symlink) | |
358 | return 0; | |
359 | ||
360 | numread = grub_xfs_read_file (node, 0, 0, size, symlink); | |
361 | if (numread != size) | |
362 | { | |
363 | grub_free (symlink); | |
364 | return 0; | |
365 | } | |
366 | symlink[size] = '\0'; | |
367 | return symlink; | |
368 | } | |
369 | } | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | ||
375 | static enum grub_fshelp_filetype | |
376 | grub_xfs_mode_to_filetype (grub_uint16_t mode) | |
377 | { | |
378 | if ((grub_be_to_cpu16 (mode) | |
379 | & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) | |
380 | return GRUB_FSHELP_DIR; | |
381 | else if ((grub_be_to_cpu16 (mode) | |
382 | & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) | |
383 | return GRUB_FSHELP_SYMLINK; | |
384 | else if ((grub_be_to_cpu16 (mode) | |
385 | & FILETYPE_INO_MASK) == FILETYPE_INO_REG) | |
386 | return GRUB_FSHELP_REG; | |
387 | return GRUB_FSHELP_UNKNOWN; | |
388 | } | |
389 | ||
390 | ||
391 | static int | |
392 | grub_xfs_iterate_dir (grub_fshelp_node_t dir, | |
393 | int NESTED_FUNC_ATTR | |
394 | (*hook) (const char *filename, | |
395 | enum grub_fshelp_filetype filetype, | |
396 | grub_fshelp_node_t node)) | |
397 | { | |
398 | struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; | |
c004e1b4 | 399 | auto int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename); |
b2499b29 | 400 | |
c004e1b4 | 401 | int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename) |
b2499b29 | 402 | { |
403 | struct grub_fshelp_node *fdiro; | |
404 | ||
405 | fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); | |
406 | if (!fdiro) | |
407 | return 0; | |
408 | ||
409 | /* The inode should be read, otherwise the filetype can | |
410 | not be determined. */ | |
411 | fdiro->ino = ino; | |
412 | fdiro->inode_read = 1; | |
413 | fdiro->data = diro->data; | |
414 | grub_xfs_read_inode (diro->data, ino, &fdiro->inode); | |
415 | ||
416 | return hook (filename, | |
417 | grub_xfs_mode_to_filetype (fdiro->inode.mode), | |
418 | fdiro); | |
419 | } | |
420 | ||
421 | switch (diro->inode.format) | |
422 | { | |
423 | case XFS_INODE_FORMAT_INO: | |
424 | { | |
425 | struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0]; | |
d4c9b428 | 426 | int smallino = !diro->inode.data.dir.dirhead.i8count; |
b2499b29 | 427 | int i; |
428 | grub_uint64_t parent; | |
429 | ||
430 | /* If small inode numbers are used to pack the direntry, the | |
431 | parent inode number is small too. */ | |
432 | if (smallino) | |
433 | { | |
d4c9b428 | 434 | parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4); |
b2499b29 | 435 | parent = grub_cpu_to_be64 (parent); |
436 | } | |
437 | else | |
438 | { | |
d4c9b428 | 439 | parent = diro->inode.data.dir.dirhead.parent.i8; |
b2499b29 | 440 | /* The header is a bit bigger than usual. */ |
441 | de = (struct grub_xfs_dir_entry *) ((char *) de + 4); | |
442 | } | |
443 | ||
444 | /* Synthesize the direntries for `.' and `..'. */ | |
445 | if (call_hook (diro->ino, ".")) | |
446 | return 1; | |
447 | ||
448 | if (call_hook (parent, "..")) | |
449 | return 1; | |
450 | ||
d4c9b428 | 451 | for (i = 0; i < diro->inode.data.dir.dirhead.count; i++) |
b2499b29 | 452 | { |
453 | grub_uint64_t ino; | |
454 | void *inopos = (((char *) de) | |
688e5699 | 455 | + sizeof (struct grub_xfs_dir_entry) |
456 | + de->len - 1); | |
457 | char name[de->len + 1]; | |
458 | ||
b2499b29 | 459 | if (smallino) |
460 | { | |
461 | ino = grub_be_to_cpu32 (*(grub_uint32_t *) inopos); | |
462 | ino = grub_cpu_to_be64 (ino); | |
463 | } | |
464 | else | |
465 | ino = *(grub_uint64_t *) inopos; | |
466 | ||
688e5699 | 467 | grub_memcpy (name, de->name, de->len); |
468 | name[de->len] = '\0'; | |
469 | if (call_hook (ino, name)) | |
b2499b29 | 470 | return 1; |
471 | ||
472 | de = ((struct grub_xfs_dir_entry *) | |
473 | (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len | |
047b67e0 | 474 | + ((smallino ? sizeof (grub_uint32_t) |
475 | : sizeof (grub_uint64_t))) - 1)); | |
b2499b29 | 476 | } |
477 | break; | |
478 | } | |
479 | ||
480 | case XFS_INODE_FORMAT_BTREE: | |
481 | case XFS_INODE_FORMAT_EXT: | |
482 | { | |
483 | grub_ssize_t numread; | |
484 | char *dirblock; | |
688e5699 | 485 | grub_uint64_t blk; |
c004e1b4 | 486 | int dirblk_size, dirblk_log2; |
b2499b29 | 487 | |
c004e1b4 | 488 | dirblk_log2 = (dir->data->sblock.log2_bsize |
489 | + dir->data->sblock.log2_dirblk); | |
490 | dirblk_size = 1 << dirblk_log2; | |
491 | ||
492 | dirblock = grub_malloc (dirblk_size); | |
688e5699 | 493 | if (! dirblock) |
b2499b29 | 494 | return 0; |
495 | ||
496 | /* Iterate over every block the directory has. */ | |
497 | for (blk = 0; | |
498 | blk < (grub_be_to_cpu64 (dir->inode.size) | |
c004e1b4 | 499 | >> dirblk_log2); |
b2499b29 | 500 | blk++) |
501 | { | |
502 | /* The header is skipped, the first direntry is stored | |
503 | from byte 16. */ | |
504 | int pos = 16; | |
505 | int entries; | |
c004e1b4 | 506 | int tail_start = (dirblk_size |
b2499b29 | 507 | - sizeof (struct grub_xfs_dirblock_tail)); |
508 | ||
509 | struct grub_xfs_dirblock_tail *tail; | |
510 | tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start]; | |
511 | ||
512 | numread = grub_xfs_read_file (dir, 0, | |
c004e1b4 | 513 | blk << dirblk_log2, |
514 | dirblk_size, dirblock); | |
515 | if (numread != dirblk_size) | |
6fa1251a | 516 | return 0; |
b2499b29 | 517 | |
518 | entries = (grub_be_to_cpu32 (tail->leaf_count) | |
519 | - grub_be_to_cpu32 (tail->leaf_stale)); | |
520 | ||
521 | /* Iterate over all entries within this block. */ | |
c004e1b4 | 522 | while (pos < (dirblk_size |
b2499b29 | 523 | - (int) sizeof (struct grub_xfs_dir2_entry))) |
524 | { | |
525 | struct grub_xfs_dir2_entry *direntry; | |
526 | grub_uint16_t *freetag; | |
527 | char *filename; | |
528 | ||
529 | direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos]; | |
530 | freetag = (grub_uint16_t *) direntry; | |
531 | ||
532 | if (*freetag == 0XFFFF) | |
533 | { | |
534 | grub_uint16_t *skip = (grub_uint16_t *) (freetag + 1); | |
535 | ||
536 | /* This entry is not used, go to the next one. */ | |
537 | pos += grub_be_to_cpu16 (*skip); | |
538 | ||
539 | continue; | |
540 | } | |
541 | ||
542 | filename = &dirblock[pos + sizeof (*direntry)]; | |
543 | /* The byte after the filename is for the tag, which | |
544 | is not used by GRUB. So it can be overwritten. */ | |
545 | filename[direntry->len] = '\0'; | |
546 | ||
547 | if (call_hook (direntry->inode, filename)) | |
548 | { | |
549 | grub_free (dirblock); | |
550 | return 1; | |
551 | } | |
552 | ||
553 | /* Check if last direntry in this block is | |
554 | reached. */ | |
555 | entries--; | |
556 | if (!entries) | |
557 | break; | |
558 | ||
559 | /* Select the next directory entry. */ | |
560 | pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len); | |
561 | pos = GRUB_XFS_ROUND_TO_DIRENT (pos); | |
562 | } | |
563 | } | |
564 | grub_free (dirblock); | |
565 | break; | |
566 | } | |
567 | ||
568 | default: | |
569 | grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
570 | "xfs does not support inode format %d yet", | |
571 | diro->inode.format); | |
572 | } | |
573 | return 0; | |
574 | } | |
575 | ||
576 | ||
577 | static struct grub_xfs_data * | |
578 | grub_xfs_mount (grub_disk_t disk) | |
579 | { | |
580 | struct grub_xfs_data *data = 0; | |
581 | ||
582 | data = grub_malloc (sizeof (struct grub_xfs_data)); | |
583 | if (!data) | |
584 | return 0; | |
585 | ||
586 | /* Read the superblock. */ | |
587 | if (grub_disk_read (disk, 0, 0, | |
588 | sizeof (struct grub_xfs_sblock), (char *) &data->sblock)) | |
589 | goto fail; | |
590 | ||
7b455f4d | 591 | if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)) |
b2499b29 | 592 | { |
593 | grub_error (GRUB_ERR_BAD_FS, "not a xfs filesystem"); | |
594 | goto fail; | |
595 | } | |
596 | ||
597 | data->diropen.data = data; | |
598 | data->diropen.ino = data->sblock.rootino; | |
599 | data->diropen.inode_read = 1; | |
688e5699 | 600 | data->bsize = grub_be_to_cpu32 (data->sblock.bsize); |
601 | data->agsize = grub_be_to_cpu32 (data->sblock.agsize); | |
b2499b29 | 602 | |
603 | data->disk = disk; | |
604 | data->inode = &data->diropen.inode; | |
605 | data->pos = 0; | |
606 | ||
607 | grub_xfs_read_inode (data, data->diropen.ino, data->inode); | |
608 | ||
609 | return data; | |
610 | fail: | |
611 | ||
0a203f83 | 612 | if (grub_errno == GRUB_ERR_OUT_OF_RANGE) |
613 | grub_error (GRUB_ERR_BAD_FS, "not an xfs filesystem"); | |
614 | ||
b2499b29 | 615 | grub_free (data); |
616 | ||
617 | return 0; | |
618 | } | |
619 | ||
620 | \f | |
621 | static grub_err_t | |
622 | grub_xfs_dir (grub_device_t device, const char *path, | |
623 | int (*hook) (const char *filename, int dir)) | |
624 | { | |
625 | struct grub_xfs_data *data = 0;; | |
626 | struct grub_fshelp_node *fdiro = 0; | |
627 | ||
628 | auto int NESTED_FUNC_ATTR iterate (const char *filename, | |
629 | enum grub_fshelp_filetype filetype, | |
630 | grub_fshelp_node_t node); | |
631 | ||
632 | int NESTED_FUNC_ATTR iterate (const char *filename, | |
633 | enum grub_fshelp_filetype filetype, | |
634 | grub_fshelp_node_t node) | |
635 | { | |
636 | grub_free (node); | |
637 | ||
638 | if (filetype == GRUB_FSHELP_DIR) | |
639 | return hook (filename, 1); | |
640 | else | |
641 | return hook (filename, 0); | |
642 | ||
643 | return 0; | |
644 | } | |
645 | ||
646 | #ifndef GRUB_UTIL | |
647 | grub_dl_ref (my_mod); | |
648 | #endif | |
649 | ||
650 | data = grub_xfs_mount (device->disk); | |
651 | if (!data) | |
652 | goto fail; | |
653 | ||
654 | grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir, | |
655 | grub_xfs_read_symlink, GRUB_FSHELP_DIR); | |
656 | if (grub_errno) | |
657 | goto fail; | |
658 | ||
659 | grub_xfs_iterate_dir (fdiro, iterate); | |
660 | ||
661 | fail: | |
662 | if (fdiro != &data->diropen) | |
663 | grub_free (fdiro); | |
664 | grub_free (data); | |
665 | ||
666 | #ifndef GRUB_UTIL | |
667 | grub_dl_unref (my_mod); | |
668 | #endif | |
669 | ||
670 | return grub_errno; | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
675 | ||
676 | /* Open a file named NAME and initialize FILE. */ | |
677 | static grub_err_t | |
678 | grub_xfs_open (struct grub_file *file, const char *name) | |
679 | { | |
680 | struct grub_xfs_data *data; | |
681 | struct grub_fshelp_node *fdiro = 0; | |
682 | ||
683 | #ifndef GRUB_UTIL | |
684 | grub_dl_ref (my_mod); | |
685 | #endif | |
686 | ||
687 | data = grub_xfs_mount (file->device->disk); | |
688 | if (!data) | |
689 | goto fail; | |
690 | ||
691 | grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir, | |
692 | grub_xfs_read_symlink, GRUB_FSHELP_REG); | |
693 | if (grub_errno) | |
694 | goto fail; | |
695 | ||
696 | if (!fdiro->inode_read) | |
697 | { | |
698 | grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode); | |
699 | if (grub_errno) | |
700 | goto fail; | |
701 | } | |
702 | ||
703 | grub_memcpy (data->inode, | |
704 | &fdiro->inode, | |
705 | sizeof (struct grub_xfs_inode)); | |
706 | grub_free (fdiro); | |
707 | ||
708 | file->size = grub_be_to_cpu64 (data->inode->size); | |
709 | file->data = data; | |
710 | file->offset = 0; | |
711 | ||
712 | return 0; | |
713 | ||
714 | fail: | |
715 | if (fdiro != &data->diropen) | |
716 | grub_free (fdiro); | |
717 | grub_free (data); | |
718 | ||
719 | #ifndef GRUB_UTIL | |
720 | grub_dl_unref (my_mod); | |
721 | #endif | |
722 | ||
723 | return grub_errno; | |
724 | } | |
725 | ||
726 | ||
727 | static grub_ssize_t | |
524a1e6a | 728 | grub_xfs_read (grub_file_t file, char *buf, grub_size_t len) |
b2499b29 | 729 | { |
730 | struct grub_xfs_data *data = | |
731 | (struct grub_xfs_data *) file->data; | |
732 | ||
733 | return grub_xfs_read_file (&data->diropen, file->read_hook, | |
734 | file->offset, len, buf); | |
735 | } | |
736 | ||
737 | ||
738 | static grub_err_t | |
739 | grub_xfs_close (grub_file_t file) | |
740 | { | |
741 | grub_free (file->data); | |
742 | ||
743 | #ifndef GRUB_UTIL | |
744 | grub_dl_unref (my_mod); | |
745 | #endif | |
746 | ||
747 | return GRUB_ERR_NONE; | |
748 | } | |
749 | ||
750 | ||
751 | static grub_err_t | |
752 | grub_xfs_label (grub_device_t device, char **label) | |
753 | { | |
754 | struct grub_xfs_data *data; | |
755 | grub_disk_t disk = device->disk; | |
756 | ||
757 | #ifndef GRUB_UTIL | |
758 | grub_dl_ref (my_mod); | |
759 | #endif | |
760 | ||
761 | data = grub_xfs_mount (disk); | |
762 | if (data) | |
7b455f4d | 763 | *label = grub_strndup ((char *) (data->sblock.label), 12); |
b2499b29 | 764 | else |
765 | *label = 0; | |
766 | ||
767 | #ifndef GRUB_UTIL | |
768 | grub_dl_unref (my_mod); | |
769 | #endif | |
770 | ||
771 | grub_free (data); | |
772 | ||
773 | return grub_errno; | |
774 | } | |
775 | ||
00c108a4 | 776 | static grub_err_t |
777 | grub_xfs_uuid (grub_device_t device, char **uuid) | |
778 | { | |
779 | struct grub_xfs_data *data; | |
780 | grub_disk_t disk = device->disk; | |
781 | ||
782 | #ifndef GRUB_UTIL | |
783 | grub_dl_ref (my_mod); | |
784 | #endif | |
785 | ||
786 | data = grub_xfs_mount (disk); | |
787 | if (data) | |
788 | { | |
789 | *uuid = grub_malloc (sizeof ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")); | |
790 | grub_sprintf (*uuid, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", | |
791 | grub_be_to_cpu16 (data->sblock.uuid[0]), grub_be_to_cpu16 (data->sblock.uuid[1]), | |
792 | grub_be_to_cpu16 (data->sblock.uuid[2]), grub_be_to_cpu16 (data->sblock.uuid[3]), | |
793 | grub_be_to_cpu16 (data->sblock.uuid[4]), grub_be_to_cpu16 (data->sblock.uuid[5]), | |
794 | grub_be_to_cpu16 (data->sblock.uuid[6]), grub_be_to_cpu16 (data->sblock.uuid[7])); | |
795 | } | |
796 | else | |
797 | *uuid = NULL; | |
798 | ||
799 | #ifndef GRUB_UTIL | |
800 | grub_dl_unref (my_mod); | |
801 | #endif | |
802 | ||
803 | grub_free (data); | |
804 | ||
805 | return grub_errno; | |
806 | } | |
807 | ||
b2499b29 | 808 | \f |
809 | ||
810 | static struct grub_fs grub_xfs_fs = | |
811 | { | |
812 | .name = "xfs", | |
813 | .dir = grub_xfs_dir, | |
814 | .open = grub_xfs_open, | |
815 | .read = grub_xfs_read, | |
816 | .close = grub_xfs_close, | |
817 | .label = grub_xfs_label, | |
00c108a4 | 818 | .uuid = grub_xfs_uuid, |
b2499b29 | 819 | .next = 0 |
820 | }; | |
821 | ||
6d099807 | 822 | GRUB_MOD_INIT(xfs) |
b2499b29 | 823 | { |
824 | grub_fs_register (&grub_xfs_fs); | |
6d099807 | 825 | #ifndef GRUB_UTIL |
b2499b29 | 826 | my_mod = mod; |
6d099807 | 827 | #endif |
b2499b29 | 828 | } |
829 | ||
6d099807 | 830 | GRUB_MOD_FINI(xfs) |
b2499b29 | 831 | { |
832 | grub_fs_unregister (&grub_xfs_fs); | |
833 | } |