~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to grub-core/fs/squash4.c

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2012-09-13 18:02:04 UTC
  • mfrom: (1.17.15 upstream)
  • mto: (17.6.27 experimental)
  • mto: This revision was merged to the branch mainline in revision 145.
  • Revision ID: package-import@ubuntu.com-20120913180204-mojnmocbimlom4im
Tags: upstream-2.00
ImportĀ upstreamĀ versionĀ 2.00

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* squash4.c - SquashFS */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2010  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
#include <grub/deflate.h>
 
29
#include <minilzo.h>
 
30
 
 
31
#include "xz.h"
 
32
#include "xz_stream.h"
 
33
 
 
34
GRUB_MOD_LICENSE ("GPLv3+");
 
35
 
 
36
/*
 
37
  object         format      Pointed by
 
38
  superblock     RAW         Fixed offset (0)
 
39
  data           RAW ?       Fixed offset (60)
 
40
  inode table    Chunk       superblock
 
41
  dir table      Chunk       superblock
 
42
  fragment table Chunk       unk1
 
43
  unk1           RAW, Chunk  superblock
 
44
  unk2           RAW         superblock
 
45
  UID/GID        Chunk       exttblptr
 
46
  exttblptr      RAW         superblock
 
47
 
 
48
  UID/GID table is the array ot uint32_t
 
49
  unk1 contains pointer to fragment table followed by some chunk.
 
50
  unk2 containts one uint64_t
 
51
*/
 
52
 
 
53
struct grub_squash_super
 
54
{
 
55
  grub_uint32_t magic;
 
56
#define SQUASH_MAGIC 0x73717368
 
57
  grub_uint32_t dummy1;
 
58
  grub_uint32_t creation_time;
 
59
  grub_uint32_t block_size;
 
60
  grub_uint32_t dummy2;
 
61
  grub_uint16_t compression;
 
62
  grub_uint16_t dummy3;
 
63
  grub_uint64_t dummy4;
 
64
  grub_uint16_t root_ino_offset;
 
65
  grub_uint32_t root_ino_chunk;
 
66
  grub_uint16_t dummy5;
 
67
  grub_uint64_t total_size;
 
68
  grub_uint64_t exttbloffset;
 
69
  grub_uint64_t dummy6;
 
70
  grub_uint64_t inodeoffset;
 
71
  grub_uint64_t diroffset;
 
72
  grub_uint64_t unk1offset;
 
73
  grub_uint64_t unk2offset;
 
74
} __attribute__ ((packed));
 
75
 
 
76
/* Chunk-based */
 
77
struct grub_squash_inode
 
78
{
 
79
  /* Same values as direlem types. */
 
80
  grub_uint16_t type;
 
81
  grub_uint16_t dummy[3];
 
82
  grub_uint32_t mtime;
 
83
  grub_uint32_t dummy2;
 
84
  union
 
85
  {
 
86
    struct {
 
87
      grub_uint32_t chunk;
 
88
      grub_uint32_t fragment;
 
89
      grub_uint32_t offset;
 
90
      grub_uint32_t size;
 
91
      grub_uint32_t block_size[0];
 
92
    }  __attribute__ ((packed)) file;
 
93
    struct {
 
94
      grub_uint64_t chunk;
 
95
      grub_uint64_t size;
 
96
      grub_uint32_t dummy1[3];
 
97
      grub_uint32_t fragment;
 
98
      grub_uint32_t offset;
 
99
      grub_uint32_t dummy3;
 
100
      grub_uint32_t block_size[0];
 
101
    }  __attribute__ ((packed)) long_file;
 
102
    struct {
 
103
      grub_uint32_t chunk;
 
104
      grub_uint32_t dummy;
 
105
      grub_uint16_t size;
 
106
      grub_uint16_t offset;
 
107
    } __attribute__ ((packed)) dir;
 
108
    struct {
 
109
      grub_uint32_t dummy1;
 
110
      grub_uint32_t size;
 
111
      grub_uint32_t chunk;
 
112
      grub_uint32_t dummy2;
 
113
      grub_uint16_t dummy3;
 
114
      grub_uint16_t offset;
 
115
    } __attribute__ ((packed)) long_dir;
 
116
    struct {
 
117
      grub_uint32_t dummy;
 
118
      grub_uint32_t namelen;
 
119
      char name[0];
 
120
    } __attribute__ ((packed)) symlink;
 
121
  }  __attribute__ ((packed));
 
122
} __attribute__ ((packed));
 
123
 
 
124
struct grub_squash_cache_inode
 
