Merge tag 'hfs-v7.0-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Merge commit. Changes below are compared with the first parent: d10a88ce.
Changes
12 files changed · +407 −122
| File | Status | Add | Del | Content |
|---|---|---|---|---|
| fs/hfs/dir.c | modified | +11 | −4 | View file |
| fs/hfs/hfs_fs.h | modified | +1 | −0 | View file |
| fs/hfs/inode.c | modified | +24 | −6 | View file |
| fs/hfs/mdb.c | modified | +41 | −25 | View file |
| fs/hfs/super.c | modified | +12 | −1 | View file |
| fs/hfsplus/attributes.c | modified | +139 | −50 | View file |
| fs/hfsplus/bnode.c | modified | +1 | −1 | View file |
| fs/hfsplus/dir.c | modified | +45 | −1 | View file |
| fs/hfsplus/hfsplus_fs.h | modified | +3 | −0 | View file |
| fs/hfsplus/inode.c | modified | +37 | −3 | View file |
| fs/hfsplus/super.c | modified | +15 | −5 | View file |
| fs/hfsplus/xattr.c | modified | +78 | −26 | View file |
Only the first 600 diff lines are shown. Download the complete patch.
fs/hfs/dir.c
View filediff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index 86a6b31..0c615c0 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -196,8 +196,8 @@ static int hfs_create(struct mnt_idmap *idmap, struct inode *dir,
int res;
inode = hfs_new_inode(dir, &dentry->d_name, mode);
- if (!inode)
- return -ENOMEM;
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
if (res) {
@@ -226,8 +226,8 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
int res;
inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode);
- if (!inode)
- return ERR_PTR(-ENOMEM);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
if (res) {
@@ -254,11 +254,18 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
*/
static int hfs_remove(struct inode *dir, struct dentry *dentry)
{
+ struct super_block *sb = dir->i_sb;
struct inode *inode = d_inode(dentry);
int res;
if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
return -ENOTEMPTY;
+
+ if (unlikely(!is_hfs_cnid_counts_valid(sb))) {
+ pr_err("cannot remove file/folder\n");
+ return -ERANGE;
+ }
+
res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
if (res)
return res;
fs/hfs/hfs_fs.h
View filediff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index e94dbc0..ac0e83f 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -199,6 +199,7 @@ extern void hfs_delete_inode(struct inode *inode);
extern const struct xattr_handler * const hfs_xattr_handlers[];
/* mdb.c */
+extern bool is_hfs_cnid_counts_valid(struct super_block *sb);
extern int hfs_mdb_get(struct super_block *sb);
extern void hfs_mdb_commit(struct super_block *sb);
extern void hfs_mdb_close(struct super_block *sb);
fs/hfs/inode.c
View filediff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 524db13..878535d 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -187,16 +187,23 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
s64 next_id;
s64 file_count;
s64 folder_count;
+ int err = -ENOMEM;
if (!inode)
- return NULL;
+ goto out_err;
+
+ err = -ERANGE;
mutex_init(&HFS_I(inode)->extents_lock);
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
spin_lock_init(&HFS_I(inode)->open_dir_lock);
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
next_id = atomic64_inc_return(&HFS_SB(sb)->next_id);
- BUG_ON(next_id > U32_MAX);
+ if (next_id > U32_MAX) {
+ atomic64_dec(&HFS_SB(sb)->next_id);
+ pr_err("cannot create new inode: next CNID exceeds limit\n");
+ goto out_discard;
+ }
inode->i_ino = (u32)next_id;
inode->i_mode = mode;
inode->i_uid = current_fsuid();
@@ -210,7 +217,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
if (S_ISDIR(mode)) {
inode->i_size = 2;
folder_count = atomic64_inc_return(&HFS_SB(sb)->folder_count);
- BUG_ON(folder_count > U32_MAX);
+ if (folder_count> U32_MAX) {
+ atomic64_dec(&HFS_SB(sb)->folder_count);
+ pr_err("cannot create new inode: folder count exceeds limit\n");
+ goto out_discard;
+ }
if (dir->i_ino == HFS_ROOT_CNID)
HFS_SB(sb)->root_dirs++;
inode->i_op = &hfs_dir_inode_operations;
@@ -220,7 +231,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
} else if (S_ISREG(mode)) {
HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks;
file_count = atomic64_inc_return(&HFS_SB(sb)->file_count);
- BUG_ON(file_count > U32_MAX);
+ if (file_count > U32_MAX) {
+ atomic64_dec(&HFS_SB(sb)->file_count);
+ pr_err("cannot create new inode: file count exceeds limit\n");
+ goto out_discard;
+ }
if (dir->i_ino == HFS_ROOT_CNID)
HFS_SB(sb)->root_files++;
inode->i_op = &hfs_file_inode_operations;
@@ -244,6 +259,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
hfs_mark_mdb_dirty(sb);
return inode;
+
+ out_discard:
+ iput(inode);
+ out_err:
+ return ERR_PTR(err);
}
void hfs_delete_inode(struct inode *inode)
@@ -252,7 +272,6 @@ void hfs_delete_inode(struct inode *inode)
hfs_dbg("ino %lu\n", inode->i_ino);
if (S_ISDIR(inode->i_mode)) {
- BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX);
atomic64_dec(&HFS_SB(sb)->folder_count);
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
HFS_SB(sb)->root_dirs--;
@@ -261,7 +280,6 @@ void hfs_delete_inode(struct inode *inode)
return;
}
- BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
atomic64_dec(&HFS_SB(sb)->file_count);
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
HFS_SB(sb)->root_files--;
fs/hfs/mdb.c
View filediff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index 53f3fae..a97cea3 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -64,6 +64,27 @@ static int hfs_get_last_session(struct super_block *sb,
return 0;
}
+bool is_hfs_cnid_counts_valid(struct super_block *sb)
+{
+ struct hfs_sb_info *sbi = HFS_SB(sb);
+ bool corrupted = false;
+
+ if (unlikely(atomic64_read(&sbi->next_id) > U32_MAX)) {
+ pr_warn("next CNID exceeds limit\n");
+ corrupted = true;
+ }
+ if (unlikely(atomic64_read(&sbi->file_count) > U32_MAX)) {
+ pr_warn("file count exceeds limit\n");
+ corrupted = true;
+ }
+ if (unlikely(atomic64_read(&sbi->folder_count) > U32_MAX)) {
+ pr_warn("folder count exceeds limit\n");
+ corrupted = true;
+ }
+
+ return !corrupted;
+}
+
/*
* hfs_mdb_get()
*
@@ -92,7 +113,7 @@ int hfs_mdb_get(struct super_block *sb)
/* See if this is an HFS filesystem */
bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
if (!bh)
- goto out;
+ return -EIO;
if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
break;
@@ -102,13 +123,14 @@ int hfs_mdb_get(struct super_block *sb)
* (should do this only for cdrom/loop though)
*/
if (hfs_part_find(sb, &part_start, &part_size))
- goto out;
+ return -EIO;
}
HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
pr_err("bad allocation block size %d\n", size);
- goto out_bh;
+ brelse(bh);
+ return -EIO;
}
size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
@@ -125,14 +147,16 @@ int hfs_mdb_get(struct super_block *sb)
brelse(bh);
if (!sb_set_blocksize(sb, size)) {
pr_err("unable to set blocksize to %u\n", size);
- goto out;
+ return -EIO;
}
bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
if (!bh)
- goto out;
- if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
- goto out_bh;
+ return -EIO;
+ if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) {
+ brelse(bh);
+ return -EIO;
+ }
HFS_SB(sb)->mdb_bh = bh;
HFS_SB(sb)->mdb = mdb;
@@ -156,6 +180,11 @@ int hfs_mdb_get(struct super_block *sb)
atomic64_set(&HFS_SB(sb)->file_count, be32_to_cpu(mdb->drFilCnt));
atomic64_set(&HFS_SB(sb)->folder_count, be32_to_cpu(mdb->drDirCnt));
+ if (!is_hfs_cnid_counts_valid(sb)) {
+ pr_warn("filesystem possibly corrupted, running fsck.hfs is recommended. Mounting read-only.\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+
/* TRY to get the alternate (backup) MDB. */
sect = part_start + part_size - 2;
bh = sb_bread512(sb, sect, mdb2);
@@ -174,7 +203,7 @@ int hfs_mdb_get(struct super_block *sb)
HFS_SB(sb)->bitmap = kzalloc(8192, GFP_KERNEL);
if (!HFS_SB(sb)->bitmap)
- goto out;
+ return -EIO;
/* read in the bitmap */
block = be16_to_cpu(mdb->drVBMSt) + part_start;
@@ -185,7 +214,7 @@ int hfs_mdb_get(struct super_block *sb)
bh = sb_bread(sb, off >> sb->s_blocksize_bits);
if (!bh) {
pr_err("unable to read volume bitmap\n");
- goto out;
+ return -EIO;
}
off2 = off & (sb->s_blocksize - 1);
len = min((int)sb->s_blocksize - off2, size);
@@ -199,17 +228,17 @@ int hfs_mdb_get(struct super_block *sb)
HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
if (!HFS_SB(sb)->ext_tree) {
pr_err("unable to open extent tree\n");
- goto out;
+ return -EIO;
}
HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
if (!HFS_SB(sb)->cat_tree) {
pr_err("unable to open catalog tree\n");
- goto out;
+ return -EIO;
}
attrib = mdb->drAtrb;
if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
- pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. mounting read-only.\n");
+ pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. Mounting read-only.\n");
sb->s_flags |= SB_RDONLY;
}
if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
@@ -229,12 +258,6 @@ int hfs_mdb_get(struct super_block *sb)
}
return 0;
-
-out_bh:
- brelse(bh);
-out:
- hfs_mdb_put(sb);
- return -EIO;
}
/*
@@ -273,15 +296,12 @@ void hfs_mdb_commit(struct super_block *sb)
/* These parameters may have been modified, so write them back */
mdb->drLsMod = hfs_mtime();
mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
- BUG_ON(atomic64_read(&HFS_SB(sb)->next_id) > U32_MAX);
mdb->drNxtCNID =
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->next_id));
mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
- BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
mdb->drFilCnt =
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->file_count));
- BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX);
mdb->drDirCnt =
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->folder_count));
@@ -359,8 +379,6 @@ void hfs_mdb_close(struct super_block *sb)
* Release the resources associated with the in-core MDB. */
void hfs_mdb_put(struct super_block *sb)
{
- if (!HFS_SB(sb))
- return;
/* free the B-trees */
hfs_btree_close(HFS_SB(sb)->ext_tree);
hfs_btree_close(HFS_SB(sb)->cat_tree);
@@ -373,6 +391,4 @@ void hfs_mdb_put(struct super_block *sb)
unload_nls(HFS_SB(sb)->nls_disk);
kfree(HFS_SB(sb)->bitmap);
- kfree(HFS_SB(sb));
- sb->s_fs_info = NULL;
}
fs/hfs/super.c
View filediff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 47f50fa..97546d6 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -34,6 +34,7 @@ MODULE_LICENSE("GPL");
static int hfs_sync_fs(struct super_block *sb, int wait)
{
+ is_hfs_cnid_counts_valid(sb);
hfs_mdb_commit(sb);
return 0;
}
@@ -65,6 +66,8 @@ static void flush_mdb(struct work_struct *work)
sbi->work_queued = 0;
spin_unlock(&sbi->work_lock);
+ is_hfs_cnid_counts_valid(sb);
+
hfs_mdb_commit(sb);
}
@@ -431,10 +434,18 @@ static int hfs_init_fs_context(struct fs_context *fc)
return 0;
}
+static void hfs_kill_super(struct super_block *sb)
+{
+ struct hfs_sb_info *hsb = HFS_SB(sb);
+
+ kill_block_super(sb);
+ kfree(hsb);
+}
+
static struct file_system_type hfs_fs_type = {
.owner = THIS_MODULE,
.name = "hfs",
- .kill_sb = kill_block_super,
+ .kill_sb = hfs_kill_super,
.fs_flags = FS_REQUIRES_DEV,
.init_fs_context = hfs_init_fs_context,
};
fs/hfsplus/attributes.c
View filediff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c
index ba26980..4b79cd6 100644
--- a/fs/hfsplus/attributes.c
+++ b/fs/hfsplus/attributes.c
@@ -117,8 +117,10 @@ static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type,
entry->inline_data.record_type = cpu_to_be32(record_type);
if (size <= HFSPLUS_MAX_INLINE_DATA_SIZE)
len = size;
- else
+ else {
+ hfs_dbg("value size %zu is too big\n", size);
return HFSPLUS_INVALID_ATTR_RECORD;
+ }
entry->inline_data.length = cpu_to_be16(len);
memcpy(entry->inline_data.raw_bytes, value, len);
/*
@@ -191,14 +193,66 @@ attr_not_found:
return 0;
}
+static
+int hfsplus_create_attr_nolock(struct inode *inode, const char *name,
+ const void *value, size_t size,
+ struct hfs_find_data *fd,
+ hfsplus_attr_entry *entry_ptr)
+{
+ struct super_block *sb = inode->i_sb;
+ int entry_size;
+ int err;
+
+ hfs_dbg("name %s, ino %ld\n",
+ name ? name : NULL, inode->i_ino);
+
+ if (name) {
+ err = hfsplus_attr_build_key(sb, fd->search_key,
+ inode->i_ino, name);
+ if (err)
+ return err;
+ } else
+ return -EINVAL;
+
+ /* Mac OS X supports only inline data attributes. */
+ entry_size = hfsplus_attr_build_record(entry_ptr,
+ HFSPLUS_ATTR_INLINE_DATA,
+ inode->i_ino,
+ value, size);
+ if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
+ if (size > HFSPLUS_MAX_INLINE_DATA_SIZE)
+ err = -E2BIG;
+ else
+ err = -EINVAL;
+ hfs_dbg("unable to store value: err %d\n", err);
+ return err;
+ }
+
+ err = hfs_brec_find(fd, hfs_find_rec_by_key);
+ if (err != -ENOENT) {
+ if (!err)
+ err = -EEXIST;
+ return err;
+ }
+
+ err = hfs_brec_insert(fd, entry_ptr, entry_size);
+ if (err) {
+ hfs_dbg("unable to store value: err %d\n", err);
+ return err;
+ }
+
+ hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
+
+ return 0;
+}
+
int hfsplus_create_attr(struct inode *inode,
- const char *name,
- const void *value, size_t size)
+ const char *name,
+ const void *value, size_t size)
{
struct super_block *sb = inode->i_sb;
struct hfs_find_data fd;
hfsplus_attr_entry *entry_ptr;
- int entry_size;
int err;
hfs_dbg("name %s, ino %ld\n",
@@ -222,39 +276,11 @@ int hfsplus_create_attr(struct inode *inode,
if (err)
goto failed_create_attr;
- if (name) {
- err = hfsplus_attr_build_key(sb, fd.search_key,
- inode->i_ino, name);
- if (err)
- goto failed_create_attr;
- } else {
- err = -EINVAL;
- goto failed_create_attr;
- }
-
- /* Mac OS X supports only inline data attributes. */
- entry_size = hfsplus_attr_build_record(entry_ptr,
- HFSPLUS_ATTR_INLINE_DATA,
- inode->i_ino,
- value, size);
- if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
- err = -EINVAL;
- goto failed_create_attr;
- }
-
- err = hfs_brec_find(&fd, hfs_find_rec_by_key);
- if (err != -ENOENT) {
- if (!err)
- err = -EEXIST;
- goto failed_create_attr;
- }
-
- err = hfs_brec_insert(&fd, entry_ptr, entry_size);
+ err = hfsplus_create_attr_nolock(inode, name, value, size,
+ &fd, entry_ptr);
if (err)
goto failed_create_attr;
- hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
-
failed_create_attr:
hfs_find_exit(&fd);
@@ -304,6 +330,37 @@ static int __hfsplus_delete_attr(struct inode *inode, u32 cnid,
return err;
}
+static
+int hfsplus_delete_attr_nolock(struct inode *inode, const char *name,
+ struct hfs_find_data *fd)
+{
+ struct super_block *sb = inode->i_sb;
+ int err;
+
+ hfs_dbg("name %s, ino %ld\n",
+ name ? name : NULL, inode->i_ino);
+
+ if (name) {
+ err = hfsplus_attr_build_key(sb, fd->search_key,
+ inode->i_ino, name);
+ if (err)
+ return err;
+ } else {
+ pr_err("invalid extended attribute name\n");
+ return -EINVAL;
+ }
+
+ err = hfs_brec_find(fd, hfs_find_rec_by_key);
+ if (err)
+ return err;
+
+ err = __hfsplus_delete_attr(inode, inode->i_ino, fd);
+ if (err)
+ return err;
+
+ return 0;
+}
+
int hfsplus_delete_attr(struct inode *inode, const char *name)
{
int err = 0;
@@ -327,22 +384,7 @@ int hfsplus_delete_attr(struct inode *inode, const char *name)
if (err)
goto out;
- if (name) {
- err = hfsplus_attr_build_key(sb, fd.search_key,
- inode->i_ino, name);
- if (err)
- goto out;
- } else {
- pr_err("invalid extended attribute name\n");
- err = -EINVAL;
- goto out;
- }
-
- err = hfs_brec_find(&fd, hfs_find_rec_by_key);
- if (err)
- goto out;
-
- err = __hfsplus_delete_attr(inode, inode->i_ino, &fd);
+ err = hfsplus_delete_attr_nolock(inode, name, &fd);
if (err)
goto out;
@@ -384,3 +426,50 @@ end_delete_all:
hfs_find_exit(&fd);
return err;
}
+
+int hfsplus_replace_attr(struct inode *inode,
+ const char *name,
+ const void *value, size_t size)
+{
+ struct super_block *sb = inode->i_sb;
+ struct hfs_find_data fd;
+ hfsplus_attr_entry *entry_ptr;
+ int err = 0;
+
+ hfs_dbg("name %s, ino %ld\n",
+ name ? name : NULL, inode->i_ino);
+
+ if (!HFSPLUS_SB(sb)->attr_tree) {
+ pr_err("attributes file doesn't exist\n");
+ return -EINVAL;
+ }
+
+ entry_ptr = hfsplus_alloc_attr_entry();
+ if (!entry_ptr)
+ return -ENOMEM;
+
+ err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
+ if (err)
+ goto failed_init_replace_attr;
+
+ /* Fail early and avoid ENOSPC during the btree operation */
+ err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1);
+ if (err)
+ goto failed_replace_attr;
+
+ err = hfsplus_delete_attr_nolock(inode, name, &fd);
+ if (err)
+ goto failed_replace_attr;
+
+ err = hfsplus_create_attr_nolock(inode, name, value, size,
+ &fd, entry_ptr);
+ if (err)
+ goto failed_replace_attr;
+
+failed_replace_attr:
+ hfs_find_exit(&fd);
+
+failed_init_replace_attr:
+ hfsplus_destroy_attr_entry(entry_ptr);
+ return err;
+}
fs/hfsplus/bnode.c
View filediff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
index 191661a..250a226 100644
This diff is incomplete. Download the complete patch.
fs/hfsplus/dir.c
View fileThis diff is incomplete. Download the complete patch.
fs/hfsplus/hfsplus_fs.h
View fileThis diff is incomplete. Download the complete patch.
fs/hfsplus/inode.c
View fileThis diff is incomplete. Download the complete patch.
fs/hfsplus/super.c
View fileThis diff is incomplete. Download the complete patch.
fs/hfsplus/xattr.c
View fileThis diff is incomplete. Download the complete patch.