Merge branch 'xfs-for-hpa' of git://zytor.com/users/pcacjr/syslinux into merge/elflin...
authorMatt Fleming <matt.fleming@intel.com>
Tue, 27 Nov 2012 18:38:18 +0000 (18:38 +0000)
committerMatt Fleming <matt.fleming@intel.com>
Tue, 27 Nov 2012 20:03:08 +0000 (20:03 +0000)
Pull XFS filesystem driver from Paulo Alcantara,

* 'xfs-for-hpa' of git://zytor.com/users/pcacjr/syslinux: (60 commits)
  xfs: Fix the way we check di_mode of an inode
  xfs: Cleanup previous commit
  xfs: Add xfs_readlink()
  xfs: Cleanup and remove some trailing whitespaces
  xfs: Add XFS_DINODE_FMT_BTREE support in xfs_next_extent()
  xfs: Cleanup and remove some trailing whitespaces
  xfs: Rework xfs_dir2_get_right_blk()
  xfs: cleanup unused structure
  xfs: Remove some trailing whitespaces
  xfs: Add full B+tree search support in xfs_dir2_node_find_entry()
  xfs: Add xfs_fmt_btree_find_entry()
  xfs: Fix memory leak in xfs_dir2_node_find_entry() function
  xfs: Cleanup xfs_readdir_dir2_leaf() function
  xfs: Implement xfs_readdir_dir2_node() function
  EXTLINUX: Add sanity check for XFS filesystems
  xfs: Add xfs_fmt_local_readdir() function
  xfs: Add xfs_fmt_local_find_entry() function
  xfs: Move readdir functions to another source file
  xfs: Remove trailing whitespace in xfs_dir2_isleaf() function
  xfs: Move dir2 functions to another source file
  ...

Conflicts:
extlinux/main.c

1  2 
core/fs/xfs/xfs.c
core/include/fs.h
extlinux/main.c

index 0000000,98d6255..89a9aef
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,439 +1,439 @@@
 -    .load_config      = generic_load_config,