125
{
 
126
  struct grub_squash_inode ino;
 
127
  grub_disk_addr_t ino_chunk;
 
128
  grub_uint16_t ino_offset;
 
129
  grub_uint32_t *block_sizes;
 
130
  grub_disk_addr_t *cumulated_block_sizes;
 
131
};
 
132
 
 
133
/* Chunk-based.  */
 
134
struct grub_squash_dirent_header
 
135
{
 
136
  /* Actually the value is the number of elements - 1.  */
 
137
  grub_uint32_t nelems;
 
138
  grub_uint32_t ino_chunk;
 
139
  grub_uint32_t dummy;
 
140
} __attribute__ ((packed));
 
141
 
 
142
struct grub_squash_dirent
 
143
{
 
144
  grub_uint16_t ino_offset;
 
145
  grub_uint16_t dummy;
 
146
  grub_uint16_t type;
 
147
  /* Actually the value is the length of name - 1.  */
 
148
  grub_uint16_t namelen;
 
149
  char name[0];
 
150
} __attribute__ ((packed));
 
151
 
 
152
enum
 
153
  {
 
154
    SQUASH_TYPE_DIR = 1,
 
155
    SQUASH_TYPE_REGULAR = 2,
 
156
    SQUASH_TYPE_SYMLINK = 3,
 
157
    SQUASH_TYPE_LONG_DIR = 8,
 
158
    SQUASH_TYPE_LONG_REGULAR = 9,
 
159
  };
 
160
 
 
161
 
 
162
struct grub_squash_frag_desc
 
163
{
 
164
  grub_uint64_t offset;
 
165
  grub_uint32_t size;
 
166
  grub_uint32_t dummy;
 
167
} __attribute__ ((packed));
 
168
 
 
169
enum
 
170
  {
 
171
    SQUASH_CHUNK_FLAGS = 0x8000,
 
172
    SQUASH_CHUNK_UNCOMPRESSED = 0x8000
 
173
  };
 
174
 
 
175
enum
 
176
  {
 
177
    SQUASH_BLOCK_FLAGS = 0x1000000,
 
178
    SQUASH_BLOCK_UNCOMPRESSED = 0x1000000
 
179
  };
 
180
 
 
181
enum
 
182
  {
 
183
    COMPRESSION_ZLIB = 1,
 
184
    COMPRESSION_LZO = 3,
 
185
    COMPRESSION_XZ = 4,
 
186
  };
 
187
 
 
188
 
 
189
#define SQUASH_CHUNK_SIZE 0x2000
 
190
#define XZBUFSIZ 0x2000
 
191
 
 
192
struct grub_squash_data
 
193
{
 
194
  grub_disk_t disk;
 
195
  struct grub_squash_super sb;
 
196
  struct grub_squash_cache_inode ino;
 
197
  grub_uint64_t fragments;
 
198
  int log2_blksz;
 
199
  grub_size_t blksz;
 
200
  grub_ssize_t (*decompress) (char *inbuf, grub_size_t insize, grub_off_t off,
 
201
                              char *outbuf, grub_size_t outsize,
 
202
                              struct grub_squash_data *data);
 
203
  struct xz_dec *xzdec;
 
204
  char *xzbuf;
 
205
};
 
206
 
 
207
struct grub_fshelp_node
 
208
{
 
209
  struct grub_squash_data *data;
 
210
  struct grub_squash_inode ino;
 
211
  grub_size_t stsize;
 
212
  struct 
 
213
  {
 
214
    grub_disk_addr_t ino_chunk;
 
215
    grub_uint16_t ino_offset;
 
216
  } stack[1];
 
217
};
 
218
 
 
219
static grub_err_t
 
220
read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len,
 
221
            grub_uint64_t chunk_start, grub_off_t offset)
 
222
{
 
223
  while (len > 0)
 
224
    {
 
225
      grub_uint64_t csize;
 
226
      grub_uint16_t d;
 
227
      grub_err_t err;
 
228
      while (1)
 
229
        {
 
230
          err = grub_disk_read (data->disk,
 
231
                                chunk_start >> GRUB_DISK_SECTOR_BITS,
 
232
                                chunk_start & (GRUB_DISK_SECTOR_SIZE - 1),
 
233
                                sizeof (d), &d);
 
234
          if (err)
 
235
            return err;
 
236
          if (offset < SQUASH_CHUNK_SIZE)
 
237
            break;
 
238
          offset -= SQUASH_CHUNK_SIZE;
 
239
          chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS);
 
240
        }
 
241
 
 
242
      csize = SQUASH_CHUNK_SIZE - offset;
 
243
      if (csize > len)
 
244
        csize = len;
 
245
  
 
246
      if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED)
 
