~ubuntu-branches/debian/sid/grub2/sid-200907171837

« back to all changes in this revision

Viewing changes to fs/sfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060105152040-1ab076d4n3y2o5yf
Tags: 1.92-1
* New upstream release.
  - Add support for GPT partition table format.
  - Add a new command "play" to play an audio file on PC.
  - Add support for Linux/ADFS partition table format.
  - Add support for BASH-like scripting.
  - Add support for Apple HFS+ filesystems.
* 01_fix_grub-install.patch: Added. Fix grub-install to use
  /bin/grub-mkimage instead of /sbin/grub-mkimage. Closes: #338824
* Do not use CDBS tarball mode anymore. Closes: #344272  

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sfs.c - Amiga Smart FileSystem.  */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2005  Free Software Foundation, Inc.
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include <grub/err.h>
 
22
#include <grub/file.h>
 
23
#include <grub/mm.h>
 
24
#include <grub/misc.h>
 
25
#include <grub/disk.h>
 
26
#include <grub/dl.h>
 
27
#include <grub/types.h>
 
28
#include <grub/fshelp.h>
 
29
 
 
30
/* The common header for a block.  */
 
31
struct grub_sfs_bheader
 
32
{
 
33
  grub_uint8_t magic[4];
 
34
  grub_uint32_t chksum;
 
35
  grub_uint32_t ipointtomyself;
 
36
} __attribute__ ((packed));
 
37
 
 
38
/* The sfs rootblock.  */
 
39
struct grub_sfs_rblock
 
40
{
 
41
  struct grub_sfs_bheader header;
 
42
  grub_uint32_t version;
 
43
  grub_uint8_t unused1[36];
 
44
  grub_uint32_t blocksize;
 
45
  grub_uint8_t unused2[40];
 
46
  grub_uint8_t unused3[8];
 
47
  grub_uint32_t rootobject;
 
48
  grub_uint32_t btree;
 
49
} __attribute__ ((packed));
 
50
 
 
51
/* A SFS object container.  */
 
52
struct grub_sfs_obj
 
53
{
 
54
  grub_uint8_t unused1[4];
 
55
  grub_uint32_t nodeid;
 
56
  grub_uint8_t unused2[4];
 
57
  union
 
58
  {
 
59
    struct
 
60
    {
 
61
      grub_uint32_t first_block;
 
62
      grub_uint32_t size;
 
63
    } file __attribute__ ((packed));
 
64
    struct
 
65
    {
 
66
      grub_uint32_t hashtable;
 
67
      grub_uint32_t dir_objc;
 
68
    } dir __attribute__ ((packed));
 
69
  } file_dir;
 
70
  grub_uint8_t unused3[4];
 
71
  grub_uint8_t type;
 
72
  grub_uint8_t filename[1];
 
73
  grub_uint8_t comment[1];
 
74
} __attribute__ ((packed));
 
75
 
 
76
#define GRUB_SFS_TYPE_DELETED   32
 
77
#define GRUB_SFS_TYPE_SYMLINK   64
 
78
#define GRUB_SFS_TYPE_DIR       128
 
79
 
 
80
/* A SFS object container.  */
 
81
struct grub_sfs_objc
 
82
{
 
83
  struct grub_sfs_bheader header;
 
84
  grub_uint32_t parent;
 
85
  grub_uint32_t next;
 
86
  grub_uint32_t prev;
 
87
  /* The amount of objects depends on the blocksize.  */
 
88
  struct grub_sfs_obj objects[1];
 
89
} __attribute__ ((packed));
 
90
 
 
91
struct grub_sfs_btree_node
 
92
{
 
93
  grub_uint32_t key;
 
94
  grub_uint32_t data;
 
95
} __attribute__ ((packed));
 
96
 
 
97
struct grub_sfs_btree_extent
 
98
{
 
99
  grub_uint32_t key;
 
100
  grub_uint32_t next;
 
101
  grub_uint32_t prev;
 
102
  grub_uint16_t size;
 
103
} __attribute__ ((packed));
 
104
 
 
105
struct grub_sfs_btree
 
106
{
 
107
  struct grub_sfs_bheader header;
 
108
  grub_uint16_t nodes;
 
109
  grub_uint8_t leaf;
 
110
  grub_uint8_t nodesize;
 
111
  /* Normally this can be kind of node, but just extents are
 
112
     supported.  */
 
113
  struct grub_sfs_btree_node node[1];
 
114
} __attribute__ ((packed));
 
115
 
 
116
 
 
117
 
 
118
struct grub_fshelp_node
 
119
{
 
120
  struct grub_sfs_data *data;
 
121
  int block;
 
122
  int size;
 
123
};
 
