core/fs: Add support to Unix File system 1/2.
[syslinux/sherbszt.git] / core / fs / ufs / ufs.c
bloba7ef28f32a5a2986c40849ee215d9c6a3297a4ba
1 /*
2 * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include <dprintf.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/dirent.h>
24 #include <cache.h>
25 #include <disk.h>
26 #include <fs.h>
27 #include <minmax.h>
28 #include "core.h"
29 #include "ufs.h"
32 * Read the super block and check magic fields based on
33 * passed paramaters.
35 static bool
36 do_checksb(struct ufs_super_block *sb, struct disk *disk,
37 const uint32_t sblock_off, const uint32_t ufs_smagic)
39 uint32_t lba;
40 static uint32_t count;
42 /* How many sectors are needed to fill sb struct */
43 if (!count)
44 count = sizeof *sb >> disk->sector_shift;
45 /* Get lba address based on sector size of disk */
46 lba = sblock_off >> (disk->sector_shift);
47 /* Read super block */
48 disk->rdwr_sectors(disk, sb, lba, count, 0);
50 if (sb->magic == ufs_smagic)
51 return true;
53 return false;
57 * Go through all possible ufs superblock offsets.
58 * TODO: Add UFS support to removable media (sb offset: 0).
60 static int
61 ufs_checksb(struct ufs_super_block *sb, struct disk *disk)
63 /* Check for UFS1 sb */
64 if (do_checksb(sb, disk, UFS1_SBLOCK_OFFSET, UFS1_SUPER_MAGIC))
65 return UFS1;
66 /* Check for UFS2 sb */
67 if (do_checksb(sb, disk, UFS2_SBLOCK_OFFSET, UFS2_SUPER_MAGIC))
68 return UFS2;
69 /* UFS2 may also exist in 256k-, but this isn't the default */
70 if (do_checksb(sb, disk, UFS2_SBLOCK2_OFFSET, UFS2_SUPER_MAGIC))
71 return UFS2_PIGGY;
73 return NONE;
77 * lblock stands for linear block address,
78 * whereas pblock is the actual blk ptr to get data from.
80 * UFS1/2 use frag addrs rather than blk ones, then
81 * the offset into the block must be calculated.
83 static const void *
84 ufs_get_cache(struct inode *inode, block_t lblock)
86 const void *data;
87 struct fs_info *fs = inode->fs;
88 struct ufs_sb_info *sb = UFS_SB(inode->fs);
89 uint64_t frag_addr, frag_offset;
90 uint32_t frag_shift;
91 block_t pblock;
93 frag_addr = ufs_bmap(inode, lblock, NULL);
94 if (!frag_addr)
95 return NULL;
97 frag_shift = fs->block_shift - sb->c_blk_frag_shift;
98 /* Get fragment byte address */
99 frag_offset = frag_addr << frag_shift;
100 /* Convert frag addr to blk addr */
101 pblock = frag_to_blk(fs, frag_addr);
102 /* Read the blk */
103 data = get_cache(fs->fs_dev, pblock);
105 /* Return offset into block */
106 return data + (frag_offset & (fs->block_size - 1));
110 * Based on fs/ext2/ext2.c
111 * find a dir entry, return it if found, or return NULL.
113 static const struct ufs_dir_entry *
114 ufs_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
116 const struct ufs_dir_entry *dir;
117 const char *data;
118 int32_t i, offset, maxoffset;
119 block_t index = 0;
121 ufs_debug("ufs_find_entry: dname: %s ", dname);
122 for (i = 0; i < inode->size; i += fs->block_size) {
123 data = ufs_get_cache(inode, index++);
124 offset = 0;
125 maxoffset = min(inode->size-i, fs->block_size);
127 /* The smallest possible size is 9 bytes */
128 while (offset < maxoffset-8) {
129 dir = (const struct ufs_dir_entry *)(data + offset);
130 if (dir->dir_entry_len > maxoffset - offset)
131 break;
134 * Name fields are variable-length and null terminated,
135 * then it's possible to use strcmp directly.
137 if (dir->inode_value && !strcmp(dname, (const char *)dir->name)) {
138 ufs_debug("(found)\n");
139 return dir;
141 offset += dir->dir_entry_len;
144 ufs_debug("(not found)\n");
145 return NULL;
149 * Get either UFS1/2 inode structures.
151 static const void *
152 ufs_get_inode(struct fs_info *fs, int inr)
154 const char *data;
155 uint32_t group, inode_offset, inode_table;
156 uint32_t block_num, block_off;
158 /* Get cylinder group nr. */
159 group = inr / UFS_SB(fs)->inodes_per_cg;
161 * Ensuring group will not exceed the range 0:groups_count-1.
162 * By the way, this should *never* happen.
163 * Unless the (on-disk) fs structure is corrupted!
165 if (group >= UFS_SB(fs)->groups_count) {
166 printf("ufs_get_inode: "
167 "group(%d) exceeded the avail. range (0:%d)\n",
168 group, UFS_SB(fs)->groups_count - 1);
169 return NULL;
172 /* Offset into inode table of the cylinder group */
173 inode_offset = inr % UFS_SB(fs)->inodes_per_cg;
174 /* Get inode table blk addr respective to cylinder group */
175 inode_table = (group * UFS_SB(fs)->blocks_per_cg) +
176 UFS_SB(fs)->off_inode_tbl;
177 /* Calculating staggering offset (UFS1 only!) */
178 if (UFS_SB(fs)->fs_type == UFS1)
179 inode_table += UFS_SB(fs)->ufs1.delta_value *
180 (group & UFS_SB(fs)->ufs1.cycle_mask);
182 /* Get blk nr and offset into the blk */
183 block_num = inode_table + inode_offset / UFS_SB(fs)->inodes_per_block;
184 block_off = inode_offset % UFS_SB(fs)->inodes_per_block;
187 * Read the blk from the blk addr previously computed;
188 * Calc the inode struct offset into the read block.
190 data = get_cache(fs->fs_dev, block_num);
191 return data + block_off * UFS_SB(fs)->inode_size;
194 static struct inode *
195 ufs1_iget_by_inr(struct fs_info *fs, uint32_t inr)
197 const struct ufs1_inode *ufs_inode;
198 struct inode *inode;
199 uint64_t *dest;
200 uint32_t *source;
201 int i;
203 ufs_inode = (struct ufs1_inode *) ufs_get_inode(fs, inr);
204 if (!ufs_inode)
205 return NULL;
207 if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
208 return NULL;
210 /* UFS1 doesn't support neither creation nor deletion times */
211 inode->refcnt = ufs_inode->link_count;
212 inode->mode = IFTODT(ufs_inode->file_mode);
213 inode->size = ufs_inode->size;
214 inode->atime = ufs_inode->a_time;
215 inode->mtime = ufs_inode->m_time;
216 inode->blocks = ufs_inode->blocks_held;
217 inode->flags = ufs_inode->flags;
220 * Copy and extend blk pointers to 64 bits, so avoid
221 * having two structures for inode private.
223 dest = (uint64_t *) inode->pvt;
224 source = (uint32_t *) ufs_inode->direct_blk_ptr;
225 for (i = 0; i < UFS_NBLOCKS; i++)
226 dest[i] = ((uint64_t) source[i]) & 0xFFFFFFFF;
228 return inode;
231 static struct inode *
232 ufs2_iget_by_inr(struct fs_info *fs, uint32_t inr)
234 const struct ufs2_inode *ufs_inode;
235 struct inode *inode;
237 ufs_inode = (struct ufs2_inode *) ufs_get_inode(fs, inr);
238 if (!ufs_inode)
239 return NULL;
241 if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
242 return NULL;
244 /* UFS2 doesn't support deletion time */
245 inode->refcnt = ufs_inode->link_count;
246 inode->mode = IFTODT(ufs_inode->file_mode);
247 inode->size = ufs_inode->size;
248 inode->atime = ufs_inode->a_time;
249 inode->ctime = ufs_inode->creat_time;
250 inode->mtime = ufs_inode->m_time;
251 inode->blocks = ufs_inode->bytes_held >> fs->block_shift;
252 inode->flags = ufs_inode->flags;
253 memcpy(inode->pvt, ufs_inode->direct_blk_ptr,
254 sizeof(uint64_t) * UFS_NBLOCKS);
256 return inode;
260 * Both ufs_iget_root and ufs_iget callback based on ufs type.
262 static struct inode *
263 ufs_iget_root(struct fs_info *fs)
265 return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE);
268 static struct inode *
269 ufs_iget(const char *dname, struct inode *parent)
271 const struct ufs_dir_entry *dir;
272 struct fs_info *fs = parent->fs;
274 dir = ufs_find_entry(fs, parent, dname);
275 if (!dir)
276 return NULL;
278 return UFS_SB(fs)->ufs_iget_by_inr(fs, dir->inode_value);
281 static void ufs1_read_blkaddrs(struct inode *inode, char *buf)
283 uint32_t dest[UFS_NBLOCKS];
284 const uint64_t *source = (uint64_t *) (inode->pvt);
285 int i;
287 /* Convert ufs_inode_pvt uint64_t fields into uint32_t
288 * Upper-half part of ufs1 private blk addrs are always supposed to be
289 * zero (it's previosuly extended by us), thus data isn't being lost. */
290 for (i = 0; i < UFS_NBLOCKS; i++) {
291 if ((source[i] >> 32) != 0) {
292 /* This should never happen, but will not prevent anything
293 * from working. */
294 ufs_debug("ufs1: inode->pvt[%d]: warning!\n", i);
297 dest[i] = (uint32_t)(source[i] & 0xFFFFFFFF);
299 memcpy(buf, (const char *) dest, inode->size);
302 static void ufs2_read_blkaddrs(struct inode *inode, char *buf)
304 memcpy(buf, (const char *) (inode->pvt), inode->size);
308 * Taken from ext2/ext2.c.
309 * Read the entire contents of an inode into a memory buffer
311 static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
313 struct fs_info *fs = inode->fs;
314 size_t block_size = BLOCK_SIZE(fs);
315 uint32_t index = 0; /* Logical block number */
316 size_t chunk;
317 const char *data;
318 char *p = buf;
320 if (inode->size > bytes)
321 bytes = inode->size;
323 while (bytes) {
324 chunk = min(bytes, block_size);
325 data = ufs_get_cache(inode, index++);
326 memcpy(p, data, chunk);
328 bytes -= chunk;
329 p += chunk;
332 return 0;
335 static int ufs_readlink(struct inode *inode, char *buf)
337 struct fs_info *fs = inode->fs;
338 uint32_t i_symlink_limit;
340 if (inode->size > BLOCK_SIZE(fs))
341 return -1; /* Error! */
343 // TODO: use UFS_SB(fs)->maxlen_isymlink instead.
344 i_symlink_limit = ((UFS_SB(fs)->fs_type == UFS1) ?
345 sizeof(uint32_t) : sizeof(uint64_t)) * UFS_NBLOCKS;
346 ufs_debug("UFS_SB(fs)->maxlen_isymlink=%d", UFS_SB(fs)->maxlen_isymlink);
348 if (inode->size <= i_symlink_limit)
349 UFS_SB(fs)->ufs_read_blkaddrs(inode, buf);
350 else
351 cache_get_file(inode, buf, inode->size);
353 return inode->size;
356 static inline enum dir_type_flags get_inode_mode(uint8_t type)
358 switch(type) {
359 case UFS_DTYPE_FIFO: return DT_FIFO;
360 case UFS_DTYPE_CHARDEV: return DT_CHR;
361 case UFS_DTYPE_DIR: return DT_DIR;
362 case UFS_DTYPE_BLOCK: return DT_BLK;
363 case UFS_DTYPE_RFILE: return DT_REG;
364 case UFS_DTYPE_SYMLINK: return DT_LNK;
365 case UFS_DTYPE_SOCKET: return DT_SOCK;
366 case UFS_DTYPE_WHITEOUT: return DT_WHT;
367 default: return DT_UNKNOWN;
372 * Read one directory entry at a time
374 static int ufs_readdir(struct file *file, struct dirent *dirent)
376 struct fs_info *fs = file->fs;
377 struct inode *inode = file->inode;
378 const struct ufs_dir_entry *dir;
379 const char *data;
380 block_t index = file->offset >> fs->block_shift;
382 if (file->offset >= inode->size)
383 return -1; /* End of file */
385 data = ufs_get_cache(inode, index);
386 dir = (const struct ufs_dir_entry *)
387 (data + (file->offset & (BLOCK_SIZE(fs) - 1)));
389 dirent->d_ino = dir->inode_value;
390 dirent->d_off = file->offset;
391 dirent->d_reclen = offsetof(struct dirent, d_name) + dir->name_length + 1;
392 dirent->d_type = get_inode_mode(dir->file_type & 0x0F);
393 memcpy(dirent->d_name, dir->name, dir->name_length);
394 dirent->d_name[dir->name_length] = '\0';
396 file->offset += dir->dir_entry_len; /* Update for next reading */
398 return 0;
401 static inline struct ufs_sb_info *
402 set_ufs_info(struct ufs_super_block *sb, int ufs_type)
404 struct ufs_sb_info *sbi;
406 sbi = malloc(sizeof *sbi);
407 if (!sbi)
408 malloc_error("ufs_sb_info structure");
410 /* Setting up UFS-dependent info */
411 if (ufs_type == UFS1) {
412 sbi->inode_size = sizeof (struct ufs1_inode);
413 sbi->groups_count = sb->ufs1.nr_frags / sb->frags_per_cg;
414 sbi->ufs1.delta_value = sb->ufs1.delta_value;
415 sbi->ufs1.cycle_mask = sb->ufs1.cycle_mask;
416 sbi->ufs_iget_by_inr = ufs1_iget_by_inr;
417 sbi->ufs_read_blkaddrs = ufs1_read_blkaddrs;
418 sbi->addr_shift = UFS1_ADDR_SHIFT;
419 } else { // UFS2 or UFS2_PIGGY
420 sbi->inode_size = sizeof (struct ufs2_inode);
421 sbi->groups_count = sb->ufs2.nr_frags / sb->frags_per_cg;
422 sbi->ufs_iget_by_inr = ufs2_iget_by_inr;
423 sbi->ufs_read_blkaddrs = ufs2_read_blkaddrs;
424 sbi->addr_shift = UFS2_ADDR_SHIFT;
426 sbi->inodes_per_block = sb->block_size / sbi->inode_size;
427 sbi->inodes_per_cg = sb->inodes_per_cg;
428 sbi->blocks_per_cg = sb->frags_per_cg >> sb->c_blk_frag_shift;
429 sbi->off_inode_tbl = sb->off_inode_tbl >> sb->c_blk_frag_shift;
430 sbi->c_blk_frag_shift = sb->c_blk_frag_shift;
431 sbi->maxlen_isymlink = sb->maxlen_isymlink;
432 sbi->fs_type = ufs_type;
434 return sbi;
438 * Init the fs metadata and return block size
440 static int ufs_fs_init(struct fs_info *fs)
442 struct disk *disk = fs->fs_dev->disk;
443 struct ufs_super_block sb;
444 struct cache *cs;
446 int ufs_type = ufs_checksb(&sb, disk);
447 if (ufs_type == NONE)
448 return -1;
450 ufs_debug("%s SB FOUND!\n", ufs_type == UFS1 ? "UFS1" : "UFS2");
451 ufs_debug("Block size: %u\n", sb.block_size);
453 fs->fs_info = (struct ufs_sb_info *) set_ufs_info(&sb, ufs_type);
454 fs->sector_shift = disk->sector_shift;
455 fs->sector_size = disk->sector_size;
456 fs->block_shift = sb.block_shift;
457 fs->block_size = sb.block_size;
459 /* Initialize the cache, and force a clean on block zero */
460 cache_init(fs->fs_dev, sb.block_shift);
461 cs = _get_cache_block(fs->fs_dev, 0);
462 memset(cs->data, 0, fs->block_size);
463 cache_lock_block(cs);
465 /* For debug purposes */
466 //ufs_checking(fs);
468 //return -1;
469 return fs->block_shift;
472 const struct fs_ops ufs_fs_ops = {
473 .fs_name = "ufs",
474 .fs_flags = FS_USEMEM | FS_THISIND,
475 .fs_init = ufs_fs_init,
476 .searchdir = NULL,
477 .getfssec = generic_getfssec,
478 .close_file = generic_close_file,
479 .mangle_name = generic_mangle_name,
480 .open_config = generic_open_config,
481 .readlink = ufs_readlink,
482 .readdir = ufs_readdir,
483 .iget_root = ufs_iget_root,
484 .iget = ufs_iget,
485 .next_extent = ufs_next_extent,