247
        {
 
248
          grub_disk_addr_t a = chunk_start + 2 + offset;
 
249
          err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
 
250
                                a & (GRUB_DISK_SECTOR_SIZE - 1),
 
251
                                csize, buf);
 
252
          if (err)
 
253
            return err;
 
254
        }
 
255
      else
 
256
        {
 
257
          char *tmp;
 
258
          grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS; 
 
259
          grub_disk_addr_t a = chunk_start + 2;
 
260
          tmp = grub_malloc (bsize);
 
261
          if (!tmp)
 
262
            return grub_errno;
 
263
          /* FIXME: buffer uncompressed data.  */
 
264
          err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS),
 
265
                                a & (GRUB_DISK_SECTOR_SIZE - 1),
 
266
                                bsize, tmp);
 
267
          if (err)
 
268
            {
 
269
              grub_free (tmp);
 
270
              return err;
 
271
            }
 
272
 
 
273
          if (data->decompress (tmp, bsize, offset,
 
274
                                buf, csize, data) < 0)
 
275
            {
 
276
              grub_free (tmp);
 
277
              return grub_errno;
 
278
            }
 
279
          grub_free (tmp);
 
280
        }
 
281
      len -= csize;
 
282
      offset += csize;
 
283
      buf = (char *) buf + csize;
 
284
    }
 
285
  return GRUB_ERR_NONE;
 
286
}
 
287
 
 
288
static grub_ssize_t
 
289
zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
 
290
                 char *outbuf, grub_size_t outsize,
 
291
                 struct grub_squash_data *data __attribute__ ((unused)))
 
292
{
 
293
  return grub_zlib_decompress (inbuf, insize, off, outbuf, outsize);
 
294
}
 
295
 
 
296
static grub_ssize_t
 
297
lzo_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
 
298
                char *outbuf, grub_size_t len, struct grub_squash_data *data)
 
299
{
 
300
  lzo_uint usize = data->blksz;
 
301
  grub_uint8_t *udata;
 
302
 
 
303
  if (usize < 8192)
 
304
    usize = 8192;
 
305
 
 
306
  udata = grub_malloc (usize);
 
307
  if (!udata)
 
308
    return -1;
 
309
 
 
310
  if (lzo1x_decompress_safe ((grub_uint8_t *) inbuf,
 
311
                             insize, udata, &usize, NULL) != LZO_E_OK)
 
312
    {
 
313
      grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
 
314
      grub_free (udata);
 
315
      return -1;
 
316
    }
 
317
  grub_memcpy (outbuf, udata + off, len);
 
318
  grub_free (udata);
 
319
  return len;
 
320
}
 
321
 
 
322
static grub_ssize_t
 
323
xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
 
324
               char *outbuf, grub_size_t len, struct grub_squash_data *data)
 
325
{
 
326
  grub_size_t ret = 0;
 
327
  grub_off_t pos = 0;
 
328
  struct xz_buf buf;
 
329
 
 
330
  xz_dec_reset (data->xzdec);
 
331
  buf.in = (grub_uint8_t *) inbuf;
 
332
  buf.in_pos = 0;
 
333
  buf.in_size = insize;
 
334
  buf.out = (grub_uint8_t *) data->xzbuf;
 
335
  buf.out_pos = 0;
 
336
  buf.out_size = XZBUFSIZ;
 
337
 
 
338
  while (len)
 
339
    {
 
340
      enum xz_ret xzret;
 
341
      
 
342
      buf.out_pos = 0;
 
343
 
 
344
      xzret = xz_dec_run (data->xzdec, &buf);
 
345
 
 
346
      if (xzret != XZ_OK && xzret != XZ_STREAM_END)
 
347
        {
 
348
          grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "invalid xz chunk");
 
349
          return -1;
 
350
        }
 
351
      if (pos + buf.out_pos >= off)
 
352
        {
 
353
          grub_ssize_t outoff = pos - off;
 
354
          grub_size_t l;
 
355
          if (outoff >= 0)
 
356
            {
 
357
              l = buf.out_pos;
 
358
              if (l > len)
 
359
                l = len;
 
360
              grub_memcpy (outbuf + outoff, buf.out, l);
 
361
            }
 
362
          else
 
363
            {
 
364
              outoff = -outoff;
 
365
              l = buf.out_pos - outoff;
 
366
              if (l > len)
 
367
                l = len;
 
368
              grub_memcpy (outbuf, buf.out + outoff, l);
 
369
            }
 
370
          ret += l;
 
371
          len -= l;
 
372
        }
 
373
      pos += buf.out_pos;
 
374
      if (xzret == XZ_STREAM_END)
 
375
        break;
 
376
    }
 
377
  return ret;
 