124
 
 
125
/* Information about a "mounted" sfs filesystem.  */
 
126
struct grub_sfs_data
 
127
{
 
128
  struct grub_sfs_rblock rblock;
 
129
  struct grub_fshelp_node diropen;
 
130
  grub_disk_t disk;
 
131
 
 
132
  /* Blocksize in sectors.  */
 
133
  unsigned int blocksize;
 
134
 
 
135
  /* Label of the filesystem.  */
 
136
  char *label;
 
137
};
 
138
 
 
139
#ifndef GRUB_UTIL
 
140
static grub_dl_t my_mod;
 
141
#endif
 
142
 
 
143
 
 
144
/* Lookup the extent starting with BLOCK in the filesystem described
 
145
   by DATA.  Return the extent size in SIZE and the following extent
 
146
   in NEXTEXT.  */
 
147
static grub_err_t
 
148
grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
 
149
                      int *size, int *nextext)
 
150
{
 
151
  char *treeblock;
 
152
  struct grub_sfs_btree *tree;
 
153
  int i;
 
154
  int next;
 
155
  int prev;
 
156
 
 
157
  treeblock = grub_malloc (data->blocksize);
 
158
  if (!block)
 
159
    return 0;
 
160
 
 
161
  next = grub_be_to_cpu32 (data->rblock.btree);
 
162
  tree = (struct grub_sfs_btree *) treeblock;
 
163
 
 
164
  /* Handle this level in the btree.  */
 
165
  do
 
166
    {
 
167
      prev = 0;
 
168
 
 
169
      grub_disk_read (data->disk, next, 0, data->blocksize, treeblock);
 
170
      if (grub_errno)
 
171
        {
 
172
          grub_free (treeblock);
 
173
          return grub_errno;
 
174
        }
 
175
 
 
176
      for (i = 0; i < tree->nodes; i++)
 
177
        {
 
178
 
 
179
#define EXTNODE(tree, index)                                            \
 
180
        ((struct grub_sfs_btree_node *) (((char *) &(tree)->node[0])    \
 
181
                                         + (index) * (tree)->nodesize))
 
182
 
 
183
          /* Follow the tree down to the leaf level.  */
 
184
          if ((grub_be_to_cpu32 (EXTNODE(tree, i)->key) >= block)
 
185
              && !tree->leaf)
 
186
            {
 
187
              next = grub_be_to_cpu32 (EXTNODE (tree, i - 1)->data);
 
188
              break;
 
189
            }
 
190
 
 
191
          /* In case the last node is reached just use that one, it is
 
192
             the right match.  */
 
193
          if (i + 1 == tree->nodes && !tree->leaf)
 
194
            {
 
195
              next = grub_be_to_cpu32 (EXTNODE (tree, i)->data);
 
196
              break;
 
197
            }
 
198
 
 
199
          /* If the leaf level is reached, just find the correct extent.  */
 
200
          if (grub_be_to_cpu32 (EXTNODE (tree, i)->key) == block && tree->leaf)
 
201
            {
 
202
              struct grub_sfs_btree_extent *extent;
 
203
              extent = (struct grub_sfs_btree_extent *) EXTNODE (tree, i);
 
204
 
 
205
              /* We found a correct leaf.  */
 
206
              *size = grub_be_to_cpu16 (extent->size);
 
207
              *nextext = grub_be_to_cpu32 (extent->next);
 
208
              
 
209
              grub_free (treeblock);
 
210
              return 0;
 
211
            }
 
212
 
 
213
#undef EXTNODE
 
214
 
 
215
        }
 
216
    } while (!tree->leaf);
 
217
 
 
218
  grub_free (treeblock);
 
219
 
 
220
  return grub_error (GRUB_ERR_FILE_READ_ERROR, "SFS extent not found");
 
221
}
 
222
 
 
223
static int
 
224
grub_sfs_read_block (grub_fshelp_node_t node, int fileblock)
 
225
{
 
226
  int blk = node->block;
 
227
  int size = 0;
 
228
  int next = 0;
 
229
 
 
230
  while (blk)
 
231
    {
 
232
      grub_err_t err;
 
233
 
 
234
      /* In case of the first block we don't have to lookup the
 
235
         extent, the minimum size is always 1.  */
 
236
      if (fileblock == 0)
 
237
        return blk;
 
238
 
 
239
      err = grub_sfs_read_extent (node->data, blk, &size, &next);
 
240
      if (err)
 
241
        return 0;
 
242
 
 
243
      if (fileblock < size)
 
244
        return fileblock + blk;
 
245
 
 
246
      fileblock -= size;
 
247
 
 
248
      blk = next;
 
249
    }
 
250
 
 
251
  grub_error (GRUB_ERR_FILE_READ_ERROR,
 
252
              "reading a SFS block outside the extent");
 
253
 
 
254
  return 0;
 
255
}
 
