]> TLD Linux GIT Repositories - packages/grub2.git/blob - grub2-xfs-V5-filesystem-format-support.patch
- partial PLD merge
[packages/grub2.git] / grub2-xfs-V5-filesystem-format-support.patch
1 From 2f725e644d8ccf001a4dccddc8abb2c9479352a7 Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Wed, 11 Jun 2014 18:36:01 +0200
4 Subject: [PATCH] xfs: V5 filesystem format support
5
6 Signed-off-by: Jan Kara <jack@suse.cz>
7 ---
8  grub-core/fs/xfs.c | 245 +++++++++++++++++++++++++++++++++++++----------------
9  1 file changed, 173 insertions(+), 72 deletions(-)
10
11 Index: grub-2.02~beta2/grub-core/fs/xfs.c
12 ===================================================================
13 --- grub-2.02~beta2.orig/grub-core/fs/xfs.c
14 +++ grub-2.02~beta2/grub-core/fs/xfs.c
15 @@ -34,6 +34,15 @@ GRUB_MOD_LICENSE ("GPLv3+");
16  #define XFS_INODE_FORMAT_EXT   2
17  #define XFS_INODE_FORMAT_BTREE 3
18  
19 +/* Superblock version field flags */
20 +#define XFS_SB_VERSION_NUMBITS          0x000f
21 +#define XFS_SB_VERSION_MOREBITSBIT      0x8000
22 +
23 +/* features2 field flags */
24 +#define XFS_SB_VERSION2_FTYPE           0x00000200      /* inode type in dir */
25 +
26 +/* incompat feature flags */
27 +#define XFS_SB_FEAT_INCOMPAT_FTYPE      (1 << 0)        /* filetype in dirent */
28  
29  struct grub_xfs_sblock
30  {
31 @@ -45,7 +54,9 @@ struct grub_xfs_sblock
32    grub_uint64_t rootino;
33    grub_uint8_t unused3[20];
34    grub_uint32_t agsize;
35 -  grub_uint8_t unused4[20];
36 +  grub_uint8_t unused4[12];
37 +  grub_uint16_t version;
38 +  grub_uint8_t unused5[6];
39    grub_uint8_t label[12];
40    grub_uint8_t log2_bsize;
41    grub_uint8_t log2_sect;
42 @@ -54,12 +65,19 @@ struct grub_xfs_sblock
43    grub_uint8_t log2_agblk;
44    grub_uint8_t unused6[67];
45    grub_uint8_t log2_dirblk;
46 +  grub_uint8_t unused7[7];
47 +  grub_uint32_t features2;
48 +  grub_uint8_t unused8[4];
49 +  grub_uint32_t sb_features_compat;
50 +  grub_uint32_t sb_features_ro_compat;
51 +  grub_uint32_t sb_features_incompat;
52 +  grub_uint32_t sb_features_log_incompat;
53  } GRUB_PACKED;
54  
55  struct grub_xfs_dir_header
56  {
57    grub_uint8_t count;
58 -  grub_uint8_t smallino;
59 +  grub_uint8_t largeino;
60    union
61    {
62      grub_uint32_t i4;
63 @@ -67,14 +85,16 @@ struct grub_xfs_dir_header
64    } GRUB_PACKED parent;
65  } GRUB_PACKED;
66  
67 +/* Structure for directory entry inlined in the inode */
68  struct grub_xfs_dir_entry
69  {
70    grub_uint8_t len;
71    grub_uint16_t offset;
72    char name[1];
73 -  /* Inode number follows, 32 bits.  */
74 +  /* Inode number follows, 32 / 64 bits.  */
75  } GRUB_PACKED;
76  
77 +/* Structure for directory entry in a block */
78  struct grub_xfs_dir2_entry
79  {
80    grub_uint64_t inode;
81 @@ -90,7 +110,8 @@ struct grub_xfs_btree_node
82    grub_uint16_t numrecs;
83    grub_uint64_t left;
84    grub_uint64_t right;
85 -  grub_uint64_t keys[1];
86 +  /* In V5 here follow crc, uuid, etc. */
87 +  /* Then follow keys and block pointers */
88  }  GRUB_PACKED;
89  
90  struct grub_xfs_btree_root
91 @@ -123,17 +144,6 @@ struct grub_xfs_inode
92    grub_uint16_t unused3;
93    grub_uint8_t fork_offset;
94    grub_uint8_t unused4[17];
95 -  union
96 -  {
97 -    char raw[156];
98 -    struct dir
99 -    {
100 -      struct grub_xfs_dir_header dirhead;
101 -      struct grub_xfs_dir_entry direntry[1];
102 -    } dir;
103 -    grub_xfs_extent extents[XFS_INODE_EXTENTS];
104 -    struct grub_xfs_btree_root btree;
105 -  } GRUB_PACKED data;
106  } GRUB_PACKED;
107  
108  struct grub_xfs_dirblock_tail
109 @@ -157,6 +167,8 @@ struct grub_xfs_data
110    int pos;
111    int bsize;
112    grub_uint32_t agsize;
113 +  unsigned int hasftype:1;
114 +  unsigned int hascrc:1;
115    struct grub_fshelp_node diropen;
116  };
117  
118 @@ -164,6 +176,24 @@ static grub_dl_t my_mod;
119  
120  \f
121  
122 +static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
123 +{
124 +  return (grub_be_to_cpu16(data->sblock.version) & XFS_SB_VERSION_NUMBITS) == 5;
125 +}
126 +
127 +static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
128 +{
129 +  grub_uint32_t version = grub_be_to_cpu16(data->sblock.version);
130 +
131 +  if ((version & XFS_SB_VERSION_NUMBITS) == 5 &&
132 +      grub_be_to_cpu32(data->sblock.sb_features_incompat) & XFS_SB_FEAT_INCOMPAT_FTYPE)
133 +    return 1;
134 +  if (version & XFS_SB_VERSION_MOREBITSBIT &&
135 +      grub_be_to_cpu32(data->sblock.features2) & XFS_SB_VERSION2_FTYPE)
136 +    return 1;
137 +  return 0;
138 +}
139 +
140  /* Filetype information as used in inodes.  */
141  #define FILETYPE_INO_MASK      0170000
142  #define FILETYPE_INO_REG       0100000
143 @@ -219,18 +249,6 @@ GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *e
144    return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 21) - 1));
145  }
146  
147 -static inline int
148 -GRUB_XFS_ROUND_TO_DIRENT (int pos)
149 -{
150 -  return ((((pos) + 8 - 1) / 8) * 8);
151 -}
152 -
153 -static inline int
154 -GRUB_XFS_NEXT_DIRENT (int pos, int len)
155 -{
156 -  return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
157 -}
158 -
159  \f
160  static inline grub_uint64_t
161  grub_xfs_inode_block (struct grub_xfs_data *data,
162 @@ -261,6 +279,92 @@ grub_xfs_inode_size(struct grub_xfs_data
163         return 1 << data->sblock.log2_inode;
164  }
165  
166 +static void *
167 +grub_xfs_inode_data(struct grub_xfs_inode *inode)
168 +{
169 +       if (inode->version <= 2)
170 +               return ((char *)inode) + 100;
171 +       return ((char *)inode) + 176;
172 +}
173 +
174 +static struct grub_xfs_dir_entry *
175 +grub_xfs_inline_de(struct grub_xfs_dir_header *head)
176 +{
177 +       /*
178 +        * With small inode numbers the header is 4 bytes smaller because of
179 +        * smaller parent pointer
180 +        */
181 +       return (void *)(((char *)head) + sizeof(struct grub_xfs_dir_header) -
182 +               (head->largeino ? 0 : sizeof(grub_uint32_t)));
183 +}
184 +
185 +static grub_uint8_t *
186 +grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
187 +                         struct grub_xfs_dir_entry *de)
188 +{
189 +       return ((grub_uint8_t *)(de + 1)) + de->len - 1 +
190 +                (data->hasftype ? 1 : 0);
191 +}
192 +
193 +static struct grub_xfs_dir_entry *
194 +grub_xfs_inline_next_de(struct grub_xfs_data *data,
195 +                       struct grub_xfs_dir_header *head,
196 +                       struct grub_xfs_dir_entry *de)
197 +{
198 +  char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
199 +
200 +  p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
201 +  if (data->hasftype)
202 +    p++;
203 +
204 +  return (struct grub_xfs_dir_entry *)p;
205 +}
206 +
207 +static struct grub_xfs_dirblock_tail *
208 +grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
209 +{
210 +  int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
211 +
212 +  return (struct grub_xfs_dirblock_tail *)
213 +    ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
214 +}
215 +
216 +static struct grub_xfs_dir2_entry *
217 +grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
218 +{
219 +  if (data->hascrc)
220 +    return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
221 +  return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
222 +}
223 +
224 +static inline int
225 +grub_xfs_round_dirent_size (int len)
226 +{
227 +  return (len + 7) & ~7;
228 +}
229 +
230 +static struct grub_xfs_dir2_entry *
231 +grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
232 +{
233 +  int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
234 +
235 +  if (data->hasftype)
236 +    size++;            /* File type */
237 +  return (struct grub_xfs_dir2_entry *)
238 +                       (((char *)de) + grub_xfs_round_dirent_size (size));
239 +}
240 +
241 +static grub_uint64_t *
242 +grub_xfs_btree_keys(struct grub_xfs_data *data,
243 +                   struct grub_xfs_btree_node *leaf)
244 +{
245 +  char *p = (char *)(leaf + 1);
246 +
247 +  if (data->hascrc)
248 +    p += 48;   /* crc, uuid, ... */
249 +  return (grub_uint64_t *)(void*)p;
250 +}
251 +
252  static grub_err_t
253  grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
254                      struct grub_xfs_inode *inode)
255 @@ -268,6 +372,9 @@ grub_xfs_read_inode (struct grub_xfs_dat
256    grub_uint64_t block = grub_xfs_inode_block (data, ino);
257    int offset = grub_xfs_inode_offset (data, ino);
258  
259 +  grub_dprintf("xfs", "Reading inode (%llu) - %llu, %d\n",
260 +              (unsigned long long) ino,
261 +              (unsigned long long) block, offset);
262    /* Read the inode.  */
263    if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
264                       inode))
265 @@ -290,6 +397,7 @@ grub_xfs_read_block (grub_fshelp_node_t
266  
267    if (node->inode.format == XFS_INODE_FORMAT_BTREE)
268      {
269 +      struct grub_xfs_btree_root *root;
270        const grub_uint64_t *keys;
271        int recoffset;
272  
273 @@ -297,15 +405,15 @@ grub_xfs_read_block (grub_fshelp_node_t
274        if (leaf == 0)
275          return 0;
276  
277 -      nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
278 -      keys = &node->inode.data.btree.keys[0];
279 +      root = grub_xfs_inode_data(&node->inode);
280 +      nrec = grub_be_to_cpu16 (root->numrecs);
281 +      keys = &root->keys[0];
282        if (node->inode.fork_offset)
283         recoffset = (node->inode.fork_offset - 1) / 2;
284        else
285         recoffset = (grub_xfs_inode_size(node->data)
286 -                    - ((char *) &node->inode.data.btree.keys
287 -                       - (char *) &node->inode))
288 -         / (2 * sizeof (grub_uint64_t));
289 +                    - ((char *) keys - (char *) &node->inode))
290 +                               / (2 * sizeof (grub_uint64_t));
291        do
292          {
293            int i;
294 @@ -327,7 +435,10 @@ grub_xfs_read_block (grub_fshelp_node_t
295                                0, node->data->bsize, leaf))
296              return 0;
297  
298 -          if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
299 +         if ((!node->data->hascrc &&
300 +              grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
301 +             (node->data->hascrc &&
302 +              grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
303              {
304                grub_free (leaf);
305                grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
306 @@ -335,8 +446,8 @@ grub_xfs_read_block (grub_fshelp_node_t
307              }
308  
309            nrec = grub_be_to_cpu16 (leaf->numrecs);
310 -          keys = &leaf->keys[0];
311 -         recoffset = ((node->data->bsize - ((char *) &leaf->keys
312 +          keys = grub_xfs_btree_keys(node->data, leaf);
313 +         recoffset = ((node->data->bsize - ((char *) keys
314                                              - (char *) leaf))
315                        / (2 * sizeof (grub_uint64_t)));
316         }
317 @@ -346,7 +457,7 @@ grub_xfs_read_block (grub_fshelp_node_t
318    else if (node->inode.format == XFS_INODE_FORMAT_EXT)
319      {
320        nrec = grub_be_to_cpu32 (node->inode.nextents);
321 -      exts = &node->inode.data.extents[0];
322 +      exts = grub_xfs_inode_data(&node->inode);
323      }
324    else
325      {
326 @@ -404,7 +515,7 @@ grub_xfs_read_symlink (grub_fshelp_node_
327    switch (node->inode.format)
328      {
329      case XFS_INODE_FORMAT_INO:
330 -      return grub_strndup (node->inode.data.raw, size);
331 +      return grub_strndup (grub_xfs_inode_data(&node->inode), size);
332  
333      case XFS_INODE_FORMAT_EXT:
334        {
335 @@ -501,23 +612,18 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
336      {
337      case XFS_INODE_FORMAT_INO:
338        {
339 -       struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
340 -       int smallino = !diro->inode.data.dir.dirhead.smallino;
341 +       struct grub_xfs_dir_header *head = grub_xfs_inode_data(&diro->inode);
342 +       struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
343 +       int smallino = !head->largeino;
344         int i;
345         grub_uint64_t parent;
346  
347         /* If small inode numbers are used to pack the direntry, the
348            parent inode number is small too.  */
349         if (smallino)
350 -         {
351 -           parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
352 -           /* The header is a bit smaller than usual.  */
353 -           de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
354 -         }
355 +         parent = grub_be_to_cpu32 (head->parent.i4);
356         else
357 -         {
358 -           parent = grub_be_to_cpu64(diro->inode.data.dir.dirhead.parent.i8);
359 -         }
360 +         parent = grub_be_to_cpu64 (head->parent.i8);
361  
362         /* Synthesize the direntries for `.' and `..'.  */
363         if (iterate_dir_call_hook (diro->ino, ".", &ctx))
364 @@ -526,12 +632,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
365         if (iterate_dir_call_hook (parent, "..", &ctx))
366           return 1;
367  
368 -       for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
369 +       for (i = 0; i < head->count; i++)
370           {
371             grub_uint64_t ino;
372 -           grub_uint8_t *inopos = (((grub_uint8_t *) de)
373 -                           + sizeof (struct grub_xfs_dir_entry)
374 -                           + de->len - 1);
375 +           grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
376             grub_uint8_t c;
377  
378             /* inopos might be unaligned.  */
379 @@ -556,10 +660,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
380               return 1;
381             de->name[de->len] = c;
382  
383 -           de = ((struct grub_xfs_dir_entry *)
384 -                 (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
385 -                  + ((smallino ? sizeof (grub_uint32_t)
386 -                      : sizeof (grub_uint64_t))) - 1));
387 +           de = grub_xfs_inline_next_de(dir->data, head, de);
388           }
389         break;
390        }
391 @@ -586,15 +687,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
392                     >> dirblk_log2);
393              blk++)
394           {
395 -           /* The header is skipped, the first direntry is stored
396 -              from byte 16.  */
397 -           int pos = 16;
398 +           struct grub_xfs_dir2_entry *direntry =
399 +                                       grub_xfs_first_de(dir->data, dirblock);
400             int entries;
401 -           int tail_start = (dirblk_size
402 -                             - sizeof (struct grub_xfs_dirblock_tail));
403 -
404 -           struct grub_xfs_dirblock_tail *tail;
405 -           tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
406 +           struct grub_xfs_dirblock_tail *tail =
407 +                                       grub_xfs_dir_tail(dir->data, dirblock);
408  
409             numread = grub_xfs_read_file (dir, 0, 0,
410                                           blk << dirblk_log2,
411 @@ -606,13 +703,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
412                        - grub_be_to_cpu32 (tail->leaf_stale));
413  
414             /* Iterate over all entries within this block.  */
415 -           while (pos < tail_start)
416 +           while ((char *)direntry < (char *)tail)
417               {
418 -               struct grub_xfs_dir2_entry *direntry;
419                 grub_uint8_t *freetag;
420                 char *filename;
421  
422 -               direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
423                 freetag = (grub_uint8_t *) direntry;
424  
425                 if (grub_get_unaligned16 (freetag) == 0XFFFF)
426 @@ -620,14 +715,16 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
427                     grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
428  
429                     /* This entry is not used, go to the next one.  */
430 -                   pos += grub_be_to_cpu16 (grub_get_unaligned16 (skip));
431 +                   direntry = (struct grub_xfs_dir2_entry *)
432 +                               (((char *)direntry) +
433 +                               grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
434  
435                     continue;
436                   }
437  
438 -               filename = &dirblock[pos + sizeof (*direntry)];
439 -               /* The byte after the filename is for the tag, which
440 -                  is not used by GRUB.  So it can be overwritten.  */
441 +               filename = (char *)(direntry + 1);
442 +               /* The byte after the filename is for the filetype, padding, or
443 +                  tag, which is not used by GRUB.  So it can be overwritten. */
444                 filename[direntry->len] = '\0';
445  
446                 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), 
447 @@ -644,8 +741,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
448                   break;
449  
450                 /* Select the next directory entry.  */
451 -               pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
452 -               pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
453 +               direntry = grub_xfs_next_de(dir->data, direntry);
454               }
455           }
456         grub_free (dirblock);
457 @@ -670,6 +766,7 @@ grub_xfs_mount (grub_disk_t disk)
458    if (!data)
459      return 0;
460  
461 +  grub_dprintf("xfs", "Reading sb\n");
462    /* Read the superblock.  */
463    if (grub_disk_read (disk, 0, 0,
464                       sizeof (struct grub_xfs_sblock), &data->sblock))
465 @@ -697,9 +794,13 @@ grub_xfs_mount (grub_disk_t disk)
466    data->diropen.inode_read = 1;
467    data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
468    data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
469 +  data->hasftype = grub_xfs_sb_hasftype(data);
470 +  data->hascrc = grub_xfs_sb_hascrc(data);
471  
472    data->disk = disk;
473    data->pos = 0;
474 +  grub_dprintf("xfs", "Reading root ino %llu\n",
475 +              (unsigned long long) grub_cpu_to_be64(data->sblock.rootino));
476  
477    grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
478