378
}
 
379
 
 
380
static struct grub_squash_data *
 
381
squash_mount (grub_disk_t disk)
 
382
{
 
383
  struct grub_squash_super sb;
 
384
  grub_err_t err;
 
385
  struct grub_squash_data *data;
 
386
  grub_uint64_t frag;
 
387
 
 
388
  err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb);
 
389
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
390
    grub_error (GRUB_ERR_BAD_FS, "not a squash4");
 
391
  if (err)
 
392
    return NULL;
 
393
  if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC)
 
394
      || sb.block_size == 0
 
395
      || ((sb.block_size - 1) & sb.block_size))
 
396
    {
 
397
      grub_error (GRUB_ERR_BAD_FS, "not squash4");
 
398
      return NULL;
 
399
    }
 
400
 
 
401
  err = grub_disk_read (disk, 
 
402
                        grub_le_to_cpu64 (sb.unk1offset)
 
403
                        >> GRUB_DISK_SECTOR_BITS, 
 
404
                        grub_le_to_cpu64 (sb.unk1offset)
 
405
                        & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag);
 
406
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
407
    grub_error (GRUB_ERR_BAD_FS, "not a squash4");
 
408
  if (err)
 
409
    return NULL;
 
410
 
 
411
  data = grub_zalloc (sizeof (*data));
 
412
  if (!data)
 
413
    return NULL;
 
414
  data->sb = sb;
 
415
  data->disk = disk;
 
416
  data->fragments = grub_le_to_cpu64 (frag);
 
417
 
 
418
  switch (sb.compression)
 
419
    {
 
420
    case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB):
 
421
      data->decompress = zlib_decompress;
 
422
      break;
 
423
    case grub_cpu_to_le16_compile_time (COMPRESSION_LZO):
 
424
      data->decompress = lzo_decompress;
 
425
      break;
 
426
    case grub_cpu_to_le16_compile_time (COMPRESSION_XZ):
 
427
      data->decompress = xz_decompress;
 
428
      data->xzbuf = grub_malloc (XZBUFSIZ);
 
429
      if (!data->xzbuf)
 
430
        {
 
431
          grub_free (data);
 
432
          return NULL;
 
433
        }
 
434
      data->xzdec = xz_dec_init (1 << 16);
 
435
      if (!data->xzdec)
 
436
        {
 
437
          grub_free (data->xzbuf);
 
438
          grub_free (data);
 
439
          return NULL;
 
440
        }
 
441
      break;
 
442
    default:
 
443
      grub_free (data);
 
444
      grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d",
 
445
                  grub_le_to_cpu16 (sb.compression));
 
446
      return NULL;
 
447
    }
 
448
 
 
449
  data->blksz = grub_le_to_cpu32 (data->sb.block_size);
 
450
  for (data->log2_blksz = 0; 
 
451
       (1U << data->log2_blksz) < data->blksz;
 
452
       data->log2_blksz++);
 
453
 
 
454
  return data;
 
455
}
 
456
 
 
457
static char *
 
458
grub_squash_read_symlink (grub_fshelp_node_t node)
 
459
{
 
460
  char *ret;
 
461
  grub_err_t err;
 
462
  ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
 
463
 
 
464
  err = read_chunk (node->data, ret,
 
465
                    grub_le_to_cpu32 (node->ino.symlink.namelen),
 
466
                    grub_le_to_cpu64 (node->data->sb.inodeoffset)
 
467
                    + node->stack[node->stsize - 1].ino_chunk,
 
468
                    node->stack[node->stsize - 1].ino_offset
 
469
                    + (node->ino.symlink.name - (char *) &node->ino));
 
470
  if (err)
 
471
    {
 
472
      grub_free (ret);
 
473
      return NULL;
 
474
    }
 
475
  ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0;
 
476
  return ret;
 
477
}
 
478
 
 
479
static int
 
480
grub_squash_iterate_dir (grub_fshelp_node_t dir,
 
481
                         int NESTED_FUNC_ATTR
 
482
                         (*hook) (const char *filename,
 
483
                                  enum grub_fshelp_filetype filetype,
 
484
                                  grub_fshelp_node_t node))
 
485
{
 
486
  grub_uint32_t off;
 
487
  grub_uint32_t endoff;
 
488
  grub_uint64_t chunk;
 
489
  unsigned i;
 
490
 
 
491
  /* FIXME: why - 3 ? */
 
492
  switch (dir->ino.type)
 
493
    {
 
494
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR):
 
495
      off = grub_le_to_cpu16 (dir->ino.dir.offset);
 
496
      endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3;
 
497
      chunk = grub_le_to_cpu32 (dir->ino.dir.chunk);
 
498
      break;
 
499
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR):
 