256
 
 
257
 
 
258
/* Read LEN bytes from the file described by DATA starting with byte
 
259
   POS.  Return the amount of read bytes in READ.  */
 
260
static grub_ssize_t
 
261
grub_sfs_read_file (grub_fshelp_node_t node,
 
262
                    void (*read_hook) (unsigned long sector,
 
263
                                       unsigned offset, unsigned length),
 
264
                    int pos, unsigned int len, char *buf)
 
265
{
 
266
  return grub_fshelp_read_file (node->data->disk, node, read_hook,
 
267
                                pos, len, buf, grub_sfs_read_block,
 
268
                                node->size, 0);
 
269
}
 
270
 
 
271
 
 
272
static struct grub_sfs_data *
 
273
grub_sfs_mount (grub_disk_t disk)
 
274
{
 
275
  struct grub_sfs_data *data;
 
276
  struct grub_sfs_objc *rootobjc;
 
277
  char *rootobjc_data = 0;
 
278
  unsigned int blk;
 
279
 
 
280
  data = grub_malloc (sizeof (*data));
 
281
  if (!data)
 
282
    return 0;
 
283
 
 
284
  /* Read the rootblock.  */
 
285
  grub_disk_read (disk, 0, 0, sizeof (struct grub_sfs_rblock),
 
286
                  (char *) &data->rblock);
 
287
  if (grub_errno)
 
288
    goto fail;
 
289
 
 
290
  /* Make sure this is a sfs filesystem.  */
 
291
  if (grub_strncmp (data->rblock.header.magic, "SFS", 4))
 
292
    {
 
293
      grub_error (GRUB_ERR_BAD_FS, "not a sfs filesystem");
 
294
      goto fail;
 
295
    }
 
296
 
 
297
  data->blocksize = grub_be_to_cpu32 (data->rblock.blocksize);
 
298
  rootobjc_data = grub_malloc (data->blocksize);
 
299
  if (!rootobjc_data)
 
300
    goto fail;
 
301
 
 
302
  /* Read the root object container.  */
 
303
  grub_disk_read (disk, grub_be_to_cpu32 (data->rblock.rootobject), 0,
 
304
                  data->blocksize, rootobjc_data);
 
305
  if (grub_errno)
 
306
    goto fail;
 
307
 
 
308
  rootobjc = (struct grub_sfs_objc *) rootobjc_data;
 
309
 
 
310
  blk = grub_be_to_cpu32 (rootobjc->objects[0].file_dir.dir.dir_objc);
 
311
  data->diropen.size = 0;
 
312
  data->diropen.block = blk;
 
313
  data->diropen.data = data;
 
314
  data->disk = disk;
 
315
  data->label = grub_strdup (rootobjc->objects[0].filename);
 
316
 
 
317
  return data;
 
318
 
 
319
 fail:
 
320
  grub_free (data);
 
321
  grub_free (rootobjc_data);
 
322
  return 0;
 
323
}
 
324
 
 
325
 
 
326
static char *
 
327
grub_sfs_read_symlink (grub_fshelp_node_t node)
 
328
{
 
329
  struct grub_sfs_data *data = node->data;
 
330
  char *symlink;
 
331
  char *block;
 
332
 
 
333
  block = grub_malloc (data->blocksize);
 
334
  if (!block)
 
335
    return 0;
 
336
 
 
337
  grub_disk_read (data->disk, node->block, 0, data->blocksize, block);
 
338
  if (grub_errno)
 
339
    {
 
340
      grub_free (block);
 
341
      return 0;
 
342
    }
 
343
 
 
344
  /* This is just a wild guess, but it always worked for me.  How the
 
345
     SLNK block looks like is not documented in the SFS docs.  */
 
346
  symlink = grub_strdup (&block[24]);
 
347
  grub_free (block);
 
348
  if (!symlink)
 
349
    return 0;
 
350
 
 
351
  return symlink;
 
352
}
 
353
 
 
354
static int
 
355
grub_sfs_iterate_dir (grub_fshelp_node_t dir,
 
356
                       int NESTED_FUNC_ATTR
 
357
                       (*hook) (const char *filename,
 
358
                                enum grub_fshelp_filetype filetype,
 
359
                                grub_fshelp_node_t node))
 