+ /*
+  * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License as
+  * published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope that it would be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write the Free Software Foundation,
+  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+  */
+ #include <dprintf.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/dirent.h>
+ #include <cache.h>
+ #include <core.h>
+ #include <disk.h>
+ #include <fs.h>
+ #include <ilog2.h>
+ #include <klibc/compiler.h>
+ #include <ctype.h>
+ #include "codepage.h"
+ #include "xfs_types.h"
+ #include "xfs_sb.h"
+ #include "xfs_ag.h"
+ #include "misc.h"
+ #include "xfs.h"
+ #include "xfs_dinode.h"
+ #include "xfs_dir2.h"
+ #include "xfs_readdir.h"
+ static inline int xfs_fmt_local_readdir(struct file *file,
+                                       struct dirent *dirent, xfs_dinode_t *core)
+ {
+     return xfs_readdir_dir2_block(file, dirent, core);
+ }
+ static inline int xfs_fmt_extents_readdir(struct file *file,
+                                         struct dirent *dirent,
+                                         xfs_dinode_t *core)
+ {
+     int retval;
+     if (be32_to_cpu(core->di_nextents) <= 1) {
+       /* Single-block Directories */
+       retval = xfs_readdir_dir2_block(file, dirent, core);
+     } else if (xfs_dir2_isleaf(file->fs, core)) {
+       /* Leaf Directory */
+       retval = xfs_readdir_dir2_leaf(file, dirent, core);
+     } else {
+       /* Node Directory */
+       retval = xfs_readdir_dir2_node(file, dirent, core);
+     }
+     return retval;
+ }
+ static int xfs_readdir(struct file *file, struct dirent *dirent)
+ {
+     struct fs_info *fs = file->fs;
+     xfs_dinode_t *core;
+     struct inode *inode = file->inode;
+     int retval = -1;
+     core = xfs_dinode_get_core(fs, inode->ino);
+     if (!core) {
+       xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino);
+       return -1;
+     }
+     if (core->di_format == XFS_DINODE_FMT_LOCAL)
+       retval = xfs_fmt_local_readdir(file, dirent, core);
+     else if (core->di_format == XFS_DINODE_FMT_EXTENTS)
+       retval = xfs_fmt_extents_readdir(file, dirent, core);
+     return retval;
+ }
+ static uint32_t xfs_getfssec(struct file *file, char *buf, int sectors,
+                            bool *have_more)
+ {
+     return generic_getfssec(file, buf, sectors, have_more);
+ }
+ static int xfs_next_extent(struct inode *inode, uint32_t lstart)
+ {
+     struct fs_info *fs = inode->fs;
+     xfs_dinode_t *core = NULL;
+     xfs_bmbt_irec_t rec;
+     block_t bno;
+     xfs_bmdr_block_t *rblock;
+     int fsize;
+     xfs_bmbt_ptr_t *pp;
+     xfs_btree_block_t *blk;
+     uint16_t nextents;
+     block_t nextbno;
+     uint32_t index;
+     (void)lstart;
+     core = xfs_dinode_get_core(fs, inode->ino);
+     if (!core) {
+       xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino);
+       goto out;
+     }
+     /* The data fork contains the file's data extents */
+     if (XFS_PVT(inode)->i_cur_extent == be32_to_cpu(core->di_nextents))
+         goto out;
+     if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+       bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
+                                               XFS_PVT(inode)->i_cur_extent++);
+       bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
+       XFS_PVT(inode)->i_offset = rec.br_startoff;
+       inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) >> SECTOR_SHIFT(fs);
+       inode->next_extent.len = ((rec.br_blockcount << BLOCK_SHIFT(fs)) +
+                                 SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs);
+     } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
+         xfs_debug("XFS_DINODE_FMT_BTREE");
+         index = XFS_PVT(inode)->i_cur_extent++;
+         rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
+         fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
+         pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
+         bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs);
+         /* Find the leaf */
+         for (;;) {
+             blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+             if (be16_to_cpu(blk->bb_level) == 0)
+                 break;
+             pp = XFS_BMBT_PTR_ADDR(fs, blk, 1,
+                     xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0));
+             bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs);
+         }
+         /* Find the right extent among threaded leaves */
+         for (;;) {
+             nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib);
+             nextents = be16_to_cpu(blk->bb_numrecs);
+             if (nextents - index > 0) {
+                 bmbt_irec_get(&rec, XFS_BMDR_REC_ADDR(blk, index + 1));
+                 bno = fsblock_to_bytes(fs, rec.br_startblock)
+                                               >> BLOCK_SHIFT(fs);
+                 XFS_PVT(inode)->i_offset = rec.br_startoff;
+                 inode->next_extent.pstart = bno << BLOCK_SHIFT(fs)
+                                                 >> SECTOR_SHIFT(fs);
+                 inode->next_extent.len = ((rec.br_blockcount
+                                             << BLOCK_SHIFT(fs))
+                                             + SECTOR_SIZE(fs) - 1)
+                                             >> SECTOR_SHIFT(fs);
+                 break;
+             }
+             index -= nextents;
+             bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs);
+             blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+         }
+     }
+     return 0;
+ out:
+     return -1;
+ }
+ static inline struct inode *xfs_fmt_local_find_entry(const char *dname,
+                                                    struct inode *parent,
+                                                    xfs_dinode_t *core)
+ {
+     return xfs_dir2_local_find_entry(dname, parent, core);
+ }
+ static inline struct inode *xfs_fmt_extents_find_entry(const char *dname,
+                                                      struct inode *parent,
+                                                      xfs_dinode_t *core)
+ {
+     struct inode *inode;
+     if (be32_to_cpu(core->di_nextents) <= 1) {
+         /* Single-block Directories */
+         inode = xfs_dir2_block_find_entry(dname, parent, core);
+     } else if (xfs_dir2_isleaf(parent->fs, core)) {
+         /* Leaf Directory */
+       inode = xfs_dir2_leaf_find_entry(dname, parent, core);
+     } else {
+         /* Node Directory */
+         inode = xfs_dir2_node_find_entry(dname, parent, core);
+     }
+     return inode;
+ }
+ static inline struct inode *xfs_fmt_btree_find_entry(const char *dname,
+                                                      struct inode *parent,
+                                                      xfs_dinode_t *core)
+ {
+     return xfs_dir2_node_find_entry(dname, parent, core);
+ }
+ static struct inode *xfs_iget(const char *dname, struct inode *parent)
+ {
+     struct fs_info *fs = parent->fs;
+     xfs_dinode_t *core = NULL;
+     struct inode *inode = NULL;
+     xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino);
+     core = xfs_dinode_get_core(fs, parent->ino);
+     if (!core) {
+         xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent->ino);
+         goto out;
+     }
+     if (core->di_format == XFS_DINODE_FMT_LOCAL) {
+       inode = xfs_fmt_local_find_entry(dname, parent, core);
+     } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+         inode = xfs_fmt_extents_find_entry(dname, parent, core);
+     } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
+         inode = xfs_fmt_btree_find_entry(dname, parent, core);
+     } else {
+       xfs_debug("format %hhu", core->di_format);
+       xfs_debug("TODO: format \"local\" and \"extents\" are the only "
+                 "supported ATM");
+       goto out;
+     }
+     if (!inode) {
+       xfs_debug("Entry not found!");
+       goto out;
+     }
+     if (inode->mode == DT_REG) {
+       XFS_PVT(inode)->i_offset = 0;
+       XFS_PVT(inode)->i_cur_extent = 0;
+     } else if (inode->mode == DT_DIR) {
+       XFS_PVT(inode)->i_btree_offset = 0;
+       XFS_PVT(inode)->i_leaf_ent_offset = 0;
+     }
+     return inode;
+ out:
+     return NULL;
+ }
+ static int xfs_readlink(struct inode *inode, char *buf)
+ {
+     struct fs_info *fs = inode->fs;
+     xfs_dinode_t *core;
+     int pathlen = -1;
+     xfs_bmbt_irec_t rec;
+     block_t db;
+     char *dir_buf;
+     core = xfs_dinode_get_core(fs, inode->ino);
+     if (!core) {
+       xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode->ino);
+       goto out;
+     }
+     pathlen = be64_to_cpu(core->di_size);
+     if (!pathlen)
+       goto out;
+     if (pathlen < 0 || pathlen > MAXPATHLEN) {
+       xfs_error("inode (%llu) bad symlink length (%d)",
+                 inode->ino, pathlen);
+       goto out;
+     }
+     if (core->di_format == XFS_DINODE_FMT_LOCAL) {
+       memcpy(buf, (char *)&core->di_literal_area[0], pathlen);
+     } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+       bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+       db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
+       dir_buf = xfs_dir2_get_dirblks(fs, db, rec.br_blockcount);
+         /*
+          * Syslinux only supports filesystem block size larger than or equal to
+        * 4 KiB. Thus, one directory block is far enough to hold the maximum
+        * symbolic link file content, which is only 1024 bytes long.
+          */
+       memcpy(buf, dir_buf, pathlen);
+       free(dir_buf);
+     }
+ out:
+     return pathlen;
+ }
+ static struct inode *xfs_iget_root(struct fs_info *fs)
+ {
+     xfs_dinode_t *core = NULL;
+     struct inode *inode = xfs_new_inode(fs);
+     xfs_debug("Looking for the root inode...");
+     core = xfs_dinode_get_core(fs, XFS_INFO(fs)->rootino);
+     if (!core) {
+       xfs_error("Inode core's magic number does not match!");
+       xfs_debug("magic number 0x%04x", be16_to_cpu(core->di_magic));
+       goto out;
+     }
+     fill_xfs_inode_pvt(fs, inode, XFS_INFO(fs)->rootino);
+     xfs_debug("Root inode has been found!");
+     if ((be16_to_cpu(core->di_mode) & S_IFMT) != S_IFDIR) {
+       xfs_error("root inode is not a directory ?! No makes sense...");
+       goto out;
+     }
+     inode->ino                        = XFS_INFO(fs)->rootino;
+     inode->mode               = DT_DIR;
+     inode->size               = be64_to_cpu(core->di_size);
+     return inode;
+ out:
+     free(inode);
+     return NULL;
+ }
+ static inline int xfs_read_superblock(struct fs_info *fs, xfs_sb_t *sb)
+ {
+     struct disk *disk = fs->fs_dev->disk;
+     if (!disk->rdwr_sectors(disk, sb, XFS_SB_DADDR, 1, false))
+       return -1;
+     return 0;
+ }
+ static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb)
+ {
+     struct xfs_fs_info *info;
+     info = malloc(sizeof *info);
+     if (!info)
+       malloc_error("xfs_fs_info structure");
+     info->blocksize           = be32_to_cpu(sb->sb_blocksize);
+     info->block_shift         = sb->sb_blocklog;
+     info->dirblksize          = 1 << (sb->sb_blocklog + sb->sb_dirblklog);
+     info->dirblklog           = sb->sb_dirblklog;
+     info->inopb_shift                 = sb->sb_inopblog;
+     info->agblk_shift                 = sb->sb_agblklog;
+     info->rootino             = be64_to_cpu(sb->sb_rootino);
+     info->agblocks            = be32_to_cpu(sb->sb_agblocks);
+     info->agblocks_shift      = sb->sb_agblklog;
+     info->agcount             = be32_to_cpu(sb->sb_agcount);
+     info->inodesize           = be16_to_cpu(sb->sb_inodesize);
+     info->inode_shift                 = sb->sb_inodelog;
+     return info;
+ }
+ static int xfs_fs_init(struct fs_info *fs)
+ {
+     struct disk *disk = fs->fs_dev->disk;
+     xfs_sb_t sb;
+     struct xfs_fs_info *info;
+     xfs_debug("fs %p", fs);
+     SECTOR_SHIFT(fs) = disk->sector_shift;
+     SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
+     if (xfs_read_superblock(fs, &sb)) {
+       xfs_error("Superblock read failed");
+       goto out;
+     }
+     if (!xfs_is_valid_magicnum(&sb)) {
+       xfs_error("Invalid superblock");
+       goto out;
+     }
+     xfs_debug("magicnum 0x%lX", be32_to_cpu(sb.sb_magicnum));
+     info = xfs_new_sb_info(&sb);
+     if (!info) {
+       xfs_error("Failed to fill in filesystem-specific info structure");
+       goto out;
+     }
+     fs->fs_info = info;
+     xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info->block_shift,
+             info->blocksize, info->blocksize);
+     xfs_debug("rootino 0x%llX (%llu)", info->rootino, info->rootino);
+     BLOCK_SHIFT(fs) = info->block_shift;
+     BLOCK_SIZE(fs) = info->blocksize;
+     cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
+     XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs));
+     return BLOCK_SHIFT(fs);
+ out:
+     return -1;
+ }
+ const struct fs_ops xfs_fs_ops = {
+     .fs_name          = "xfs",
+     .fs_flags         = FS_USEMEM | FS_THISIND,
+     .fs_init          = xfs_fs_init,
+     .iget_root                = xfs_iget_root,
+     .searchdir                = NULL,
+     .getfssec         = xfs_getfssec,
++    .open_config      = generic_open_config,
+     .close_file         = generic_close_file,
+     .mangle_name      = generic_mangle_name,
+     .readdir          = xfs_readdir,
+     .iget             = xfs_iget,
+     .next_extent      = xfs_next_extent,
+     .readlink         = xfs_readlink,
+ };
Simple merge
diff --cc extlinux/main.c
@@@ -396,12 -440,62 +440,62 @@@ int install_bootblock(int fd, const cha
      return 0;
  }
  