500
      off = grub_le_to_cpu16 (dir->ino.long_dir.offset);
 
501
      endoff = grub_le_to_cpu16 (dir->ino.long_dir.size) + off - 3;
 
502
      chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk);
 
503
      break;
 
504
    default:
 
505
      grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x",
 
506
                  grub_le_to_cpu16 (dir->ino.type));
 
507
      return 0;
 
508
    }
 
509
 
 
510
  {
 
511
    grub_fshelp_node_t node;
 
512
    node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
 
513
    if (!node)
 
514
      return 0;
 
515
    grub_memcpy (node, dir,
 
516
                 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
 
517
    if (hook (".", GRUB_FSHELP_DIR, node))
 
518
      return 1;
 
519
 
 
520
    if (dir->stsize != 1)
 
521
      {
 
522
        grub_err_t err;
 
523
 
 
524
        node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
 
525
        if (!node)
 
526
          return 0;
 
527
 
 
528
        grub_memcpy (node, dir,
 
529
                     sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
 
530
 
 
531
        node->stsize--;
 
532
        err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
 
533
                          grub_le_to_cpu64 (dir->data->sb.inodeoffset)
 
534
                          + node->stack[node->stsize - 1].ino_chunk,
 
535
                          node->stack[node->stsize - 1].ino_offset);
 
536
        if (err)
 
537
          return 0;
 
538
 
 
539
        if (hook ("..", GRUB_FSHELP_DIR, node))
 
540
          return 1;
 
541
      }
 
542
  }
 
543
 
 
544
  while (off < endoff)
 
545
    {
 
546
      struct grub_squash_dirent_header dh;
 
547
      grub_err_t err;
 
548
 
 
549
      err = read_chunk (dir->data, &dh, sizeof (dh),
 
550
                        grub_le_to_cpu64 (dir->data->sb.diroffset)
 
551
                        + chunk, off);
 
552
      if (err)
 
553
        return 0;
 
554
      off += sizeof (dh);
 
555
      for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++)
 
556
        {
 
557
          char *buf;
 
558
          int r;
 
559
          struct grub_fshelp_node *node;
 
560
          enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
 
561
          struct grub_squash_dirent di;
 
562
          struct grub_squash_inode ino;
 
563
 
 
564
          err = read_chunk (dir->data, &di, sizeof (di),
 
565
                            grub_le_to_cpu64 (dir->data->sb.diroffset)
 
566
                            + chunk, off);
 
567
          if (err)
 
568
            return 0;
 
569
          off += sizeof (di);
 
570
 
 
571
          err = read_chunk (dir->data, &ino, sizeof (ino),
 
572
                            grub_le_to_cpu64 (dir->data->sb.inodeoffset)
 
573
                            + grub_le_to_cpu32 (dh.ino_chunk),
 
574
                            grub_cpu_to_le16 (di.ino_offset));
 
575
          if (err)
 
576
            return 0;
 
577
 
 
578
          buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2);
 
579
          if (!buf)
 
580
            return 0;
 
581
          err = read_chunk (dir->data, buf,
 
582
                            grub_le_to_cpu16 (di.namelen) + 1,
 
583
                            grub_le_to_cpu64 (dir->data->sb.diroffset)
 
584
                            + chunk, off);
 
585
          if (err)
 
586
            return 0;
 
587
 
 
588
          off += grub_le_to_cpu16 (di.namelen) + 1;
 
589
          buf[grub_le_to_cpu16 (di.namelen) + 1] = 0;
 
590
          if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR)
 
591
            filetype = GRUB_FSHELP_DIR;
 
592
          if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
 
593
            filetype = GRUB_FSHELP_SYMLINK;
 
594
 
 
595
          node = grub_malloc (sizeof (*node)
 
596
                              + (dir->stsize + 1) * sizeof (dir->stack[0]));
 
597
          if (! node)
 
598
            return 0;
 
599
 
 
600
          grub_memcpy (node, dir,
 
601
                       sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
 
602
 
 
603
          node->ino = ino;
 
604
          node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
 
605
          node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
 
606
          node->stsize++;
 
607
          r = hook (buf, filetype, node);
 
608
 
 
609
          grub_free (buf);
 
610
          if (r)
 
611
            return r;
 
612
        }
 
613
    }
 
614
  return 0;
 
615
}
 
616
 
 
617
static grub_err_t
 
618
make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root)
 
619
{
 
620
  grub_memset (root, 0, sizeof (*root));
 
621
  root->data = data;
 
622
  root->stsize = 1;
 
623
  root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk);
 
624
  root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset);
 
