Commit ba1f9db9 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hch/hfsplus

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hch/hfsplus:
  hfsplus: ensure bio requests are not smaller than the hardware sectors
  hfsplus: Add additional range check to handle on-disk corruptions
  hfsplus: Add error propagation for hfsplus_ext_write_extent_locked
  hfsplus: add error checking for hfs_find_init()
  hfsplus: lift the 2TB size limit
  hfsplus: fix overflow in hfsplus_read_wrapper
  hfsplus: fix overflow in hfsplus_get_block
  hfsplus: assignments inside `if' condition clean-up
parents 49302baa 6596528e
...@@ -43,6 +43,10 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) ...@@ -43,6 +43,10 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
node->tree->node_size - (rec + 1) * 2); node->tree->node_size - (rec + 1) * 2);
if (!recoff) if (!recoff)
return 0; return 0;
if (recoff > node->tree->node_size - 2) {
printk(KERN_ERR "hfs: recoff %d too large\n", recoff);
return 0;
}
retval = hfs_bnode_read_u16(node, recoff) + 2; retval = hfs_bnode_read_u16(node, recoff) + 2;
if (retval > node->tree->max_key_len + 2) { if (retval > node->tree->max_key_len + 2) {
......
...@@ -212,7 +212,9 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, ...@@ -212,7 +212,9 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n",
str->name, cnid, inode->i_nlink); str->name, cnid, inode->i_nlink);
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
if (err)
return err;
hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
entry_size = hfsplus_fill_cat_thread(sb, &entry, entry_size = hfsplus_fill_cat_thread(sb, &entry,
...@@ -269,7 +271,9 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) ...@@ -269,7 +271,9 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n",
str ? str->name : NULL, cnid); str ? str->name : NULL, cnid);
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
if (err)
return err;
if (!str) { if (!str) {
int len; int len;
...@@ -347,12 +351,14 @@ int hfsplus_rename_cat(u32 cnid, ...@@ -347,12 +351,14 @@ int hfsplus_rename_cat(u32 cnid,
struct hfs_find_data src_fd, dst_fd; struct hfs_find_data src_fd, dst_fd;
hfsplus_cat_entry entry; hfsplus_cat_entry entry;
int entry_size, type; int entry_size, type;
int err = 0; int err;
dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
cnid, src_dir->i_ino, src_name->name, cnid, src_dir->i_ino, src_name->name,
dst_dir->i_ino, dst_name->name); dst_dir->i_ino, dst_name->name);
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
if (err)
return err;
dst_fd = src_fd; dst_fd = src_fd;
/* find the old dir entry and read the data */ /* find the old dir entry and read the data */
......
...@@ -38,7 +38,9 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, ...@@ -38,7 +38,9 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
sb = dir->i_sb; sb = dir->i_sb;
dentry->d_fsdata = NULL; dentry->d_fsdata = NULL;
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
if (err)
return ERR_PTR(err);
hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
again: again:
err = hfs_brec_read(&fd, &entry, sizeof(entry)); err = hfs_brec_read(&fd, &entry, sizeof(entry));
...@@ -132,7 +134,9 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -132,7 +134,9 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
if (filp->f_pos >= inode->i_size) if (filp->f_pos >= inode->i_size)
return 0; return 0;
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
if (err)
return err;
hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
err = hfs_brec_find(&fd); err = hfs_brec_find(&fd);
if (err) if (err)
......
...@@ -119,22 +119,31 @@ static void __hfsplus_ext_write_extent(struct inode *inode, ...@@ -119,22 +119,31 @@ static void __hfsplus_ext_write_extent(struct inode *inode,
set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags); set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
} }
static void hfsplus_ext_write_extent_locked(struct inode *inode) static int hfsplus_ext_write_extent_locked(struct inode *inode)
{ {
int res;
if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) { if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
struct hfs_find_data fd; struct hfs_find_data fd;
hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
if (res)
return res;
__hfsplus_ext_write_extent(inode, &fd); __hfsplus_ext_write_extent(inode, &fd);
hfs_find_exit(&fd); hfs_find_exit(&fd);
} }
return 0;
} }
void hfsplus_ext_write_extent(struct inode *inode) int hfsplus_ext_write_extent(struct inode *inode)
{ {
int res;
mutex_lock(&HFSPLUS_I(inode)->extents_lock); mutex_lock(&HFSPLUS_I(inode)->extents_lock);
hfsplus_ext_write_extent_locked(inode); res = hfsplus_ext_write_extent_locked(inode);
mutex_unlock(&HFSPLUS_I(inode)->extents_lock); mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
return res;
} }
static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd, static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
...@@ -194,9 +203,11 @@ static int hfsplus_ext_read_extent(struct inode *inode, u32 block) ...@@ -194,9 +203,11 @@ static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
block < hip->cached_start + hip->cached_blocks) block < hip->cached_start + hip->cached_blocks)
return 0; return 0;
hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
res = __hfsplus_ext_cache_extent(&fd, inode, block); if (!res) {
hfs_find_exit(&fd); res = __hfsplus_ext_cache_extent(&fd, inode, block);
hfs_find_exit(&fd);
}
return res; return res;
} }
...@@ -209,6 +220,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, ...@@ -209,6 +220,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
int res = -EIO; int res = -EIO;
u32 ablock, dblock, mask; u32 ablock, dblock, mask;
sector_t sector;
int was_dirty = 0; int was_dirty = 0;
int shift; int shift;
...@@ -255,10 +267,12 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, ...@@ -255,10 +267,12 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
done: done:
dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n",
inode->i_ino, (long long)iblock, dblock); inode->i_ino, (long long)iblock, dblock);
mask = (1 << sbi->fs_shift) - 1; mask = (1 << sbi->fs_shift) - 1;
map_bh(bh_result, sb, sector = ((sector_t)dblock << sbi->fs_shift) +
(dblock << sbi->fs_shift) + sbi->blockoffset + sbi->blockoffset + (iblock & mask);
(iblock & mask)); map_bh(bh_result, sb, sector);
if (create) { if (create) {
set_buffer_new(bh_result); set_buffer_new(bh_result);
hip->phys_size += sb->s_blocksize; hip->phys_size += sb->s_blocksize;
...@@ -371,7 +385,9 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid, ...@@ -371,7 +385,9 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
if (total_blocks == blocks) if (total_blocks == blocks)
return 0; return 0;
hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
if (res)
return res;
do { do {
res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid, res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid,
total_blocks, type); total_blocks, type);
...@@ -469,7 +485,9 @@ int hfsplus_file_extend(struct inode *inode) ...@@ -469,7 +485,9 @@ int hfsplus_file_extend(struct inode *inode)
insert_extent: insert_extent:
dprint(DBG_EXTENT, "insert new extent\n"); dprint(DBG_EXTENT, "insert new extent\n");
hfsplus_ext_write_extent_locked(inode); res = hfsplus_ext_write_extent_locked(inode);
if (res)
goto out;
memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
hip->cached_extents[0].start_block = cpu_to_be32(start); hip->cached_extents[0].start_block = cpu_to_be32(start);
...@@ -500,7 +518,6 @@ void hfsplus_file_truncate(struct inode *inode) ...@@ -500,7 +518,6 @@ void hfsplus_file_truncate(struct inode *inode)
struct page *page; struct page *page;
void *fsdata; void *fsdata;
u32 size = inode->i_size; u32 size = inode->i_size;
int res;
res = pagecache_write_begin(NULL, mapping, size, 0, res = pagecache_write_begin(NULL, mapping, size, 0,
AOP_FLAG_UNINTERRUPTIBLE, AOP_FLAG_UNINTERRUPTIBLE,
...@@ -523,7 +540,12 @@ void hfsplus_file_truncate(struct inode *inode) ...@@ -523,7 +540,12 @@ void hfsplus_file_truncate(struct inode *inode)
goto out; goto out;
mutex_lock(&hip->extents_lock); mutex_lock(&hip->extents_lock);
hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
if (res) {
mutex_unlock(&hip->extents_lock);
/* XXX: We lack error handling of hfsplus_file_truncate() */
return;
}
while (1) { while (1) {
if (alloc_cnt == hip->first_blocks) { if (alloc_cnt == hip->first_blocks) {
hfsplus_free_extents(sb, hip->first_extents, hfsplus_free_extents(sb, hip->first_extents,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include "hfsplus_raw.h" #include "hfsplus_raw.h"
#define DBG_BNODE_REFS 0x00000001 #define DBG_BNODE_REFS 0x00000001
...@@ -110,7 +111,9 @@ struct hfsplus_vh; ...@@ -110,7 +111,9 @@ struct hfsplus_vh;
struct hfs_btree; struct hfs_btree;
struct hfsplus_sb_info { struct hfsplus_sb_info {
void *s_vhdr_buf;
struct hfsplus_vh *s_vhdr; struct hfsplus_vh *s_vhdr;
void *s_backup_vhdr_buf;
struct hfsplus_vh *s_backup_vhdr; struct hfsplus_vh *s_backup_vhdr;
struct hfs_btree *ext_tree; struct hfs_btree *ext_tree;
struct hfs_btree *cat_tree; struct hfs_btree *cat_tree;
...@@ -258,6 +261,15 @@ struct hfsplus_readdir_data { ...@@ -258,6 +261,15 @@ struct hfsplus_readdir_data {
struct hfsplus_cat_key key; struct hfsplus_cat_key key;
}; };
/*
* Find minimum acceptible I/O size for an hfsplus sb.
*/
static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
{
return max_t(unsigned short, bdev_logical_block_size(sb->s_bdev),
HFSPLUS_SECTOR_SIZE);
}
#define hfs_btree_open hfsplus_btree_open #define hfs_btree_open hfsplus_btree_open
#define hfs_btree_close hfsplus_btree_close #define hfs_btree_close hfsplus_btree_close
#define hfs_btree_write hfsplus_btree_write #define hfs_btree_write hfsplus_btree_write
...@@ -374,7 +386,7 @@ extern const struct file_operations hfsplus_dir_operations; ...@@ -374,7 +386,7 @@ extern const struct file_operations hfsplus_dir_operations;
/* extents.c */ /* extents.c */
int hfsplus_ext_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *); int hfsplus_ext_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
void hfsplus_ext_write_extent(struct inode *); int hfsplus_ext_write_extent(struct inode *);
int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int); int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
int hfsplus_free_fork(struct super_block *, u32, int hfsplus_free_fork(struct super_block *, u32,
struct hfsplus_fork_raw *, int); struct hfsplus_fork_raw *, int);
...@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent, ...@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent,
/* wrapper.c */ /* wrapper.c */
int hfsplus_read_wrapper(struct super_block *); int hfsplus_read_wrapper(struct super_block *);
int hfs_part_find(struct super_block *, sector_t *, sector_t *); int hfs_part_find(struct super_block *, sector_t *, sector_t *);
int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
void *data, int rw); void *buf, void **data, int rw);
/* time macros */ /* time macros */
#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U) #define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
......
...@@ -195,11 +195,13 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, ...@@ -195,11 +195,13 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir,
hip->flags = 0; hip->flags = 0;
set_bit(HFSPLUS_I_RSRC, &hip->flags); set_bit(HFSPLUS_I_RSRC, &hip->flags);
hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
err = hfsplus_find_cat(sb, dir->i_ino, &fd); if (!err) {
if (!err) err = hfsplus_find_cat(sb, dir->i_ino, &fd);
err = hfsplus_cat_read_inode(inode, &fd); if (!err)
hfs_find_exit(&fd); err = hfsplus_cat_read_inode(inode, &fd);
hfs_find_exit(&fd);
}
if (err) { if (err) {
iput(inode); iput(inode);
return ERR_PTR(err); return ERR_PTR(err);
......
...@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm, ...@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm,
return -ENOENT; return -ENOENT;
} }
static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
sector_t *part_start, sector_t *part_size) struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
{ {
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
int size = be32_to_cpu(pm->pmMapBlkCnt); int size = be32_to_cpu(pm->pmMapBlkCnt);
int buf_size = hfsplus_min_io_size(sb);
int res; int res;
int i = 0; int i = 0;
...@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, ...@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
if (++i >= size) if (++i >= size)
return -ENOENT; return -ENOENT;
res = hfsplus_submit_bio(sb->s_bdev, pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
*part_start + HFS_PMAP_BLK + i, if ((u8 *)pm - (u8 *)buf >= buf_size) {
pm, READ); res = hfsplus_submit_bio(sb,
if (res) *part_start + HFS_PMAP_BLK + i,
return res; buf, (void **)&pm, READ);
if (res)
return res;
}
} while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC)); } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
return -ENOENT; return -ENOENT;
...@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, ...@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
int hfs_part_find(struct super_block *sb, int hfs_part_find(struct super_block *sb,
sector_t *part_start, sector_t *part_size) sector_t *part_start, sector_t *part_size)
{ {
void *data; void *buf, *data;
int res; int res;
data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
if (!data) if (!buf)
return -ENOMEM; return -ENOMEM;
res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK, res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
data, READ); buf, &data, READ);
if (res) if (res)
goto out; goto out;
...@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb, ...@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb,
res = hfs_parse_old_pmap(sb, data, part_start, part_size); res = hfs_parse_old_pmap(sb, data, part_start, part_size);
break; break;
case HFS_NEW_PMAP_MAGIC: case HFS_NEW_PMAP_MAGIC:
res = hfs_parse_new_pmap(sb, data, part_start, part_size); res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size);
break; break;
default: default:
res = -ENOENT; res = -ENOENT;
break; break;
} }
out: out:
kfree(data); kfree(buf);
return res; return res;
} }
...@@ -73,11 +73,13 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) ...@@ -73,11 +73,13 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
inode->i_ino == HFSPLUS_ROOT_CNID) { inode->i_ino == HFSPLUS_ROOT_CNID) {
hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); if (!err) {
if (!err) err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
err = hfsplus_cat_read_inode(inode, &fd); if (!err)
hfs_find_exit(&fd); err = hfsplus_cat_read_inode(inode, &fd);
hfs_find_exit(&fd);
}
} else { } else {
err = hfsplus_system_read_inode(inode); err = hfsplus_system_read_inode(inode);
} }
...@@ -133,9 +135,13 @@ static int hfsplus_system_write_inode(struct inode *inode) ...@@ -133,9 +135,13 @@ static int hfsplus_system_write_inode(struct inode *inode)
static int hfsplus_write_inode(struct inode *inode, static int hfsplus_write_inode(struct inode *inode,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
int err;
dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino); dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
hfsplus_ext_write_extent(inode); err = hfsplus_ext_write_extent(inode);
if (err)
return err;
if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
inode->i_ino == HFSPLUS_ROOT_CNID) inode->i_ino == HFSPLUS_ROOT_CNID)
...@@ -197,17 +203,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) ...@@ -197,17 +203,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
write_backup = 1; write_backup = 1;
} }
error2 = hfsplus_submit_bio(sb->s_bdev, error2 = hfsplus_submit_bio(sb,
sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
sbi->s_vhdr, WRITE_SYNC); sbi->s_vhdr_buf, NULL, WRITE_SYNC);
if (!error) if (!error)
error = error2; error = error2;
if (!write_backup) if (!write_backup)
goto out; goto out;
error2 = hfsplus_submit_bio(sb->s_bdev, error2 = hfsplus_submit_bio(sb,
sbi->part_start + sbi->sect_count - 2, sbi->part_start + sbi->sect_count - 2,
sbi->s_backup_vhdr, WRITE_SYNC); sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
if (!error) if (!error)
error2 = error; error2 = error;
out: out:
...@@ -251,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb) ...@@ -251,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb)
hfs_btree_close(sbi->ext_tree); hfs_btree_close(sbi->ext_tree);
iput(sbi->alloc_file); iput(sbi->alloc_file);
iput(sbi->hidden_dir); iput(sbi->hidden_dir);
kfree(sbi->s_vhdr); kfree(sbi->s_vhdr_buf);
kfree(sbi->s_backup_vhdr); kfree(sbi->s_backup_vhdr_buf);
unload_nls(sbi->nls); unload_nls(sbi->nls);
kfree(sb->s_fs_info); kfree(sb->s_fs_info);
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
...@@ -393,6 +399,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) ...@@ -393,6 +399,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
if (!sbi->rsrc_clump_blocks) if (!sbi->rsrc_clump_blocks)
sbi->rsrc_clump_blocks = 1; sbi->rsrc_clump_blocks = 1;
err = generic_check_addressable(sbi->alloc_blksz_shift,
sbi->total_blocks);
if (err) {
printk(KERN_ERR "hfs: filesystem size too large.\n");
goto out_free_vhdr;
}
/* Set up operations so we can load metadata */ /* Set up operations so we can load metadata */
sb->s_op = &hfsplus_sops; sb->s_op = &hfsplus_sops;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
...@@ -417,6 +430,8 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) ...@@ -417,6 +430,8 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
sb->s_flags |= MS_RDONLY; sb->s_flags |= MS_RDONLY;
} }
err = -EINVAL;
/* Load metadata objects (B*Trees) */ /* Load metadata objects (B*Trees) */
sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
if (!sbi->ext_tree) { if (!sbi->ext_tree) {
...@@ -447,7 +462,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) ...@@ -447,7 +462,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
str.name = HFSP_HIDDENDIR_NAME; str.name = HFSP_HIDDENDIR_NAME;
hfs_find_init(sbi->cat_tree, &fd); err = hfs_find_init(sbi->cat_tree, &fd);
if (err)
goto out_put_root;
hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
hfs_find_exit(&fd); hfs_find_exit(&fd);
......
...@@ -142,7 +142,11 @@ int hfsplus_uni2asc(struct super_block *sb, ...@@ -142,7 +142,11 @@ int hfsplus_uni2asc(struct super_block *sb,
/* search for single decomposed char */ /* search for single decomposed char */
if (likely(compose)) if (likely(compose))
ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0); ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0);
if (ce1 && (cc = ce1[0])) { if (ce1)
cc = ce1[0];
else
cc = 0;
if (cc) {
/* start of a possibly decomposed Hangul char */ /* start of a possibly decomposed Hangul char */
if (cc != 0xffff) if (cc != 0xffff)
goto done; goto done;
...@@ -209,7 +213,8 @@ int hfsplus_uni2asc(struct super_block *sb, ...@@ -209,7 +213,8 @@ int hfsplus_uni2asc(struct super_block *sb,
i++; i++;
ce2 = ce1; ce2 = ce1;
} }
if ((cc = ce2[0])) { cc = ce2[0];
if (cc) {
ip += i; ip += i;
ustrlen -= i; ustrlen -= i;
goto done; goto done;
...@@ -301,7 +306,11 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, ...@@ -301,7 +306,11 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { while (outlen < HFSPLUS_MAX_STRLEN && len > 0) {
size = asc2unichar(sb, astr, len, &c); size = asc2unichar(sb, astr, len, &c);
if (decompose && (dstr = decompose_unichar(c, &dsize))) { if (decompose)
dstr = decompose_unichar(c, &dsize);
else
dstr = NULL;
if (dstr) {
if (outlen + dsize > HFSPLUS_MAX_STRLEN) if (outlen + dsize > HFSPLUS_MAX_STRLEN)
break; break;
do { do {
...@@ -346,15 +355,23 @@ int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode, ...@@ -346,15 +355,23 @@ int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
astr += size; astr += size;
len -= size; len -= size;
if (decompose && (dstr = decompose_unichar(c, &dsize))) { if (decompose)
dstr = decompose_unichar(c, &dsize);
else
dstr = NULL;
if (dstr) {
do { do {
c2 = *dstr++; c2 = *dstr++;
if (!casefold || (c2 = case_fold(c2))) if (casefold)
c2 = case_fold(c2);
if (!casefold || c2)
hash = partial_name_hash(c2, hash); hash = partial_name_hash(c2, hash);
} while (--dsize > 0); } while (--dsize > 0);
} else { } else {
c2 = c; c2 = c;
if (!casefold || (c2 = case_fold(c2))) if (casefold)
c2 = case_fold(c2);
if (!casefold || c2)
hash = partial_name_hash(c2, hash); hash = partial_name_hash(c2, hash);
} }
} }
...@@ -422,12 +439,14 @@ int hfsplus_compare_dentry(const struct dentry *parent, ...@@ -422,12 +439,14 @@ int hfsplus_compare_dentry(const struct dentry *parent,
c1 = *dstr1; c1 = *dstr1;
c2 = *dstr2; c2 = *dstr2;
if (casefold) { if (casefold) {
if (!(c1 = case_fold(c1))) { c1 = case_fold(c1);
if (!c1) {
dstr1++; dstr1++;
dsize1--; dsize1--;
continue; continue;
} }
if (!(c2 = case_fold(c2))) { c2 = case_fold(c2);
if (!c2) {
dstr2++; dstr2++;
dsize2--; dsize2--;
continue; continue;
......
...@@ -31,25 +31,67 @@ static void hfsplus_end_io_sync(struct bio *bio, int err) ...@@ -31,25 +31,67 @@ static void hfsplus_end_io_sync(struct bio *bio, int err)
complete(bio->bi_private); complete(bio->bi_private);
} }
int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, /*
void *data, int rw) * hfsplus_submit_bio - Perfrom block I/O
* @sb: super block of volume for I/O
* @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
* @buf: buffer for I/O
* @data: output pointer for location of requested data
* @rw: direction of I/O
*
* The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
* HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
* @data will return a pointer to the start of the requested sector,
* which may not be the same location as @buf.
*
* If @sector is not aligned to the bdev logical block size it will
* be rounded down. For writes this means that @buf should contain data
* that starts at the rounded-down address. As long as the data was
* read using hfsplus_submit_bio() and the same buffer is used things
* will work correctly.
*/
int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
void *buf, void **data, int rw)
{ {
DECLARE_COMPLETION_ONSTACK(wait); DECLARE_COMPLETION_ONSTACK(wait);
struct bio *bio; struct bio *bio;
int ret = 0; int ret = 0;
unsigned int io_size;
loff_t start;
int offset;
/*
* Align sector to hardware sector size and find offset. We
* assume that io_size is a power of two, which _should_
* be true.
*/
io_size = hfsplus_min_io_size(sb);
start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
offset = start & (io_size - 1);
sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
bio = bio_alloc(GFP_NOIO, 1); bio = bio_alloc(GFP_NOIO, 1);
bio->bi_sector = sector; bio->bi_sector = sector;
bio->bi_bdev = bdev; bio->bi_bdev = sb->s_bdev;
bio->bi_end_io = hfsplus_end_io_sync; bio->bi_end_io = hfsplus_end_io_sync;
bio->bi_private = &wait; bio->bi_private = &wait;
/* if (!(rw & WRITE) && data)
* We always submit one sector at a time, so bio_add_page must not fail. *data = (u8 *)buf + offset;
*/
if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE, while (io_size > 0) {
offset_in_page(data)) != HFSPLUS_SECTOR_SIZE) unsigned int page_offset = offset_in_page(buf);
BUG(); unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
io_size);
ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
if (ret != len) {
ret = -EIO;
goto out;
}
io_size -= len;
buf = (u8 *)buf + len;
}
submit_bio(rw, bio); submit_bio(rw, bio);
wait_for_completion(&wait); wait_for_completion(&wait);
...@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, ...@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
if (!bio_flagged(bio, BIO_UPTODATE)) if (!bio_flagged(bio, BIO_UPTODATE))
ret = -EIO; ret = -EIO;
out:
bio_put(bio); bio_put(bio);
return ret; return ret < 0 ? ret : 0;
} }
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
...@@ -141,23 +184,19 @@ int hfsplus_read_wrapper(struct super_block *sb) ...@@ -141,23 +184,19 @@ int hfsplus_read_wrapper(struct super_block *sb)
if (hfsplus_get_last_session(sb, &part_start, &part_size)) if (hfsplus_get_last_session(sb, &part_start, &part_size))
goto out; goto out;
if ((u64)part_start + part_size > 0x100000000ULL) {
pr_err("hfs: volumes larger than 2TB are not supported yet\n");
goto out;
}
error = -ENOMEM; error = -ENOMEM;
sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
if (!sbi->s_vhdr) if (!sbi->s_vhdr_buf)
goto out; goto out;
sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
if (!sbi->s_backup_vhdr) if (!sbi->s_backup_vhdr_buf)
goto out_free_vhdr; goto out_free_vhdr;
reread: reread:
error = hfsplus_submit_bio(sb->s_bdev, error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
part_start + HFSPLUS_VOLHEAD_SECTOR, sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
sbi->s_vhdr, READ); READ);
if (error) if (error)
goto out_free_backup_vhdr; goto out_free_backup_vhdr;
...@@ -172,8 +211,9 @@ int hfsplus_read_wrapper(struct super_block *sb) ...@@ -172,8 +211,9 @@ int hfsplus_read_wrapper(struct super_block *sb)
if (!hfsplus_read_mdb(sbi->s_vhdr, &wd)) if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
goto out_free_backup_vhdr; goto out_free_backup_vhdr;
wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; part_start += (sector_t)wd.ablk_start +
part_size = wd.embed_count * wd.ablk_size; (sector_t)wd.embed_start * wd.ablk_size;
part_size = (sector_t)wd.embed_count * wd.ablk_size;
goto reread; goto reread;
default: default:
/* /*
...@@ -186,9 +226,9 @@ int hfsplus_read_wrapper(struct super_block *sb) ...@@ -186,9 +226,9 @@ int hfsplus_read_wrapper(struct super_block *sb)
goto reread; goto reread;
} }
error = hfsplus_submit_bio(sb->s_bdev, error = hfsplus_submit_bio(sb, part_start + part_size - 2,
part_start + part_size - 2, sbi->s_backup_vhdr_buf,
sbi->s_backup_vhdr, READ); (void **)&sbi->s_backup_vhdr, READ);
if (error) if (error)
goto out_free_backup_vhdr; goto out_free_backup_vhdr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment