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