Home | History | Annotate | Download | only in ufs
      1 /*
      2  * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv (at) gmail.com>
      3  *
      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.
      8  *
      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.
     13  *
     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.
     18 */
     19 
     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"
     30 
     31 /*
     32  * Read the super block and check magic fields based on
     33  * passed paramaters.
     34  */
     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)
     38 {
     39     uint32_t lba;
     40     static uint32_t count;
     41 
     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);
     49 
     50     if (sb->magic == ufs_smagic)
     51 	return true;
     52 
     53     return false;
     54 }
     55 
     56 /*
     57  * Go through all possible ufs superblock offsets.
     58  * TODO: Add UFS support to removable media (sb offset: 0).
     59  */
     60 static int
     61 ufs_checksb(struct ufs_super_block *sb, struct disk *disk)
     62 {
     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;
     72 
     73     return NONE;
     74 }
     75 
     76 /*
     77  * lblock stands for linear block address,
     78  * whereas pblock is the actual blk ptr to get data from.
     79  *
     80  * UFS1/2 use frag addrs rather than blk ones, then
     81  * the offset into the block must be calculated.
     82  */
     83 static const void *
     84 ufs_get_cache(struct inode *inode, block_t lblock)
     85 {
     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;
     92 
     93     frag_addr = ufs_bmap(inode, lblock, NULL);
     94     if (!frag_addr)
     95 	return NULL;
     96 
     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);
    104 
    105     /* Return offset into block */
    106     return data + (frag_offset & (fs->block_size - 1));
    107 }
    108 
    109 /*
    110  * Based on fs/ext2/ext2.c
    111  * find a dir entry, return it if found, or return NULL.
    112  */
    113 static const struct ufs_dir_entry *
    114 ufs_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
    115 {
    116     const struct ufs_dir_entry *dir;
    117     const char *data;
    118     int32_t i, offset, maxoffset;
    119     block_t index = 0;
    120 
    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);
    126 
    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;
    132 
    133 	    /*
    134 	     * Name fields are variable-length and null terminated,
    135 	     * then it's possible to use strcmp directly.
    136 	     */
    137 	    if (dir->inode_value && !strcmp(dname, (const char *)dir->name)) {
    138 		ufs_debug("(found)\n");
    139 		return dir;
    140 	    }
    141 	    offset += dir->dir_entry_len;
    142 	}
    143     }
    144     ufs_debug("(not found)\n");
    145     return NULL;
    146 }
    147 
    148 /*
    149  * Get either UFS1/2 inode structures.
    150  */
    151 static const void *
    152 ufs_get_inode(struct fs_info *fs, int inr)
    153 {
    154     const char *data;
    155     uint32_t group, inode_offset, inode_table;
    156     uint32_t block_num, block_off;
    157 
    158     /* Get cylinder group nr. */
    159     group = inr / UFS_SB(fs)->inodes_per_cg;
    160     /*
    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!
    164      */
    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;
    170     }
    171 
    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);
    181 
    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;
    185 
    186     /*
    187      * Read the blk from the blk addr previously computed;
    188      * Calc the inode struct offset into the read block.
    189      */
    190     data = get_cache(fs->fs_dev, block_num);
    191     return data + block_off * UFS_SB(fs)->inode_size;
    192 }
    193 
    194 static struct inode *
    195 ufs1_iget_by_inr(struct fs_info *fs, uint32_t inr)
    196 {
    197     const struct ufs1_inode *ufs_inode;
    198     struct inode *inode;
    199     uint64_t *dest;
    200     uint32_t *source;
    201     int i;
    202 
    203     ufs_inode = (struct ufs1_inode *) ufs_get_inode(fs, inr);
    204     if (!ufs_inode)
    205 	return NULL;
    206 
    207     if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
    208 	return NULL;
    209 
    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;
    218 
    219     /*
    220      * Copy and extend blk pointers to 64 bits, so avoid
    221      * having two structures for inode private.
    222      */
    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;
    227 
    228     return inode;
    229 }
    230 
    231 static struct inode *
    232 ufs2_iget_by_inr(struct fs_info *fs, uint32_t inr)
    233 {
    234     const struct ufs2_inode *ufs_inode;
    235     struct inode *inode;
    236 
    237     ufs_inode = (struct ufs2_inode *) ufs_get_inode(fs, inr);
    238     if (!ufs_inode)
    239 	return NULL;
    240 
    241     if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt))))
    242 	return NULL;
    243 
    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);
    255 
    256     return inode;
    257 }
    258 
    259 /*
    260  * Both ufs_iget_root and ufs_iget callback based on ufs type.
    261  */
    262 static struct inode *
    263 ufs_iget_root(struct fs_info *fs)
    264 {
    265     return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE);
    266 }
    267 
    268 static struct inode *
    269 ufs_iget(const char *dname, struct inode *parent)
    270 {
    271     const struct ufs_dir_entry *dir;
    272     struct fs_info *fs = parent->fs;
    273 
    274     dir = ufs_find_entry(fs, parent, dname);
    275     if (!dir)
    276 	return NULL;
    277 
    278     return UFS_SB(fs)->ufs_iget_by_inr(fs, dir->inode_value);
    279 }
    280 
    281 static void ufs1_read_blkaddrs(struct inode *inode, char *buf)
    282 {
    283     uint32_t dest[UFS_NBLOCKS];
    284     const uint64_t *source = (uint64_t *) (inode->pvt);
    285     int i;
    286 
    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);
    295         }
    296 
    297         dest[i] = (uint32_t)(source[i] & 0xFFFFFFFF);
    298     }
    299     memcpy(buf, (const char *) dest, inode->size);
    300 }
    301 
    302 static void ufs2_read_blkaddrs(struct inode *inode, char *buf)
    303 {
    304     memcpy(buf, (const char *) (inode->pvt), inode->size);
    305 }
    306 
    307 /*
    308  * Taken from ext2/ext2.c.
    309  * Read the entire contents of an inode into a memory buffer
    310  */
    311 static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
    312 {
    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;
    319 
    320     if (inode->size > bytes)
    321         bytes = inode->size;
    322 
    323     while (bytes) {
    324         chunk = min(bytes, block_size);
    325         data = ufs_get_cache(inode, index++);
    326         memcpy(p, data, chunk);
    327 
    328         bytes -= chunk;
    329         p += chunk;
    330     }
    331 
    332     return 0;
    333 }
    334 
    335 static int ufs_readlink(struct inode *inode, char *buf)
    336 {
    337     struct fs_info *fs = inode->fs;
    338     uint32_t i_symlink_limit;
    339 
    340     if (inode->size > BLOCK_SIZE(fs))
    341         return -1;              /* Error! */
    342 
    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);
    347 
    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);
    352 
    353     return inode->size;
    354 }
    355 
    356 static inline enum dir_type_flags get_inode_mode(uint8_t type)
    357 {
    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;
    368     }
    369 }
    370 
    371 /*
    372  * Read one directory entry at a time
    373  */
    374 static int ufs_readdir(struct file *file, struct dirent *dirent)
    375 {
    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;
    381 
    382     if (file->offset >= inode->size)
    383 	return -1;		/* End of file */
    384 
    385     data = ufs_get_cache(inode, index);
    386     dir = (const struct ufs_dir_entry *)
    387 	(data + (file->offset & (BLOCK_SIZE(fs) - 1)));
    388 
    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';
    395 
    396     file->offset += dir->dir_entry_len;  /* Update for next reading */
    397 
    398     return 0;
    399 }
    400 
    401 static inline struct ufs_sb_info *
    402 set_ufs_info(struct ufs_super_block *sb, int ufs_type)
    403 {
    404     struct ufs_sb_info *sbi;
    405 
    406     sbi = malloc(sizeof *sbi);
    407     if (!sbi)
    408 	malloc_error("ufs_sb_info structure");
    409 
    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;
    425     }
    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;
    433 
    434     return sbi;
    435 }
    436 
    437 /*
    438  * Init the fs metadata and return block size
    439  */
    440 static int ufs_fs_init(struct fs_info *fs)
    441 {
    442     struct disk *disk = fs->fs_dev->disk;
    443     struct ufs_super_block sb;
    444     struct cache *cs;
    445 
    446     int ufs_type = ufs_checksb(&sb, disk);
    447     if (ufs_type == NONE)
    448 	return -1;
    449 
    450     ufs_debug("%s SB FOUND!\n", ufs_type == UFS1 ? "UFS1" : "UFS2");
    451     ufs_debug("Block size: %u\n", sb.block_size);
    452 
    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;
    458 
    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);
    464 
    465     /* For debug purposes */
    466     //ufs_checking(fs);
    467 
    468     //return -1;
    469     return fs->block_shift;
    470 }
    471 
    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,
    486 };
    487