625
 return read_chunk (data, &root->ino, sizeof (root->ino),
 
626
                    grub_le_to_cpu64 (data->sb.inodeoffset) 
 
627
                    + root->stack[0].ino_chunk,
 
628
                    root->stack[0].ino_offset);
 
629
}
 
630
 
 
631
static void
 
632
squash_unmount (struct grub_squash_data *data)
 
633
{
 
634
  if (data->xzdec)
 
635
    xz_dec_end (data->xzdec);
 
636
  grub_free (data->xzbuf);
 
637
  grub_free (data->ino.cumulated_block_sizes);
 
638
  grub_free (data->ino.block_sizes);
 
639
  grub_free (data);
 
640
}
 
641
 
 
642
 
 
643
static grub_err_t
 
644
grub_squash_dir (grub_device_t device, const char *path,
 
645
               int (*hook) (const char *filename,
 
646
                            const struct grub_dirhook_info *info))
 
647
{
 
648
  auto int NESTED_FUNC_ATTR iterate (const char *filename,
 
649
                                     enum grub_fshelp_filetype filetype,
 
650
                                     grub_fshelp_node_t node);
 
651
 
 
652
  int NESTED_FUNC_ATTR iterate (const char *filename,
 
653
                                enum grub_fshelp_filetype filetype,
 
654
                                grub_fshelp_node_t node)
 
655
    {
 
656
      struct grub_dirhook_info info;
 
657
      grub_memset (&info, 0, sizeof (info));
 
658
      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
 
659
      info.mtimeset = 1;
 
660
      info.mtime = grub_le_to_cpu32 (node->ino.mtime);
 
661
      grub_free (node);
 
662
      return hook (filename, &info);
 
663
    }
 
664
 
 
665
  struct grub_squash_data *data = 0;
 
666
  struct grub_fshelp_node *fdiro = 0;
 
667
  struct grub_fshelp_node root;
 
668
  grub_err_t err;
 
669
 
 
670
  data = squash_mount (device->disk);
 
671
  if (! data)
 
672
    return grub_errno;
 
673
 
 
674
  err = make_root_node (data, &root);
 
675
  if (err)
 
676
    return err;
 
677
 
 
678
  grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
 
679
                         grub_squash_read_symlink, GRUB_FSHELP_DIR);
 
680
  if (!grub_errno)
 
681
    grub_squash_iterate_dir (fdiro, iterate);
 
682
 
 
683
  squash_unmount (data);
 
684
 
 
685
  return grub_errno;
 
686
}
 
687
 
 
688
static grub_err_t
 
689
grub_squash_open (struct grub_file *file, const char *name)
 
690
{
 
691
  struct grub_squash_data *data = 0;
 
692
  struct grub_fshelp_node *fdiro = 0;
 
693
  struct grub_fshelp_node root;
 
694
  grub_err_t err;
 
695
 
 
696
  data = squash_mount (file->device->disk);
 
697
  if (! data)
 
698
    return grub_errno;
 
699
 
 
700
  err = make_root_node (data, &root);
 
701
  if (err)
 
702
    return err;
 
703
 
 
704
  grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir,
 
705
                         grub_squash_read_symlink, GRUB_FSHELP_REG);
 
706
  if (grub_errno)
 
707
    {
 
708
      squash_unmount (data);
 
709
      return grub_errno;
 
710
    }
 
711
 
 
712
  file->data = data;
 
713
  data->ino.ino = fdiro->ino;
 
714
  data->ino.block_sizes = NULL;
 
715
  data->ino.cumulated_block_sizes = NULL;
 
716
  data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk;
 
717
  data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset;
 
718
 
 
719
  switch (fdiro->ino.type)
 
720
    {
 
721
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
 
722
      file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size);
 
723
      break;
 
724
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
 
725
      file->size = grub_le_to_cpu32 (fdiro->ino.file.size);
 
726
      break;
 
727
    default:
 
728
      {
 
729
        grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type);
 
730
        grub_free (fdiro);
 
731
        squash_unmount (data);
 
732
        return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type);
 
733
      }
 
734
    }
 
735
 
 
736
  grub_free (fdiro);
 
737
 
 
738
  return GRUB_ERR_NONE;
 
739
}
 
740
 
 
741
static grub_ssize_t
 
742
direct_read (struct grub_squash_data *data, 
 
743
             struct grub_squash_cache_inode *ino,
 
744
             grub_off_t off, char *buf, grub_size_t len)
 