360
{
 
361
  struct grub_fshelp_node *node = 0;
 
362
  struct grub_sfs_data *data = dir->data;
 
363
  char *objc_data;
 
364
  struct grub_sfs_objc *objc;
 
365
  unsigned int next = dir->block;
 
366
  int pos;
 
367
 
 
368
  auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
 
369
                                                  int size, int type);
 
370
 
 
371
  int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block,
 
372
                                             int size, int type)
 
373
    {
 
374
      node = grub_malloc (sizeof (*node));
 
375
      if (!node)
 
376
        return 1;
 
377
 
 
378
      node->data = data;
 
379
      node->size = size;
 
380
      node->block = block;
 
381
      
 
382
      return hook (name, type, node);
 
383
    }
 
384
 
 
385
  objc_data = grub_malloc (data->blocksize);
 
386
  if (!objc_data)
 
387
    goto fail;
 
388
 
 
389
  /* The Object container can consist of multiple blocks, iterate over
 
390
     every block.  */
 
391
  while (next)
 
392
    {
 
393
      grub_disk_read (data->disk, next, 0, data->blocksize, objc_data);
 
394
      if (grub_errno)
 
395
        goto fail;
 
396
 
 
397
      objc = (struct grub_sfs_objc *) objc_data;
 
398
 
 
399
      pos = (char *) &objc->objects[0] - (char *) objc;
 
400
 
 
401
      /* Iterate over all entries in this block.  */
 
402
      while (pos + sizeof (struct grub_sfs_obj) < data->blocksize)
 
403
        {
 
404
          struct grub_sfs_obj *obj;
 
405
          obj = (struct grub_sfs_obj *) ((char *) objc + pos);
 
406
          char *filename = obj->filename;
 
407
          int len;
 
408
          enum grub_fshelp_filetype type;
 
409
          unsigned int block;
 
410
 
 
411
          /* The filename and comment dynamically increase the size of
 
412
             the object.  */
 
413
          len = grub_strlen (filename);
 
414
          len += grub_strlen (filename + len + 1);
 
415
 
 
416
          pos += sizeof (*obj) + len;
 
417
          /* Round up to a multiple of two bytes.  */
 
418
          pos = ((pos + 1) >> 1) << 1;
 
419
 
 
420
          if (grub_strlen (filename) == 0)
 
421
            continue;
 
422
 
 
423
          /* First check if the file was not deleted.  */
 
424
          if (obj->type & GRUB_SFS_TYPE_DELETED)
 
425
            continue;
 
426
          else if (obj->type & GRUB_SFS_TYPE_SYMLINK)
 
427
            type = GRUB_FSHELP_SYMLINK;
 
428
          else if (obj->type & GRUB_SFS_TYPE_DIR)
 
429
            type = GRUB_FSHELP_DIR;
 
430
          else
 
431
            type = GRUB_FSHELP_REG;
 
432
 
 
433
          if (type == GRUB_FSHELP_DIR)
 
434
            block = grub_be_to_cpu32 (obj->file_dir.dir.dir_objc);
 
435
          else
 
436
            block = grub_be_to_cpu32 (obj->file_dir.file.first_block);
 
437
 
 
438
          if (grub_sfs_create_node (filename, block,
 
439
                                    grub_be_to_cpu32 (obj->file_dir.file.size),
 
440
                                    type))
 
441
            {
 
442
              grub_free (objc_data);
 
443
              return 1;
 
444
            }
 
445
        }
 
446
 
 
447
      next = grub_be_to_cpu32 (objc->next);
 
448
    }
 
449
 
 
450
 fail:
 
451
  grub_free (objc_data);
 
452
  return 1;
 
453
}
 
454
 
 
455
 
 
456
/* Open a file named NAME and initialize FILE.  */
 
457
static grub_err_t
 
458
grub_sfs_open (struct grub_file *file, const char *name)
 
459
{
 
460
  struct grub_sfs_data *data;
 
461
  struct grub_fshelp_node *fdiro = 0;
 
462
  
 
463
#ifndef GRUB_UTIL
 
464
  grub_dl_ref (my_mod);
 
465
#endif
 
466
  
 
467
  data = grub_sfs_mount (file->device->disk);
 
468
  if (!data)
 
469
    goto fail;
 
470
  
 
471
  grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_sfs_iterate_dir,
 
472
                         grub_sfs_read_symlink, GRUB_FSHELP_REG);
 
473
  if (grub_errno)
 
474
    goto fail;
 
475
  
 
476
  file->size = fdiro->size;
 
477
  data->diropen = *fdiro;
 
478
  grub_free (fdiro);
 
479
 
 
480
  file->data = data;
 
481
  file->offset = 0;
 
482
 
 
483
  return 0;
 
