diff options
Diffstat (limited to 'fs/ext2')
| -rw-r--r-- | fs/ext2/Kconfig | 16 | ||||
| -rw-r--r-- | fs/ext2/Makefile | 5 | ||||
| -rw-r--r-- | fs/ext2/acl.c | 6 | ||||
| -rw-r--r-- | fs/ext2/acl.h | 2 | ||||
| -rw-r--r-- | fs/ext2/balloc.c | 151 | ||||
| -rw-r--r-- | fs/ext2/dir.c | 318 | ||||
| -rw-r--r-- | fs/ext2/ext2.h | 69 | ||||
| -rw-r--r-- | fs/ext2/file.c | 149 | ||||
| -rw-r--r-- | fs/ext2/ialloc.c | 7 | ||||
| -rw-r--r-- | fs/ext2/inode.c | 152 | ||||
| -rw-r--r-- | fs/ext2/ioctl.c | 14 | ||||
| -rw-r--r-- | fs/ext2/namei.c | 102 | ||||
| -rw-r--r-- | fs/ext2/super.c | 634 | ||||
| -rw-r--r-- | fs/ext2/trace.c | 6 | ||||
| -rw-r--r-- | fs/ext2/trace.h | 94 | ||||
| -rw-r--r-- | fs/ext2/xattr.c | 41 | ||||
| -rw-r--r-- | fs/ext2/xattr.h | 2 | ||||
| -rw-r--r-- | fs/ext2/xattr_security.c | 2 | ||||
| -rw-r--r-- | fs/ext2/xattr_trusted.c | 2 | ||||
| -rw-r--r-- | fs/ext2/xattr_user.c | 2 |
20 files changed, 1041 insertions, 733 deletions
diff --git a/fs/ext2/Kconfig b/fs/ext2/Kconfig index 1248ff4ef562..d5bce83ad905 100644 --- a/fs/ext2/Kconfig +++ b/fs/ext2/Kconfig @@ -1,14 +1,22 @@ # SPDX-License-Identifier: GPL-2.0-only config EXT2_FS - tristate "Second extended fs support" + tristate "Second extended fs support (DEPRECATED)" + select BUFFER_HEAD select FS_IOMAP help Ext2 is a standard Linux file system for hard disks. - To compile this file system support as a module, choose M here: the - module will be called ext2. + This filesystem driver is deprecated because it does not properly + support inode time stamps beyond 03:14:07 UTC on 19 January 2038. - If unsure, say Y. + Ext2 users are advised to use ext4 driver to access their filesystem. + The driver is fully compatible, supports filesystems without journal + or extents, and also supports larger time stamps if the filesystem + is created with at least 256 byte inodes. + + This code is kept as a simple reference for filesystem developers. + + If unsure, say N. config EXT2_FS_XATTR bool "Ext2 extended attributes" diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index 311479d864a7..8860948ef9ca 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -6,7 +6,10 @@ obj-$(CONFIG_EXT2_FS) += ext2.o ext2-y := balloc.o dir.o file.o ialloc.o inode.o \ - ioctl.o namei.o super.o symlink.o + ioctl.o namei.o super.o symlink.o trace.o + +# For tracepoints to include our trace.h from tracepoint infrastructure +CFLAGS_trace.o := -I$(src) ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 440d5f1e9d47..7e54c31589c7 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -219,7 +219,7 @@ __ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type) * inode->i_mutex: down */ int -ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, +ext2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, struct posix_acl *acl, int type) { int error; @@ -228,7 +228,7 @@ ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, umode_t mode = inode->i_mode; if (type == ACL_TYPE_ACCESS && acl) { - error = posix_acl_update_mode(&init_user_ns, inode, &mode, + error = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode, &acl); if (error) return error; @@ -237,7 +237,7 @@ ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, error = __ext2_set_acl(inode, acl, type); if (!error && update_mode) { inode->i_mode = mode; - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); mark_inode_dirty(inode); } return error; diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h index 3841becb94ff..4a8443a2b8ec 100644 --- a/fs/ext2/acl.h +++ b/fs/ext2/acl.h @@ -56,7 +56,7 @@ static inline int ext2_acl_count(size_t size) /* acl.c */ extern struct posix_acl *ext2_get_acl(struct inode *inode, int type, bool rcu); -extern int ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, +extern int ext2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, struct posix_acl *acl, int type); extern int ext2_init_acl (struct inode *, struct inode *); diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index eca60b747c6b..b8cfab8f98b9 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -36,8 +36,6 @@ */ -#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) - struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, unsigned int block_group, struct buffer_head ** bh) @@ -79,26 +77,33 @@ static int ext2_valid_block_bitmap(struct super_block *sb, ext2_grpblk_t next_zero_bit; ext2_fsblk_t bitmap_blk; ext2_fsblk_t group_first_block; + ext2_grpblk_t max_bit; group_first_block = ext2_group_first_block_no(sb, block_group); + max_bit = ext2_group_last_block_no(sb, block_group) - group_first_block; /* check whether block bitmap block number is set */ bitmap_blk = le32_to_cpu(desc->bg_block_bitmap); offset = bitmap_blk - group_first_block; - if (!ext2_test_bit(offset, bh->b_data)) + if (offset < 0 || offset > max_bit || + !ext2_test_bit(offset, bh->b_data)) /* bad block bitmap */ goto err_out; /* check whether the inode bitmap block number is set */ bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap); offset = bitmap_blk - group_first_block; - if (!ext2_test_bit(offset, bh->b_data)) + if (offset < 0 || offset > max_bit || + !ext2_test_bit(offset, bh->b_data)) /* bad block bitmap */ goto err_out; /* check whether the inode table block number is set */ bitmap_blk = le32_to_cpu(desc->bg_inode_table); offset = bitmap_blk - group_first_block; + if (offset < 0 || offset > max_bit || + offset + EXT2_SB(sb)->s_itb_per_group - 1 > max_bit) + goto err_out; next_zero_bit = ext2_find_next_zero_bit(bh->b_data, offset + EXT2_SB(sb)->s_itb_per_group, offset); @@ -414,7 +419,7 @@ void ext2_init_block_alloc_info(struct inode *inode) struct ext2_block_alloc_info *block_i; struct super_block *sb = inode->i_sb; - block_i = kmalloc(sizeof(*block_i), GFP_NOFS); + block_i = kmalloc(sizeof(*block_i), GFP_KERNEL); if (block_i) { struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node; @@ -474,8 +479,8 @@ void ext2_discard_reservation(struct inode *inode) * @block: start physical block to free * @count: number of blocks to free */ -void ext2_free_blocks (struct inode * inode, unsigned long block, - unsigned long count) +void ext2_free_blocks(struct inode * inode, ext2_fsblk_t block, + unsigned long count) { struct buffer_head *bitmap_bh = NULL; struct buffer_head * bh2; @@ -718,36 +723,34 @@ fail_access: } /** - * find_next_reservable_window(): - * find a reservable space within the given range. - * It does not allocate the reservation window for now: - * alloc_new_reservation() will do the work later. - * - * @search_head: the head of the searching list; - * This is not necessarily the list head of the whole filesystem - * - * We have both head and start_block to assist the search - * for the reservable space. The list starts from head, - * but we will shift to the place where start_block is, - * then start from there, when looking for a reservable space. + * find_next_reservable_window - Find a reservable space within the given range. + * @search_head: The list to search. + * @my_rsv: The reservation we're currently using. + * @sb: The super block. + * @start_block: The first block we consider to start the real search from + * @last_block: The maximum block number that our goal reservable space + * could start from. * - * @sb: the super block. + * It does not allocate the reservation window: alloc_new_reservation() + * will do the work later. * - * @start_block: the first block we consider to start the real search from + * We search the given range, rather than the whole reservation double + * linked list, (start_block, last_block) to find a free region that is + * of my size and has not been reserved. * - * @last_block: - * the maximum block number that our goal reservable space - * could start from. This is normally the last block in this - * group. The search will end when we found the start of next - * possible reservable space is out of this boundary. - * This could handle the cross boundary reservation window - * request. + * @search_head is not necessarily the list head of the whole filesystem. + * We have both head and @start_block to assist the search for the + * reservable space. The list starts from head, but we will shift to + * the place where start_block is, then start from there, when looking + * for a reservable space. * - * basically we search from the given range, rather than the whole - * reservation double linked list, (start_block, last_block) - * to find a free region that is of my size and has not - * been reserved. + * @last_block is normally the last block in this group. The search will end + * when we found the start of next possible reservable space is out + * of this boundary. This could handle the cross boundary reservation + * window request. * + * Return: -1 if we could not find a range of sufficient size. If we could, + * return 0 and fill in @my_rsv with the range information. */ static int find_next_reservable_window( struct ext2_reserve_window_node *search_head, @@ -835,41 +838,34 @@ static int find_next_reservable_window( } /** - * alloc_new_reservation()--allocate a new reservation window + * alloc_new_reservation - Allocate a new reservation window. + * @my_rsv: The reservation we're currently using. + * @grp_goal: The goal block relative to the start of the group. + * @sb: The super block. + * @group: The group we are trying to allocate in. + * @bitmap_bh: The block group block bitmap. * - * To make a new reservation, we search part of the filesystem - * reservation list (the list that inside the group). We try to - * allocate a new reservation window near the allocation goal, - * or the beginning of the group, if there is no goal. + * To make a new reservation, we search part of the filesystem reservation + * list (the list inside the group). We try to allocate a new + * reservation window near @grp_goal, or the beginning of the + * group, if @grp_goal is negative. * - * We first find a reservable space after the goal, then from - * there, we check the bitmap for the first free block after - * it. If there is no free block until the end of group, then the - * whole group is full, we failed. Otherwise, check if the free - * block is inside the expected reservable space, if so, we - * succeed. - * If the first free block is outside the reservable space, then - * start from the first free block, we search for next available - * space, and go on. + * We first find a reservable space after the goal, then from there, + * we check the bitmap for the first free block after it. If there is + * no free block until the end of group, then the whole group is full, + * we failed. Otherwise, check if the free block is inside the expected + * reservable space, if so, we succeed. * - * on succeed, a new reservation will be found and inserted into the list - * It contains at least one free block, and it does not overlap with other - * reservation windows. + * If the first free block is outside the reservable space, then start + * from the first free block, we search for next available space, and + * go on. * - * failed: we failed to find a reservation window in this group - * - * @my_rsv: the reservation - * - * @grp_goal: The goal (group-relative). It is where the search for a - * free reservable space should start from. - * if we have a goal(goal >0 ), then start from there, - * no goal(goal = -1), we start from the first block - * of the group. - * - * @sb: the super block - * @group: the group we are trying to allocate in - * @bitmap_bh: the block group block bitmap + * on succeed, a new reservation will be found and inserted into the + * list. It contains at least one free block, and it does not overlap + * with other reservation windows. * + * Return: 0 on success, -1 if we failed to find a reservation window + * in this group */ static int alloc_new_reservation(struct ext2_reserve_window_node *my_rsv, ext2_grpblk_t grp_goal, struct super_block *sb, @@ -1133,8 +1129,13 @@ ext2_try_to_allocate_with_rsv(struct super_block *sb, unsigned int group, if ((my_rsv->rsv_start > group_last_block) || (my_rsv->rsv_end < group_first_block)) { + ext2_error(sb, __func__, + "Reservation out of group %u range goal %d fsb[%lu,%lu] rsv[%lu, %lu]", + group, grp_goal, group_first_block, + group_last_block, my_rsv->rsv_start, + my_rsv->rsv_end); rsv_window_dump(&EXT2_SB(sb)->s_rsv_window_root, 1); - BUG(); + return -1; } ret = ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal, &num, &my_rsv->rsv_window); @@ -1195,6 +1196,7 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, * @goal: given target block(filesystem wide) * @count: target number of blocks to allocate * @errp: error code + * @flags: allocate flags * * ext2_new_blocks uses a goal block to assist allocation. If the goal is * free, or there is a free block within 32 blocks of the goal, that block @@ -1204,7 +1206,7 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, * This function also updates quota and i_blocks field. */ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, - unsigned long *count, int *errp) + unsigned long *count, int *errp, unsigned int flags) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; @@ -1243,15 +1245,15 @@ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, es = EXT2_SB(sb)->s_es; ext2_debug("goal=%lu.\n", goal); /* - * Allocate a block from reservation only when - * filesystem is mounted with reservation(default,-o reservation), and - * it's a regular file, and - * the desired window size is greater than 0 (One could use ioctl - * command EXT2_IOC_SETRSVSZ to set the window size to 0 to turn off - * reservation on that particular file) + * Allocate a block from reservation only when the filesystem is + * mounted with reservation(default,-o reservation), and it's a regular + * file, and the desired window size is greater than 0 (One could use + * ioctl command EXT2_IOC_SETRSVSZ to set the window size to 0 to turn + * off reservation on that particular file). Also do not use the + * reservation window if the caller asked us not to do it. */ block_i = EXT2_I(inode)->i_block_alloc_info; - if (block_i) { + if (!(flags & EXT2_ALLOC_NORESERVE) && block_i) { windowsz = block_i->rsv_window_node.rsv_goal_size; if (windowsz > 0) my_rsv = &block_i->rsv_window_node; @@ -1431,13 +1433,6 @@ out: return 0; } -ext2_fsblk_t ext2_new_block(struct inode *inode, unsigned long goal, int *errp) -{ - unsigned long count = 1; - - return ext2_new_blocks(inode, goal, &count, errp); -} - #ifdef EXT2FS_DEBUG unsigned long ext2_count_free(struct buffer_head *map, unsigned int numchars) diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index e5cbc27ba459..b07b3b369710 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -81,34 +81,34 @@ ext2_last_byte(struct inode *inode, unsigned long page_nr) return last_byte; } -static void ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) +static void ext2_commit_chunk(struct folio *folio, loff_t pos, unsigned len) { - struct address_space *mapping = page->mapping; + struct address_space *mapping = folio->mapping; struct inode *dir = mapping->host; inode_inc_iversion(dir); - block_write_end(NULL, mapping, pos, len, len, page, NULL); + block_write_end(pos, len, len, folio); if (pos+len > dir->i_size) { i_size_write(dir, pos+len); mark_inode_dirty(dir); } - unlock_page(page); + folio_unlock(folio); } -static bool ext2_check_page(struct page *page, int quiet, char *kaddr) +static bool ext2_check_folio(struct folio *folio, int quiet, char *kaddr) { - struct inode *dir = page->mapping->host; + struct inode *dir = folio->mapping->host; struct super_block *sb = dir->i_sb; unsigned chunk_size = ext2_chunk_size(dir); u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count); unsigned offs, rec_len; - unsigned limit = PAGE_SIZE; + unsigned limit = folio_size(folio); ext2_dirent *p; char *error; - if ((dir->i_size >> PAGE_SHIFT) == page->index) { - limit = dir->i_size & ~PAGE_MASK; + if (dir->i_size < folio_pos(folio) + limit) { + limit = offset_in_folio(folio, dir->i_size); if (limit & (chunk_size - 1)) goto Ebadsize; if (!limit) @@ -132,7 +132,7 @@ static bool ext2_check_page(struct page *page, int quiet, char *kaddr) if (offs != limit) goto Eend; out: - SetPageChecked(page); + folio_set_checked(folio); return true; /* Too bad, we had an error */ @@ -160,49 +160,51 @@ Einumber: bad_entry: if (!quiet) ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - " - "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", - dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs, + "offset=%llu, inode=%lu, rec_len=%d, name_len=%d", + dir->i_ino, error, folio_pos(folio) + offs, (unsigned long) le32_to_cpu(p->inode), rec_len, p->name_len); goto fail; Eend: if (!quiet) { p = (ext2_dirent *)(kaddr + offs); - ext2_error(sb, "ext2_check_page", + ext2_error(sb, "ext2_check_folio", "entry in directory #%lu spans the page boundary" - "offset=%lu, inode=%lu", - dir->i_ino, (page->index<<PAGE_SHIFT)+offs, + "offset=%llu, inode=%lu", + dir->i_ino, folio_pos(folio) + offs, (unsigned long) le32_to_cpu(p->inode)); } fail: - SetPageError(page); return false; } /* - * Calls to ext2_get_page()/ext2_put_page() must be nested according to the - * rules documented in kmap_local_page()/kunmap_local(). + * Calls to ext2_get_folio()/folio_release_kmap() must be nested according + * to the rules documented in kmap_local_folio()/kunmap_local(). * - * NOTE: ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() - * and should be treated as a call to ext2_get_page() for nesting purposes. + * NOTE: ext2_find_entry() and ext2_dotdot() act as a call + * to folio_release_kmap() and should be treated as a call to + * folio_release_kmap() for nesting purposes. */ -static struct page * ext2_get_page(struct inode *dir, unsigned long n, - int quiet, void **page_addr) +static void *ext2_get_folio(struct inode *dir, unsigned long n, + int quiet, struct folio **foliop) { struct address_space *mapping = dir->i_mapping; struct folio *folio = read_mapping_folio(mapping, n, NULL); + void *kaddr; if (IS_ERR(folio)) - return &folio->page; - *page_addr = kmap_local_folio(folio, n & (folio_nr_pages(folio) - 1)); + return ERR_CAST(folio); + kaddr = kmap_local_folio(folio, 0); if (unlikely(!folio_test_checked(folio))) { - if (!ext2_check_page(&folio->page, quiet, *page_addr)) + if (!ext2_check_folio(folio, quiet, kaddr)) goto fail; } - return &folio->page; + *foliop = folio; + return kaddr; fail: - ext2_put_page(&folio->page, *page_addr); + folio_release_kmap(folio, kaddr); return ERR_PTR(-EIO); } @@ -240,7 +242,7 @@ ext2_validate_entry(char *base, unsigned offset, unsigned mask) break; p = ext2_next_entry(p); } - return (char *)p - base; + return offset_in_page(p); } static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) @@ -261,7 +263,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx) unsigned long n = pos >> PAGE_SHIFT; unsigned long npages = dir_pages(inode); unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); - bool need_revalidate = !inode_eq_iversion(inode, file->f_version); + bool need_revalidate = !inode_eq_iversion(inode, *(u64 *)file->private_data); bool has_filetype; if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) @@ -271,23 +273,24 @@ ext2_readdir(struct file *file, struct dir_context *ctx) EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE); for ( ; n < npages; n++, offset = 0) { - char *kaddr, *limit; ext2_dirent *de; - struct page *page = ext2_get_page(inode, n, 0, (void **)&kaddr); + struct folio *folio; + char *kaddr = ext2_get_folio(inode, n, 0, &folio); + char *limit; - if (IS_ERR(page)) { + if (IS_ERR(kaddr)) { ext2_error(sb, __func__, "bad page in #%lu", inode->i_ino); ctx->pos += PAGE_SIZE - offset; - return PTR_ERR(page); + return PTR_ERR(kaddr); } if (unlikely(need_revalidate)) { if (offset) { offset = ext2_validate_entry(kaddr, offset, chunk_mask); ctx->pos = (n<<PAGE_SHIFT) + offset; } - file->f_version = inode_query_iversion(inode); + *(u64 *)file->private_data = inode_query_iversion(inode); need_revalidate = false; } de = (ext2_dirent *)(kaddr+offset); @@ -296,7 +299,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx) if (de->rec_len == 0) { ext2_error(sb, __func__, "zero-length directory entry"); - ext2_put_page(page, kaddr); + folio_release_kmap(folio, de); return -EIO; } if (de->inode) { @@ -308,13 +311,13 @@ ext2_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit(ctx, de->name, de->name_len, le32_to_cpu(de->inode), d_type)) { - ext2_put_page(page, kaddr); + folio_release_kmap(folio, de); return 0; } } ctx->pos += ext2_rec_len_from_disk(de->rec_len); } - ext2_put_page(page, kaddr); + folio_release_kmap(folio, kaddr); } return 0; } @@ -327,64 +330,56 @@ ext2_readdir(struct file *file, struct dir_context *ctx) * and the entry itself. Page is returned mapped and unlocked. * Entry is guaranteed to be valid. * - * On Success ext2_put_page() should be called on *res_page. + * On Success folio_release_kmap() should be called on *foliop. * - * NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to - * the rules documented in kmap_local_page()/kunmap_local(). + * NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested + * according to the rules documented in kmap_local_folio()/kunmap_local(). * - * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and - * should be treated as a call to ext2_get_page() for nesting purposes. + * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_folio() + * and should be treated as a call to ext2_get_folio() for nesting + * purposes. */ struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir, - const struct qstr *child, struct page **res_page, - void **res_page_addr) + const struct qstr *child, struct folio **foliop) { const char *name = child->name; int namelen = child->len; unsigned reclen = EXT2_DIR_REC_LEN(namelen); unsigned long start, n; unsigned long npages = dir_pages(dir); - struct page *page = NULL; struct ext2_inode_info *ei = EXT2_I(dir); ext2_dirent * de; - void *page_addr; if (npages == 0) goto out; - /* OFFSET_CACHE */ - *res_page = NULL; - *res_page_addr = NULL; - start = ei->i_dir_start_lookup; if (start >= npages) start = 0; n = start; do { - char *kaddr; - page = ext2_get_page(dir, n, 0, &page_addr); - if (IS_ERR(page)) - return ERR_CAST(page); + char *kaddr = ext2_get_folio(dir, n, 0, foliop); + if (IS_ERR(kaddr)) + return ERR_CAST(kaddr); - kaddr = page_addr; de = (ext2_dirent *) kaddr; kaddr += ext2_last_byte(dir, n) - reclen; while ((char *) de <= kaddr) { if (de->rec_len == 0) { ext2_error(dir->i_sb, __func__, "zero-length directory entry"); - ext2_put_page(page, page_addr); + folio_release_kmap(*foliop, de); goto out; } if (ext2_match(namelen, name, de)) goto found; de = ext2_next_entry(de); } - ext2_put_page(page, page_addr); + folio_release_kmap(*foliop, kaddr); if (++n >= npages) n = 0; - /* next page is past the blocks we've got */ + /* next folio is past the blocks we've got */ if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) { ext2_error(dir->i_sb, __func__, "dir %lu size %lld exceeds block count %llu", @@ -397,8 +392,6 @@ out: return ERR_PTR(-ENOENT); found: - *res_page = page; - *res_page_addr = page_addr; ei->i_dir_start_lookup = n; return de; } @@ -407,50 +400,43 @@ found: * Return the '..' directory entry and the page in which the entry was found * (as a parameter - p). * - * On Success ext2_put_page() should be called on *p. + * On Success folio_release_kmap() should be called on *foliop. * - * NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to - * the rules documented in kmap_local_page()/kunmap_local(). + * NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested + * according to the rules documented in kmap_local_folio()/kunmap_local(). * - * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and - * should be treated as a call to ext2_get_page() for nesting purposes. + * ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_folio() + * and should be treated as a call to ext2_get_folio() for nesting + * purposes. */ -struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p, - void **pa) +struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct folio **foliop) { - void *page_addr; - struct page *page = ext2_get_page(dir, 0, 0, &page_addr); - ext2_dirent *de = NULL; - - if (!IS_ERR(page)) { - de = ext2_next_entry((ext2_dirent *) page_addr); - *p = page; - *pa = page_addr; - } - return de; + ext2_dirent *de = ext2_get_folio(dir, 0, 0, foliop); + + if (!IS_ERR(de)) + return ext2_next_entry(de); + return NULL; } int ext2_inode_by_name(struct inode *dir, const struct qstr *child, ino_t *ino) { struct ext2_dir_entry_2 *de; - struct page *page; - void *page_addr; - - de = ext2_find_entry(dir, child, &page, &page_addr); + struct folio *folio; + + de = ext2_find_entry(dir, child, &folio); if (IS_ERR(de)) return PTR_ERR(de); *ino = le32_to_cpu(de->inode); - ext2_put_page(page, page_addr); + folio_release_kmap(folio, de); return 0; } -static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len) +static int ext2_prepare_chunk(struct folio *folio, loff_t pos, unsigned len) { - return __block_write_begin(page, pos, len, ext2_get_block); + return __block_write_begin(folio, pos, len, ext2_get_block); } - static int ext2_handle_dirsync(struct inode *dir) { int err; @@ -461,26 +447,27 @@ static int ext2_handle_dirsync(struct inode *dir) return err; } -void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, - struct page *page, void *page_addr, struct inode *inode, - int update_times) +int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, + struct folio *folio, struct inode *inode, bool update_times) { - loff_t pos = page_offset(page) + - (char *) de - (char *) page_addr; + loff_t pos = folio_pos(folio) + offset_in_folio(folio, de); unsigned len = ext2_rec_len_from_disk(de->rec_len); int err; - lock_page(page); - err = ext2_prepare_chunk(page, pos, len); - BUG_ON(err); + folio_lock(folio); + err = ext2_prepare_chunk(folio, pos, len); + if (err) { + folio_unlock(folio); + return err; + } de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type(de, inode); - ext2_commit_chunk(page, pos, len); + ext2_commit_chunk(folio, pos, len); if (update_times) - dir->i_mtime = dir->i_ctime = current_time(dir); + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); - ext2_handle_dirsync(dir); + return ext2_handle_dirsync(dir); } /* @@ -494,8 +481,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) unsigned chunk_size = ext2_chunk_size(dir); unsigned reclen = EXT2_DIR_REC_LEN(namelen); unsigned short rec_len, name_len; - struct page *page = NULL; - void *page_addr = NULL; + struct folio *folio = NULL; ext2_dirent * de; unsigned long npages = dir_pages(dir); unsigned long n; @@ -504,22 +490,19 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) /* * We take care of directory expansion in the same loop. - * This code plays outside i_size, so it locks the page + * This code plays outside i_size, so it locks the folio * to protect that region. */ for (n = 0; n <= npages; n++) { - char *kaddr; + char *kaddr = ext2_get_folio(dir, n, 0, &folio); char *dir_end; - page = ext2_get_page(dir, n, 0, &page_addr); - err = PTR_ERR(page); - if (IS_ERR(page)) - goto out; - lock_page(page); - kaddr = page_addr; + if (IS_ERR(kaddr)) + return PTR_ERR(kaddr); + folio_lock(folio); dir_end = kaddr + ext2_last_byte(dir, n); de = (ext2_dirent *)kaddr; - kaddr += PAGE_SIZE - reclen; + kaddr += folio_size(folio) - reclen; while ((char *)de <= kaddr) { if ((char *)de == dir_end) { /* We hit i_size */ @@ -546,16 +529,15 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) goto got_it; de = (ext2_dirent *) ((char *) de + rec_len); } - unlock_page(page); - ext2_put_page(page, page_addr); + folio_unlock(folio); + folio_release_kmap(folio, kaddr); } BUG(); return -EINVAL; got_it: - pos = page_offset(page) + - (char *)de - (char *)page_addr; - err = ext2_prepare_chunk(page, pos, rec_len); + pos = folio_pos(folio) + offset_in_folio(folio, de); + err = ext2_prepare_chunk(folio, pos, rec_len); if (err) goto out_unlock; if (de->inode) { @@ -568,18 +550,17 @@ got_it: memcpy(de->name, name, namelen); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type (de, inode); - ext2_commit_chunk(page, pos, rec_len); - dir->i_mtime = dir->i_ctime = current_time(dir); + ext2_commit_chunk(folio, pos, rec_len); + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); err = ext2_handle_dirsync(dir); /* OFFSET_CACHE */ out_put: - ext2_put_page(page, page_addr); -out: + folio_release_kmap(folio, de); return err; out_unlock: - unlock_page(page); + folio_unlock(folio); goto out_put; } @@ -587,44 +568,47 @@ out_unlock: * ext2_delete_entry deletes a directory entry by merging it with the * previous entry. Page is up-to-date. */ -int ext2_delete_entry (struct ext2_dir_entry_2 *dir, struct page *page, - char *kaddr) +int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct folio *folio) { - struct inode *inode = page->mapping->host; - unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); - unsigned to = ((char *)dir - kaddr) + - ext2_rec_len_from_disk(dir->rec_len); + struct inode *inode = folio->mapping->host; + size_t from, to; + char *kaddr; loff_t pos; - ext2_dirent * pde = NULL; - ext2_dirent * de = (ext2_dirent *) (kaddr + from); + ext2_dirent *de, *pde = NULL; int err; + from = offset_in_folio(folio, dir); + to = from + ext2_rec_len_from_disk(dir->rec_len); + kaddr = (char *)dir - from; + from &= ~(ext2_chunk_size(inode)-1); + de = (ext2_dirent *)(kaddr + from); + while ((char*)de < (char*)dir) { if (de->rec_len == 0) { ext2_error(inode->i_sb, __func__, "zero-length directory entry"); - err = -EIO; - goto out; + return -EIO; } pde = de; de = ext2_next_entry(de); } if (pde) - from = (char *)pde - kaddr; - pos = page_offset(page) + from; - lock_page(page); - err = ext2_prepare_chunk(page, pos, to - from); - BUG_ON(err); + from = offset_in_folio(folio, pde); + pos = folio_pos(folio) + from; + folio_lock(folio); + err = ext2_prepare_chunk(folio, pos, to - from); + if (err) { + folio_unlock(folio); + return err; + } if (pde) pde->rec_len = ext2_rec_len_to_disk(to - from); dir->inode = 0; - ext2_commit_chunk(page, pos, to - from); - inode->i_ctime = inode->i_mtime = current_time(inode); + ext2_commit_chunk(folio, pos, to - from); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(inode); - err = ext2_handle_dirsync(inode); -out: - return err; + return ext2_handle_dirsync(inode); } /* @@ -632,21 +616,21 @@ out: */ int ext2_make_empty(struct inode *inode, struct inode *parent) { - struct page *page = grab_cache_page(inode->i_mapping, 0); + struct folio *folio = filemap_grab_folio(inode->i_mapping, 0); unsigned chunk_size = ext2_chunk_size(inode); struct ext2_dir_entry_2 * de; int err; void *kaddr; - if (!page) - return -ENOMEM; + if (IS_ERR(folio)) + return PTR_ERR(folio); - err = ext2_prepare_chunk(page, 0, chunk_size); + err = ext2_prepare_chunk(folio, 0, chunk_size); if (err) { - unlock_page(page); + folio_unlock(folio); goto fail; } - kaddr = kmap_atomic(page); + kaddr = kmap_local_folio(folio, 0); memset(kaddr, 0, chunk_size); de = (struct ext2_dir_entry_2 *)kaddr; de->name_len = 1; @@ -661,32 +645,30 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) de->inode = cpu_to_le32(parent->i_ino); memcpy (de->name, "..\0", 4); ext2_set_de_type (de, inode); - kunmap_atomic(kaddr); - ext2_commit_chunk(page, 0, chunk_size); + kunmap_local(kaddr); + ext2_commit_chunk(folio, 0, chunk_size); err = ext2_handle_dirsync(inode); fail: - put_page(page); + folio_put(folio); return err; } /* * routine to check that the specified directory is empty (for rmdir) */ -int ext2_empty_dir (struct inode * inode) +int ext2_empty_dir(struct inode *inode) { - void *page_addr = NULL; - struct page *page = NULL; + struct folio *folio; + char *kaddr; unsigned long i, npages = dir_pages(inode); for (i = 0; i < npages; i++) { - char *kaddr; - ext2_dirent * de; - page = ext2_get_page(inode, i, 0, &page_addr); + ext2_dirent *de; - if (IS_ERR(page)) + kaddr = ext2_get_folio(inode, i, 0, &folio); + if (IS_ERR(kaddr)) return 0; - kaddr = page_addr; de = (ext2_dirent *)kaddr; kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1); @@ -712,17 +694,39 @@ int ext2_empty_dir (struct inode * inode) } de = ext2_next_entry(de); } - ext2_put_page(page, page_addr); + folio_release_kmap(folio, kaddr); } return 1; not_empty: - ext2_put_page(page, page_addr); + folio_release_kmap(folio, kaddr); return 0; } +static int ext2_dir_open(struct inode *inode, struct file *file) +{ + file->private_data = kzalloc(sizeof(u64), GFP_KERNEL); + if (!file->private_data) + return -ENOMEM; + return 0; +} + +static int ext2_dir_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static loff_t ext2_dir_llseek(struct file *file, loff_t offset, int whence) +{ + return generic_llseek_cookie(file, offset, whence, + (u64 *)file->private_data); +} + const struct file_operations ext2_dir_operations = { - .llseek = generic_file_llseek, + .open = ext2_dir_open, + .release = ext2_dir_release, + .llseek = ext2_dir_llseek, .read = generic_read_dir, .iterate_shared = ext2_readdir, .unlocked_ioctl = ext2_ioctl, diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 28de11a22e5f..cf97b76e9fd3 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -70,10 +70,7 @@ struct mb_cache; * second extended-fs super-block data in memory */ struct ext2_sb_info { - unsigned long s_frag_size; /* Size of a fragment in bytes */ - unsigned long s_frags_per_block;/* Number of fragments per block */ unsigned long s_inodes_per_block;/* Number of inodes per block */ - unsigned long s_frags_per_group;/* Number of fragments in a group */ unsigned long s_blocks_per_group;/* Number of blocks in a group */ unsigned long s_inodes_per_group;/* Number of inodes in a group */ unsigned long s_itb_per_group; /* Number of inode table blocks per group */ @@ -178,8 +175,9 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb) * Macro-instructions used to manage several block sizes */ #define EXT2_MIN_BLOCK_SIZE 1024 -#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MAX_BLOCK_SIZE 65536 #define EXT2_MIN_BLOCK_LOG_SIZE 10 +#define EXT2_MAX_BLOCK_LOG_SIZE 16 #define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) @@ -188,15 +186,6 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb) #define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) /* - * Macro-instructions used to manage fragments - */ -#define EXT2_MIN_FRAG_SIZE 1024 -#define EXT2_MAX_FRAG_SIZE 4096 -#define EXT2_MIN_FRAG_LOG_SIZE 10 -#define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size) -#define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block) - -/* * Structure of a blocks group descriptor */ struct ext2_group_desc @@ -379,6 +368,7 @@ struct ext2_inode { #define EXT2_MOUNT_ERRORS_CONT 0x000010 /* Continue on errors */ #define EXT2_MOUNT_ERRORS_RO 0x000020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x000040 /* Panic on errors */ +#define EXT2_MOUNT_ERRORS_MASK 0x000070 #define EXT2_MOUNT_MINIX_DF 0x000080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NOBH 0x000100 /* No buffer_heads */ #define EXT2_MOUNT_NO_UID32 0x000200 /* Disable 32-bit UIDs */ @@ -410,6 +400,12 @@ struct ext2_inode { #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE /* + * Allocation flags + */ +#define EXT2_ALLOC_NORESERVE 0x1 /* Do not use reservation + * window for allocation */ + +/* * Structure of the super block */ struct ext2_super_block { @@ -679,7 +675,7 @@ struct ext2_inode_info { struct inode vfs_inode; struct list_head i_orphan; /* unlinked but open inodes */ #ifdef CONFIG_QUOTA - struct dquot *i_dquot[MAXQUOTAS]; + struct dquot __rcu *i_dquot[MAXQUOTAS]; #endif }; @@ -706,13 +702,11 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode) /* balloc.c */ extern int ext2_bg_has_super(struct super_block *sb, int group); extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); -extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *); -extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, - unsigned long *, int *); +extern ext2_fsblk_t ext2_new_blocks(struct inode *, ext2_fsblk_t, + unsigned long *, int *, unsigned int); extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, unsigned int count); -extern void ext2_free_blocks (struct inode *, unsigned long, - unsigned long); +extern void ext2_free_blocks(struct inode *, ext2_fsblk_t, unsigned long); extern unsigned long ext2_count_free_blocks (struct super_block *); extern unsigned long ext2_count_dirs (struct super_block *); extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, @@ -724,23 +718,17 @@ extern void ext2_init_block_alloc_info(struct inode *); extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv); /* dir.c */ -extern int ext2_add_link (struct dentry *, struct inode *); -extern int ext2_inode_by_name(struct inode *dir, +int ext2_add_link(struct dentry *, struct inode *); +int ext2_inode_by_name(struct inode *dir, const struct qstr *child, ino_t *ino); -extern int ext2_make_empty(struct inode *, struct inode *); -extern struct ext2_dir_entry_2 *ext2_find_entry(struct inode *, const struct qstr *, - struct page **, void **res_page_addr); -extern int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page, - char *kaddr); -extern int ext2_empty_dir (struct inode *); -extern struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p, void **pa); -extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, void *, - struct inode *, int); -static inline void ext2_put_page(struct page *page, void *page_addr) -{ - kunmap_local(page_addr); - put_page(page); -} +int ext2_make_empty(struct inode *, struct inode *); +struct ext2_dir_entry_2 *ext2_find_entry(struct inode *, const struct qstr *, + struct folio **foliop); +int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct folio *folio); +int ext2_empty_dir(struct inode *); +struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct folio **foliop); +int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, + struct folio *folio, struct inode *inode, bool update_times); /* ialloc.c */ extern struct inode * ext2_new_inode (struct inode *, umode_t, const struct qstr *); @@ -752,18 +740,19 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned); extern struct inode *ext2_iget (struct super_block *, unsigned long); extern int ext2_write_inode (struct inode *, struct writeback_control *); extern void ext2_evict_inode(struct inode *); +void ext2_write_failed(struct address_space *mapping, loff_t to); extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); -extern int ext2_setattr (struct user_namespace *, struct dentry *, struct iattr *); -extern int ext2_getattr (struct user_namespace *, const struct path *, +extern int ext2_setattr (struct mnt_idmap *, struct dentry *, struct iattr *); +extern int ext2_getattr (struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int); extern void ext2_set_inode_flags(struct inode *inode); extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); /* ioctl.c */ -extern int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa); -extern int ext2_fileattr_set(struct user_namespace *mnt_userns, - struct dentry *dentry, struct fileattr *fa); +extern int ext2_fileattr_get(struct dentry *dentry, struct file_kattr *fa); +extern int ext2_fileattr_set(struct mnt_idmap *idmap, + struct dentry *dentry, struct file_kattr *fa); extern long ext2_ioctl(struct file *, unsigned int, unsigned long); extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long); diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 6b4bebe982ca..76bddce462fc 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -25,9 +25,11 @@ #include <linux/quotaops.h> #include <linux/iomap.h> #include <linux/uio.h> +#include <linux/buffer_head.h> #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "trace.h" #ifdef CONFIG_FS_DAX static ssize_t ext2_dax_read_iter(struct kiocb *iocb, struct iov_iter *to) @@ -101,7 +103,7 @@ static vm_fault_t ext2_dax_fault(struct vm_fault *vmf) } filemap_invalidate_lock_shared(inode->i_mapping); - ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, NULL, &ext2_iomap_ops); + ret = dax_iomap_fault(vmf, 0, NULL, NULL, &ext2_iomap_ops); filemap_invalidate_unlock_shared(inode->i_mapping); if (write) @@ -120,17 +122,19 @@ static const struct vm_operations_struct ext2_dax_vm_ops = { .pfn_mkwrite = ext2_dax_fault, }; -static int ext2_file_mmap(struct file *file, struct vm_area_struct *vma) +static int ext2_file_mmap_prepare(struct vm_area_desc *desc) { + struct file *file = desc->file; + if (!IS_DAX(file_inode(file))) - return generic_file_mmap(file, vma); + return generic_file_mmap_prepare(desc); file_accessed(file); - vma->vm_ops = &ext2_dax_vm_ops; + desc->vm_ops = &ext2_dax_vm_ops; return 0; } #else -#define ext2_file_mmap generic_file_mmap +#define ext2_file_mmap_prepare generic_file_mmap_prepare #endif /* @@ -153,7 +157,7 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync) int ret; struct super_block *sb = file->f_mapping->host->i_sb; - ret = generic_file_fsync(file, start, end, datasync); + ret = generic_buffers_fsync(file, start, end, datasync); if (ret == -EIO) /* We don't really know where the IO error happened... */ ext2_error(sb, __func__, @@ -161,12 +165,130 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync) return ret; } +static ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + ssize_t ret; + + trace_ext2_dio_read_begin(iocb, to, 0); + inode_lock_shared(inode); + ret = iomap_dio_rw(iocb, to, &ext2_iomap_ops, NULL, 0, NULL, 0); + inode_unlock_shared(inode); + trace_ext2_dio_read_end(iocb, to, ret); + + return ret; +} + +static int ext2_dio_write_end_io(struct kiocb *iocb, ssize_t size, + int error, unsigned int flags) +{ + loff_t pos = iocb->ki_pos; + struct inode *inode = file_inode(iocb->ki_filp); + + if (error) + goto out; + + /* + * If we are extending the file, we have to update i_size here before + * page cache gets invalidated in iomap_dio_rw(). This prevents racing + * buffered reads from zeroing out too much from page cache pages. + * Note that all extending writes always happens synchronously with + * inode lock held by ext2_dio_write_iter(). So it is safe to update + * inode size here for extending file writes. + */ + pos += size; + if (pos > i_size_read(inode)) { + i_size_write(inode, pos); + mark_inode_dirty(inode); + } +out: + trace_ext2_dio_write_endio(iocb, size, error); + return error; +} + +static const struct iomap_dio_ops ext2_dio_write_ops = { + .end_io = ext2_dio_write_end_io, +}; + +static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + ssize_t ret; + unsigned int flags = 0; + unsigned long blocksize = inode->i_sb->s_blocksize; + loff_t offset = iocb->ki_pos; + loff_t count = iov_iter_count(from); + ssize_t status = 0; + + trace_ext2_dio_write_begin(iocb, from, 0); + inode_lock(inode); + ret = generic_write_checks(iocb, from); + if (ret <= 0) + goto out_unlock; + + ret = kiocb_modified(iocb); + if (ret) + goto out_unlock; + + /* use IOMAP_DIO_FORCE_WAIT for unaligned or extending writes */ + if (iocb->ki_pos + iov_iter_count(from) > i_size_read(inode) || + (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(from), blocksize))) + flags |= IOMAP_DIO_FORCE_WAIT; + + ret = iomap_dio_rw(iocb, from, &ext2_iomap_ops, &ext2_dio_write_ops, + flags, NULL, 0); + + /* ENOTBLK is magic return value for fallback to buffered-io */ + if (ret == -ENOTBLK) + ret = 0; + + if (ret < 0 && ret != -EIOCBQUEUED) + ext2_write_failed(inode->i_mapping, offset + count); + + /* handle case for partial write and for fallback to buffered write */ + if (ret >= 0 && iov_iter_count(from)) { + loff_t pos, endbyte; + int ret2; + + iocb->ki_flags &= ~IOCB_DIRECT; + pos = iocb->ki_pos; + status = generic_perform_write(iocb, from); + if (unlikely(status < 0)) { + ret = status; + goto out_unlock; + } + + ret += status; + endbyte = pos + status - 1; + ret2 = filemap_write_and_wait_range(inode->i_mapping, pos, + endbyte); + if (!ret2) + invalidate_mapping_pages(inode->i_mapping, + pos >> PAGE_SHIFT, + endbyte >> PAGE_SHIFT); + if (ret > 0) + generic_write_sync(iocb, ret); + } + +out_unlock: + inode_unlock(inode); + if (status) + trace_ext2_dio_write_buff_end(iocb, from, status); + trace_ext2_dio_write_end(iocb, from, ret); + return ret; +} + static ssize_t ext2_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { #ifdef CONFIG_FS_DAX if (IS_DAX(iocb->ki_filp->f_mapping->host)) return ext2_dax_read_iter(iocb, to); #endif + if (iocb->ki_flags & IOCB_DIRECT) + return ext2_dio_read_iter(iocb, to); + return generic_file_read_iter(iocb, to); } @@ -176,9 +298,18 @@ static ssize_t ext2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (IS_DAX(iocb->ki_filp->f_mapping->host)) return ext2_dax_write_iter(iocb, from); #endif + if (iocb->ki_flags & IOCB_DIRECT) + return ext2_dio_write_iter(iocb, from); + return generic_file_write_iter(iocb, from); } +static int ext2_file_open(struct inode *inode, struct file *filp) +{ + filp->f_mode |= FMODE_CAN_ODIRECT; + return dquot_file_open(inode, filp); +} + const struct file_operations ext2_file_operations = { .llseek = generic_file_llseek, .read_iter = ext2_file_read_iter, @@ -187,12 +318,12 @@ const struct file_operations ext2_file_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = ext2_compat_ioctl, #endif - .mmap = ext2_file_mmap, - .open = dquot_file_open, + .mmap_prepare = ext2_file_mmap_prepare, + .open = ext2_file_open, .release = ext2_release_file, .fsync = ext2_fsync, .get_unmapped_area = thp_get_unmapped_area, - .splice_read = generic_file_splice_read, + .splice_read = filemap_splice_read, .splice_write = iter_file_splice_write, }; diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 78b8686d9a4a..fdf63e9c6e7c 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -273,7 +273,6 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent) if ((parent == d_inode(sb->s_root)) || (EXT2_I(parent)->i_flags & EXT2_TOPDIR_FL)) { - struct ext2_group_desc *best_desc = NULL; int best_ndir = inodes_per_group; int best_group = -1; @@ -291,10 +290,8 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent) continue; best_group = group; best_ndir = le16_to_cpu(desc->bg_used_dirs_count); - best_desc = desc; } if (best_group >= 0) { - desc = best_desc; group = best_group; goto found; } @@ -545,11 +542,11 @@ got: inode->i_uid = current_fsuid(); inode->i_gid = dir->i_gid; } else - inode_init_owner(&init_user_ns, inode, dir, mode); + inode_init_owner(&nop_mnt_idmap, inode, dir, mode); inode->i_ino = ino; inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + simple_inode_init_ts(inode); memset(ei->i_data, 0, sizeof(ei->i_data)); ei->i_flags = ext2_mask_flags(mode, EXT2_I(dir)->i_flags & EXT2_FL_INHERITED); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 69aed9e2359e..dbfe9098a124 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -56,7 +56,7 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode) static void ext2_truncate_blocks(struct inode *inode, loff_t offset); -static void ext2_write_failed(struct address_space *mapping, loff_t to) +void ext2_write_failed(struct address_space *mapping, loff_t to) { struct inode *inode = mapping->host; @@ -385,12 +385,16 @@ ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks, } /** - * ext2_alloc_blocks: multiple allocate blocks needed for a branch - * @indirect_blks: the number of blocks need to allocate for indirect - * blocks - * @blks: the number of blocks need to allocate for direct blocks - * @new_blocks: on return it will store the new block numbers for - * the indirect blocks(if needed) and the first direct block, + * ext2_alloc_blocks: Allocate multiple blocks needed for a branch. + * @inode: Owner. + * @goal: Preferred place for allocation. + * @indirect_blks: The number of blocks needed to allocate for indirect blocks. + * @blks: The number of blocks need to allocate for direct blocks. + * @new_blocks: On return it will store the new block numbers for + * the indirect blocks(if needed) and the first direct block. + * @err: Error pointer. + * + * Return: Number of blocks allocated. */ static int ext2_alloc_blocks(struct inode *inode, ext2_fsblk_t goal, int indirect_blks, int blks, @@ -415,7 +419,7 @@ static int ext2_alloc_blocks(struct inode *inode, while (1) { count = target; /* allocating blocks for indirect blocks and direct blocks */ - current_block = ext2_new_blocks(inode,goal,&count,err); + current_block = ext2_new_blocks(inode, goal, &count, err, 0); if (*err) goto failed_out; @@ -595,7 +599,7 @@ static void ext2_splice_branch(struct inode *inode, if (where->bh) mark_buffer_dirty_inode(where->bh, inode); - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); mark_inode_dirty(inode); } @@ -750,7 +754,7 @@ static int ext2_get_blocks(struct inode *inode, */ err = sb_issue_zeroout(inode->i_sb, le32_to_cpu(chain[depth-1].key), count, - GFP_NOFS); + GFP_KERNEL); if (err) { mutex_unlock(&ei->truncate_mutex); goto cleanup; @@ -809,9 +813,27 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, bool new = false, boundary = false; u32 bno; int ret; + bool create = flags & IOMAP_WRITE; + + /* + * For writes that could fill holes inside i_size on a + * DIO_SKIP_HOLES filesystem we forbid block creations: only + * overwrites are permitted. + */ + if ((flags & IOMAP_DIRECT) && + (first_block << blkbits) < i_size_read(inode)) + create = 0; + + /* + * Writes that span EOF might trigger an IO size update on completion, + * so consider them to be dirty for the purposes of O_DSYNC even if + * there is no other metadata changes pending or have been made here. + */ + if ((flags & IOMAP_WRITE) && offset + length > i_size_read(inode)) + iomap->flags |= IOMAP_F_DIRTY; ret = ext2_get_blocks(inode, first_block, max_blocks, - &bno, &new, &boundary, flags & IOMAP_WRITE); + &bno, &new, &boundary, create); if (ret < 0) return ret; @@ -823,6 +845,12 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, iomap->bdev = inode->i_sb->s_bdev; if (ret == 0) { + /* + * Switch to buffered-io for writing to holes in a non-extent + * based filesystem to avoid stale data exposure problem. + */ + if (!create && (flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT)) + return -ENOTBLK; iomap->type = IOMAP_HOLE; iomap->addr = IOMAP_NULL_ADDR; iomap->length = 1 << blkbits; @@ -844,6 +872,13 @@ static int ext2_iomap_end(struct inode *inode, loff_t offset, loff_t length, ssize_t written, unsigned flags, struct iomap *iomap) { + /* + * Switch to buffered-io in case of any error. + * Blocks allocated can be used by the buffered-io path. + */ + if ((flags & IOMAP_DIRECT) && (flags & IOMAP_WRITE) && written == 0) + return -ENOTBLK; + if (iomap->type == IOMAP_MAPPED && written < length && (flags & IOMAP_WRITE)) @@ -860,9 +895,19 @@ int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { int ret; + loff_t i_size; inode_lock(inode); - len = min_t(u64, len, i_size_read(inode)); + i_size = i_size_read(inode); + /* + * iomap_fiemap() returns EINVAL for 0 length. Make sure we don't trim + * length to 0 but still trim the range as much as possible since + * ext2_get_blocks() iterates unmapped space block by block which is + * slow. + */ + if (i_size == 0) + i_size = 1; + len = min_t(u64, len, i_size); ret = iomap_fiemap(inode, fieinfo, start, len, &ext2_iomap_ops); inode_unlock(inode); @@ -880,24 +925,25 @@ static void ext2_readahead(struct readahead_control *rac) } static int -ext2_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, struct page **pagep, void **fsdata) +ext2_write_begin(const struct kiocb *iocb, struct address_space *mapping, + loff_t pos, unsigned len, struct folio **foliop, void **fsdata) { int ret; - ret = block_write_begin(mapping, pos, len, pagep, ext2_get_block); + ret = block_write_begin(mapping, pos, len, foliop, ext2_get_block); if (ret < 0) ext2_write_failed(mapping, pos + len); return ret; } -static int ext2_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) +static int ext2_write_end(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct folio *folio, void *fsdata) { int ret; - ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); + ret = generic_write_end(iocb, mapping, pos, len, copied, folio, fsdata); if (ret < len) ext2_write_failed(mapping, pos + len); return ret; @@ -908,22 +954,6 @@ static sector_t ext2_bmap(struct address_space *mapping, sector_t block) return generic_block_bmap(mapping,block,ext2_get_block); } -static ssize_t -ext2_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; - size_t count = iov_iter_count(iter); - loff_t offset = iocb->ki_pos; - ssize_t ret; - - ret = blockdev_direct_IO(iocb, inode, iter, ext2_get_block); - if (ret < 0 && iov_iter_rw(iter) == WRITE) - ext2_write_failed(mapping, offset + count); - return ret; -} - static int ext2_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -946,16 +976,14 @@ const struct address_space_operations ext2_aops = { .write_begin = ext2_write_begin, .write_end = ext2_write_end, .bmap = ext2_bmap, - .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, .migrate_folio = buffer_migrate_folio, .is_partially_uptodate = block_is_partially_uptodate, - .error_remove_page = generic_error_remove_page, + .error_remove_folio = generic_error_remove_folio, }; static const struct address_space_operations ext2_dax_aops = { .writepages = ext2_dax_writepages, - .direct_IO = noop_direct_IO, .dirty_folio = noop_dirty_folio, }; @@ -1067,8 +1095,8 @@ no_top: */ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) { - unsigned long block_to_free = 0, count = 0; - unsigned long nr; + ext2_fsblk_t block_to_free = 0, count = 0; + ext2_fsblk_t nr; for ( ; p < q ; p++) { nr = le32_to_cpu(*p); @@ -1108,7 +1136,7 @@ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth) { struct buffer_head * bh; - unsigned long nr; + ext2_fsblk_t nr; if (depth--) { int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); @@ -1259,9 +1287,8 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) inode_dio_wait(inode); if (IS_DAX(inode)) - error = dax_zero_range(inode, newsize, - PAGE_ALIGN(newsize) - newsize, NULL, - &ext2_iomap_ops); + error = dax_truncate_page(inode, newsize, NULL, + &ext2_iomap_ops); else error = block_truncate_page(inode->i_mapping, newsize, ext2_get_block); @@ -1273,7 +1300,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) __ext2_truncate_blocks(inode, newsize); filemap_invalidate_unlock(inode->i_mapping); - inode->i_mtime = inode->i_ctime = current_time(inode); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); if (inode_needs_sync(inode)) { sync_mapping_buffers(inode->i_mapping); sync_inode_metadata(inode, 1); @@ -1371,7 +1398,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) + if (!(inode_state_read_once(inode) & I_NEW)) return inode; ei = EXT2_I(inode); @@ -1394,10 +1421,9 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) i_gid_write(inode, i_gid); set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); inode->i_size = le32_to_cpu(raw_inode->i_size); - inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime); - inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime); - inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime); - inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0; + inode_set_atime(inode, (signed)le32_to_cpu(raw_inode->i_atime), 0); + inode_set_ctime(inode, (signed)le32_to_cpu(raw_inode->i_ctime), 0); + inode_set_mtime(inode, (signed)le32_to_cpu(raw_inode->i_mtime), 0); ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); /* We now have enough fields to check if the inode was active or not. * This is needed because nfsd might try to access dead inodes @@ -1526,9 +1552,9 @@ static int __ext2_write_inode(struct inode *inode, int do_sync) } raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); raw_inode->i_size = cpu_to_le32(inode->i_size); - raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec); - raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); - raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); + raw_inode->i_atime = cpu_to_le32(inode_get_atime_sec(inode)); + raw_inode->i_ctime = cpu_to_le32(inode_get_ctime_sec(inode)); + raw_inode->i_mtime = cpu_to_le32(inode_get_mtime_sec(inode)); raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); @@ -1592,7 +1618,7 @@ int ext2_write_inode(struct inode *inode, struct writeback_control *wbc) return __ext2_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); } -int ext2_getattr(struct user_namespace *mnt_userns, const struct path *path, +int ext2_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int query_flags) { struct inode *inode = d_inode(path->dentry); @@ -1614,28 +1640,28 @@ int ext2_getattr(struct user_namespace *mnt_userns, const struct path *path, STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); - generic_fillattr(&init_user_ns, inode, stat); + generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); return 0; } -int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, +int ext2_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr) { struct inode *inode = d_inode(dentry); int error; - error = setattr_prepare(&init_user_ns, dentry, iattr); + error = setattr_prepare(&nop_mnt_idmap, dentry, iattr); if (error) return error; - if (is_quota_modification(mnt_userns, inode, iattr)) { + if (is_quota_modification(&nop_mnt_idmap, inode, iattr)) { error = dquot_initialize(inode); if (error) return error; } - if (i_uid_needs_update(mnt_userns, iattr, inode) || - i_gid_needs_update(mnt_userns, iattr, inode)) { - error = dquot_transfer(mnt_userns, inode, iattr); + if (i_uid_needs_update(&nop_mnt_idmap, iattr, inode) || + i_gid_needs_update(&nop_mnt_idmap, iattr, inode)) { + error = dquot_transfer(&nop_mnt_idmap, inode, iattr); if (error) return error; } @@ -1644,9 +1670,9 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, if (error) return error; } - setattr_copy(&init_user_ns, inode, iattr); + setattr_copy(&nop_mnt_idmap, inode, iattr); if (iattr->ia_valid & ATTR_MODE) - error = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode); + error = posix_acl_chmod(&nop_mnt_idmap, dentry, inode->i_mode); mark_inode_dirty(inode); return error; diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index e8340bf09b10..c3fea55b8efa 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -18,7 +18,7 @@ #include <linux/uaccess.h> #include <linux/fileattr.h> -int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa) +int ext2_fileattr_get(struct dentry *dentry, struct file_kattr *fa) { struct ext2_inode_info *ei = EXT2_I(d_inode(dentry)); @@ -27,8 +27,8 @@ int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa) return 0; } -int ext2_fileattr_set(struct user_namespace *mnt_userns, - struct dentry *dentry, struct fileattr *fa) +int ext2_fileattr_set(struct mnt_idmap *idmap, + struct dentry *dentry, struct file_kattr *fa) { struct inode *inode = d_inode(dentry); struct ext2_inode_info *ei = EXT2_I(inode); @@ -44,7 +44,7 @@ int ext2_fileattr_set(struct user_namespace *mnt_userns, (fa->flags & EXT2_FL_USER_MODIFIABLE); ext2_set_inode_flags(inode); - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); mark_inode_dirty(inode); return 0; @@ -66,7 +66,7 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case EXT2_IOC_SETVERSION: { __u32 generation; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) @@ -77,7 +77,7 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } inode_lock(inode); - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); inode->i_generation = generation; inode_unlock(inode); @@ -99,7 +99,7 @@ setversion_out: if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) return -ENOTTY; - if (!inode_owner_or_capable(&init_user_ns, inode)) + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) return -EACCES; if (get_user(rsv_window_size, (int __user *)arg)) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index c056957221a2..bde617a66cec 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -99,7 +99,7 @@ struct dentry *ext2_get_parent(struct dentry *child) * If the create succeeds, we fill in the inode information * with d_instantiate(). */ -static int ext2_create (struct user_namespace * mnt_userns, +static int ext2_create (struct mnt_idmap * idmap, struct inode * dir, struct dentry * dentry, umode_t mode, bool excl) { @@ -119,7 +119,7 @@ static int ext2_create (struct user_namespace * mnt_userns, return ext2_add_nondir(dentry, inode); } -static int ext2_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, +static int ext2_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct file *file, umode_t mode) { struct inode *inode = ext2_new_inode(dir, mode, NULL); @@ -133,7 +133,7 @@ static int ext2_tmpfile(struct user_namespace *mnt_userns, struct inode *dir, return finish_open_simple(file, 0); } -static int ext2_mknod (struct user_namespace * mnt_userns, struct inode * dir, +static int ext2_mknod (struct mnt_idmap * idmap, struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode * inode; @@ -154,7 +154,7 @@ static int ext2_mknod (struct user_namespace * mnt_userns, struct inode * dir, return err; } -static int ext2_symlink (struct user_namespace * mnt_userns, struct inode * dir, +static int ext2_symlink (struct mnt_idmap * idmap, struct inode * dir, struct dentry * dentry, const char * symname) { struct super_block * sb = dir->i_sb; @@ -211,7 +211,7 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir, if (err) return err; - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); inode_inc_link_count(inode); ihold(inode); @@ -225,15 +225,16 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir, return err; } -static int ext2_mkdir(struct user_namespace * mnt_userns, - struct inode * dir, struct dentry * dentry, umode_t mode) +static struct dentry *ext2_mkdir(struct mnt_idmap * idmap, + struct inode * dir, struct dentry * dentry, + umode_t mode) { struct inode * inode; int err; err = dquot_initialize(dir); if (err) - return err; + return ERR_PTR(err); inode_inc_link_count(dir); @@ -258,7 +259,7 @@ static int ext2_mkdir(struct user_namespace * mnt_userns, d_instantiate_new(dentry, inode); out: - return err; + return ERR_PTR(err); out_fail: inode_dec_link_count(inode); @@ -269,30 +270,29 @@ out_dir: goto out; } -static int ext2_unlink(struct inode * dir, struct dentry *dentry) +static int ext2_unlink(struct inode *dir, struct dentry *dentry) { - struct inode * inode = d_inode(dentry); - struct ext2_dir_entry_2 * de; - struct page * page; - void *page_addr; + struct inode *inode = d_inode(dentry); + struct ext2_dir_entry_2 *de; + struct folio *folio; int err; err = dquot_initialize(dir); if (err) goto out; - de = ext2_find_entry(dir, &dentry->d_name, &page, &page_addr); + de = ext2_find_entry(dir, &dentry->d_name, &folio); if (IS_ERR(de)) { err = PTR_ERR(de); goto out; } - err = ext2_delete_entry (de, page, page_addr); - ext2_put_page(page, page_addr); + err = ext2_delete_entry(de, folio); + folio_release_kmap(folio, de); if (err) goto out; - inode->i_ctime = dir->i_ctime; + inode_set_ctime_to_ts(inode, inode_get_ctime(dir)); inode_dec_link_count(inode); err = 0; out: @@ -315,19 +315,18 @@ static int ext2_rmdir (struct inode * dir, struct dentry *dentry) return err; } -static int ext2_rename (struct user_namespace * mnt_userns, +static int ext2_rename (struct mnt_idmap * idmap, struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry, unsigned int flags) { struct inode * old_inode = d_inode(old_dentry); struct inode * new_inode = d_inode(new_dentry); - struct page * dir_page = NULL; - void *dir_page_addr; + struct folio *dir_folio = NULL; struct ext2_dir_entry_2 * dir_de = NULL; - struct page * old_page; - void *old_page_addr; + struct folio * old_folio; struct ext2_dir_entry_2 * old_de; + bool old_is_dir = S_ISDIR(old_inode->i_mode); int err; if (flags & ~RENAME_NOREPLACE) @@ -335,52 +334,50 @@ static int ext2_rename (struct user_namespace * mnt_userns, err = dquot_initialize(old_dir); if (err) - goto out; + return err; err = dquot_initialize(new_dir); if (err) - goto out; + return err; - old_de = ext2_find_entry(old_dir, &old_dentry->d_name, &old_page, - &old_page_addr); - if (IS_ERR(old_de)) { - err = PTR_ERR(old_de); - goto out; - } + old_de = ext2_find_entry(old_dir, &old_dentry->d_name, &old_folio); + if (IS_ERR(old_de)) + return PTR_ERR(old_de); - if (S_ISDIR(old_inode->i_mode)) { + if (old_is_dir && old_dir != new_dir) { err = -EIO; - dir_de = ext2_dotdot(old_inode, &dir_page, &dir_page_addr); + dir_de = ext2_dotdot(old_inode, &dir_folio); if (!dir_de) goto out_old; } if (new_inode) { - void *page_addr; - struct page *new_page; + struct folio *new_folio; struct ext2_dir_entry_2 *new_de; err = -ENOTEMPTY; - if (dir_de && !ext2_empty_dir (new_inode)) + if (old_is_dir && !ext2_empty_dir(new_inode)) goto out_dir; new_de = ext2_find_entry(new_dir, &new_dentry->d_name, - &new_page, &page_addr); + &new_folio); if (IS_ERR(new_de)) { err = PTR_ERR(new_de); goto out_dir; } - ext2_set_link(new_dir, new_de, new_page, page_addr, old_inode, 1); - ext2_put_page(new_page, page_addr); - new_inode->i_ctime = current_time(new_inode); - if (dir_de) + err = ext2_set_link(new_dir, new_de, new_folio, old_inode, true); + folio_release_kmap(new_folio, new_de); + if (err) + goto out_dir; + inode_set_ctime_current(new_inode); + if (old_is_dir) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { err = ext2_add_link(new_dentry, old_inode); if (err) goto out_dir; - if (dir_de) + if (old_is_dir) inode_inc_link_count(new_dir); } @@ -388,29 +385,22 @@ static int ext2_rename (struct user_namespace * mnt_userns, * Like most other Unix systems, set the ctime for inodes on a * rename. */ - old_inode->i_ctime = current_time(old_inode); + inode_set_ctime_current(old_inode); mark_inode_dirty(old_inode); - ext2_delete_entry(old_de, old_page, old_page_addr); - - if (dir_de) { + err = ext2_delete_entry(old_de, old_folio); + if (!err && old_is_dir) { if (old_dir != new_dir) - ext2_set_link(old_inode, dir_de, dir_page, - dir_page_addr, new_dir, 0); + err = ext2_set_link(old_inode, dir_de, dir_folio, + new_dir, false); - ext2_put_page(dir_page, dir_page_addr); inode_dec_link_count(old_dir); } - - ext2_put_page(old_page, old_page_addr); - return 0; - out_dir: if (dir_de) - ext2_put_page(dir_page, dir_page_addr); + folio_release_kmap(dir_folio, dir_de); out_old: - ext2_put_page(old_page, old_page_addr); -out: + folio_release_kmap(old_folio, old_de); return err; } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 69c88facfe90..121e634c792a 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -23,7 +23,8 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/blkdev.h> -#include <linux/parser.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> #include <linux/random.h> #include <linux/buffer_head.h> #include <linux/exportfs.h> @@ -40,7 +41,6 @@ #include "acl.h" static void ext2_write_super(struct super_block *sb); -static int ext2_remount (struct super_block * sb, int * flags, char * data); static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf); static int ext2_sync_fs(struct super_block *sb, int wait); static int ext2_freeze(struct super_block *sb); @@ -81,6 +81,33 @@ void ext2_error(struct super_block *sb, const char *function, } } +static void ext2_msg_fc(struct fs_context *fc, const char *prefix, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *s_id; + + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + s_id = fc->root->d_sb->s_id; + } else { + /* get last path component of source */ + s_id = strrchr(fc->source, '/'); + if (s_id) + s_id++; + else + s_id = fc->source; + } + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk("%sEXT2-fs (%s): %pV\n", prefix, s_id, &vaf); + + va_end(args); +} + void ext2_msg(struct super_block *sb, const char *prefix, const char *fmt, ...) { @@ -213,8 +240,7 @@ static int __init init_inodecache(void) { ext2_inode_cachep = kmem_cache_create_usercopy("ext2_inode_cache", sizeof(struct ext2_inode_info), 0, - (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD| - SLAB_ACCOUNT), + SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, offsetof(struct ext2_inode_info, i_data), sizeof_field(struct ext2_inode_info, i_data), init_once); @@ -320,7 +346,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, siz static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off); static int ext2_quota_on(struct super_block *sb, int type, int format_id, const struct path *path); -static struct dquot **ext2_get_dquots(struct inode *inode) +static struct dquot __rcu **ext2_get_dquots(struct inode *inode) { return EXT2_I(inode)->i_dquot; } @@ -347,7 +373,6 @@ static const struct super_operations ext2_sops = { .freeze_fs = ext2_freeze, .unfreeze_fs = ext2_unfreeze, .statfs = ext2_statfs, - .remount_fs = ext2_remount, .show_options = ext2_show_options, #ifdef CONFIG_QUOTA .quota_read = ext2_quota_read, @@ -397,235 +422,224 @@ static struct dentry *ext2_fh_to_parent(struct super_block *sb, struct fid *fid, } static const struct export_operations ext2_export_ops = { + .encode_fh = generic_encode_ino32_fh, .fh_to_dentry = ext2_fh_to_dentry, .fh_to_parent = ext2_fh_to_parent, .get_parent = ext2_get_parent, }; -static unsigned long get_sb_block(void **data) -{ - unsigned long sb_block; - char *options = (char *) *data; - - if (!options || strncmp(options, "sb=", 3) != 0) - return 1; /* Default location */ - options += 3; - sb_block = simple_strtoul(options, &options, 0); - if (*options && *options != ',') { - printk("EXT2-fs: Invalid sb specification: %s\n", - (char *) *data); - return 1; - } - if (*options == ',') - options++; - *data = (void *) options; - return sb_block; -} - enum { - Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, - Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, - Opt_err_ro, Opt_nouid32, Opt_debug, - Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, - Opt_acl, Opt_noacl, Opt_xip, Opt_dax, Opt_ignore, Opt_err, Opt_quota, - Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation + Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, + Opt_sb, Opt_errors, Opt_nouid32, Opt_debug, Opt_oldalloc, Opt_orlov, + Opt_nobh, Opt_user_xattr, Opt_acl, Opt_xip, Opt_dax, Opt_ignore, + Opt_quota, Opt_usrquota, Opt_grpquota, Opt_reservation, +}; + +static const struct constant_table ext2_param_errors[] = { + {"continue", EXT2_MOUNT_ERRORS_CONT}, + {"panic", EXT2_MOUNT_ERRORS_PANIC}, + {"remount-ro", EXT2_MOUNT_ERRORS_RO}, + {} }; -static const match_table_t tokens = { - {Opt_bsd_df, "bsddf"}, - {Opt_minix_df, "minixdf"}, - {Opt_grpid, "grpid"}, - {Opt_grpid, "bsdgroups"}, - {Opt_nogrpid, "nogrpid"}, - {Opt_nogrpid, "sysvgroups"}, - {Opt_resgid, "resgid=%u"}, - {Opt_resuid, "resuid=%u"}, - {Opt_sb, "sb=%u"}, - {Opt_err_cont, "errors=continue"}, - {Opt_err_panic, "errors=panic"}, - {Opt_err_ro, "errors=remount-ro"}, - {Opt_nouid32, "nouid32"}, - {Opt_debug, "debug"}, - {Opt_oldalloc, "oldalloc"}, - {Opt_orlov, "orlov"}, - {Opt_nobh, "nobh"}, - {Opt_user_xattr, "user_xattr"}, - {Opt_nouser_xattr, "nouser_xattr"}, - {Opt_acl, "acl"}, - {Opt_noacl, "noacl"}, - {Opt_xip, "xip"}, - {Opt_dax, "dax"}, - {Opt_grpquota, "grpquota"}, - {Opt_ignore, "noquota"}, - {Opt_quota, "quota"}, - {Opt_usrquota, "usrquota"}, - {Opt_reservation, "reservation"}, - {Opt_noreservation, "noreservation"}, - {Opt_err, NULL} +static const struct fs_parameter_spec ext2_param_spec[] = { + fsparam_flag ("bsddf", Opt_bsd_df), + fsparam_flag ("minixdf", Opt_minix_df), + fsparam_flag ("grpid", Opt_grpid), + fsparam_flag ("bsdgroups", Opt_grpid), + fsparam_flag ("nogrpid", Opt_nogrpid), + fsparam_flag ("sysvgroups", Opt_nogrpid), + fsparam_gid ("resgid", Opt_resgid), + fsparam_uid ("resuid", Opt_resuid), + fsparam_u32 ("sb", Opt_sb), + fsparam_enum ("errors", Opt_errors, ext2_param_errors), + fsparam_flag ("nouid32", Opt_nouid32), + fsparam_flag ("debug", Opt_debug), + fsparam_flag ("oldalloc", Opt_oldalloc), + fsparam_flag ("orlov", Opt_orlov), + fsparam_flag ("nobh", Opt_nobh), + fsparam_flag_no ("user_xattr", Opt_user_xattr), + fsparam_flag_no ("acl", Opt_acl), + fsparam_flag ("xip", Opt_xip), + fsparam_flag ("dax", Opt_dax), + fsparam_flag ("grpquota", Opt_grpquota), + fsparam_flag ("noquota", Opt_ignore), + fsparam_flag ("quota", Opt_quota), + fsparam_flag ("usrquota", Opt_usrquota), + fsparam_flag_no ("reservation", Opt_reservation), + {} }; -static int parse_options(char *options, struct super_block *sb, - struct ext2_mount_options *opts) +#define EXT2_SPEC_s_resuid (1 << 0) +#define EXT2_SPEC_s_resgid (1 << 1) + +struct ext2_fs_context { + unsigned long vals_s_flags; /* Bits to set in s_flags */ + unsigned long mask_s_flags; /* Bits changed in s_flags */ + unsigned int vals_s_mount_opt; + unsigned int mask_s_mount_opt; + kuid_t s_resuid; + kgid_t s_resgid; + unsigned long s_sb_block; + unsigned int spec; + +}; + +static inline void ctx_set_mount_opt(struct ext2_fs_context *ctx, + unsigned long flag) +{ + ctx->mask_s_mount_opt |= flag; + ctx->vals_s_mount_opt |= flag; +} + +static inline void ctx_clear_mount_opt(struct ext2_fs_context *ctx, + unsigned long flag) +{ + ctx->mask_s_mount_opt |= flag; + ctx->vals_s_mount_opt &= ~flag; +} + +static inline unsigned long +ctx_test_mount_opt(struct ext2_fs_context *ctx, unsigned long flag) +{ + return (ctx->vals_s_mount_opt & flag); +} + +static inline bool +ctx_parsed_mount_opt(struct ext2_fs_context *ctx, unsigned long flag) { - char *p; - substring_t args[MAX_OPT_ARGS]; - int option; - kuid_t uid; - kgid_t gid; - - if (!options) - return 1; - - while ((p = strsep (&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_bsd_df: - clear_opt (opts->s_mount_opt, MINIX_DF); - break; - case Opt_minix_df: - set_opt (opts->s_mount_opt, MINIX_DF); - break; - case Opt_grpid: - set_opt (opts->s_mount_opt, GRPID); - break; - case Opt_nogrpid: - clear_opt (opts->s_mount_opt, GRPID); - break; - case Opt_resuid: - if (match_int(&args[0], &option)) - return 0; - uid = make_kuid(current_user_ns(), option); - if (!uid_valid(uid)) { - ext2_msg(sb, KERN_ERR, "Invalid uid value %d", option); - return 0; - - } - opts->s_resuid = uid; - break; - case Opt_resgid: - if (match_int(&args[0], &option)) - return 0; - gid = make_kgid(current_user_ns(), option); - if (!gid_valid(gid)) { - ext2_msg(sb, KERN_ERR, "Invalid gid value %d", option); - return 0; - } - opts->s_resgid = gid; - break; - case Opt_sb: - /* handled by get_sb_block() instead of here */ - /* *sb_block = match_int(&args[0]); */ - break; - case Opt_err_panic: - clear_opt (opts->s_mount_opt, ERRORS_CONT); - clear_opt (opts->s_mount_opt, ERRORS_RO); - set_opt (opts->s_mount_opt, ERRORS_PANIC); - break; - case Opt_err_ro: - clear_opt (opts->s_mount_opt, ERRORS_CONT); - clear_opt (opts->s_mount_opt, ERRORS_PANIC); - set_opt (opts->s_mount_opt, ERRORS_RO); - break; - case Opt_err_cont: - clear_opt (opts->s_mount_opt, ERRORS_RO); - clear_opt (opts->s_mount_opt, ERRORS_PANIC); - set_opt (opts->s_mount_opt, ERRORS_CONT); - break; - case Opt_nouid32: - set_opt (opts->s_mount_opt, NO_UID32); - break; - case Opt_debug: - set_opt (opts->s_mount_opt, DEBUG); - break; - case Opt_oldalloc: - set_opt (opts->s_mount_opt, OLDALLOC); - break; - case Opt_orlov: - clear_opt (opts->s_mount_opt, OLDALLOC); - break; - case Opt_nobh: - ext2_msg(sb, KERN_INFO, - "nobh option not supported"); - break; + return (ctx->mask_s_mount_opt & flag); +} + +static void ext2_free_fc(struct fs_context *fc) +{ + kfree(fc->fs_private); +} + +static int ext2_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct ext2_fs_context *ctx = fc->fs_private; + int opt; + struct fs_parse_result result; + + opt = fs_parse(fc, ext2_param_spec, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_bsd_df: + ctx_clear_mount_opt(ctx, EXT2_MOUNT_MINIX_DF); + break; + case Opt_minix_df: + ctx_set_mount_opt(ctx, EXT2_MOUNT_MINIX_DF); + break; + case Opt_grpid: + ctx_set_mount_opt(ctx, EXT2_MOUNT_GRPID); + break; + case Opt_nogrpid: + ctx_clear_mount_opt(ctx, EXT2_MOUNT_GRPID); + break; + case Opt_resuid: + ctx->s_resuid = result.uid; + ctx->spec |= EXT2_SPEC_s_resuid; + break; + case Opt_resgid: + ctx->s_resgid = result.gid; + ctx->spec |= EXT2_SPEC_s_resgid; + break; + case Opt_sb: + /* Note that this is silently ignored on remount */ + ctx->s_sb_block = result.uint_32; + break; + case Opt_errors: + ctx_clear_mount_opt(ctx, EXT2_MOUNT_ERRORS_MASK); + ctx_set_mount_opt(ctx, result.uint_32); + break; + case Opt_nouid32: + ctx_set_mount_opt(ctx, EXT2_MOUNT_NO_UID32); + break; + case Opt_debug: + ctx_set_mount_opt(ctx, EXT2_MOUNT_DEBUG); + break; + case Opt_oldalloc: + ctx_set_mount_opt(ctx, EXT2_MOUNT_OLDALLOC); + break; + case Opt_orlov: + ctx_clear_mount_opt(ctx, EXT2_MOUNT_OLDALLOC); + break; + case Opt_nobh: + ext2_msg_fc(fc, KERN_INFO, "nobh option not supported\n"); + break; #ifdef CONFIG_EXT2_FS_XATTR - case Opt_user_xattr: - set_opt (opts->s_mount_opt, XATTR_USER); - break; - case Opt_nouser_xattr: - clear_opt (opts->s_mount_opt, XATTR_USER); - break; + case Opt_user_xattr: + if (!result.negated) + ctx_set_mount_opt(ctx, EXT2_MOUNT_XATTR_USER); + else + ctx_clear_mount_opt(ctx, EXT2_MOUNT_XATTR_USER); + break; #else - case Opt_user_xattr: - case Opt_nouser_xattr: - ext2_msg(sb, KERN_INFO, "(no)user_xattr options" - "not supported"); - break; + case Opt_user_xattr: + ext2_msg_fc(fc, KERN_INFO, "(no)user_xattr options not supported"); + break; #endif #ifdef CONFIG_EXT2_FS_POSIX_ACL - case Opt_acl: - set_opt(opts->s_mount_opt, POSIX_ACL); - break; - case Opt_noacl: - clear_opt(opts->s_mount_opt, POSIX_ACL); - break; + case Opt_acl: + if (!result.negated) + ctx_set_mount_opt(ctx, EXT2_MOUNT_POSIX_ACL); + else + ctx_clear_mount_opt(ctx, EXT2_MOUNT_POSIX_ACL); + break; #else - case Opt_acl: - case Opt_noacl: - ext2_msg(sb, KERN_INFO, - "(no)acl options not supported"); - break; + case Opt_acl: + ext2_msg_fc(fc, KERN_INFO, "(no)acl options not supported"); + break; #endif - case Opt_xip: - ext2_msg(sb, KERN_INFO, "use dax instead of xip"); - set_opt(opts->s_mount_opt, XIP); - fallthrough; - case Opt_dax: + case Opt_xip: + ext2_msg_fc(fc, KERN_INFO, "use dax instead of xip"); + ctx_set_mount_opt(ctx, EXT2_MOUNT_XIP); + fallthrough; + case Opt_dax: #ifdef CONFIG_FS_DAX - ext2_msg(sb, KERN_WARNING, - "DAX enabled. Warning: EXPERIMENTAL, use at your own risk"); - set_opt(opts->s_mount_opt, DAX); + ext2_msg_fc(fc, KERN_WARNING, + "DAX enabled. Warning: DAX support in ext2 driver is deprecated" + " and will be removed at the end of 2025. Please use ext4 driver instead."); + ctx_set_mount_opt(ctx, EXT2_MOUNT_DAX); #else - ext2_msg(sb, KERN_INFO, "dax option not supported"); + ext2_msg_fc(fc, KERN_INFO, "dax option not supported"); #endif - break; + break; #if defined(CONFIG_QUOTA) - case Opt_quota: - case Opt_usrquota: - set_opt(opts->s_mount_opt, USRQUOTA); - break; - - case Opt_grpquota: - set_opt(opts->s_mount_opt, GRPQUOTA); - break; + case Opt_quota: + case Opt_usrquota: + ctx_set_mount_opt(ctx, EXT2_MOUNT_USRQUOTA); + break; + + case Opt_grpquota: + ctx_set_mount_opt(ctx, EXT2_MOUNT_GRPQUOTA); + break; #else - case Opt_quota: - case Opt_usrquota: - case Opt_grpquota: - ext2_msg(sb, KERN_INFO, - "quota operations not supported"); - break; + case Opt_quota: + case Opt_usrquota: + case Opt_grpquota: + ext2_msg_fc(fc, KERN_INFO, "quota operations not supported"); + break; #endif - - case Opt_reservation: - set_opt(opts->s_mount_opt, RESERVATION); - ext2_msg(sb, KERN_INFO, "reservations ON"); - break; - case Opt_noreservation: - clear_opt(opts->s_mount_opt, RESERVATION); - ext2_msg(sb, KERN_INFO, "reservations OFF"); - break; - case Opt_ignore: - break; - default: - return 0; + case Opt_reservation: + if (!result.negated) { + ctx_set_mount_opt(ctx, EXT2_MOUNT_RESERVATION); + ext2_msg_fc(fc, KERN_INFO, "reservations ON"); + } else { + ctx_clear_mount_opt(ctx, EXT2_MOUNT_RESERVATION); + ext2_msg_fc(fc, KERN_INFO, "reservations OFF"); } + break; + case Opt_ignore: + break; + default: + return -EINVAL; } - return 1; + return 0; } static int ext2_setup_super (struct super_block * sb, @@ -668,10 +682,9 @@ static int ext2_setup_super (struct super_block * sb, es->s_max_mnt_count = cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); le16_add_cpu(&es->s_mnt_count, 1); if (test_opt (sb, DEBUG)) - ext2_msg(sb, KERN_INFO, "%s, %s, bs=%lu, fs=%lu, gc=%lu, " + ext2_msg(sb, KERN_INFO, "%s, %s, bs=%lu, gc=%lu, " "bpg=%lu, ipg=%lu, mo=%04lx]", EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize, - sbi->s_frag_size, sbi->s_groups_count, EXT2_BLOCKS_PER_GROUP(sb), EXT2_INODES_PER_GROUP(sb), @@ -802,24 +815,83 @@ static unsigned long descriptor_loc(struct super_block *sb, return ext2_group_first_block_no(sb, bg) + ext2_bg_has_super(sb, bg); } -static int ext2_fill_super(struct super_block *sb, void *data, int silent) +/* + * Set all mount options either from defaults on disk, or from parsed + * options. Parsed/specified options override on-disk defaults. + */ +static void ext2_set_options(struct fs_context *fc, struct ext2_sb_info *sbi) +{ + struct ext2_fs_context *ctx = fc->fs_private; + struct ext2_super_block *es = sbi->s_es; + unsigned long def_mount_opts = le32_to_cpu(es->s_default_mount_opts); + + /* Copy parsed mount options to sbi */ + sbi->s_mount_opt = ctx->vals_s_mount_opt; + + /* Use in-superblock defaults only if not specified during parsing */ + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_DEBUG) && + def_mount_opts & EXT2_DEFM_DEBUG) + set_opt(sbi->s_mount_opt, DEBUG); + + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_GRPID) && + def_mount_opts & EXT2_DEFM_BSDGROUPS) + set_opt(sbi->s_mount_opt, GRPID); + + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_NO_UID32) && + def_mount_opts & EXT2_DEFM_UID16) + set_opt(sbi->s_mount_opt, NO_UID32); + +#ifdef CONFIG_EXT2_FS_XATTR + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_XATTR_USER) && + def_mount_opts & EXT2_DEFM_XATTR_USER) + set_opt(sbi->s_mount_opt, XATTR_USER); +#endif +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_POSIX_ACL) && + def_mount_opts & EXT2_DEFM_ACL) + set_opt(sbi->s_mount_opt, POSIX_ACL); +#endif + + if (!ctx_parsed_mount_opt(ctx, EXT2_MOUNT_ERRORS_MASK)) { + if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC) + set_opt(sbi->s_mount_opt, ERRORS_PANIC); + else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE) + set_opt(sbi->s_mount_opt, ERRORS_CONT); + else + set_opt(sbi->s_mount_opt, ERRORS_RO); + } + + if (ctx->spec & EXT2_SPEC_s_resuid) + sbi->s_resuid = ctx->s_resuid; + else + sbi->s_resuid = make_kuid(&init_user_ns, + le16_to_cpu(es->s_def_resuid)); + + if (ctx->spec & EXT2_SPEC_s_resgid) + sbi->s_resgid = ctx->s_resgid; + else + sbi->s_resgid = make_kgid(&init_user_ns, + le16_to_cpu(es->s_def_resgid)); +} + +static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) { + struct ext2_fs_context *ctx = fc->fs_private; + int silent = fc->sb_flags & SB_SILENT; struct buffer_head * bh; struct ext2_sb_info * sbi; struct ext2_super_block * es; struct inode *root; unsigned long block; - unsigned long sb_block = get_sb_block(&data); + unsigned long sb_block = ctx->s_sb_block; unsigned long logic_sb_block; unsigned long offset = 0; - unsigned long def_mount_opts; long ret = -ENOMEM; int blocksize = BLOCK_SIZE; int db_count; int i, j; __le32 features; int err; - struct ext2_mount_options opts; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -878,42 +950,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (sb->s_magic != EXT2_SUPER_MAGIC) goto cantfind_ext2; - opts.s_mount_opt = 0; - /* Set defaults before we parse the mount options */ - def_mount_opts = le32_to_cpu(es->s_default_mount_opts); - if (def_mount_opts & EXT2_DEFM_DEBUG) - set_opt(opts.s_mount_opt, DEBUG); - if (def_mount_opts & EXT2_DEFM_BSDGROUPS) - set_opt(opts.s_mount_opt, GRPID); - if (def_mount_opts & EXT2_DEFM_UID16) - set_opt(opts.s_mount_opt, NO_UID32); -#ifdef CONFIG_EXT2_FS_XATTR - if (def_mount_opts & EXT2_DEFM_XATTR_USER) - set_opt(opts.s_mount_opt, XATTR_USER); -#endif -#ifdef CONFIG_EXT2_FS_POSIX_ACL - if (def_mount_opts & EXT2_DEFM_ACL) - set_opt(opts.s_mount_opt, POSIX_ACL); -#endif - - if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC) - set_opt(opts.s_mount_opt, ERRORS_PANIC); - else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE) - set_opt(opts.s_mount_opt, ERRORS_CONT); - else - set_opt(opts.s_mount_opt, ERRORS_RO); - - opts.s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid)); - opts.s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid)); - - set_opt(opts.s_mount_opt, RESERVATION); - - if (!parse_options((char *) data, sb, &opts)) - goto failed_mount; - - sbi->s_mount_opt = opts.s_mount_opt; - sbi->s_resuid = opts.s_resuid; - sbi->s_resgid = opts.s_resgid; + ext2_set_options(fc, sbi); sb->s_flags = (sb->s_flags & ~SB_POSIXACL) | (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0); @@ -945,6 +982,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + if (le32_to_cpu(es->s_log_block_size) > + (EXT2_MAX_BLOCK_LOG_SIZE - BLOCK_SIZE_BITS)) { + ext2_msg(sb, KERN_ERR, + "Invalid log block size: %u", + le32_to_cpu(es->s_log_block_size)); + goto failed_mount; + } blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); if (test_opt(sb, DAX)) { @@ -1005,14 +1049,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) } } - sbi->s_frag_size = EXT2_MIN_FRAG_SIZE << - le32_to_cpu(es->s_log_frag_size); - if (sbi->s_frag_size == 0) - goto cantfind_ext2; - sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size; - sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); - sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); @@ -1038,11 +1075,10 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } - if (sb->s_blocksize != sbi->s_frag_size) { + if (es->s_log_frag_size != es->s_log_block_size) { ext2_msg(sb, KERN_ERR, - "error: fragsize %lu != blocksize %lu" - "(not supported yet)", - sbi->s_frag_size, sb->s_blocksize); + "error: fragsize log %u != blocksize log %u", + le32_to_cpu(es->s_log_frag_size), sb->s_blocksize_bits); goto failed_mount; } @@ -1059,12 +1095,6 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sbi->s_blocks_per_group, sbi->s_inodes_per_group + 3); goto failed_mount; } - if (sbi->s_frags_per_group > sb->s_blocksize * 8) { - ext2_msg(sb, KERN_ERR, - "error: #fragments per group too big: %lu", - sbi->s_frags_per_group); - goto failed_mount; - } if (sbi->s_inodes_per_group < sbi->s_inodes_per_block || sbi->s_inodes_per_group > sb->s_blocksize * 8) { ext2_msg(sb, KERN_ERR, @@ -1332,23 +1362,21 @@ static void ext2_write_super(struct super_block *sb) ext2_sync_fs(sb, 1); } -static int ext2_remount (struct super_block * sb, int * flags, char * data) +static int ext2_reconfigure(struct fs_context *fc) { + struct ext2_fs_context *ctx = fc->fs_private; + struct super_block *sb = fc->root->d_sb; struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; struct ext2_mount_options new_opts; + int flags = fc->sb_flags; int err; sync_filesystem(sb); - spin_lock(&sbi->s_lock); - new_opts.s_mount_opt = sbi->s_mount_opt; - new_opts.s_resuid = sbi->s_resuid; - new_opts.s_resgid = sbi->s_resgid; - spin_unlock(&sbi->s_lock); - - if (!parse_options(data, sb, &new_opts)) - return -EINVAL; + new_opts.s_mount_opt = ctx->vals_s_mount_opt; + new_opts.s_resuid = ctx->s_resuid; + new_opts.s_resgid = ctx->s_resgid; spin_lock(&sbi->s_lock); es = sbi->s_es; @@ -1357,9 +1385,9 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) "dax flag with busy inodes while remounting"); new_opts.s_mount_opt ^= EXT2_MOUNT_DAX; } - if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) + if ((bool)(flags & SB_RDONLY) == sb_rdonly(sb)) goto out_set; - if (*flags & SB_RDONLY) { + if (flags & SB_RDONLY) { if (le16_to_cpu(es->s_state) & EXT2_VALID_FS || !(sbi->s_mount_state & EXT2_VALID_FS)) goto out_set; @@ -1478,10 +1506,9 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) return 0; } -static struct dentry *ext2_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ext2_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, ext2_fill_super); + return get_tree_bdev(fc, ext2_fill_super); } #ifdef CONFIG_QUOTA @@ -1564,7 +1591,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type, } lock_buffer(bh); memcpy(bh->b_data+offset, data, tocopy); - flush_dcache_page(bh->b_page); + flush_dcache_folio(bh->b_folio); set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); @@ -1580,7 +1607,7 @@ out: if (inode->i_size < off+len-towrite) i_size_write(inode, off+len-towrite); inode_inc_iversion(inode); - inode->i_mtime = inode->i_ctime = current_time(inode); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); mark_inode_dirty(inode); return len - towrite; } @@ -1632,12 +1659,49 @@ out: #endif +static const struct fs_context_operations ext2_context_ops = { + .parse_param = ext2_parse_param, + .get_tree = ext2_get_tree, + .reconfigure = ext2_reconfigure, + .free = ext2_free_fc, +}; + +static int ext2_init_fs_context(struct fs_context *fc) +{ + struct ext2_fs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + struct super_block *sb = fc->root->d_sb; + struct ext2_sb_info *sbi = EXT2_SB(sb); + + spin_lock(&sbi->s_lock); + ctx->vals_s_mount_opt = sbi->s_mount_opt; + ctx->vals_s_flags = sb->s_flags; + ctx->s_resuid = sbi->s_resuid; + ctx->s_resgid = sbi->s_resgid; + spin_unlock(&sbi->s_lock); + } else { + ctx->s_sb_block = 1; + ctx_set_mount_opt(ctx, EXT2_MOUNT_RESERVATION); + } + + fc->fs_private = ctx; + fc->ops = &ext2_context_ops; + + return 0; +} + static struct file_system_type ext2_fs_type = { .owner = THIS_MODULE, .name = "ext2", - .mount = ext2_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, + .init_fs_context = ext2_init_fs_context, + .parameters = ext2_param_spec, }; MODULE_ALIAS_FS("ext2"); diff --git a/fs/ext2/trace.c b/fs/ext2/trace.c new file mode 100644 index 000000000000..b01cdf6526fd --- /dev/null +++ b/fs/ext2/trace.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ext2.h" +#include <linux/uio.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/fs/ext2/trace.h b/fs/ext2/trace.h new file mode 100644 index 000000000000..7d230e13576e --- /dev/null +++ b/fs/ext2/trace.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ext2 + +#if !defined(_EXT2_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _EXT2_TRACE_H + +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(ext2_dio_class, + TP_PROTO(struct kiocb *iocb, struct iov_iter *iter, ssize_t ret), + TP_ARGS(iocb, iter, ret), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, isize) + __field(loff_t, pos) + __field(size_t, count) + __field(int, ki_flags) + __field(bool, aio) + __field(ssize_t, ret) + ), + TP_fast_assign( + __entry->dev = file_inode(iocb->ki_filp)->i_sb->s_dev; + __entry->ino = file_inode(iocb->ki_filp)->i_ino; + __entry->isize = file_inode(iocb->ki_filp)->i_size; + __entry->pos = iocb->ki_pos; + __entry->count = iov_iter_count(iter); + __entry->ki_flags = iocb->ki_flags; + __entry->aio = !is_sync_kiocb(iocb); + __entry->ret = ret; + ), + TP_printk("dev %d:%d ino 0x%lx isize 0x%llx pos 0x%llx len %zu flags %s aio %d ret %zd", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->isize, + __entry->pos, + __entry->count, + __print_flags(__entry->ki_flags, "|", TRACE_IOCB_STRINGS), + __entry->aio, + __entry->ret) +); + +#define DEFINE_DIO_RW_EVENT(name) \ +DEFINE_EVENT(ext2_dio_class, name, \ + TP_PROTO(struct kiocb *iocb, struct iov_iter *iter, ssize_t ret), \ + TP_ARGS(iocb, iter, ret)) +DEFINE_DIO_RW_EVENT(ext2_dio_write_begin); +DEFINE_DIO_RW_EVENT(ext2_dio_write_end); +DEFINE_DIO_RW_EVENT(ext2_dio_write_buff_end); +DEFINE_DIO_RW_EVENT(ext2_dio_read_begin); +DEFINE_DIO_RW_EVENT(ext2_dio_read_end); + +TRACE_EVENT(ext2_dio_write_endio, + TP_PROTO(struct kiocb *iocb, ssize_t size, int ret), + TP_ARGS(iocb, size, ret), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, isize) + __field(loff_t, pos) + __field(ssize_t, size) + __field(int, ki_flags) + __field(bool, aio) + __field(int, ret) + ), + TP_fast_assign( + __entry->dev = file_inode(iocb->ki_filp)->i_sb->s_dev; + __entry->ino = file_inode(iocb->ki_filp)->i_ino; + __entry->isize = file_inode(iocb->ki_filp)->i_size; + __entry->pos = iocb->ki_pos; + __entry->size = size; + __entry->ki_flags = iocb->ki_flags; + __entry->aio = !is_sync_kiocb(iocb); + __entry->ret = ret; + ), + TP_printk("dev %d:%d ino 0x%lx isize 0x%llx pos 0x%llx len %zd flags %s aio %d ret %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->isize, + __entry->pos, + __entry->size, + __print_flags(__entry->ki_flags, "|", TRACE_IOCB_STRINGS), + __entry->aio, + __entry->ret) +); + +#endif /* _EXT2_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace +#include <trace/define_trace.h> diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 641abfa4b718..c885dcc3bd0d 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -98,11 +98,11 @@ static struct buffer_head *ext2_xattr_cache_find(struct inode *, static void ext2_xattr_rehash(struct ext2_xattr_header *, struct ext2_xattr_entry *); -static const struct xattr_handler *ext2_xattr_handler_map[] = { +static const struct xattr_handler * const ext2_xattr_handler_map[] = { [EXT2_XATTR_INDEX_USER] = &ext2_xattr_user_handler, #ifdef CONFIG_EXT2_FS_POSIX_ACL - [EXT2_XATTR_INDEX_POSIX_ACL_ACCESS] = &posix_acl_access_xattr_handler, - [EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT] = &posix_acl_default_xattr_handler, + [EXT2_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access, + [EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default, #endif [EXT2_XATTR_INDEX_TRUSTED] = &ext2_xattr_trusted_handler, #ifdef CONFIG_EXT2_FS_SECURITY @@ -110,13 +110,9 @@ static const struct xattr_handler *ext2_xattr_handler_map[] = { #endif }; -const struct xattr_handler *ext2_xattr_handlers[] = { +const struct xattr_handler * const ext2_xattr_handlers[] = { &ext2_xattr_user_handler, &ext2_xattr_trusted_handler, -#ifdef CONFIG_EXT2_FS_POSIX_ACL - &posix_acl_access_xattr_handler, - &posix_acl_default_xattr_handler, -#endif #ifdef CONFIG_EXT2_FS_SECURITY &ext2_xattr_security_handler, #endif @@ -125,14 +121,18 @@ const struct xattr_handler *ext2_xattr_handlers[] = { #define EA_BLOCK_CACHE(inode) (EXT2_SB(inode->i_sb)->s_ea_block_cache) -static inline const struct xattr_handler * -ext2_xattr_handler(int name_index) +static inline const char *ext2_xattr_prefix(int name_index, + struct dentry *dentry) { const struct xattr_handler *handler = NULL; if (name_index > 0 && name_index < ARRAY_SIZE(ext2_xattr_handler_map)) handler = ext2_xattr_handler_map[name_index]; - return handler; + + if (!xattr_handler_can_list(handler, dentry)) + return NULL; + + return xattr_prefix(handler); } static bool @@ -333,11 +333,10 @@ bad_block: /* list the attribute names */ for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); entry = EXT2_XATTR_NEXT(entry)) { - const struct xattr_handler *handler = - ext2_xattr_handler(entry->e_name_index); + const char *prefix; - if (handler && (!handler->list || handler->list(dentry))) { - const char *prefix = handler->prefix ?: handler->name; + prefix = ext2_xattr_prefix(entry->e_name_index, dentry); + if (prefix) { size_t prefix_len = strlen(prefix); size_t size = prefix_len + entry->e_name_len + 1; @@ -553,7 +552,6 @@ bad_block: error = -ENOMEM; if (header == NULL) goto cleanup; - end = (char *)header + sb->s_blocksize; header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC); header->h_blocks = header->h_refcount = cpu_to_le32(1); last = here = ENTRY(header+1); @@ -744,10 +742,13 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* We need to allocate a new block */ ext2_fsblk_t goal = ext2_group_first_block_no(sb, EXT2_I(inode)->i_block_group); - int block = ext2_new_block(inode, goal, &error); + unsigned long count = 1; + ext2_fsblk_t block = ext2_new_blocks(inode, goal, + &count, &error, + EXT2_ALLOC_NORESERVE); if (error) goto cleanup; - ea_idebug(inode, "creating block %d", block); + ea_idebug(inode, "creating block %lu", block); new_bh = sb_getblk(sb, block); if (unlikely(!new_bh)) { @@ -775,7 +776,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* Update the inode. */ EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; - inode->i_ctime = current_time(inode); + inode_set_ctime_current(inode); if (IS_SYNC(inode)) { error = sync_inode_metadata(inode, 1); /* In case sync failed due to ENOSPC the inode was actually @@ -873,7 +874,7 @@ ext2_xattr_cache_insert(struct mb_cache *cache, struct buffer_head *bh) __u32 hash = le32_to_cpu(HDR(bh)->h_hash); int error; - error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr, + error = mb_cache_entry_create(cache, GFP_KERNEL, hash, bh->b_blocknr, true); if (error) { if (error == -EBUSY) { diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index 7925f596e8e2..6a4966949047 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -72,7 +72,7 @@ extern void ext2_xattr_delete_inode(struct inode *); extern struct mb_cache *ext2_xattr_create_cache(void); extern void ext2_xattr_destroy_cache(struct mb_cache *cache); -extern const struct xattr_handler *ext2_xattr_handlers[]; +extern const struct xattr_handler * const ext2_xattr_handlers[]; # else /* CONFIG_EXT2_FS_XATTR */ diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c index ebade1f52451..db47b8ab153e 100644 --- a/fs/ext2/xattr_security.c +++ b/fs/ext2/xattr_security.c @@ -19,7 +19,7 @@ ext2_xattr_security_get(const struct xattr_handler *handler, static int ext2_xattr_security_set(const struct xattr_handler *handler, - struct user_namespace *mnt_userns, + struct mnt_idmap *idmap, struct dentry *unused, struct inode *inode, const char *name, const void *value, size_t size, int flags) diff --git a/fs/ext2/xattr_trusted.c b/fs/ext2/xattr_trusted.c index 18a87d5dd1ab..995f931228ce 100644 --- a/fs/ext2/xattr_trusted.c +++ b/fs/ext2/xattr_trusted.c @@ -26,7 +26,7 @@ ext2_xattr_trusted_get(const struct xattr_handler *handler, static int ext2_xattr_trusted_set(const struct xattr_handler *handler, - struct user_namespace *mnt_userns, + struct mnt_idmap *idmap, struct dentry *unused, struct inode *inode, const char *name, const void *value, size_t size, int flags) diff --git a/fs/ext2/xattr_user.c b/fs/ext2/xattr_user.c index 58092449f8ff..dd1507231081 100644 --- a/fs/ext2/xattr_user.c +++ b/fs/ext2/xattr_user.c @@ -30,7 +30,7 @@ ext2_xattr_user_get(const struct xattr_handler *handler, static int ext2_xattr_user_set(const struct xattr_handler *handler, - struct user_namespace *mnt_userns, + struct mnt_idmap *idmap, struct dentry *unused, struct inode *inode, const char *name, const void *value, size_t size, int flags) |
