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>
27 #include <grub/i18n.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
32 #define GRUB_UFS_MAGIC 0x19540119
34 #define GRUB_UFS_MAGIC 0x11954
37 #define GRUB_UFS_INODE 2
38 #define GRUB_UFS_FILETYPE_DIR 4
39 #define GRUB_UFS_FILETYPE_LNK 10
40 #define GRUB_UFS_MAX_SYMLNK_CNT 8
42 #define GRUB_UFS_DIRBLKS 12
43 #define GRUB_UFS_INDIRBLKS 3
45 #define GRUB_UFS_ATTR_TYPE 0160000
46 #define GRUB_UFS_ATTR_FILE 0100000
47 #define GRUB_UFS_ATTR_DIR 0040000
48 #define GRUB_UFS_ATTR_LNK 0120000
50 #define GRUB_UFS_VOLNAME_LEN 32
53 #define grub_ufs_to_cpu16 grub_be_to_cpu16
54 #define grub_ufs_to_cpu32 grub_be_to_cpu32
55 #define grub_ufs_to_cpu64 grub_be_to_cpu64
56 #define grub_cpu_to_ufs32_compile_time grub_cpu_to_be32_compile_time
58 #define grub_ufs_to_cpu16 grub_le_to_cpu16
59 #define grub_ufs_to_cpu32 grub_le_to_cpu32
60 #define grub_ufs_to_cpu64 grub_le_to_cpu64
61 #define grub_cpu_to_ufs32_compile_time grub_cpu_to_le32_compile_time
65 typedef grub_uint64_t grub_ufs_blk_t
;
66 static inline grub_disk_addr_t
67 grub_ufs_to_cpu_blk (grub_ufs_blk_t blk
)
69 return grub_ufs_to_cpu64 (blk
);
72 typedef grub_uint32_t grub_ufs_blk_t
;
73 static inline grub_disk_addr_t
74 grub_ufs_to_cpu_blk (grub_ufs_blk_t blk
)
76 return grub_ufs_to_cpu32 (blk
);
80 /* Calculate in which group the inode can be found. */
81 #define UFS_BLKSZ(sblock) (grub_ufs_to_cpu32 (sblock->bsize))
82 #define UFS_LOG_BLKSZ(sblock) (data->log2_blksz)
85 #define INODE_ENDIAN(data,field,bits1,bits2) grub_ufs_to_cpu##bits2 (data->inode.field)
87 #define INODE_ENDIAN(data,field,bits1,bits2) grub_ufs_to_cpu##bits1 (data->inode.field)
90 #define INODE_SIZE(data) grub_ufs_to_cpu64 (data->inode.size)
91 #define INODE_MODE(data) grub_ufs_to_cpu16 (data->inode.mode)
93 #define LOG_INODE_BLKSZ 3
95 #define LOG_INODE_BLKSZ 2
98 #define UFS_INODE_PER_BLOCK 2
100 #define UFS_INODE_PER_BLOCK 4
102 #define INODE_DIRBLOCKS(data,blk) INODE_ENDIAN \
103 (data,blocks.dir_blocks[blk],32,64)
104 #define INODE_INDIRBLOCKS(data,blk) INODE_ENDIAN \
105 (data,blocks.indir_blocks[blk],32,64)
107 /* The blocks on which the superblock can be found. */
108 static int sblocklist
[] = { 128, 16, 0, 512, -1 };
110 struct grub_ufs_sblock
112 grub_uint8_t unused
[16];
113 /* The offset of the inodes in the cylinder group. */
114 grub_uint32_t inoblk_offs
;
116 grub_uint8_t unused2
[4];
118 /* The start of the cylinder group. */
119 grub_uint32_t cylg_offset
;
120 grub_uint32_t cylg_mask
;
123 grub_uint8_t unused4
[12];
125 /* The size of a block in bytes. */
127 grub_uint8_t unused5
[48];
129 /* The size of filesystem blocks to disk blocks. */
130 grub_uint32_t log2_blksz
;
131 grub_uint8_t unused6
[40];
132 grub_uint32_t uuidhi
;
133 grub_uint32_t uuidlow
;
134 grub_uint8_t unused7
[32];
136 /* Inodes stored per cylinder group. */
137 grub_uint32_t ino_per_group
;
139 /* The frags per cylinder group. */
140 grub_uint32_t frags_per_group
;
142 grub_uint8_t unused8
[488];
144 /* Volume name for UFS2. */
145 grub_uint8_t volume_name
[GRUB_UFS_VOLNAME_LEN
];
146 grub_uint8_t unused9
[360];
148 grub_uint64_t mtime2
;
149 grub_uint8_t unused10
[292];
151 /* Magic value to check if this is really a UFS filesystem. */
157 struct grub_ufs_inode
160 grub_uint16_t nlinks
;
163 grub_uint32_t blocksize
;
165 grub_int64_t nblocks
;
169 grub_uint64_t create_time
;
170 grub_uint32_t atime_usec
;
171 grub_uint32_t mtime_usec
;
172 grub_uint32_t ctime_usec
;
173 grub_uint32_t create_time_sec
;
175 grub_uint32_t kernel_flags
;
178 grub_uint64_t ext
[2];
183 grub_uint64_t dir_blocks
[GRUB_UFS_DIRBLKS
];
184 grub_uint64_t indir_blocks
[GRUB_UFS_INDIRBLKS
];
186 grub_uint8_t symlink
[(GRUB_UFS_DIRBLKS
+ GRUB_UFS_INDIRBLKS
) * 8];
189 grub_uint8_t unused
[24];
193 struct grub_ufs_inode
196 grub_uint16_t nlinks
;
201 grub_uint32_t atime_usec
;
203 grub_uint32_t mtime_usec
;
205 grub_uint32_t ctime_usec
;
210 grub_uint32_t dir_blocks
[GRUB_UFS_DIRBLKS
];
211 grub_uint32_t indir_blocks
[GRUB_UFS_INDIRBLKS
];
213 grub_uint8_t symlink
[(GRUB_UFS_DIRBLKS
+ GRUB_UFS_INDIRBLKS
) * 4];
216 grub_uint32_t nblocks
;
218 grub_uint32_t unused
;
219 grub_uint8_t pad
[12];
223 /* Directory entry. */
224 struct grub_ufs_dirent
227 grub_uint16_t direntlen
;
230 grub_uint16_t namelen
;
233 grub_uint8_t filetype_bsd
;
234 grub_uint8_t namelen_bsd
;
239 /* Information about a "mounted" ufs filesystem. */
242 struct grub_ufs_sblock sblock
;
244 struct grub_ufs_inode inode
;
250 static grub_dl_t my_mod
;
252 /* Forward declaration. */
253 static grub_err_t
grub_ufs_find_file (struct grub_ufs_data
*data
,
257 static grub_disk_addr_t
258 grub_ufs_get_file_block (struct grub_ufs_data
*data
, grub_disk_addr_t blk
)
260 unsigned long indirsz
;
261 int log2_blksz
, log_indirsz
;
264 if (blk
< GRUB_UFS_DIRBLKS
)
265 return INODE_DIRBLOCKS (data
, blk
);
267 log2_blksz
= grub_ufs_to_cpu32 (data
->sblock
.log2_blksz
);
269 blk
-= GRUB_UFS_DIRBLKS
;
271 log_indirsz
= data
->log2_blksz
- LOG_INODE_BLKSZ
;
272 indirsz
= 1 << log_indirsz
;
274 /* Single indirect block. */
277 grub_ufs_blk_t indir
;
278 grub_disk_read (data
->disk
,
279 ((grub_disk_addr_t
) INODE_INDIRBLOCKS (data
, 0))
281 blk
* sizeof (indir
), sizeof (indir
), &indir
);
286 /* Double indirect block. */
287 if (blk
< (grub_disk_addr_t
) indirsz
* (grub_disk_addr_t
) indirsz
)
289 grub_ufs_blk_t indir
;
291 grub_disk_read (data
->disk
,
292 ((grub_disk_addr_t
) INODE_INDIRBLOCKS (data
, 1))
294 (blk
>> log_indirsz
) * sizeof (indir
),
295 sizeof (indir
), &indir
);
296 grub_disk_read (data
->disk
,
297 grub_ufs_to_cpu_blk (indir
) << log2_blksz
,
298 (blk
& ((1 << log_indirsz
) - 1)) * sizeof (indir
),
299 sizeof (indir
), &indir
);
304 blk
-= (grub_disk_addr_t
) indirsz
* (grub_disk_addr_t
) indirsz
;
306 /* Triple indirect block. */
307 if (!(blk
>> (3 * log_indirsz
)))
309 grub_ufs_blk_t indir
;
311 grub_disk_read (data
->disk
,
312 ((grub_disk_addr_t
) INODE_INDIRBLOCKS (data
, 2))
314 (blk
>> (2 * log_indirsz
)) * sizeof (indir
),
315 sizeof (indir
), &indir
);
316 grub_disk_read (data
->disk
,
317 grub_ufs_to_cpu_blk (indir
) << log2_blksz
,
318 ((blk
>> log_indirsz
)
319 & ((1 << log_indirsz
) - 1)) * sizeof (indir
),
320 sizeof (indir
), &indir
);
322 grub_disk_read (data
->disk
,
323 grub_ufs_to_cpu_blk (indir
) << log2_blksz
,
324 (blk
& ((1 << log_indirsz
) - 1)) * sizeof (indir
),
325 sizeof (indir
), &indir
);
330 grub_error (GRUB_ERR_BAD_FS
,
331 "ufs does not support quadruple indirect blocks");
336 /* Read LEN bytes from the file described by DATA starting with byte
337 POS. Return the amount of read bytes in READ. */
339 grub_ufs_read_file (struct grub_ufs_data
*data
,
340 grub_disk_read_hook_t read_hook
, void *read_hook_data
,
341 grub_off_t pos
, grub_size_t len
, char *buf
)
343 struct grub_ufs_sblock
*sblock
= &data
->sblock
;
347 /* Adjust len so it we can't read past the end of the file. */
348 if (len
+ pos
> INODE_SIZE (data
))
349 len
= INODE_SIZE (data
) - pos
;
351 blockcnt
= (len
+ pos
+ UFS_BLKSZ (sblock
) - 1) >> UFS_LOG_BLKSZ (sblock
);
353 for (i
= pos
>> UFS_LOG_BLKSZ (sblock
); i
< blockcnt
; i
++)
355 grub_disk_addr_t blknr
;
357 grub_off_t blockend
= UFS_BLKSZ (sblock
);
361 blockoff
= pos
& (UFS_BLKSZ (sblock
) - 1);
363 blknr
= grub_ufs_get_file_block (data
, i
);
368 if (i
== blockcnt
- 1)
370 blockend
= (len
+ pos
) & (UFS_BLKSZ (sblock
) - 1);
373 blockend
= UFS_BLKSZ (sblock
);
377 if (i
== (pos
>> UFS_LOG_BLKSZ (sblock
)))
379 skipfirst
= blockoff
;
380 blockend
-= skipfirst
;
383 /* XXX: If the block number is 0 this block is not stored on
384 disk but is zero filled instead. */
387 data
->disk
->read_hook
= read_hook
;
388 data
->disk
->read_hook_data
= read_hook_data
;
389 grub_disk_read (data
->disk
,
390 blknr
<< grub_ufs_to_cpu32 (data
->sblock
.log2_blksz
),
391 skipfirst
, blockend
, buf
);
392 data
->disk
->read_hook
= 0;
397 grub_memset (buf
, UFS_BLKSZ (sblock
) - skipfirst
, 0);
399 buf
+= UFS_BLKSZ (sblock
) - skipfirst
;
405 /* Read inode INO from the mounted filesystem described by DATA. This
406 inode is used by default now. */
408 grub_ufs_read_inode (struct grub_ufs_data
*data
, int ino
, char *inode
)
410 struct grub_ufs_sblock
*sblock
= &data
->sblock
;
412 /* Determine the group the inode is in. */
413 int group
= ino
/ grub_ufs_to_cpu32 (sblock
->ino_per_group
);
415 /* Determine the inode within the group. */
416 int grpino
= ino
% grub_ufs_to_cpu32 (sblock
->ino_per_group
);
418 /* The first block of the group. */
419 int grpblk
= group
* (grub_ufs_to_cpu32 (sblock
->frags_per_group
));
422 grpblk
+= grub_ufs_to_cpu32 (sblock
->cylg_offset
)
423 * (group
& (~grub_ufs_to_cpu32 (sblock
->cylg_mask
)));
428 inode
= (char *) &data
->inode
;
432 grub_disk_read (data
->disk
,
433 ((grub_ufs_to_cpu32 (sblock
->inoblk_offs
) + grpblk
)
434 << grub_ufs_to_cpu32 (data
->sblock
.log2_blksz
))
435 + grpino
/ UFS_INODE_PER_BLOCK
,
436 (grpino
% UFS_INODE_PER_BLOCK
)
437 * sizeof (struct grub_ufs_inode
),
438 sizeof (struct grub_ufs_inode
),
445 /* Lookup the symlink the current inode points to. INO is the inode
446 number of the directory the symlink is relative to. */
448 grub_ufs_lookup_symlink (struct grub_ufs_data
*data
, int ino
)
451 grub_size_t sz
= INODE_SIZE (data
);
453 if (++data
->linknest
> GRUB_UFS_MAX_SYMLNK_CNT
)
454 return grub_error (GRUB_ERR_SYMLINK_LOOP
, N_("too deep nesting of symlinks"));
456 symlink
= grub_malloc (sz
+ 1);
459 /* Normally we should just check that data->inode.nblocks == 0.
460 However old Linux doesn't maintain nblocks correctly and so it's always
461 0. If size is bigger than inline space then the symlink is surely not
463 /* Check against zero is paylindromic, no need to swap. */
464 if (data
->inode
.nblocks
== 0
465 && INODE_SIZE (data
) <= sizeof (data
->inode
.symlink
))
466 grub_strcpy (symlink
, (char *) data
->inode
.symlink
);
469 if (grub_ufs_read_file (data
, 0, 0, 0, sz
, symlink
) < 0)
477 /* The symlink is an absolute path, go back to the root inode. */
478 if (symlink
[0] == '/')
479 ino
= GRUB_UFS_INODE
;
481 /* Now load in the old inode. */
482 if (grub_ufs_read_inode (data
, ino
, 0))
488 grub_ufs_find_file (data
, symlink
);
496 /* Find the file with the pathname PATH on the filesystem described by
499 grub_ufs_find_file (struct grub_ufs_data
*data
, const char *path
)
502 const char *next
= path
;
503 unsigned int pos
= 0;
507 /* We reject filenames longer than the one we're looking
508 for without reading, so this allocation is enough. */
509 filename
= grub_malloc (grub_strlen (path
) + 2);
515 struct grub_ufs_dirent dirent
;
518 /* Skip the first slash. */
523 grub_free (filename
);
524 return GRUB_ERR_NONE
;
527 if ((INODE_MODE(data
) & GRUB_UFS_ATTR_TYPE
)
528 != GRUB_UFS_ATTR_DIR
)
530 grub_error (GRUB_ERR_BAD_FILE_TYPE
,
531 N_("not a directory"));
535 /* Extract the actual part from the pathname. */
536 for (next
= name
; *next
&& *next
!= '/'; next
++);
537 for (pos
= 0; ; pos
+= grub_ufs_to_cpu16 (dirent
.direntlen
))
541 if (pos
>= INODE_SIZE (data
))
543 grub_error (GRUB_ERR_FILE_NOT_FOUND
,
544 N_("file `%s' not found"),
549 if (grub_ufs_read_file (data
, 0, 0, pos
, sizeof (dirent
),
550 (char *) &dirent
) < 0)
554 namelen
= dirent
.namelen_bsd
;
556 namelen
= grub_ufs_to_cpu16 (dirent
.namelen
);
558 if (namelen
< next
- name
)
561 if (grub_ufs_read_file (data
, 0, 0, pos
+ sizeof (dirent
),
562 next
- name
+ (namelen
!= next
- name
),
566 if (grub_strncmp (name
, filename
, next
- name
) == 0
567 && (namelen
== next
- name
|| filename
[next
- name
] == '\0'))
570 grub_ufs_read_inode (data
, grub_ufs_to_cpu32 (dirent
.ino
), 0);
572 if ((INODE_MODE(data
) & GRUB_UFS_ATTR_TYPE
)
573 == GRUB_UFS_ATTR_LNK
)
575 grub_ufs_lookup_symlink (data
, dirino
);
585 grub_free (filename
);
590 /* Mount the filesystem on the disk DISK. */
591 static struct grub_ufs_data
*
592 grub_ufs_mount (grub_disk_t disk
)
594 struct grub_ufs_data
*data
;
595 int *sblklist
= sblocklist
;
597 data
= grub_malloc (sizeof (struct grub_ufs_data
));
601 /* Find a UFS sblock. */
602 while (*sblklist
!= -1)
604 grub_disk_read (disk
, *sblklist
, 0, sizeof (struct grub_ufs_sblock
),
609 /* No need to byteswap bsize in this check. It works the same on both
611 if (data
->sblock
.magic
== grub_cpu_to_ufs32_compile_time (GRUB_UFS_MAGIC
)
612 && data
->sblock
.bsize
!= 0
613 && ((data
->sblock
.bsize
& (data
->sblock
.bsize
- 1)) == 0)
614 && data
->sblock
.ino_per_group
!= 0)
616 for (data
->log2_blksz
= 0;
617 (1U << data
->log2_blksz
) < grub_ufs_to_cpu32 (data
->sblock
.bsize
);
629 if (grub_errno
== GRUB_ERR_NONE
|| grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
632 grub_error (GRUB_ERR_BAD_FS
, "not an ufs2 filesystem");
634 grub_error (GRUB_ERR_BAD_FS
, "not an ufs1 filesystem");
645 grub_ufs_dir (grub_device_t device
, const char *path
,
646 grub_fs_dir_hook_t hook
, void *hook_data
)
648 struct grub_ufs_data
*data
;
649 unsigned int pos
= 0;
651 data
= grub_ufs_mount (device
->disk
);
655 grub_ufs_read_inode (data
, GRUB_UFS_INODE
, 0);
659 if (!path
|| path
[0] != '/')
661 grub_error (GRUB_ERR_BAD_FILENAME
, N_("invalid file name `%s'"), path
);
665 grub_ufs_find_file (data
, path
);
669 if ((INODE_MODE (data
) & GRUB_UFS_ATTR_TYPE
) != GRUB_UFS_ATTR_DIR
)
671 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a directory"));
675 while (pos
< INODE_SIZE (data
))
677 struct grub_ufs_dirent dirent
;
680 if (grub_ufs_read_file (data
, 0, 0, pos
, sizeof (dirent
),
681 (char *) &dirent
) < 0)
684 if (dirent
.direntlen
== 0)
688 namelen
= dirent
.namelen_bsd
;
690 namelen
= grub_ufs_to_cpu16 (dirent
.namelen
);
693 char *filename
= grub_malloc (namelen
+ 1);
696 struct grub_dirhook_info info
;
697 struct grub_ufs_inode inode
;
699 grub_memset (&info
, 0, sizeof (info
));
701 if (grub_ufs_read_file (data
, 0, 0, pos
+ sizeof (dirent
),
702 namelen
, filename
) < 0)
704 grub_free (filename
);
708 filename
[namelen
] = '\0';
709 grub_ufs_read_inode (data
, grub_ufs_to_cpu32 (dirent
.ino
),
712 info
.dir
= ((grub_ufs_to_cpu16 (inode
.mode
) & GRUB_UFS_ATTR_TYPE
)
713 == GRUB_UFS_ATTR_DIR
);
715 info
.mtime
= grub_ufs_to_cpu64 (inode
.mtime
);
717 info
.mtime
= grub_ufs_to_cpu32 (inode
.mtime
);
721 if (hook (filename
, &info
, hook_data
))
723 grub_free (filename
);
727 grub_free (filename
);
729 pos
+= grub_ufs_to_cpu16 (dirent
.direntlen
);
739 /* Open a file named NAME and initialize FILE. */
741 grub_ufs_open (struct grub_file
*file
, const char *name
)
743 struct grub_ufs_data
*data
;
744 data
= grub_ufs_mount (file
->device
->disk
);
748 grub_ufs_read_inode (data
, 2, 0);
755 if (!name
|| name
[0] != '/')
757 grub_error (GRUB_ERR_BAD_FILENAME
, N_("invalid file name `%s'"), name
);
761 grub_ufs_find_file (data
, name
);
769 file
->size
= INODE_SIZE (data
);
771 return GRUB_ERR_NONE
;
776 grub_ufs_read (grub_file_t file
, char *buf
, grub_size_t len
)
778 struct grub_ufs_data
*data
=
779 (struct grub_ufs_data
*) file
->data
;
781 return grub_ufs_read_file (data
, file
->read_hook
, file
->read_hook_data
,
782 file
->offset
, len
, buf
);
787 grub_ufs_close (grub_file_t file
)
789 grub_free (file
->data
);
791 return GRUB_ERR_NONE
;
795 grub_ufs_label (grub_device_t device
, char **label
)
797 struct grub_ufs_data
*data
= 0;
799 grub_dl_ref (my_mod
);
803 data
= grub_ufs_mount (device
->disk
);
805 *label
= grub_strdup ((char *) data
->sblock
.volume_name
);
807 grub_dl_unref (my_mod
);
815 grub_ufs_uuid (grub_device_t device
, char **uuid
)
817 struct grub_ufs_data
*data
;
818 grub_disk_t disk
= device
->disk
;
820 grub_dl_ref (my_mod
);
822 data
= grub_ufs_mount (disk
);
823 if (data
&& (data
->sblock
.uuidhi
!= 0 || data
->sblock
.uuidlow
!= 0))
824 *uuid
= grub_xasprintf ("%08x%08x",
825 (unsigned) grub_ufs_to_cpu32 (data
->sblock
.uuidhi
),
826 (unsigned) grub_ufs_to_cpu32 (data
->sblock
.uuidlow
));
830 grub_dl_unref (my_mod
);
840 grub_ufs_mtime (grub_device_t device
, grub_int32_t
*tm
)
842 struct grub_ufs_data
*data
= 0;
844 grub_dl_ref (my_mod
);
846 data
= grub_ufs_mount (device
->disk
);
851 *tm
= grub_ufs_to_cpu32 (data
->sblock
.mtime
);
853 if (*tm
< (grub_int64_t
) grub_ufs_to_cpu64 (data
->sblock
.mtime2
))
854 *tm
= grub_ufs_to_cpu64 (data
->sblock
.mtime2
);
858 grub_dl_unref (my_mod
);
867 static struct grub_fs grub_ufs_fs
=
872 #ifdef MODE_BIGENDIAN
879 .open
= grub_ufs_open
,
880 .read
= grub_ufs_read
,
881 .close
= grub_ufs_close
,
882 .label
= grub_ufs_label
,
883 .uuid
= grub_ufs_uuid
,
884 .mtime
= grub_ufs_mtime
,
885 /* FIXME: set reserved_first_sector. */
887 .blocklist_install
= 1,
895 #ifdef MODE_BIGENDIAN
896 GRUB_MOD_INIT(ufs1_be
)
902 grub_fs_register (&grub_ufs_fs
);
909 #ifdef MODE_BIGENDIAN
910 GRUB_MOD_FINI(ufs1_be
)
916 grub_fs_unregister (&grub_ufs_fs
);