source: grub-pc/trunk/fuentes/.pc/xfs-crc-fix-symlink.patch/grub-core/fs/xfs.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 29.0 KB
Line 
1/* xfs.c - XFS.  */
2/*
3 *  GRUB  --  GRand Unified Bootloader
4 *  Copyright (C) 2005,2006,2007,2008,2009  Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
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
29GRUB_MOD_LICENSE ("GPLv3+");
30
31#define XFS_INODE_EXTENTS       9
32
33#define XFS_INODE_FORMAT_INO    1
34#define XFS_INODE_FORMAT_EXT    2
35#define XFS_INODE_FORMAT_BTREE  3
36
37/* Superblock version field flags */
38#define XFS_SB_VERSION_NUMBITS          0x000f
39#define XFS_SB_VERSION_ATTRBIT          0x0010
40#define XFS_SB_VERSION_NLINKBIT         0x0020
41#define XFS_SB_VERSION_QUOTABIT         0x0040
42#define XFS_SB_VERSION_ALIGNBIT         0x0080
43#define XFS_SB_VERSION_DALIGNBIT        0x0100
44#define XFS_SB_VERSION_LOGV2BIT         0x0400
45#define XFS_SB_VERSION_SECTORBIT        0x0800
46#define XFS_SB_VERSION_EXTFLGBIT        0x1000
47#define XFS_SB_VERSION_DIRV2BIT         0x2000
48#define XFS_SB_VERSION_MOREBITSBIT      0x8000
49#define XFS_SB_VERSION_BITS_SUPPORTED \
50        (XFS_SB_VERSION_NUMBITS | \
51         XFS_SB_VERSION_ATTRBIT | \
52         XFS_SB_VERSION_NLINKBIT | \
53         XFS_SB_VERSION_QUOTABIT | \
54         XFS_SB_VERSION_ALIGNBIT | \
55         XFS_SB_VERSION_DALIGNBIT | \
56         XFS_SB_VERSION_LOGV2BIT | \
57         XFS_SB_VERSION_SECTORBIT | \
58         XFS_SB_VERSION_EXTFLGBIT | \
59         XFS_SB_VERSION_DIRV2BIT | \
60         XFS_SB_VERSION_MOREBITSBIT)
61
62/* Recognized xfs format versions */
63#define XFS_SB_VERSION_4                4       /* Good old XFS filesystem */
64#define XFS_SB_VERSION_5                5       /* CRC enabled filesystem */
65
66/* features2 field flags */
67#define XFS_SB_VERSION2_LAZYSBCOUNTBIT  0x00000002      /* Superblk counters */
68#define XFS_SB_VERSION2_ATTR2BIT        0x00000008      /* Inline attr rework */
69#define XFS_SB_VERSION2_PROJID32BIT     0x00000080      /* 32-bit project ids */
70#define XFS_SB_VERSION2_FTYPE           0x00000200      /* inode type in dir */
71#define XFS_SB_VERSION2_BITS_SUPPORTED \
72        (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
73         XFS_SB_VERSION2_ATTR2BIT | \
74         XFS_SB_VERSION2_PROJID32BIT | \
75         XFS_SB_VERSION2_FTYPE)
76
77/* incompat feature flags */
78#define XFS_SB_FEAT_INCOMPAT_FTYPE      (1 << 0)        /* filetype in dirent */
79#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
80        (XFS_SB_FEAT_INCOMPAT_FTYPE)
81
82struct grub_xfs_sblock
83{
84  grub_uint8_t magic[4];
85  grub_uint32_t bsize;
86  grub_uint8_t unused1[24];
87  grub_uint16_t uuid[8];
88  grub_uint8_t unused2[8];
89  grub_uint64_t rootino;
90  grub_uint8_t unused3[20];
91  grub_uint32_t agsize;
92  grub_uint8_t unused4[12];
93  grub_uint16_t version;
94  grub_uint8_t unused5[6];
95  grub_uint8_t label[12];
96  grub_uint8_t log2_bsize;
97  grub_uint8_t log2_sect;
98  grub_uint8_t log2_inode;
99  grub_uint8_t log2_inop;
100  grub_uint8_t log2_agblk;
101  grub_uint8_t unused6[67];
102  grub_uint8_t log2_dirblk;
103  grub_uint8_t unused7[7];
104  grub_uint32_t features2;
105  grub_uint8_t unused8[4];
106  grub_uint32_t sb_features_compat;
107  grub_uint32_t sb_features_ro_compat;
108  grub_uint32_t sb_features_incompat;
109  grub_uint32_t sb_features_log_incompat;
110} GRUB_PACKED;
111
112struct grub_xfs_dir_header
113{
114  grub_uint8_t count;
115  grub_uint8_t largeino;
116  union
117  {
118    grub_uint32_t i4;
119    grub_uint64_t i8;
120  } GRUB_PACKED parent;
121} GRUB_PACKED;
122
123/* Structure for directory entry inlined in the inode */
124struct grub_xfs_dir_entry
125{
126  grub_uint8_t len;
127  grub_uint16_t offset;
128  char name[1];
129  /* Inode number follows, 32 / 64 bits.  */
130} GRUB_PACKED;
131
132/* Structure for directory entry in a block */
133struct grub_xfs_dir2_entry
134{
135  grub_uint64_t inode;
136  grub_uint8_t len;
137} GRUB_PACKED;
138
139struct grub_xfs_extent
140{
141  /* This should be a bitfield but bietfields are unportable, so just have
142     a raw array and functions extracting useful info from it.
143   */
144  grub_uint32_t raw[4];
145} GRUB_PACKED;
146
147struct grub_xfs_btree_node
148{
149  grub_uint8_t magic[4];
150  grub_uint16_t level;
151  grub_uint16_t numrecs;
152  grub_uint64_t left;
153  grub_uint64_t right;
154  /* In V5 here follow crc, uuid, etc. */
155  /* Then follow keys and block pointers */
156} GRUB_PACKED;
157
158struct grub_xfs_btree_root
159{
160  grub_uint16_t level;
161  grub_uint16_t numrecs;
162  grub_uint64_t keys[1];
163} GRUB_PACKED;
164
165struct grub_xfs_time
166{
167  grub_uint32_t sec;
168  grub_uint32_t nanosec;
169} GRUB_PACKED;
170
171struct grub_xfs_inode
172{
173  grub_uint8_t magic[2];
174  grub_uint16_t mode;
175  grub_uint8_t version;
176  grub_uint8_t format;
177  grub_uint8_t unused2[26];
178  struct grub_xfs_time atime;
179  struct grub_xfs_time mtime;
180  struct grub_xfs_time ctime;
181  grub_uint64_t size;
182  grub_uint64_t nblocks;
183  grub_uint32_t extsize;
184  grub_uint32_t nextents;
185  grub_uint16_t unused3;
186  grub_uint8_t fork_offset;
187  grub_uint8_t unused4[17];
188} GRUB_PACKED;
189
190#define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
191#define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
192
193struct grub_xfs_dirblock_tail
194{
195  grub_uint32_t leaf_count;
196  grub_uint32_t leaf_stale;
197} GRUB_PACKED;
198
199struct grub_fshelp_node
200{
201  struct grub_xfs_data *data;
202  grub_uint64_t ino;
203  int inode_read;
204  struct grub_xfs_inode inode;
205};
206
207struct grub_xfs_data
208{
209  struct grub_xfs_sblock sblock;
210  grub_disk_t disk;
211  int pos;
212  int bsize;
213  grub_uint32_t agsize;
214  unsigned int hasftype:1;
215  unsigned int hascrc:1;
216  struct grub_fshelp_node diropen;
217};
218
219static grub_dl_t my_mod;
220
221
222
223static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
224{
225  return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
226          grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
227}
228
229static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
230{
231  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
232        grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
233      data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
234    return 1;
235  if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
236      data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
237    return 1;
238  return 0;
239}
240
241static int grub_xfs_sb_valid(struct grub_xfs_data *data)
242{
243  grub_dprintf("xfs", "Validating superblock\n");
244  if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
245      || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
246      || ((int) data->sblock.log2_bsize
247          + (int) data->sblock.log2_dirblk) >= 27)
248    {
249      grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
250      return 0;
251    }
252  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
253       grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
254    {
255      grub_dprintf("xfs", "XFS v5 superblock detected\n");
256      if (data->sblock.sb_features_incompat &
257          grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
258        {
259          grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
260                      "incompatible features");
261          return 0;
262        }
263      return 1;
264    }
265  else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
266           grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
267    {
268      grub_dprintf("xfs", "XFS v4 superblock detected\n");
269      if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
270        {
271          grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
272                      "is unsupported");
273          return 0;
274        }
275      if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
276          (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
277           data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
278        {
279          grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
280                      "bits");
281          return 0;
282        }
283      return 1;
284    }
285  return 0;
286}
287
288/* Filetype information as used in inodes.  */
289#define FILETYPE_INO_MASK       0170000
290#define FILETYPE_INO_REG        0100000
291#define FILETYPE_INO_DIRECTORY  0040000
292#define FILETYPE_INO_SYMLINK    0120000
293
294static inline int
295GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
296{
297  return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
298}
299
300static inline grub_uint64_t
301GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
302                      grub_uint64_t ino)
303{
304  return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
305}
306
307static inline grub_uint64_t
308GRUB_XFS_INO_AG (struct grub_xfs_data *data,
309                 grub_uint64_t ino)
310{
311  return (ino >> GRUB_XFS_INO_AGBITS (data));
312}
313
314static inline grub_disk_addr_t
315GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
316{
317  return ((fsb >> data->sblock.log2_agblk) * data->agsize
318          + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
319}
320
321static inline grub_uint64_t
322GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex)
323{
324  return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23
325          | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9);
326}
327
328static inline grub_uint64_t
329GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex)
330{
331  return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1])
332                           & (0x1ff)) << 43
333          | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11
334          | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21);
335}
336
337static inline grub_uint64_t
338GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex)
339{
340  return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1));
341}
342
343
344static inline grub_uint64_t
345grub_xfs_inode_block (struct grub_xfs_data *data,
346                      grub_uint64_t ino)
347{
348  long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
349  long long ag = GRUB_XFS_INO_AG (data, ino);
350  long long block;
351
352  block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
353  block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
354  return block;
355}
356
357
358static inline int
359grub_xfs_inode_offset (struct grub_xfs_data *data,
360                       grub_uint64_t ino)
361{
362  int inoag = GRUB_XFS_INO_INOINAG (data, ino);
363  return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
364          data->sblock.log2_inode);
365}
366
367static inline grub_size_t
368grub_xfs_inode_size(struct grub_xfs_data *data)
369{
370  return 1 << data->sblock.log2_inode;
371}
372
373/*
374 * Returns size occupied by XFS inode stored in memory - we store struct
375 * grub_fshelp_node there but on disk inode size may be actually larger than
376 * struct grub_xfs_inode so we need to account for that so that we can read
377 * from disk directly into in-memory structure.
378 */
379static inline grub_size_t
380grub_xfs_fshelp_size(struct grub_xfs_data *data)
381{
382  return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode)
383               + grub_xfs_inode_size(data);
384}
385
386/* This should return void * but XFS code is error-prone with alignment, so
387   return char to retain cast-align.
388 */
389static char *
390grub_xfs_inode_data(struct grub_xfs_inode *inode)
391{
392        if (inode->version <= 2)
393                return ((char *)inode) + XFS_V2_INODE_SIZE;
394        return ((char *)inode) + XFS_V3_INODE_SIZE;
395}
396
397static struct grub_xfs_dir_entry *
398grub_xfs_inline_de(struct grub_xfs_dir_header *head)
399{
400  /*
401    With small inode numbers the header is 4 bytes smaller because of
402    smaller parent pointer
403  */
404  return (struct grub_xfs_dir_entry *)
405    (((char *) head) + sizeof(struct grub_xfs_dir_header) -
406     (head->largeino ? 0 : sizeof(grub_uint32_t)));
407}
408
409static grub_uint8_t *
410grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
411                          struct grub_xfs_dir_entry *de)
412{
413  return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0);
414}
415
416static struct grub_xfs_dir_entry *
417grub_xfs_inline_next_de(struct grub_xfs_data *data,
418                        struct grub_xfs_dir_header *head,
419                        struct grub_xfs_dir_entry *de)
420{
421  char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
422
423  p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
424  if (data->hasftype)
425    p++;
426
427  return (struct grub_xfs_dir_entry *)p;
428}
429
430static struct grub_xfs_dirblock_tail *
431grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
432{
433  int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
434
435  return (struct grub_xfs_dirblock_tail *)
436    ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
437}
438
439static struct grub_xfs_dir2_entry *
440grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
441{
442  if (data->hascrc)
443    return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
444  return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
445}
446
447static struct grub_xfs_dir2_entry *
448grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
449{
450  int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
451
452  if (data->hasftype)
453    size++;             /* File type */
454  return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
455}
456
457/* This should return void * but XFS code is error-prone with alignment, so
458   return char to retain cast-align.
459 */
460static char *
461grub_xfs_btree_keys(struct grub_xfs_data *data,
462                    struct grub_xfs_btree_node *leaf)
463{
464  char *keys = (char *)(leaf + 1);
465
466  if (data->hascrc)
467    keys += 48; /* skip crc, uuid, ... */
468  return keys;
469}
470
471static grub_err_t
472grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
473                     struct grub_xfs_inode *inode)
474{
475  grub_uint64_t block = grub_xfs_inode_block (data, ino);
476  int offset = grub_xfs_inode_offset (data, ino);
477
478  grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
479               ino, block, offset);
480  /* Read the inode.  */
481  if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
482                      inode))
483    return grub_errno;
484
485  if (grub_strncmp ((char *) inode->magic, "IN", 2))
486    return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
487
488  return 0;
489}
490
491static grub_uint64_t
492get_fsb (const void *keys, int idx)
493{
494  const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx;
495  return grub_be_to_cpu64 (grub_get_unaligned64 (p));
496}
497
498static grub_disk_addr_t
499grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
500{
501  struct grub_xfs_btree_node *leaf = 0;
502  int ex, nrec;
503  struct grub_xfs_extent *exts;
504  grub_uint64_t ret = 0;
505
506  if (node->inode.format == XFS_INODE_FORMAT_BTREE)
507    {
508      struct grub_xfs_btree_root *root;
509      const char *keys;
510      int recoffset;
511
512      leaf = grub_malloc (node->data->bsize);
513      if (leaf == 0)
514        return 0;
515
516      root = (struct grub_xfs_btree_root *) grub_xfs_inode_data(&node->inode);
517      nrec = grub_be_to_cpu16 (root->numrecs);
518      keys = (char *) &root->keys[0];
519      if (node->inode.fork_offset)
520        recoffset = (node->inode.fork_offset - 1) / 2;
521      else
522        recoffset = (grub_xfs_inode_size(node->data)
523                     - ((char *) keys - (char *) &node->inode))
524                                / (2 * sizeof (grub_uint64_t));
525      do
526        {
527          int i;
528
529          for (i = 0; i < nrec; i++)
530            {
531              if (fileblock < get_fsb(keys, i))
532                break;
533            }
534
535          /* Sparse block.  */
536          if (i == 0)
537            {
538              grub_free (leaf);
539              return 0;
540            }
541
542          if (grub_disk_read (node->data->disk,
543                              GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
544                              0, node->data->bsize, leaf))
545            return 0;
546
547          if ((!node->data->hascrc &&
548               grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
549              (node->data->hascrc &&
550               grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
551            {
552              grub_free (leaf);
553              grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
554              return 0;
555            }
556
557          nrec = grub_be_to_cpu16 (leaf->numrecs);
558          keys = grub_xfs_btree_keys(node->data, leaf);
559          recoffset = ((node->data->bsize - ((char *) keys
560                                             - (char *) leaf))
561                       / (2 * sizeof (grub_uint64_t)));
562        }
563      while (leaf->level);
564      exts = (struct grub_xfs_extent *) keys;
565    }
566  else if (node->inode.format == XFS_INODE_FORMAT_EXT)
567    {
568      nrec = grub_be_to_cpu32 (node->inode.nextents);
569      exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode);
570    }
571  else
572    {
573      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
574                  "XFS does not support inode format %d yet",
575                  node->inode.format);
576      return 0;
577    }
578
579  /* Iterate over each extent to figure out which extent has
580     the block we are looking for.  */
581  for (ex = 0; ex < nrec; ex++)
582    {
583      grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex);
584      grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex);
585      grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex);
586
587      /* Sparse block.  */
588      if (fileblock < offset)
589        break;
590      else if (fileblock < offset + size)
591        {
592          ret = (fileblock - offset + start);
593          break;
594        }
595    }
596
597  grub_free (leaf);
598
599  return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
600}
601
602
603/* Read LEN bytes from the file described by DATA starting with byte
604   POS.  Return the amount of read bytes in READ.  */
605static grub_ssize_t
606grub_xfs_read_file (grub_fshelp_node_t node,
607                     grub_disk_read_hook_t read_hook, void *read_hook_data,
608                     grub_off_t pos, grub_size_t len, char *buf)
609{
610  return grub_fshelp_read_file (node->data->disk, node,
611                                read_hook, read_hook_data,
612                                pos, len, buf, grub_xfs_read_block,
613                                grub_be_to_cpu64 (node->inode.size),
614                                node->data->sblock.log2_bsize
615                                - GRUB_DISK_SECTOR_BITS, 0);
616}
617
618
619static char *
620grub_xfs_read_symlink (grub_fshelp_node_t node)
621{
622  int size = grub_be_to_cpu64 (node->inode.size);
623
624  switch (node->inode.format)
625    {
626    case XFS_INODE_FORMAT_INO:
627      return grub_strndup (grub_xfs_inode_data(&node->inode), size);
628
629    case XFS_INODE_FORMAT_EXT:
630      {
631        char *symlink;
632        grub_ssize_t numread;
633
634        symlink = grub_malloc (size + 1);
635        if (!symlink)
636          return 0;
637
638        numread = grub_xfs_read_file (node, 0, 0, 0, size, symlink);
639        if (numread != size)
640          {
641            grub_free (symlink);
642            return 0;
643          }
644        symlink[size] = '\0';
645        return symlink;
646      }
647    }
648
649  return 0;
650}
651
652
653static enum grub_fshelp_filetype
654grub_xfs_mode_to_filetype (grub_uint16_t mode)
655{
656  if ((grub_be_to_cpu16 (mode)
657       & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
658    return GRUB_FSHELP_DIR;
659  else if ((grub_be_to_cpu16 (mode)
660            & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
661    return GRUB_FSHELP_SYMLINK;
662  else if ((grub_be_to_cpu16 (mode)
663            & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
664    return GRUB_FSHELP_REG;
665  return GRUB_FSHELP_UNKNOWN;
666}
667
668
669/* Context for grub_xfs_iterate_dir.  */
670struct grub_xfs_iterate_dir_ctx
671{
672  grub_fshelp_iterate_dir_hook_t hook;
673  void *hook_data;
674  struct grub_fshelp_node *diro;
675};
676
677/* Helper for grub_xfs_iterate_dir.  */
678static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename,
679                                  struct grub_xfs_iterate_dir_ctx *ctx)
680{
681  struct grub_fshelp_node *fdiro;
682  grub_err_t err;
683
684  fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1);
685  if (!fdiro)
686    {
687      grub_print_error ();
688      return 0;
689    }
690
691  /* The inode should be read, otherwise the filetype can
692     not be determined.  */
693  fdiro->ino = ino;
694  fdiro->inode_read = 1;
695  fdiro->data = ctx->diro->data;
696  err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
697  if (err)
698    {
699      grub_print_error ();
700      return 0;
701    }
702
703  return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
704                    fdiro, ctx->hook_data);
705}
706
707static int
708grub_xfs_iterate_dir (grub_fshelp_node_t dir,
709                      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
710{
711  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
712  struct grub_xfs_iterate_dir_ctx ctx = {
713    .hook = hook,
714    .hook_data = hook_data,
715    .diro = diro
716  };
717
718  switch (diro->inode.format)
719    {
720    case XFS_INODE_FORMAT_INO:
721      {
722        struct grub_xfs_dir_header *head = (struct grub_xfs_dir_header *) grub_xfs_inode_data(&diro->inode);
723        struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
724        int smallino = !head->largeino;
725        int i;
726        grub_uint64_t parent;
727
728        /* If small inode numbers are used to pack the direntry, the
729           parent inode number is small too.  */
730        if (smallino)
731          parent = grub_be_to_cpu32 (head->parent.i4);
732        else
733          parent = grub_be_to_cpu64 (head->parent.i8);
734
735        /* Synthesize the direntries for `.' and `..'.  */
736        if (iterate_dir_call_hook (diro->ino, ".", &ctx))
737          return 1;
738
739        if (iterate_dir_call_hook (parent, "..", &ctx))
740          return 1;
741
742        for (i = 0; i < head->count; i++)
743          {
744            grub_uint64_t ino;
745            grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
746            grub_uint8_t c;
747
748            /* inopos might be unaligned.  */
749            if (smallino)
750              ino = (((grub_uint32_t) inopos[0]) << 24)
751                | (((grub_uint32_t) inopos[1]) << 16)
752                | (((grub_uint32_t) inopos[2]) << 8)
753                | (((grub_uint32_t) inopos[3]) << 0);
754            else
755              ino = (((grub_uint64_t) inopos[0]) << 56)
756                | (((grub_uint64_t) inopos[1]) << 48)
757                | (((grub_uint64_t) inopos[2]) << 40)
758                | (((grub_uint64_t) inopos[3]) << 32)
759                | (((grub_uint64_t) inopos[4]) << 24)
760                | (((grub_uint64_t) inopos[5]) << 16)
761                | (((grub_uint64_t) inopos[6]) << 8)
762                | (((grub_uint64_t) inopos[7]) << 0);
763
764            c = de->name[de->len];
765            de->name[de->len] = '\0';
766            if (iterate_dir_call_hook (ino, de->name, &ctx))
767              return 1;
768            de->name[de->len] = c;
769
770            de = grub_xfs_inline_next_de(dir->data, head, de);
771          }
772        break;
773      }
774
775    case XFS_INODE_FORMAT_BTREE:
776    case XFS_INODE_FORMAT_EXT:
777      {
778        grub_ssize_t numread;
779        char *dirblock;
780        grub_uint64_t blk;
781        int dirblk_size, dirblk_log2;
782
783        dirblk_log2 = (dir->data->sblock.log2_bsize
784                       + dir->data->sblock.log2_dirblk);
785        dirblk_size = 1 << dirblk_log2;
786
787        dirblock = grub_malloc (dirblk_size);
788        if (! dirblock)
789          return 0;
790
791        /* Iterate over every block the directory has.  */
792        for (blk = 0;
793             blk < (grub_be_to_cpu64 (dir->inode.size)
794                    >> dirblk_log2);
795             blk++)
796          {
797            struct grub_xfs_dir2_entry *direntry =
798                                        grub_xfs_first_de(dir->data, dirblock);
799            int entries;
800            struct grub_xfs_dirblock_tail *tail =
801                                        grub_xfs_dir_tail(dir->data, dirblock);
802
803            numread = grub_xfs_read_file (dir, 0, 0,
804                                          blk << dirblk_log2,
805                                          dirblk_size, dirblock);
806            if (numread != dirblk_size)
807              return 0;
808
809            entries = (grub_be_to_cpu32 (tail->leaf_count)
810                       - grub_be_to_cpu32 (tail->leaf_stale));
811
812            /* Iterate over all entries within this block.  */
813            while ((char *)direntry < (char *)tail)
814              {
815                grub_uint8_t *freetag;
816                char *filename;
817
818                freetag = (grub_uint8_t *) direntry;
819
820                if (grub_get_unaligned16 (freetag) == 0XFFFF)
821                  {
822                    grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
823
824                    /* This entry is not used, go to the next one.  */
825                    direntry = (struct grub_xfs_dir2_entry *)
826                                (((char *)direntry) +
827                                grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
828
829                    continue;
830                  }
831
832                filename = (char *)(direntry + 1);
833                /* The byte after the filename is for the filetype, padding, or
834                   tag, which is not used by GRUB.  So it can be overwritten. */
835                filename[direntry->len] = '\0';
836
837                if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), 
838                                           filename, &ctx))
839                  {
840                    grub_free (dirblock);
841                    return 1;
842                  }
843
844                /* Check if last direntry in this block is
845                   reached.  */
846                entries--;
847                if (!entries)
848                  break;
849
850                /* Select the next directory entry.  */
851                direntry = grub_xfs_next_de(dir->data, direntry);
852              }
853          }
854        grub_free (dirblock);
855        break;
856      }
857
858    default:
859      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
860                  "XFS does not support inode format %d yet",
861                  diro->inode.format);
862    }
863  return 0;
864}
865
866
867static struct grub_xfs_data *
868grub_xfs_mount (grub_disk_t disk)
869{
870  struct grub_xfs_data *data = 0;
871
872  data = grub_zalloc (sizeof (struct grub_xfs_data));
873  if (!data)
874    return 0;
875
876  grub_dprintf("xfs", "Reading sb\n");
877  /* Read the superblock.  */
878  if (grub_disk_read (disk, 0, 0,
879                      sizeof (struct grub_xfs_sblock), &data->sblock))
880    goto fail;
881
882  if (!grub_xfs_sb_valid(data))
883    goto fail;
884
885  data = grub_realloc (data,
886                       sizeof (struct grub_xfs_data)
887                       - sizeof (struct grub_xfs_inode)
888                       + grub_xfs_inode_size(data) + 1);
889
890  if (! data)
891    goto fail;
892
893  data->diropen.data = data;
894  data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino);
895  data->diropen.inode_read = 1;
896  data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
897  data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
898  data->hasftype = grub_xfs_sb_hasftype(data);
899  data->hascrc = grub_xfs_sb_hascrc(data);
900
901  data->disk = disk;
902  data->pos = 0;
903  grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
904               grub_cpu_to_be64(data->sblock.rootino));
905
906  grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
907
908  return data;
909 fail:
910
911  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
912    grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem");
913
914  grub_free (data);
915
916  return 0;
917}
918
919
920/* Context for grub_xfs_dir.  */
921struct grub_xfs_dir_ctx
922{
923  grub_fs_dir_hook_t hook;
924  void *hook_data;
925};
926
927/* Helper for grub_xfs_dir.  */
928static int
929grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
930                   grub_fshelp_node_t node, void *data)
931{
932  struct grub_xfs_dir_ctx *ctx = data;
933  struct grub_dirhook_info info;
934
935  grub_memset (&info, 0, sizeof (info));
936  if (node->inode_read)
937    {
938      info.mtimeset = 1;
939      info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
940    }
941  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
942  grub_free (node);
943  return ctx->hook (filename, &info, ctx->hook_data);
944}
945
946static grub_err_t
947grub_xfs_dir (grub_device_t device, const char *path,
948              grub_fs_dir_hook_t hook, void *hook_data)
949{
950  struct grub_xfs_dir_ctx ctx = { hook, hook_data };
951  struct grub_xfs_data *data = 0;
952  struct grub_fshelp_node *fdiro = 0;
953
954  grub_dl_ref (my_mod);
955
956  data = grub_xfs_mount (device->disk);
957  if (!data)
958    goto mount_fail;
959
960  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
961                         grub_xfs_read_symlink, GRUB_FSHELP_DIR);
962  if (grub_errno)
963    goto fail;
964
965  grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
966
967 fail:
968  if (fdiro != &data->diropen)
969    grub_free (fdiro);
970  grub_free (data);
971
972 mount_fail:
973
974  grub_dl_unref (my_mod);
975
976  return grub_errno;
977}
978
979
980/* Open a file named NAME and initialize FILE.  */
981static grub_err_t
982grub_xfs_open (struct grub_file *file, const char *name)
983{
984  struct grub_xfs_data *data;
985  struct grub_fshelp_node *fdiro = 0;
986
987  grub_dl_ref (my_mod);
988
989  data = grub_xfs_mount (file->device->disk);
990  if (!data)
991    goto mount_fail;
992
993  grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
994                         grub_xfs_read_symlink, GRUB_FSHELP_REG);
995  if (grub_errno)
996    goto fail;
997
998  if (!fdiro->inode_read)
999    {
1000      grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
1001      if (grub_errno)
1002        goto fail;
1003    }
1004
1005  if (fdiro != &data->diropen)
1006    {
1007      grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data));
1008      grub_free (fdiro);
1009    }
1010
1011  file->size = grub_be_to_cpu64 (data->diropen.inode.size);
1012  file->data = data;
1013  file->offset = 0;
1014
1015  return 0;
1016
1017 fail:
1018  if (fdiro != &data->diropen)
1019    grub_free (fdiro);
1020  grub_free (data);
1021
1022 mount_fail:
1023  grub_dl_unref (my_mod);
1024
1025  return grub_errno;
1026}
1027
1028
1029static grub_ssize_t
1030grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
1031{
1032  struct grub_xfs_data *data =
1033    (struct grub_xfs_data *) file->data;
1034
1035  return grub_xfs_read_file (&data->diropen,
1036                             file->read_hook, file->read_hook_data,
1037                             file->offset, len, buf);
1038}
1039
1040
1041static grub_err_t
1042grub_xfs_close (grub_file_t file)
1043{
1044  grub_free (file->data);
1045
1046  grub_dl_unref (my_mod);
1047
1048  return GRUB_ERR_NONE;
1049}
1050
1051
1052static grub_err_t
1053grub_xfs_label (grub_device_t device, char **label)
1054{
1055  struct grub_xfs_data *data;
1056  grub_disk_t disk = device->disk;
1057
1058  grub_dl_ref (my_mod);
1059
1060  data = grub_xfs_mount (disk);
1061  if (data)
1062    *label = grub_strndup ((char *) (data->sblock.label), 12);
1063  else
1064    *label = 0;
1065
1066  grub_dl_unref (my_mod);
1067
1068  grub_free (data);
1069
1070  return grub_errno;
1071}
1072
1073static grub_err_t
1074grub_xfs_uuid (grub_device_t device, char **uuid)
1075{
1076  struct grub_xfs_data *data;
1077  grub_disk_t disk = device->disk;
1078
1079  grub_dl_ref (my_mod);
1080
1081  data = grub_xfs_mount (disk);
1082  if (data)
1083    {
1084      *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1085                             grub_be_to_cpu16 (data->sblock.uuid[0]),
1086                             grub_be_to_cpu16 (data->sblock.uuid[1]),
1087                             grub_be_to_cpu16 (data->sblock.uuid[2]),
1088                             grub_be_to_cpu16 (data->sblock.uuid[3]),
1089                             grub_be_to_cpu16 (data->sblock.uuid[4]),
1090                             grub_be_to_cpu16 (data->sblock.uuid[5]),
1091                             grub_be_to_cpu16 (data->sblock.uuid[6]),
1092                             grub_be_to_cpu16 (data->sblock.uuid[7]));
1093    }
1094  else
1095    *uuid = NULL;
1096
1097  grub_dl_unref (my_mod);
1098
1099  grub_free (data);
1100
1101  return grub_errno;
1102}
1103
1104
1105
1106static struct grub_fs grub_xfs_fs =
1107  {
1108    .name = "xfs",
1109    .dir = grub_xfs_dir,
1110    .open = grub_xfs_open,
1111    .read = grub_xfs_read,
1112    .close = grub_xfs_close,
1113    .label = grub_xfs_label,
1114    .uuid = grub_xfs_uuid,
1115#ifdef GRUB_UTIL
1116    .reserved_first_sector = 0,
1117    .blocklist_install = 1,
1118#endif
1119    .next = 0
1120  };
1121
1122GRUB_MOD_INIT(xfs)
1123{
1124  grub_fs_register (&grub_xfs_fs);
1125  my_mod = mod;
1126}
1127
1128GRUB_MOD_FINI(xfs)
1129{
1130  grub_fs_unregister (&grub_xfs_fs);
1131}
Note: See TracBrowser for help on using the repository browser.