+ static int rewrite_boot_image(int devfd, const char *filename)
+ {
+     int fd;
+     int ret;
+     int modbytes;
+     char path[PATH_MAX];
+     char slash;
+     /* Let's create LDLINUX.SYS file again (if it already exists, of course) */
+     fd = open(filename,  O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
+             S_IRUSR | S_IRGRP | S_IROTH);
+     if (fd < 0) {
+       perror(filename);
+       return -1;
+     }
+     /* Write boot image data into LDLINUX.SYS file */
+     ret = xpwrite(fd, boot_image, boot_image_len, 0);
+     if (ret != boot_image_len) {
+       perror("writing bootblock");
+       goto error;
+     }
+     /* Write ADV */
+     ret = xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, boot_image_len);
+     if (ret != 2 * ADV_SIZE) {
+       fprintf(stderr, "%s: write failure on %s\n", program, filename);
+       goto error;
+     }
+     sscanf(filename, "%s%cldlinux.sys", path, &slash);
+     /* Map the file, and patch the initial sector accordingly */
+     modbytes = patch_file_and_bootblock(fd, path, devfd);
+     /* Write the patch area again - this relies on the file being overwritten
+      * in place! */
+     ret = xpwrite(fd, boot_image, modbytes, 0);
+     if (ret != modbytes) {
+       fprintf(stderr, "%s: write failure on %s\n", program, filename);
+       goto error;
+     }
+     return fd;
+ error:
+     close(fd);
+     return -1;
+ }
  int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
  {
 -    char *file, *oldfile;
 +    char *file, *oldfile, *c32file;
      int fd = -1, dirfd = -1;
-     int modbytes;
 -    int r1, r2;
 +    int r1, r2, r3;
  
      r1 = asprintf(&file, "%s%sldlinux.sys",
                  path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
@@@ -796,13 -883,16 +933,16 @@@ static char * get_default_subvol(char 
     return subvol;
  }
  
 -int install_file(const char *path, int devfd, struct stat *rst)
 +static int install_file(const char *path, int devfd, struct stat *rst)
  {
-       if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
-               return ext2_fat_install_file(path, devfd, rst);
-       else if (fs_type == BTRFS)
-               return btrfs_install_file(path, devfd, rst);
-       return 1;
+     if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
+       return ext2_fat_install_file(path, devfd, rst);
+     else if (fs_type == BTRFS)
+       return btrfs_install_file(path, devfd, rst);
+     else if (fs_type == XFS)
+       return xfs_install_file(path, devfd, rst);
+     return 1;
  }
  
  #ifdef __KLIBC__