484
 
 
485
 fail:
 
486
  if (data && fdiro != &data->diropen)
 
487
    grub_free (fdiro);
 
488
  if (data)
 
489
    grub_free (data->label);
 
490
  grub_free (data);
 
491
  
 
492
#ifndef GRUB_UTIL
 
493
  grub_dl_unref (my_mod);
 
494
#endif
 
495
 
 
496
  return grub_errno;
 
497
}
 
498
 
 
499
 
 
500
static grub_err_t
 
501
grub_sfs_close (grub_file_t file)
 
502
{
 
503
  grub_free (file->data);
 
504
 
 
505
#ifndef GRUB_UTIL
 
506
  grub_dl_unref (my_mod);
 
507
#endif
 
508
 
 
509
  return GRUB_ERR_NONE;
 
510
}
 
511
 
 
512
 
 
513
/* Read LEN bytes data from FILE into BUF.  */
 
514
static grub_ssize_t
 
515
grub_sfs_read (grub_file_t file, char *buf, grub_ssize_t len)
 
516
{
 
517
  struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
 
518
 
 
519
  int size = grub_sfs_read_file (&data->diropen, file->read_hook,
 
520
                                 file->offset, len, buf);
 
521
 
 
522
  return size;
 
523
}
 
524
 
 
525
 
 
526
static grub_err_t
 
527
grub_sfs_dir (grub_device_t device, const char *path, 
 
528
               int (*hook) (const char *filename, int dir))
 
529
{
 
530
  struct grub_sfs_data *data = 0;
 
531
  struct grub_fshelp_node *fdiro = 0;
 
532
  
 
533
  auto int NESTED_FUNC_ATTR iterate (const char *filename,
 
534
                                     enum grub_fshelp_filetype filetype,
 
535
                                     grub_fshelp_node_t node);
 
536
 
 
537
  int NESTED_FUNC_ATTR iterate (const char *filename,
 
538
                                enum grub_fshelp_filetype filetype,
 
539
                                grub_fshelp_node_t node)
 
540
    {
 
541
      grub_free (node);
 
542
      
 
543
      if (filetype == GRUB_FSHELP_DIR)
 
544
        return hook (filename, 1);
 
545
      else
 
546
        return hook (filename, 0);
 
547
      
 
548
      return 0;
 
549
    }
 
550
 
 
551
#ifndef GRUB_UTIL
 
552
  grub_dl_ref (my_mod);
 
553
#endif
 
554
  
 
555
  data = grub_sfs_mount (device->disk);
 
556
  if (!data)
 
557
    goto fail;
 
558
 
 
559
  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_sfs_iterate_dir,
 
560
                        grub_sfs_read_symlink, GRUB_FSHELP_DIR);
 
561
  if (grub_errno)
 
562
    goto fail;
 
563
 
 
564
  grub_sfs_iterate_dir (fdiro, iterate);
 
565
  
 
566
 fail:
 
567
  if (data && fdiro != &data->diropen)
 
568
    grub_free (fdiro);
 
569
  if (data)
 
570
    grub_free (data->label);
 
571
  grub_free (data);
 
572
 
 
573
#ifndef GRUB_UTIL
 
574
  grub_dl_unref (my_mod);
 
575
#endif
 
576
 
 
577
  return grub_errno;
 
578
}
 
579
 
 
580
 
 
581
static grub_err_t
 
582
grub_sfs_label (grub_device_t device, char **label)
 
583
{
 
584
  struct grub_sfs_data *data;
 
585
  grub_disk_t disk = device->disk;
 
586
 
 
587
  data = grub_sfs_mount (disk);
 
588
  if (data)
 
589
    *label = data->label;
 
590
 
 
591
  grub_free (data);
 
592
 
 
593
  return grub_errno;
 
594
}
 
595
 
 
596
 
 
597
static struct grub_fs grub_sfs_fs =
 
598
  {
 
599
    .name = "sfs",
 
600
    .dir = grub_sfs_dir,
 
601
    .open = grub_sfs_open,
 
602
    .read = grub_sfs_read,
 
603
    .close = grub_sfs_close,
 
604
    .label = grub_sfs_label,
 
605
    .next = 0
 
606
  };
 
607
 
 
608
GRUB_MOD_INIT(sfs)
 
609
{
 
610
  grub_fs_register (&grub_sfs_fs);
 
611
#ifndef GRUB_UTIL
 
612
  my_mod = mod;
 
613
#endif
 
614
}
 
615
 
 
616
GRUB_MOD_FINI(sfs)
 
617
{
 
618
  grub_fs_unregister (&grub_sfs_fs);
 
619
}