Home | History | Annotate | Download | only in xfs
      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