diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 13d58c51fc46..da8e5342c91c 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " @@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) u32 bytes_read; u32 bytes_to_read; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; @@ -344,7 +346,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) struct hfs_bnode *node, *node2; struct address_space *mapping; struct page *page; - int size, block, i, hash; + int block, i, hash; loff_t off; if (cnid >= tree->node_count) { @@ -352,9 +354,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) return NULL; } - size = sizeof(struct hfs_bnode) + tree->pages_per_bnode * - sizeof(struct page *); - node = kzalloc(size, GFP_KERNEL); + node = kzalloc_flex(*node, page, tree->pages_per_bnode, GFP_KERNEL); if (!node) return NULL; node->tree = tree; diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 7f5339ee57c1..1bfa36d71e24 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -340,7 +340,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str) { struct super_block *sb; struct hfs_find_data fd; - struct hfs_readdir_data *rd; int res, type; hfs_dbg("name %s, cnid %u\n", str ? str->name : NULL, cnid); @@ -366,14 +365,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str) } } - /* we only need to take spinlock for exclusion with ->release() */ - spin_lock(&HFS_I(dir)->open_dir_lock); - list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) { - if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) - rd->file->f_pos--; - } - spin_unlock(&HFS_I(dir)->open_dir_lock); - res = hfs_brec_remove(&fd); if (res) goto out; diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index c4c6e1623f55..e13450bb933e 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -97,7 +97,15 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx) } if (ctx->pos >= inode->i_size) goto out; - err = hfs_brec_goto(&fd, ctx->pos - 1); + rd = file->private_data; + if (rd && rd->pos == ctx->pos) { + memcpy(fd.search_key, &rd->key, sizeof(struct hfs_cat_key)); + err = hfs_brec_find(&fd); + if (err == -ENOENT) + err = hfs_brec_goto(&fd, 1); + } else { + err = hfs_brec_goto(&fd, ctx->pos - 1); + } if (err) goto out; @@ -146,7 +154,6 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx) if (err) goto out; } - rd = file->private_data; if (!rd) { rd = kmalloc_obj(struct hfs_readdir_data); if (!rd) { @@ -154,15 +161,8 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx) goto out; } file->private_data = rd; - rd->file = file; - spin_lock(&HFS_I(inode)->open_dir_lock); - list_add(&rd->list, &HFS_I(inode)->open_dir_list); - spin_unlock(&HFS_I(inode)->open_dir_lock); } - /* - * Can be done after the list insertion; exclusion with - * hfs_delete_cat() is provided by directory lock. - */ + rd->pos = ctx->pos; memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key)); out: hfs_find_exit(&fd); @@ -171,13 +171,7 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx) static int hfs_dir_release(struct inode *inode, struct file *file) { - struct hfs_readdir_data *rd = file->private_data; - if (rd) { - spin_lock(&HFS_I(inode)->open_dir_lock); - list_del(&rd->list); - spin_unlock(&HFS_I(inode)->open_dir_lock); - kfree(rd); - } + kfree(file->private_data); return 0; } @@ -306,10 +300,15 @@ static int hfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, res = hfs_cat_move(d_inode(old_dentry)->i_ino, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); - if (!res) + if (!res) { + struct inode *inode = d_inode(old_dentry); + hfs_cat_build_key(old_dir->i_sb, - (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key, + (btree_key *)&HFS_I(inode)->cat_key, new_dir->i_ino, &new_dentry->d_name); + inode_set_ctime_current(inode); + mark_inode_dirty(inode); + } return res; } diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 3f2293ff6fdd..2e958c46bc0a 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -14,8 +14,7 @@ /*======== Data structures kept in memory ========*/ struct hfs_readdir_data { - struct list_head list; - struct file *file; + loff_t pos; struct hfs_cat_key key; }; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 1b23448c9a48..f3624514fcb0 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -36,8 +36,6 @@ struct hfs_inode_info { struct hfs_cat_key cat_key; - struct list_head open_dir_list; - spinlock_t open_dir_lock; struct inode *rsrc_inode; struct mutex extents_lock; diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index f41cc261684d..ac4a9055c5c0 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -196,8 +196,6 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t 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); if (next_id > U32_MAX) { @@ -205,7 +203,7 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t pr_err("cannot create new inode: next CNID exceeds limit\n"); goto out_discard; } - inode->i_ino = (u32)next_id; + inode->i_ino = (u32)next_id - 1; inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); @@ -349,12 +347,11 @@ static int hfs_read_inode(struct inode *inode, void *data) struct hfs_iget_data *idata = data; struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); hfs_cat_rec *rec; + struct timespec64 mtime; HFS_I(inode)->flags = 0; HFS_I(inode)->rsrc_inode = NULL; 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); /* Initialize the inode */ inode->i_uid = hsb->s_uid; @@ -384,8 +381,10 @@ static int hfs_read_inode(struct inode *inode, void *data) inode->i_mode |= S_IWUGO; inode->i_mode &= ~hsb->s_file_umask; inode->i_mode |= S_IFREG; - inode_set_mtime_to_ts(inode, - inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->file.MdDat)))); + mtime = hfs_m_to_utime(rec->file.MdDat); + inode_set_ctime_to_ts(inode, mtime); + inode_set_atime_to_ts(inode, mtime); + inode_set_mtime_to_ts(inode, mtime); inode->i_op = &hfs_file_inode_operations; inode->i_fop = &hfs_file_operations; inode->i_mapping->a_ops = &hfs_aops; @@ -395,8 +394,10 @@ static int hfs_read_inode(struct inode *inode, void *data) inode->i_size = be16_to_cpu(rec->dir.Val) + 2; HFS_I(inode)->fs_blocks = 0; inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask); - inode_set_mtime_to_ts(inode, - inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->dir.MdDat)))); + mtime = hfs_m_to_utime(rec->dir.MdDat); + inode_set_ctime_to_ts(inode, mtime); + inode_set_atime_to_ts(inode, mtime); + inode_set_mtime_to_ts(inode, mtime); inode->i_op = &hfs_dir_inode_operations; inode->i_fop = &hfs_dir_operations; break; @@ -666,7 +667,7 @@ int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, truncate_setsize(inode, attr->ia_size); hfs_file_truncate(inode); - simple_inode_init_ts(inode); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); } setattr_copy(&nop_mnt_idmap, inode, attr); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index a4f2a2bfa6d3..a466c401f6bb 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -338,7 +338,7 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc) sbi->sb = sb; sb->s_op = &hfs_super_operations; sb->s_xattr = hfs_xattr_handlers; - sb->s_flags |= SB_NODIRATIME; + sb->s_flags |= SB_NOATIME | SB_NODIRATIME; mutex_init(&sbi->bitmap_lock); res = hfs_mdb_get(sb); diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index f8b5a8ae58ff..d088fb7eb0df 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -25,6 +25,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) struct page **pagep; u32 l; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; @@ -455,7 +457,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) struct hfs_bnode *node, *node2; struct address_space *mapping; struct page *page; - int size, block, i, hash; + int block, i, hash; loff_t off; if (cnid >= tree->node_count) { @@ -464,9 +466,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) return NULL; } - size = sizeof(struct hfs_bnode) + tree->pages_per_bnode * - sizeof(struct page *); - node = kzalloc(size, GFP_KERNEL); + node = kzalloc_flex(*node, page, tree->pages_per_bnode); if (!node) return NULL; node->tree = tree; diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 761c74ccd653..394542a47e60 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -365,6 +365,8 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) } size = tree->node_size; + if (size < HFSPLUS_NODE_MINSZ || size > HFSPLUS_NODE_MXSZ) + goto fail_page; if (!is_power_of_2(size)) goto fail_page; if (!tree->node_count) diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 9bc8a6c48fd3..776ce36cf076 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -333,7 +333,6 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str) struct super_block *sb = dir->i_sb; struct hfs_find_data fd; struct hfsplus_fork_raw fork; - struct list_head *pos; int err, off; u16 type; @@ -392,16 +391,6 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str) hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); } - /* we only need to take spinlock for exclusion with ->release() */ - spin_lock(&HFSPLUS_I(dir)->open_dir_lock); - list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) { - struct hfsplus_readdir_data *rd = - list_entry(pos, struct hfsplus_readdir_data, list); - if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) - rd->file->f_pos--; - } - spin_unlock(&HFSPLUS_I(dir)->open_dir_lock); - err = hfs_brec_remove(&fd); if (err) goto out; diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 47194370c2c5..8bf6c7cdd9a8 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -185,7 +185,15 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) } if (ctx->pos >= inode->i_size) goto out; - err = hfs_brec_goto(&fd, ctx->pos - 1); + rd = file->private_data; + if (rd && rd->pos == ctx->pos) { + memcpy(fd.search_key, &rd->key, sizeof(struct hfsplus_cat_key)); + err = hfs_brec_find(&fd, hfs_find_rec_by_key); + if (err == -ENOENT) + err = hfs_brec_goto(&fd, 1); + } else { + err = hfs_brec_goto(&fd, ctx->pos - 1); + } if (err) goto out; for (;;) { @@ -261,7 +269,6 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) if (err) goto out; } - rd = file->private_data; if (!rd) { rd = kmalloc_obj(struct hfsplus_readdir_data); if (!rd) { @@ -269,15 +276,8 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) goto out; } file->private_data = rd; - rd->file = file; - spin_lock(&HFSPLUS_I(inode)->open_dir_lock); - list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); - spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); } - /* - * Can be done after the list insertion; exclusion with - * hfsplus_delete_cat() is provided by directory lock. - */ + rd->pos = ctx->pos; memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); out: kfree(strbuf); @@ -287,13 +287,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) static int hfsplus_dir_release(struct inode *inode, struct file *file) { - struct hfsplus_readdir_data *rd = file->private_data; - if (rd) { - spin_lock(&HFSPLUS_I(inode)->open_dir_lock); - list_del(&rd->list); - spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); - kfree(rd); - } + kfree(file->private_data); return 0; } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 3545b8dbf11c..ec04b82ad927 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -214,8 +214,6 @@ struct hfsplus_inode_info { sector_t fs_blocks; u8 userflags; /* BSD user file flags */ u32 subfolders; /* Subfolder count (HFSX only) */ - struct list_head open_dir_list; - spinlock_t open_dir_lock; loff_t phys_size; struct inode vfs_inode; @@ -272,8 +270,7 @@ struct hfs_find_data { }; struct hfsplus_readdir_data { - struct list_head list; - struct file *file; + loff_t pos; struct hfsplus_cat_key key; }; @@ -600,7 +597,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 5565c14b4bf6..1c57c873f05d 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -125,9 +125,44 @@ static ssize_t hfsplus_direct_IO(struct kiocb *iocb, struct iov_iter *iter) struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; + loff_t isize; size_t count = iov_iter_count(iter); + loff_t end = iocb->ki_pos + count; ssize_t ret; + /* + * The hfsplus_get_block() only allows creating the next sequential block. + * For direct writes beyond EOF, expand the file first. + */ + if (iov_iter_rw(iter) == WRITE && iocb->ki_pos > i_size_read(inode)) { + loff_t start_off, end_off; + loff_t start_page, end_page; + + isize = i_size_read(inode); + + /* + * Wait for any in-flight DIO on this inode to finish before + * calling generic_cont_expand_simple(). + */ + inode_dio_wait(inode); + + ret = generic_cont_expand_simple(inode, iocb->ki_pos); + if (ret) + return ret; + + start_off = isize; + end_off = (end > 0) ? end - 1 : end; + + ret = filemap_write_and_wait_range(mapping, start_off, end_off); + if (ret) + return ret; + + start_page = start_off >> PAGE_SHIFT; + end_page = end_off >> PAGE_SHIFT; + + invalidate_inode_pages2_range(mapping, start_page, end_page); + } + ret = blockdev_direct_IO(iocb, inode, iter, hfsplus_get_block); /* @@ -135,8 +170,7 @@ static ssize_t hfsplus_direct_IO(struct kiocb *iocb, struct iov_iter *iter) * blocks outside i_size. Trim these off again. */ if (unlikely(iov_iter_rw(iter) == WRITE && ret < 0)) { - loff_t isize = i_size_read(inode); - loff_t end = iocb->ki_pos + count; + isize = i_size_read(inode); if (end > isize) hfsplus_write_failed(mapping, end); @@ -449,8 +483,6 @@ struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir, simple_inode_init_ts(inode); hip = HFSPLUS_I(inode); - INIT_LIST_HEAD(&hip->open_dir_list); - spin_lock_init(&hip->open_dir_lock); mutex_init(&hip->extents_lock); atomic_set(&hip->opencnt, 0); hip->extent_state = 0; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 40a0feda716b..5777e31de45a 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -91,8 +91,6 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) HFSPLUS_I(inode)->fs_blocks = 0; HFSPLUS_I(inode)->userflags = 0; HFSPLUS_I(inode)->subfolders = 0; - INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); - spin_lock_init(&HFSPLUS_I(inode)->open_dir_lock); HFSPLUS_I(inode)->phys_size = 0; if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index 452a1f9becb2..21a1c196c71f 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -317,7 +317,6 @@ static int hfsplus_create_attributes_file(struct super_block *sb) next_node++; } - hfsplus_mark_inode_dirty(HFSPLUS_ATTR_TREE_I(sb), HFSPLUS_I_ATTR_DIRTY); hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h index 07dfc39630ab..45fb4c9ff9f5 100644 --- a/include/linux/hfs_common.h +++ b/include/linux/hfs_common.h @@ -513,6 +513,7 @@ struct hfs_btree_header_rec { /* HFS+ BTree misc info */ #define HFSPLUS_TREE_HEAD 0 #define HFSPLUS_NODE_MXSZ 32768 +#define HFSPLUS_NODE_MINSZ 512 #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 #define HFSPLUS_BTREE_HDR_MAP_REC_INDEX 2 /* Map (bitmap) record in Header node */