745
{
 
746
  grub_err_t err;
 
747
  grub_off_t cumulated_uncompressed_size = 0;
 
748
  grub_uint64_t a = 0;
 
749
  grub_size_t i;
 
750
  grub_size_t origlen = len;
 
751
 
 
752
  switch (ino->ino.type)
 
753
    {
 
754
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
 
755
      a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
 
756
      break;
 
757
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
 
758
      a = grub_le_to_cpu32 (ino->ino.file.chunk);
 
759
      break;
 
760
    }
 
761
 
 
762
  if (!ino->block_sizes)
 
763
    {
 
764
      grub_off_t total_size = 0;
 
765
      grub_size_t total_blocks;
 
766
      grub_size_t block_offset = 0;
 
767
      switch (ino->ino.type)
 
768
        {
 
769
        case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
 
770
          total_size = grub_le_to_cpu64 (ino->ino.long_file.size);
 
771
          block_offset = ((char *) &ino->ino.long_file.block_size
 
772
                          - (char *) &ino->ino);
 
773
          break;
 
774
        case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
 
775
          total_size = grub_le_to_cpu32 (ino->ino.file.size);
 
776
          block_offset = ((char *) &ino->ino.file.block_size
 
777
                          - (char *) &ino->ino);
 
778
          break;
 
779
        }
 
780
      total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz);
 
781
      ino->block_sizes = grub_malloc (total_blocks
 
782
                                      * sizeof (ino->block_sizes[0]));
 
783
      ino->cumulated_block_sizes = grub_malloc (total_blocks
 
784
                                                * sizeof (ino->cumulated_block_sizes[0]));
 
785
      if (!ino->block_sizes || !ino->cumulated_block_sizes)
 
786
        {
 
787
          grub_free (ino->block_sizes);
 
788
          grub_free (ino->cumulated_block_sizes);
 
789
          ino->block_sizes = 0;
 
790
          ino->cumulated_block_sizes = 0;
 
791
          return -1;
 
792
        }
 
793
      err = read_chunk (data, ino->block_sizes,
 
794
                        total_blocks * sizeof (ino->block_sizes[0]),
 
795
                        grub_le_to_cpu64 (data->sb.inodeoffset)
 
796
                        + ino->ino_chunk,
 
797
                        ino->ino_offset + block_offset);
 
798
      if (err)
 
799
        {
 
800
          grub_free (ino->block_sizes);
 
801
          grub_free (ino->cumulated_block_sizes);
 
802
          ino->block_sizes = 0;
 
803
          ino->cumulated_block_sizes = 0;
 
804
          return -1;
 
805
        }
 
806
      ino->cumulated_block_sizes[0] = 0;
 
807
      for (i = 1; i < total_blocks; i++)
 
808
        ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1]
 
809
          + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS);
 
810
    }
 
811
 
 
812
  if (a == 0)
 
813
    a = sizeof (struct grub_squash_super);
 
814
  i = off >> data->log2_blksz;
 
815
  cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i;
 
816
  while (cumulated_uncompressed_size < off + len)
 
817
    {
 
818
      grub_size_t boff, curread;
 
819
      boff = off - cumulated_uncompressed_size;
 
820
      curread = data->blksz - boff;
 
821
      if (curread > len)
 
822
        curread = len;
 
823
      if (!(ino->block_sizes[i]
 
824
            & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
 
825
        {
 
826
          char *block;
 
827
          grub_size_t csize;
 
828
          csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS;
 
829
          block = grub_malloc (csize);
 
830
          if (!block)
 
831
            return -1;
 
832
          err = grub_disk_read (data->disk,
 
833
                                (ino->cumulated_block_sizes[i] + a)
 
834
                                >> GRUB_DISK_SECTOR_BITS,
 
835
                                (ino->cumulated_block_sizes[i] + a)
 
836
                                & (GRUB_DISK_SECTOR_SIZE - 1),
 
837
                                csize, block);
 
838
          if (err)
 
839
            {
 
840
              grub_free (block);
 
841
              return -1;
 
842
            }
 
843
          if (data->decompress (block, csize, boff, buf, curread, data)
 
844
              != (grub_ssize_t) curread)
 
845
            {
 
846
              grub_free (block);
 
847
              if (!grub_errno)
 
848
                grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
 
849
              return -1;
 
850
            }
 
851
          grub_free (block);
 
852
        }
 
853
      else
 
854
        err = grub_disk_read (data->disk, 
 
855
                              (ino->cumulated_block_sizes[i] + a + boff)
 
856
                              >> GRUB_DISK_SECTOR_BITS,
 
857
                              (ino->cumulated_block_sizes[i] + a + boff)
 
858
                              & (GRUB_DISK_SECTOR_SIZE - 1),
 
859
                              curread, buf);
 
860
      if (err)
 
861
        return -1;
 
862
      off += curread;
 
863
      len -= curread;
 
864
      buf += curread;
 
865
      cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size);
 
866
      i++;
 
867
    }
 
868
  return origlen;
 
869
}
 
