1
/* ufs.c - Unix File System */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2004,2005,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
21
#include <grub/file.h>
23
#include <grub/misc.h>
24
#include <grub/disk.h>
26
#include <grub/types.h>
29
#define GRUB_UFS_MAGIC 0x19540119
31
#define GRUB_UFS_MAGIC 0x11954
34
#define GRUB_UFS_INODE 2
35
#define GRUB_UFS_FILETYPE_DIR 4
36
#define GRUB_UFS_FILETYPE_LNK 10
37
#define GRUB_UFS_MAX_SYMLNK_CNT 8
39
#define GRUB_UFS_DIRBLKS 12
40
#define GRUB_UFS_INDIRBLKS 3
42
#define GRUB_UFS_ATTR_TYPE 0160000
43
#define GRUB_UFS_ATTR_FILE 0100000
44
#define GRUB_UFS_ATTR_DIR 0040000
45
#define GRUB_UFS_ATTR_LNK 0120000
47
#define GRUB_UFS_VOLNAME_LEN 32
49
/* Calculate in which group the inode can be found. */
50
#define UFS_BLKSZ(sblock) (grub_le_to_cpu32 (sblock->bsize))
52
#define INODE(data,field) data->inode. field
54
#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits2 (data->inode.field)
56
#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits1 (data->inode.field)
59
#define INODE_SIZE(data) INODE_ENDIAN (data,size,32,64)
60
#define INODE_NBLOCKS(data) INODE_ENDIAN (data,nblocks,32,64)
62
#define INODE_MODE(data) INODE_ENDIAN (data,mode,16,16)
69
#define UFS_INODE_PER_BLOCK 2
71
#define UFS_INODE_PER_BLOCK 4
73
#define INODE_DIRBLOCKS(data,blk) INODE_ENDIAN \
74
(data,blocks.dir_blocks[blk],32,64)
75
#define INODE_INDIRBLOCKS(data,blk) INODE_ENDIAN \
76
(data,blocks.indir_blocks[blk],32,64)
78
/* The blocks on which the superblock can be found. */
79
static int sblocklist[] = { 128, 16, 0, 512, -1 };
81
struct grub_ufs_sblock
83
grub_uint8_t unused[16];
84
/* The offset of the inodes in the cylinder group. */
85
grub_uint32_t inoblk_offs;
87
grub_uint8_t unused2[4];
89
/* The start of the cylinder group. */
90
grub_uint32_t cylg_offset;
91
grub_uint32_t cylg_mask;
94
grub_uint8_t unused4[12];
96
/* The size of a block in bytes. */
98
grub_uint8_t unused5[48];
100
/* The size of filesystem blocks to disk blocks. */
101
grub_uint32_t log2_blksz;
102
grub_uint8_t unused6[40];
103
grub_uint32_t uuidhi;
104
grub_uint32_t uuidlow;
105
grub_uint8_t unused7[32];
107
/* Inodes stored per cylinder group. */
108
grub_uint32_t ino_per_group;
110
/* The frags per cylinder group. */
111
grub_uint32_t frags_per_group;
113
grub_uint8_t unused8[488];
115
/* Volume name for UFS2. */
116
grub_uint8_t volume_name[GRUB_UFS_VOLNAME_LEN];
117
grub_uint8_t unused9[360];
119
grub_uint64_t mtime2;
120
grub_uint8_t unused10[292];
122
/* Magic value to check if this is really a UFS filesystem. */
128
struct grub_ufs_inode
131
grub_uint16_t nlinks;
134
grub_uint32_t blocksize;
136
grub_int64_t nblocks;
140
grub_uint64_t create_time;
141
grub_uint32_t atime_sec;
142
grub_uint32_t mtime_sec;
143
grub_uint32_t ctime_sec;
144
grub_uint32_t create_time_sec;
146
grub_uint32_t kernel_flags;
149
grub_uint64_t ext[2];
154
grub_uint64_t dir_blocks[GRUB_UFS_DIRBLKS];
155
grub_uint64_t indir_blocks[GRUB_UFS_INDIRBLKS];
157
grub_uint8_t symlink[(GRUB_UFS_DIRBLKS + GRUB_UFS_INDIRBLKS) * 8];
160
grub_uint8_t unused[24];
161
} __attribute__ ((packed));
164
struct grub_ufs_inode
167
grub_uint16_t nlinks;
178
grub_uint32_t dir_blocks[GRUB_UFS_DIRBLKS];
179
grub_uint32_t indir_blocks[GRUB_UFS_INDIRBLKS];
181
grub_uint8_t symlink[(GRUB_UFS_DIRBLKS + GRUB_UFS_INDIRBLKS) * 4];
184
grub_uint32_t nblocks;
186
grub_uint32_t unused;
187
grub_uint8_t pad[12];
188
} __attribute__ ((packed));
191
/* Directory entry. */
192
struct grub_ufs_dirent
195
grub_uint16_t direntlen;
198
grub_uint16_t namelen;
201
grub_uint8_t filetype_bsd;
202
grub_uint8_t namelen_bsd;
205
} __attribute__ ((packed));
207
/* Information about a "mounted" ufs filesystem. */
210
struct grub_ufs_sblock sblock;
212
struct grub_ufs_inode inode;
217
static grub_dl_t my_mod;
219
/* Forward declaration. */
220
static grub_err_t grub_ufs_find_file (struct grub_ufs_data *data,
224
static grub_disk_addr_t
225
grub_ufs_get_file_block (struct grub_ufs_data *data, unsigned int blk)
227
struct grub_ufs_sblock *sblock = &data->sblock;
228
unsigned int indirsz;
232
if (blk < GRUB_UFS_DIRBLKS)
233
return INODE_DIRBLOCKS (data, blk);
235
log2_blksz = grub_le_to_cpu32 (data->sblock.log2_blksz);
237
blk -= GRUB_UFS_DIRBLKS;
239
indirsz = UFS_BLKSZ (sblock) / INODE_BLKSZ;
240
/* Single indirect block. */
244
grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)];
246
grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)];
248
grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 0) << log2_blksz,
249
0, sizeof (indir), indir);
254
/* Double indirect block. */
255
if (blk < indirsz * indirsz)
258
grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)];
260
grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)];
263
grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 1) << log2_blksz,
264
0, sizeof (indir), indir);
265
grub_disk_read (data->disk,
266
(indir [blk / indirsz])
268
0, sizeof (indir), indir);
270
return indir[blk % indirsz];
274
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
275
"ufs does not support triple indirect blocks");
280
/* Read LEN bytes from the file described by DATA starting with byte
281
POS. Return the amount of read bytes in READ. */
283
grub_ufs_read_file (struct grub_ufs_data *data,
284
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
285
unsigned offset, unsigned length),
286
int pos, grub_size_t len, char *buf)
288
struct grub_ufs_sblock *sblock = &data->sblock;
292
/* Adjust len so it we can't read past the end of the file. */
293
if (len + pos > INODE_SIZE (data))
294
len = INODE_SIZE (data) - pos;
296
blockcnt = (len + pos + UFS_BLKSZ (sblock) - 1) / UFS_BLKSZ (sblock);
298
for (i = pos / UFS_BLKSZ (sblock); i < blockcnt; i++)
301
int blockoff = pos % UFS_BLKSZ (sblock);
302
int blockend = UFS_BLKSZ (sblock);
306
blknr = grub_ufs_get_file_block (data, i);
311
if (i == blockcnt - 1)
313
blockend = (len + pos) % UFS_BLKSZ (sblock);
316
blockend = UFS_BLKSZ (sblock);
320
if (i == (pos / (int) UFS_BLKSZ (sblock)))
322
skipfirst = blockoff;
323
blockend -= skipfirst;
326
/* XXX: If the block number is 0 this block is not stored on
327
disk but is zero filled instead. */
330
data->disk->read_hook = read_hook;
331
grub_disk_read (data->disk,
332
blknr << grub_le_to_cpu32 (data->sblock.log2_blksz),
333
skipfirst, blockend, buf);
334
data->disk->read_hook = 0;
339
grub_memset (buf, UFS_BLKSZ (sblock) - skipfirst, 0);
341
buf += UFS_BLKSZ (sblock) - skipfirst;
347
/* Read inode INO from the mounted filesystem described by DATA. This
348
inode is used by default now. */
350
grub_ufs_read_inode (struct grub_ufs_data *data, int ino, char *inode)
352
struct grub_ufs_sblock *sblock = &data->sblock;
354
/* Determine the group the inode is in. */
355
int group = ino / grub_le_to_cpu32 (sblock->ino_per_group);
357
/* Determine the inode within the group. */
358
int grpino = ino % grub_le_to_cpu32 (sblock->ino_per_group);
360
/* The first block of the group. */
361
int grpblk = group * (grub_le_to_cpu32 (sblock->frags_per_group));
364
grpblk += grub_le_to_cpu32 (sblock->cylg_offset)
365
* (group & (~grub_le_to_cpu32 (sblock->cylg_mask)));
370
inode = (char *) &data->inode;
374
grub_disk_read (data->disk,
375
((grub_le_to_cpu32 (sblock->inoblk_offs) + grpblk)
376
<< grub_le_to_cpu32 (data->sblock.log2_blksz))
377
+ grpino / UFS_INODE_PER_BLOCK,
378
(grpino % UFS_INODE_PER_BLOCK)
379
* sizeof (struct grub_ufs_inode),
380
sizeof (struct grub_ufs_inode),
387
/* Lookup the symlink the current inode points to. INO is the inode
388
number of the directory the symlink is relative to. */
390
grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino)
392
char symlink[INODE_SIZE (data)];
394
if (++data->linknest > GRUB_UFS_MAX_SYMLNK_CNT)
395
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
397
if (INODE_NBLOCKS (data) == 0)
398
grub_strcpy (symlink, (char *) INODE (data, symlink));
401
grub_disk_read (data->disk,
402
(INODE_DIRBLOCKS (data, 0)
403
<< grub_le_to_cpu32 (data->sblock.log2_blksz)),
404
0, INODE_SIZE (data), symlink);
405
symlink[INODE_SIZE (data)] = '\0';
408
/* The symlink is an absolute path, go back to the root inode. */
409
if (symlink[0] == '/')
410
ino = GRUB_UFS_INODE;
412
/* Now load in the old inode. */
413
if (grub_ufs_read_inode (data, ino, 0))
416
grub_ufs_find_file (data, symlink);
418
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
424
/* Find the file with the pathname PATH on the filesystem described by
427
grub_ufs_find_file (struct grub_ufs_data *data, const char *path)
429
char fpath[grub_strlen (path) + 1];
432
unsigned int pos = 0;
435
grub_strcpy (fpath, path);
437
/* Skip the first slash. */
445
/* Extract the actual part from the pathname. */
446
next = grub_strchr (name, '/');
455
struct grub_ufs_dirent dirent;
458
if (grub_strlen (name) == 0)
459
return GRUB_ERR_NONE;
461
if (grub_ufs_read_file (data, 0, pos, sizeof (dirent),
462
(char *) &dirent) < 0)
466
namelen = dirent.namelen_bsd;
468
namelen = grub_le_to_cpu16 (dirent.namelen);
471
char filename[namelen + 1];
473
if (grub_ufs_read_file (data, 0, pos + sizeof (dirent),
474
namelen, filename) < 0)
477
filename[namelen] = '\0';
479
if (!grub_strcmp (name, filename))
482
grub_ufs_read_inode (data, grub_le_to_cpu32 (dirent.ino), 0);
484
if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE)
485
== GRUB_UFS_ATTR_LNK)
487
grub_ufs_lookup_symlink (data, dirino);
498
next = grub_strchr (name, '/');
505
if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR)
506
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
512
pos += grub_le_to_cpu16 (dirent.direntlen);
513
} while (pos < INODE_SIZE (data));
515
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
520
/* Mount the filesystem on the disk DISK. */
521
static struct grub_ufs_data *
522
grub_ufs_mount (grub_disk_t disk)
524
struct grub_ufs_data *data;
525
int *sblklist = sblocklist;
527
data = grub_malloc (sizeof (struct grub_ufs_data));
531
/* Find a UFS sblock. */
532
while (*sblklist != -1)
534
grub_disk_read (disk, *sblklist, 0, sizeof (struct grub_ufs_sblock),
539
if (grub_le_to_cpu32 (data->sblock.magic) == GRUB_UFS_MAGIC)
550
if (grub_errno == GRUB_ERR_NONE || grub_errno == GRUB_ERR_OUT_OF_RANGE)
553
grub_error (GRUB_ERR_BAD_FS, "not an ufs2 filesystem");
555
grub_error (GRUB_ERR_BAD_FS, "not an ufs1 filesystem");
566
grub_ufs_dir (grub_device_t device, const char *path,
567
int (*hook) (const char *filename,
568
const struct grub_dirhook_info *info))
570
struct grub_ufs_data *data;
571
struct grub_ufs_sblock *sblock;
572
unsigned int pos = 0;
574
data = grub_ufs_mount (device->disk);
578
grub_ufs_read_inode (data, GRUB_UFS_INODE, 0);
582
sblock = &data->sblock;
584
if (!path || path[0] != '/')
586
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
590
grub_ufs_find_file (data, path);
594
if ((INODE_MODE (data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR)
596
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
600
while (pos < INODE_SIZE (data))
602
struct grub_ufs_dirent dirent;
605
if (grub_ufs_read_file (data, 0, pos, sizeof (dirent),
606
(char *) &dirent) < 0)
610
namelen = dirent.namelen_bsd;
612
namelen = grub_le_to_cpu16 (dirent.namelen);
616
char filename[namelen + 1];
617
struct grub_dirhook_info info;
618
struct grub_ufs_inode inode;
620
grub_memset (&info, 0, sizeof (info));
622
if (grub_ufs_read_file (data, 0, pos + sizeof (dirent),
623
namelen, filename) < 0)
626
filename[namelen] = '\0';
627
grub_ufs_read_inode (data, dirent.ino, (char *) &inode);
629
info.dir = ((grub_le_to_cpu16 (inode.mode) & GRUB_UFS_ATTR_TYPE)
630
== GRUB_UFS_ATTR_DIR);
631
info.mtime = grub_le_to_cpu64 (inode.mtime);
634
if (hook (filename, &info))
638
pos += grub_le_to_cpu16 (dirent.direntlen);
648
/* Open a file named NAME and initialize FILE. */
650
grub_ufs_open (struct grub_file *file, const char *name)
652
struct grub_ufs_data *data;
653
data = grub_ufs_mount (file->device->disk);
657
grub_ufs_read_inode (data, 2, 0);
664
if (!name || name[0] != '/')
666
grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
670
grub_ufs_find_file (data, name);
678
file->size = INODE_SIZE (data);
680
return GRUB_ERR_NONE;
685
grub_ufs_read (grub_file_t file, char *buf, grub_size_t len)
687
struct grub_ufs_data *data =
688
(struct grub_ufs_data *) file->data;
690
return grub_ufs_read_file (data, file->read_hook, file->offset, len, buf);
695
grub_ufs_close (grub_file_t file)
697
grub_free (file->data);
699
return GRUB_ERR_NONE;
705
grub_ufs_label (grub_device_t device, char **label)
707
struct grub_ufs_data *data = 0;
709
grub_dl_ref (my_mod);
713
data = grub_ufs_mount (device->disk);
715
*label = grub_strdup ((char *) data->sblock.volume_name);
717
grub_dl_unref (my_mod);
726
grub_ufs_uuid (grub_device_t device, char **uuid)
728
struct grub_ufs_data *data;
729
grub_disk_t disk = device->disk;
731
grub_dl_ref (my_mod);
733
data = grub_ufs_mount (disk);
734
if (data && (data->sblock.uuidhi != 0 || data->sblock.uuidlow != 0))
735
*uuid = grub_xasprintf ("%08x%08x",
736
(unsigned) grub_le_to_cpu32 (data->sblock.uuidhi),
737
(unsigned) grub_le_to_cpu32 (data->sblock.uuidlow));
741
grub_dl_unref (my_mod);
751
grub_ufs_mtime (grub_device_t device, grub_int32_t *tm)
753
struct grub_ufs_data *data = 0;
755
grub_dl_ref (my_mod);
757
data = grub_ufs_mount (device->disk);
762
*tm = grub_le_to_cpu64 (data->sblock.mtime2);
764
*tm = grub_le_to_cpu32 (data->sblock.mtime);
767
grub_dl_unref (my_mod);
776
static struct grub_fs grub_ufs_fs =
784
.open = grub_ufs_open,
785
.read = grub_ufs_read,
786
.close = grub_ufs_close,
788
.label = grub_ufs_label,
790
.uuid = grub_ufs_uuid,
791
.mtime = grub_ufs_mtime,
801
grub_fs_register (&grub_ufs_fs);
811
grub_fs_unregister (&grub_ufs_fs);