1 /* 2 * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr (at) zytor.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it would be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write the Free Software Foundation, 15 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 */ 17 18 #include <dprintf.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <sys/dirent.h> 22 #include <cache.h> 23 #include <core.h> 24 #include <disk.h> 25 #include <fs.h> 26 #include <ilog2.h> 27 #include <klibc/compiler.h> 28 #include <ctype.h> 29 30 #include "codepage.h" 31 #include "xfs_types.h" 32 #include "xfs_sb.h" 33 #include "xfs_ag.h" 34 #include "misc.h" 35 #include "xfs.h" 36 #include "xfs_dinode.h" 37 #include "xfs_dir2.h" 38 #include "xfs_readdir.h" 39 40 static inline int xfs_fmt_local_readdir(struct file *file, 41 struct dirent *dirent, 42 xfs_dinode_t *core) 43 { 44 return xfs_readdir_dir2_local(file, dirent, core); 45 } 46 47 static inline int xfs_fmt_extents_readdir(struct file *file, 48 struct dirent *dirent, 49 xfs_dinode_t *core) 50 { 51 if (be32_to_cpu(core->di_nextents) <= 1) { 52 /* Single-block Directories */ 53 return xfs_readdir_dir2_block(file, dirent, core); 54 } else if (xfs_dir2_isleaf(file->fs, core)) { 55 /* Leaf Directory */ 56 return xfs_readdir_dir2_leaf(file, dirent, core); 57 } else { 58 /* Node Directory */ 59 return xfs_readdir_dir2_node(file, dirent, core); 60 } 61 } 62 63 static int xfs_readdir(struct file *file, struct dirent *dirent) 64 { 65 struct fs_info *fs = file->fs; 66 xfs_dinode_t *core; 67 struct inode *inode = file->inode; 68 69 xfs_debug("file %p dirent %p"); 70 71 core = xfs_dinode_get_core(fs, inode->ino); 72 if (!core) { 73 xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); 74 return -1; 75 } 76 77 if (core->di_format == XFS_DINODE_FMT_LOCAL) 78 return xfs_fmt_local_readdir(file, dirent, core); 79 else if (core->di_format == XFS_DINODE_FMT_EXTENTS) 80 return xfs_fmt_extents_readdir(file, dirent, core); 81 82 return -1; 83 } 84 85 static uint32_t xfs_getfssec(struct file *file, char *buf, int sectors, 86 bool *have_more) 87 { 88 return generic_getfssec(file, buf, sectors, have_more); 89 } 90 91 static int xfs_next_extent(struct inode *inode, uint32_t lstart) 92 { 93 struct fs_info *fs = inode->fs; 94 xfs_dinode_t *core = NULL; 95 xfs_bmbt_irec_t rec; 96 block_t bno; 97 xfs_bmdr_block_t *rblock; 98 int fsize; 99 xfs_bmbt_ptr_t *pp; 100 xfs_btree_block_t *blk; 101 uint16_t nextents; 102 block_t nextbno; 103 uint32_t index; 104 105 (void)lstart; 106 107 xfs_debug("inode %p lstart %lu", inode, lstart); 108 109 core = xfs_dinode_get_core(fs, inode->ino); 110 if (!core) { 111 xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); 112 goto out; 113 } 114 115 /* The data fork contains the file's data extents */ 116 if (XFS_PVT(inode)->i_cur_extent == be32_to_cpu(core->di_nextents)) 117 goto out; 118 119 if (core->di_format == XFS_DINODE_FMT_EXTENTS) { 120 bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + 121 XFS_PVT(inode)->i_cur_extent++); 122 123 bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); 124 125 XFS_PVT(inode)->i_offset = rec.br_startoff; 126 127 inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) >> SECTOR_SHIFT(fs); 128 inode->next_extent.len = ((rec.br_blockcount << BLOCK_SHIFT(fs)) + 129 SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs); 130 } else if (core->di_format == XFS_DINODE_FMT_BTREE) { 131 xfs_debug("XFS_DINODE_FMT_BTREE"); 132 index = XFS_PVT(inode)->i_cur_extent++; 133 rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0]; 134 fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK); 135 pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0)); 136 bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); 137 138 /* Find the leaf */ 139 for (;;) { 140 blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); 141 if (be16_to_cpu(blk->bb_level) == 0) 142 break; 143 144 pp = XFS_BMBT_PTR_ADDR(fs, blk, 1, 145 xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0)); 146 bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); 147 } 148 149 /* Find the right extent among threaded leaves */ 150 for (;;) { 151 nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib); 152 nextents = be16_to_cpu(blk->bb_numrecs); 153 if (nextents - index > 0) { 154 bmbt_irec_get(&rec, XFS_BMDR_REC_ADDR(blk, index + 1)); 155 156 bno = fsblock_to_bytes(fs, rec.br_startblock) 157 >> BLOCK_SHIFT(fs); 158 159 XFS_PVT(inode)->i_offset = rec.br_startoff; 160 161 inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) 162 >> SECTOR_SHIFT(fs); 163 inode->next_extent.len = ((rec.br_blockcount 164 << BLOCK_SHIFT(fs)) 165 + SECTOR_SIZE(fs) - 1) 166 >> SECTOR_SHIFT(fs); 167 break; 168 } 169 170 index -= nextents; 171 bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs); 172 blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); 173 } 174 } 175 176 return 0; 177 178 out: 179 return -1; 180 } 181 182 static inline struct inode *xfs_fmt_local_find_entry(const char *dname, 183 struct inode *parent, 184 xfs_dinode_t *core) 185 { 186 return xfs_dir2_local_find_entry(dname, parent, core); 187 } 188 189 static inline struct inode *xfs_fmt_extents_find_entry(const char *dname, 190 struct inode *parent, 191 xfs_dinode_t *core) 192 { 193 if (be32_to_cpu(core->di_nextents) <= 1) { 194 /* Single-block Directories */ 195 return xfs_dir2_block_find_entry(dname, parent, core); 196 } else if (xfs_dir2_isleaf(parent->fs, core)) { 197 /* Leaf Directory */ 198 return xfs_dir2_leaf_find_entry(dname, parent, core); 199 } else { 200 /* Node Directory */ 201 return xfs_dir2_node_find_entry(dname, parent, core); 202 } 203 } 204 205 static inline struct inode *xfs_fmt_btree_find_entry(const char *dname, 206 struct inode *parent, 207 xfs_dinode_t *core) 208 { 209 return xfs_dir2_node_find_entry(dname, parent, core); 210 } 211 212 static struct inode *xfs_iget(const char *dname, struct inode *parent) 213 { 214 struct fs_info *fs = parent->fs; 215 xfs_dinode_t *core = NULL; 216 struct inode *inode = NULL; 217 218 xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino); 219 220 core = xfs_dinode_get_core(fs, parent->ino); 221 if (!core) { 222 xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent->ino); 223 goto out; 224 } 225 226 if (core->di_format == XFS_DINODE_FMT_LOCAL) { 227 inode = xfs_fmt_local_find_entry(dname, parent, core); 228 } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { 229 inode = xfs_fmt_extents_find_entry(dname, parent, core); 230 } else if (core->di_format == XFS_DINODE_FMT_BTREE) { 231 inode = xfs_fmt_btree_find_entry(dname, parent, core); 232 } 233 234 if (!inode) { 235 xfs_debug("Entry not found!"); 236 goto out; 237 } 238 239 if (inode->mode == DT_REG) { 240 XFS_PVT(inode)->i_offset = 0; 241 XFS_PVT(inode)->i_cur_extent = 0; 242 } else if (inode->mode == DT_DIR) { 243 XFS_PVT(inode)->i_btree_offset = 0; 244 XFS_PVT(inode)->i_leaf_ent_offset = 0; 245 } 246 247 return inode; 248 249 out: 250 return NULL; 251 } 252 253 static int xfs_readlink(struct inode *inode, char *buf) 254 { 255 struct fs_info *fs = inode->fs; 256 xfs_dinode_t *core; 257 int pathlen = -1; 258 xfs_bmbt_irec_t rec; 259 block_t db; 260 const char *dir_buf; 261 262 xfs_debug("inode %p buf %p", inode, buf); 263 264 core = xfs_dinode_get_core(fs, inode->ino); 265 if (!core) { 266 xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode->ino); 267 goto out; 268 } 269 270 pathlen = be64_to_cpu(core->di_size); 271 if (!pathlen) 272 goto out; 273 274 if (pathlen < 0 || pathlen > MAXPATHLEN) { 275 xfs_error("inode (%llu) bad symlink length (%d)", 276 inode->ino, pathlen); 277 goto out; 278 } 279 280 if (core->di_format == XFS_DINODE_FMT_LOCAL) { 281 memcpy(buf, (char *)&core->di_literal_area[0], pathlen); 282 } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { 283 bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]); 284 db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); 285 dir_buf = xfs_dir2_dirblks_get_cached(fs, db, rec.br_blockcount); 286 287 /* 288 * Syslinux only supports filesystem block size larger than or equal to 289 * 4 KiB. Thus, one directory block is far enough to hold the maximum 290 * symbolic link file content, which is only 1024 bytes long. 291 */ 292 memcpy(buf, dir_buf, pathlen); 293 } 294 295 out: 296 return pathlen; 297 } 298 299 static struct inode *xfs_iget_root(struct fs_info *fs) 300 { 301 xfs_dinode_t *core = NULL; 302 struct inode *inode = xfs_new_inode(fs); 303 304 xfs_debug("Looking for the root inode..."); 305 306 core = xfs_dinode_get_core(fs, XFS_INFO(fs)->rootino); 307 if (!core) { 308 xfs_error("Inode core's magic number does not match!"); 309 xfs_debug("magic number 0x%04x", be16_to_cpu(core->di_magic)); 310 goto out; 311 } 312 313 fill_xfs_inode_pvt(fs, inode, XFS_INFO(fs)->rootino); 314 315 xfs_debug("Root inode has been found!"); 316 317 if ((be16_to_cpu(core->di_mode) & S_IFMT) != S_IFDIR) { 318 xfs_error("root inode is not a directory ?! No makes sense..."); 319 goto out; 320 } 321 322 inode->ino = XFS_INFO(fs)->rootino; 323 inode->mode = DT_DIR; 324 inode->size = be64_to_cpu(core->di_size); 325 326 return inode; 327 328 out: 329 free(inode); 330 331 return NULL; 332 } 333 334 static inline int xfs_read_superblock(struct fs_info *fs, xfs_sb_t *sb) 335 { 336 struct disk *disk = fs->fs_dev->disk; 337 338 if (!disk->rdwr_sectors(disk, sb, XFS_SB_DADDR, 1, false)) 339 return -1; 340 341 return 0; 342 } 343 344 static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb) 345 { 346 struct xfs_fs_info *info; 347 348 info = malloc(sizeof *info); 349 if (!info) 350 malloc_error("xfs_fs_info structure"); 351 352 info->blocksize = be32_to_cpu(sb->sb_blocksize); 353 info->block_shift = sb->sb_blocklog; 354 info->dirblksize = 1 << (sb->sb_blocklog + sb->sb_dirblklog); 355 info->dirblklog = sb->sb_dirblklog; 356 info->inopb_shift = sb->sb_inopblog; 357 info->agblk_shift = sb->sb_agblklog; 358 info->rootino = be64_to_cpu(sb->sb_rootino); 359 info->agblocks = be32_to_cpu(sb->sb_agblocks); 360 info->agblocks_shift = sb->sb_agblklog; 361 info->agcount = be32_to_cpu(sb->sb_agcount); 362 info->inodesize = be16_to_cpu(sb->sb_inodesize); 363 info->inode_shift = sb->sb_inodelog; 364 365 return info; 366 } 367 368 static int xfs_fs_init(struct fs_info *fs) 369 { 370 struct disk *disk = fs->fs_dev->disk; 371 xfs_sb_t sb; 372 struct xfs_fs_info *info; 373 374 xfs_debug("fs %p", fs); 375 376 SECTOR_SHIFT(fs) = disk->sector_shift; 377 SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs); 378 379 if (xfs_read_superblock(fs, &sb)) { 380 xfs_error("Superblock read failed"); 381 goto out; 382 } 383 384 if (!xfs_is_valid_magicnum(&sb)) { 385 xfs_error("Invalid superblock"); 386 goto out; 387 } 388 389 xfs_debug("magicnum 0x%lX", be32_to_cpu(sb.sb_magicnum)); 390 391 info = xfs_new_sb_info(&sb); 392 if (!info) { 393 xfs_error("Failed to fill in filesystem-specific info structure"); 394 goto out; 395 } 396 397 fs->fs_info = info; 398 399 xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info->block_shift, 400 info->blocksize, info->blocksize); 401 402 xfs_debug("rootino 0x%llX (%llu)", info->rootino, info->rootino); 403 404 BLOCK_SHIFT(fs) = info->block_shift; 405 BLOCK_SIZE(fs) = info->blocksize; 406 407 cache_init(fs->fs_dev, BLOCK_SHIFT(fs)); 408 409 XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs)); 410 411 return BLOCK_SHIFT(fs); 412 413 out: 414 return -1; 415 } 416 417 const struct fs_ops xfs_fs_ops = { 418 .fs_name = "xfs", 419 .fs_flags = FS_USEMEM | FS_THISIND, 420 .fs_init = xfs_fs_init, 421 .iget_root = xfs_iget_root, 422 .searchdir = NULL, 423 .getfssec = xfs_getfssec, 424 .open_config = generic_open_config, 425 .close_file = generic_close_file, 426 .mangle_name = generic_mangle_name, 427 .readdir = xfs_readdir, 428 .iget = xfs_iget, 429 .next_extent = xfs_next_extent, 430 .readlink = xfs_readlink, 431 .fs_uuid = NULL, 432 }; 433