870
 
 
871
 
 
872
static grub_ssize_t
 
873
grub_squash_read_data (struct grub_squash_data *data, 
 
874
                       struct grub_squash_cache_inode *ino,
 
875
                       grub_off_t off, char *buf, grub_size_t len)
 
876
{
 
877
  grub_err_t err;
 
878
  grub_uint64_t a = 0, b;
 
879
  grub_uint32_t fragment = 0;
 
880
  int compressed = 0;
 
881
  struct grub_squash_frag_desc frag;
 
882
 
 
883
  switch (ino->ino.type)
 
884
    {
 
885
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
 
886
      a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
 
887
      fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
 
888
      break;
 
889
    case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
 
890
      a = grub_le_to_cpu32 (ino->ino.file.chunk);
 
891
      fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
 
892
      break;
 
893
    }
 
894
 
 
895
  if (fragment == 0xffffffff)
 
896
    return direct_read (data, ino, off, buf, len);
 
897
 
 
898
  err = read_chunk (data, &frag, sizeof (frag),
 
899
                    data->fragments, sizeof (frag) * fragment);
 
900
  if (err)
 
901
    return -1;
 
902
  a += grub_le_to_cpu64 (frag.offset);
 
903
  compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED));
 
904
  if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR))
 
905
    b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
 
906
  else
 
907
    b = grub_le_to_cpu32 (ino->ino.file.offset) + off;
 
908
  
 
909
  /* FIXME: cache uncompressed chunks.  */
 
910
  if (compressed)
 
911
    {
 
912
      char *block;
 
913
      block = grub_malloc (grub_le_to_cpu32 (frag.size));
 
914
      if (!block)
 
915
        return -1;
 
916
      err = grub_disk_read (data->disk,
 
917
                            a >> GRUB_DISK_SECTOR_BITS,
 
918
                            a & (GRUB_DISK_SECTOR_SIZE - 1),
 
919
                            grub_le_to_cpu32 (frag.size), block);
 
920
      if (err)
 
921
        {
 
922
          grub_free (block);
 
923
          return -1;
 
924
        }
 
925
      if (data->decompress (block, grub_le_to_cpu32 (frag.size),
 
926
                            b, buf, len, data)
 
927
          != (grub_ssize_t) len)
 
928
        {
 
929
          grub_free (block);
 
930
          if (!grub_errno)
 
931
            grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk");
 
932
          return -1;
 
933
        }
 
934
      grub_free (block);
 
935
    }
 
936
  else
 
937
    {
 
938
      err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS,
 
939
                          (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
 
940
      if (err)
 
941
        return -1;
 
942
    }
 
943
  return len;
 
944
}
 
945
 
 
946
static grub_ssize_t
 
947
grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
 
948
{
 
949
  struct grub_squash_data *data = file->data;
 
950
 
 
951
  return grub_squash_read_data (data, &data->ino,
 
952
                                file->offset, buf, len);
 
953
}
 
954
 
 
955
static grub_err_t
 
956
grub_squash_close (grub_file_t file)
 
957
{
 
958
  squash_unmount (file->data);
 
959
  return GRUB_ERR_NONE;
 
960
}
 
961
 
 
962
static grub_err_t
 
963
grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
 
964
{
 
965
  struct grub_squash_data *data = 0;
 
966
 
 
967
  data = squash_mount (dev->disk);
 
968
  if (! data)
 
969
    return grub_errno;
 
970
  *tm = grub_le_to_cpu32 (data->sb.creation_time);
 
971
  squash_unmount (data);
 
972
  return GRUB_ERR_NONE;
 
973
 
974
 
 
975
static struct grub_fs grub_squash_fs =
 
976
  {
 
977
    .name = "squash4",
 
978
    .dir = grub_squash_dir,
 
979
    .open = grub_squash_open,
 
980
    .read = grub_squash_read,
 
981
    .close = grub_squash_close,
 
982
    .mtime = grub_squash_mtime,
 
983
#ifdef GRUB_UTIL
 
984
    .reserved_first_sector = 0,
 
985
    .blocklist_install = 0,
 
986
#endif
 
987
    .next = 0
 
988
  };
 
989
 
 
990
GRUB_MOD_INIT(squash4)
 
991
{
 
992
  grub_fs_register (&grub_squash_fs);
 
993
}
 
994
 
 
995
GRUB_MOD_FINI(squash4)
 
996
{
 
997
  grub_fs_unregister (&grub_squash_fs);
 
998
}
 
999