162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/ext4/ioctl.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1993, 1994, 1995 662306a36Sopenharmony_ci * Remy Card (card@masi.ibp.fr) 762306a36Sopenharmony_ci * Laboratoire MASI - Institut Blaise Pascal 862306a36Sopenharmony_ci * Universite Pierre et Marie Curie (Paris VI) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/capability.h> 1362306a36Sopenharmony_ci#include <linux/time.h> 1462306a36Sopenharmony_ci#include <linux/compat.h> 1562306a36Sopenharmony_ci#include <linux/mount.h> 1662306a36Sopenharmony_ci#include <linux/file.h> 1762306a36Sopenharmony_ci#include <linux/quotaops.h> 1862306a36Sopenharmony_ci#include <linux/random.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/iversion.h> 2262306a36Sopenharmony_ci#include <linux/fileattr.h> 2362306a36Sopenharmony_ci#include <linux/uuid.h> 2462306a36Sopenharmony_ci#include "ext4_jbd2.h" 2562306a36Sopenharmony_ci#include "ext4.h" 2662306a36Sopenharmony_ci#include <linux/fsmap.h> 2762306a36Sopenharmony_ci#include "fsmap.h" 2862306a36Sopenharmony_ci#include <trace/events/ext4.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_citypedef void ext4_update_sb_callback(struct ext4_super_block *es, 3162306a36Sopenharmony_ci const void *arg); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Superblock modification callback function for changing file system 3562306a36Sopenharmony_ci * label 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci /* Sanity check, this should never happen */ 4062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci memcpy(es->s_volume_name, (char *)arg, EXT4_LABEL_MAX); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Superblock modification callback function for changing file system 4762306a36Sopenharmony_ci * UUID. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic 5562306a36Sopenharmony_ciint ext4_update_primary_sb(struct super_block *sb, handle_t *handle, 5662306a36Sopenharmony_ci ext4_update_sb_callback func, 5762306a36Sopenharmony_ci const void *arg) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int err = 0; 6062306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 6162306a36Sopenharmony_ci struct buffer_head *bh = sbi->s_sbh; 6262306a36Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci trace_ext4_update_sb(sb, bh->b_blocknr, 1); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 6762306a36Sopenharmony_ci err = ext4_journal_get_write_access(handle, sb, 6862306a36Sopenharmony_ci bh, 6962306a36Sopenharmony_ci EXT4_JTR_NONE); 7062306a36Sopenharmony_ci if (err) 7162306a36Sopenharmony_ci goto out_err; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci lock_buffer(bh); 7462306a36Sopenharmony_ci func(es, arg); 7562306a36Sopenharmony_ci ext4_superblock_csum_set(sb); 7662306a36Sopenharmony_ci unlock_buffer(bh); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (buffer_write_io_error(bh) || !buffer_uptodate(bh)) { 7962306a36Sopenharmony_ci ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to " 8062306a36Sopenharmony_ci "superblock detected"); 8162306a36Sopenharmony_ci clear_buffer_write_io_error(bh); 8262306a36Sopenharmony_ci set_buffer_uptodate(bh); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 8662306a36Sopenharmony_ci if (err) 8762306a36Sopenharmony_ci goto out_err; 8862306a36Sopenharmony_ci err = sync_dirty_buffer(bh); 8962306a36Sopenharmony_ciout_err: 9062306a36Sopenharmony_ci ext4_std_error(sb, err); 9162306a36Sopenharmony_ci return err; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Update one backup superblock in the group 'grp' using the callback 9662306a36Sopenharmony_ci * function 'func' and argument 'arg'. If the handle is NULL the 9762306a36Sopenharmony_ci * modification is not journalled. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Returns: 0 when no modification was done (no superblock in the group) 10062306a36Sopenharmony_ci * 1 when the modification was successful 10162306a36Sopenharmony_ci * <0 on error 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic int ext4_update_backup_sb(struct super_block *sb, 10462306a36Sopenharmony_ci handle_t *handle, ext4_group_t grp, 10562306a36Sopenharmony_ci ext4_update_sb_callback func, const void *arg) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int err = 0; 10862306a36Sopenharmony_ci ext4_fsblk_t sb_block; 10962306a36Sopenharmony_ci struct buffer_head *bh; 11062306a36Sopenharmony_ci unsigned long offset = 0; 11162306a36Sopenharmony_ci struct ext4_super_block *es; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!ext4_bg_has_super(sb, grp)) 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * For the group 0 there is always 1k padding, so we have 11862306a36Sopenharmony_ci * either adjust offset, or sb_block depending on blocksize 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci if (grp == 0) { 12162306a36Sopenharmony_ci sb_block = 1 * EXT4_MIN_BLOCK_SIZE; 12262306a36Sopenharmony_ci offset = do_div(sb_block, sb->s_blocksize); 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci sb_block = ext4_group_first_block_no(sb, grp); 12562306a36Sopenharmony_ci offset = 0; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci trace_ext4_update_sb(sb, sb_block, handle ? 1 : 0); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci bh = ext4_sb_bread(sb, sb_block, 0); 13162306a36Sopenharmony_ci if (IS_ERR(bh)) 13262306a36Sopenharmony_ci return PTR_ERR(bh); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (handle) { 13562306a36Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 13662306a36Sopenharmony_ci err = ext4_journal_get_write_access(handle, sb, 13762306a36Sopenharmony_ci bh, 13862306a36Sopenharmony_ci EXT4_JTR_NONE); 13962306a36Sopenharmony_ci if (err) 14062306a36Sopenharmony_ci goto out_bh; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci es = (struct ext4_super_block *) (bh->b_data + offset); 14462306a36Sopenharmony_ci lock_buffer(bh); 14562306a36Sopenharmony_ci if (ext4_has_metadata_csum(sb) && 14662306a36Sopenharmony_ci es->s_checksum != ext4_superblock_csum(sb, es)) { 14762306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, "Invalid checksum for backup " 14862306a36Sopenharmony_ci "superblock %llu", sb_block); 14962306a36Sopenharmony_ci unlock_buffer(bh); 15062306a36Sopenharmony_ci goto out_bh; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci func(es, arg); 15362306a36Sopenharmony_ci if (ext4_has_metadata_csum(sb)) 15462306a36Sopenharmony_ci es->s_checksum = ext4_superblock_csum(sb, es); 15562306a36Sopenharmony_ci set_buffer_uptodate(bh); 15662306a36Sopenharmony_ci unlock_buffer(bh); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (handle) { 15962306a36Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 16062306a36Sopenharmony_ci if (err) 16162306a36Sopenharmony_ci goto out_bh; 16262306a36Sopenharmony_ci } else { 16362306a36Sopenharmony_ci BUFFER_TRACE(bh, "marking dirty"); 16462306a36Sopenharmony_ci mark_buffer_dirty(bh); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci err = sync_dirty_buffer(bh); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciout_bh: 16962306a36Sopenharmony_ci brelse(bh); 17062306a36Sopenharmony_ci ext4_std_error(sb, err); 17162306a36Sopenharmony_ci return (err) ? err : 1; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Update primary and backup superblocks using the provided function 17662306a36Sopenharmony_ci * func and argument arg. 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Only the primary superblock and at most two backup superblock 17962306a36Sopenharmony_ci * modifications are journalled; the rest is modified without journal. 18062306a36Sopenharmony_ci * This is safe because e2fsck will re-write them if there is a problem, 18162306a36Sopenharmony_ci * and we're very unlikely to ever need more than two backups. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic 18462306a36Sopenharmony_ciint ext4_update_superblocks_fn(struct super_block *sb, 18562306a36Sopenharmony_ci ext4_update_sb_callback func, 18662306a36Sopenharmony_ci const void *arg) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci handle_t *handle; 18962306a36Sopenharmony_ci ext4_group_t ngroups; 19062306a36Sopenharmony_ci unsigned int three = 1; 19162306a36Sopenharmony_ci unsigned int five = 5; 19262306a36Sopenharmony_ci unsigned int seven = 7; 19362306a36Sopenharmony_ci int err = 0, ret, i; 19462306a36Sopenharmony_ci ext4_group_t grp, primary_grp; 19562306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * We can't update superblocks while the online resize is running 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING, 20162306a36Sopenharmony_ci &sbi->s_ext4_flags)) { 20262306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, "Can't modify superblock while" 20362306a36Sopenharmony_ci "performing online resize"); 20462306a36Sopenharmony_ci return -EBUSY; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * We're only going to update primary superblock and two 20962306a36Sopenharmony_ci * backup superblocks in this transaction. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 3); 21262306a36Sopenharmony_ci if (IS_ERR(handle)) { 21362306a36Sopenharmony_ci err = PTR_ERR(handle); 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Update primary superblock */ 21862306a36Sopenharmony_ci err = ext4_update_primary_sb(sb, handle, func, arg); 21962306a36Sopenharmony_ci if (err) { 22062306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, "Failed to update primary " 22162306a36Sopenharmony_ci "superblock"); 22262306a36Sopenharmony_ci goto out_journal; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci primary_grp = ext4_get_group_number(sb, sbi->s_sbh->b_blocknr); 22662306a36Sopenharmony_ci ngroups = ext4_get_groups_count(sb); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Update backup superblocks. We have to start from group 0 23062306a36Sopenharmony_ci * because it might not be where the primary superblock is 23162306a36Sopenharmony_ci * if the fs is mounted with -o sb=<backup_sb_block> 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci i = 0; 23462306a36Sopenharmony_ci grp = 0; 23562306a36Sopenharmony_ci while (grp < ngroups) { 23662306a36Sopenharmony_ci /* Skip primary superblock */ 23762306a36Sopenharmony_ci if (grp == primary_grp) 23862306a36Sopenharmony_ci goto next_grp; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = ext4_update_backup_sb(sb, handle, grp, func, arg); 24162306a36Sopenharmony_ci if (ret < 0) { 24262306a36Sopenharmony_ci /* Ignore bad checksum; try to update next sb */ 24362306a36Sopenharmony_ci if (ret == -EFSBADCRC) 24462306a36Sopenharmony_ci goto next_grp; 24562306a36Sopenharmony_ci err = ret; 24662306a36Sopenharmony_ci goto out_journal; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci i += ret; 25062306a36Sopenharmony_ci if (handle && i > 1) { 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * We're only journalling primary superblock and 25362306a36Sopenharmony_ci * two backup superblocks; the rest is not 25462306a36Sopenharmony_ci * journalled. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci err = ext4_journal_stop(handle); 25762306a36Sopenharmony_ci if (err) 25862306a36Sopenharmony_ci goto out; 25962306a36Sopenharmony_ci handle = NULL; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_cinext_grp: 26262306a36Sopenharmony_ci grp = ext4_list_backups(sb, &three, &five, &seven); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciout_journal: 26662306a36Sopenharmony_ci if (handle) { 26762306a36Sopenharmony_ci ret = ext4_journal_stop(handle); 26862306a36Sopenharmony_ci if (ret && !err) 26962306a36Sopenharmony_ci err = ret; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ciout: 27262306a36Sopenharmony_ci clear_bit_unlock(EXT4_FLAGS_RESIZING, &sbi->s_ext4_flags); 27362306a36Sopenharmony_ci smp_mb__after_atomic(); 27462306a36Sopenharmony_ci return err ? err : 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Swap memory between @a and @b for @len bytes. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * @a: pointer to first memory area 28162306a36Sopenharmony_ci * @b: pointer to second memory area 28262306a36Sopenharmony_ci * @len: number of bytes to swap 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic void memswap(void *a, void *b, size_t len) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci unsigned char *ap, *bp; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ap = (unsigned char *)a; 29062306a36Sopenharmony_ci bp = (unsigned char *)b; 29162306a36Sopenharmony_ci while (len-- > 0) { 29262306a36Sopenharmony_ci swap(*ap, *bp); 29362306a36Sopenharmony_ci ap++; 29462306a36Sopenharmony_ci bp++; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * Swap i_data and associated attributes between @inode1 and @inode2. 30062306a36Sopenharmony_ci * This function is used for the primary swap between inode1 and inode2 30162306a36Sopenharmony_ci * and also to revert this primary swap in case of errors. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Therefore you have to make sure, that calling this method twice 30462306a36Sopenharmony_ci * will revert all changes. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * @inode1: pointer to first inode 30762306a36Sopenharmony_ci * @inode2: pointer to second inode 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic void swap_inode_data(struct inode *inode1, struct inode *inode2) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci loff_t isize; 31262306a36Sopenharmony_ci struct ext4_inode_info *ei1; 31362306a36Sopenharmony_ci struct ext4_inode_info *ei2; 31462306a36Sopenharmony_ci unsigned long tmp; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ei1 = EXT4_I(inode1); 31762306a36Sopenharmony_ci ei2 = EXT4_I(inode2); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci swap(inode1->i_version, inode2->i_version); 32062306a36Sopenharmony_ci swap(inode1->i_atime, inode2->i_atime); 32162306a36Sopenharmony_ci swap(inode1->i_mtime, inode2->i_mtime); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data)); 32462306a36Sopenharmony_ci tmp = ei1->i_flags & EXT4_FL_SHOULD_SWAP; 32562306a36Sopenharmony_ci ei1->i_flags = (ei2->i_flags & EXT4_FL_SHOULD_SWAP) | 32662306a36Sopenharmony_ci (ei1->i_flags & ~EXT4_FL_SHOULD_SWAP); 32762306a36Sopenharmony_ci ei2->i_flags = tmp | (ei2->i_flags & ~EXT4_FL_SHOULD_SWAP); 32862306a36Sopenharmony_ci swap(ei1->i_disksize, ei2->i_disksize); 32962306a36Sopenharmony_ci ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS); 33062306a36Sopenharmony_ci ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci isize = i_size_read(inode1); 33362306a36Sopenharmony_ci i_size_write(inode1, i_size_read(inode2)); 33462306a36Sopenharmony_ci i_size_write(inode2, isize); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_civoid ext4_reset_inode_seed(struct inode *inode) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 34062306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 34162306a36Sopenharmony_ci __le32 inum = cpu_to_le32(inode->i_ino); 34262306a36Sopenharmony_ci __le32 gen = cpu_to_le32(inode->i_generation); 34362306a36Sopenharmony_ci __u32 csum; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb)) 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, sizeof(inum)); 34962306a36Sopenharmony_ci ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, sizeof(gen)); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * Swap the information from the given @inode and the inode 35462306a36Sopenharmony_ci * EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other 35562306a36Sopenharmony_ci * important fields of the inodes. 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * @sb: the super block of the filesystem 35862306a36Sopenharmony_ci * @idmap: idmap of the mount the inode was found from 35962306a36Sopenharmony_ci * @inode: the inode to swap with EXT4_BOOT_LOADER_INO 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_cistatic long swap_inode_boot_loader(struct super_block *sb, 36362306a36Sopenharmony_ci struct mnt_idmap *idmap, 36462306a36Sopenharmony_ci struct inode *inode) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci handle_t *handle; 36762306a36Sopenharmony_ci int err; 36862306a36Sopenharmony_ci struct inode *inode_bl; 36962306a36Sopenharmony_ci struct ext4_inode_info *ei_bl; 37062306a36Sopenharmony_ci qsize_t size, size_bl, diff; 37162306a36Sopenharmony_ci blkcnt_t blocks; 37262306a36Sopenharmony_ci unsigned short bytes; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, 37562306a36Sopenharmony_ci EXT4_IGET_SPECIAL | EXT4_IGET_BAD); 37662306a36Sopenharmony_ci if (IS_ERR(inode_bl)) 37762306a36Sopenharmony_ci return PTR_ERR(inode_bl); 37862306a36Sopenharmony_ci ei_bl = EXT4_I(inode_bl); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Protect orig inodes against a truncate and make sure, 38162306a36Sopenharmony_ci * that only 1 swap_inode_boot_loader is running. */ 38262306a36Sopenharmony_ci lock_two_nondirectories(inode, inode_bl); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || 38562306a36Sopenharmony_ci IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || 38662306a36Sopenharmony_ci (EXT4_I(inode)->i_flags & EXT4_JOURNAL_DATA_FL) || 38762306a36Sopenharmony_ci ext4_has_inline_data(inode)) { 38862306a36Sopenharmony_ci err = -EINVAL; 38962306a36Sopenharmony_ci goto journal_err_out; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || 39362306a36Sopenharmony_ci !inode_owner_or_capable(idmap, inode) || 39462306a36Sopenharmony_ci !capable(CAP_SYS_ADMIN)) { 39562306a36Sopenharmony_ci err = -EPERM; 39662306a36Sopenharmony_ci goto journal_err_out; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 40062306a36Sopenharmony_ci err = filemap_write_and_wait(inode->i_mapping); 40162306a36Sopenharmony_ci if (err) 40262306a36Sopenharmony_ci goto err_out; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci err = filemap_write_and_wait(inode_bl->i_mapping); 40562306a36Sopenharmony_ci if (err) 40662306a36Sopenharmony_ci goto err_out; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Wait for all existing dio workers */ 40962306a36Sopenharmony_ci inode_dio_wait(inode); 41062306a36Sopenharmony_ci inode_dio_wait(inode_bl); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci truncate_inode_pages(&inode->i_data, 0); 41362306a36Sopenharmony_ci truncate_inode_pages(&inode_bl->i_data, 0); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2); 41662306a36Sopenharmony_ci if (IS_ERR(handle)) { 41762306a36Sopenharmony_ci err = -EINVAL; 41862306a36Sopenharmony_ci goto err_out; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT, handle); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Protect extent tree against block allocations via delalloc */ 42362306a36Sopenharmony_ci ext4_double_down_write_data_sem(inode, inode_bl); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (is_bad_inode(inode_bl) || !S_ISREG(inode_bl->i_mode)) { 42662306a36Sopenharmony_ci /* this inode has never been used as a BOOT_LOADER */ 42762306a36Sopenharmony_ci set_nlink(inode_bl, 1); 42862306a36Sopenharmony_ci i_uid_write(inode_bl, 0); 42962306a36Sopenharmony_ci i_gid_write(inode_bl, 0); 43062306a36Sopenharmony_ci inode_bl->i_flags = 0; 43162306a36Sopenharmony_ci ei_bl->i_flags = 0; 43262306a36Sopenharmony_ci inode_set_iversion(inode_bl, 1); 43362306a36Sopenharmony_ci i_size_write(inode_bl, 0); 43462306a36Sopenharmony_ci EXT4_I(inode_bl)->i_disksize = inode_bl->i_size; 43562306a36Sopenharmony_ci inode_bl->i_mode = S_IFREG; 43662306a36Sopenharmony_ci if (ext4_has_feature_extents(sb)) { 43762306a36Sopenharmony_ci ext4_set_inode_flag(inode_bl, EXT4_INODE_EXTENTS); 43862306a36Sopenharmony_ci ext4_ext_tree_init(handle, inode_bl); 43962306a36Sopenharmony_ci } else 44062306a36Sopenharmony_ci memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data)); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci err = dquot_initialize(inode); 44462306a36Sopenharmony_ci if (err) 44562306a36Sopenharmony_ci goto err_out1; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci size = (qsize_t)(inode->i_blocks) * (1 << 9) + inode->i_bytes; 44862306a36Sopenharmony_ci size_bl = (qsize_t)(inode_bl->i_blocks) * (1 << 9) + inode_bl->i_bytes; 44962306a36Sopenharmony_ci diff = size - size_bl; 45062306a36Sopenharmony_ci swap_inode_data(inode, inode_bl); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci inode_set_ctime_current(inode); 45362306a36Sopenharmony_ci inode_set_ctime_current(inode_bl); 45462306a36Sopenharmony_ci inode_inc_iversion(inode); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci inode->i_generation = get_random_u32(); 45762306a36Sopenharmony_ci inode_bl->i_generation = get_random_u32(); 45862306a36Sopenharmony_ci ext4_reset_inode_seed(inode); 45962306a36Sopenharmony_ci ext4_reset_inode_seed(inode_bl); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ext4_discard_preallocations(inode, 0); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode); 46462306a36Sopenharmony_ci if (err < 0) { 46562306a36Sopenharmony_ci /* No need to update quota information. */ 46662306a36Sopenharmony_ci ext4_warning(inode->i_sb, 46762306a36Sopenharmony_ci "couldn't mark inode #%lu dirty (err %d)", 46862306a36Sopenharmony_ci inode->i_ino, err); 46962306a36Sopenharmony_ci /* Revert all changes: */ 47062306a36Sopenharmony_ci swap_inode_data(inode, inode_bl); 47162306a36Sopenharmony_ci ext4_mark_inode_dirty(handle, inode); 47262306a36Sopenharmony_ci goto err_out1; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci blocks = inode_bl->i_blocks; 47662306a36Sopenharmony_ci bytes = inode_bl->i_bytes; 47762306a36Sopenharmony_ci inode_bl->i_blocks = inode->i_blocks; 47862306a36Sopenharmony_ci inode_bl->i_bytes = inode->i_bytes; 47962306a36Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode_bl); 48062306a36Sopenharmony_ci if (err < 0) { 48162306a36Sopenharmony_ci /* No need to update quota information. */ 48262306a36Sopenharmony_ci ext4_warning(inode_bl->i_sb, 48362306a36Sopenharmony_ci "couldn't mark inode #%lu dirty (err %d)", 48462306a36Sopenharmony_ci inode_bl->i_ino, err); 48562306a36Sopenharmony_ci goto revert; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Bootloader inode should not be counted into quota information. */ 48962306a36Sopenharmony_ci if (diff > 0) 49062306a36Sopenharmony_ci dquot_free_space(inode, diff); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci err = dquot_alloc_space(inode, -1 * diff); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (err < 0) { 49562306a36Sopenharmony_cirevert: 49662306a36Sopenharmony_ci /* Revert all changes: */ 49762306a36Sopenharmony_ci inode_bl->i_blocks = blocks; 49862306a36Sopenharmony_ci inode_bl->i_bytes = bytes; 49962306a36Sopenharmony_ci swap_inode_data(inode, inode_bl); 50062306a36Sopenharmony_ci ext4_mark_inode_dirty(handle, inode); 50162306a36Sopenharmony_ci ext4_mark_inode_dirty(handle, inode_bl); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cierr_out1: 50562306a36Sopenharmony_ci ext4_journal_stop(handle); 50662306a36Sopenharmony_ci ext4_double_up_write_data_sem(inode, inode_bl); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cierr_out: 50962306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 51062306a36Sopenharmony_cijournal_err_out: 51162306a36Sopenharmony_ci unlock_two_nondirectories(inode, inode_bl); 51262306a36Sopenharmony_ci iput(inode_bl); 51362306a36Sopenharmony_ci return err; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* 51762306a36Sopenharmony_ci * If immutable is set and we are not clearing it, we're not allowed to change 51862306a36Sopenharmony_ci * anything else in the inode. Don't error out if we're only trying to set 51962306a36Sopenharmony_ci * immutable on an immutable file. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic int ext4_ioctl_check_immutable(struct inode *inode, __u32 new_projid, 52262306a36Sopenharmony_ci unsigned int flags) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 52562306a36Sopenharmony_ci unsigned int oldflags = ei->i_flags; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (!(oldflags & EXT4_IMMUTABLE_FL) || !(flags & EXT4_IMMUTABLE_FL)) 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if ((oldflags & ~EXT4_IMMUTABLE_FL) != (flags & ~EXT4_IMMUTABLE_FL)) 53162306a36Sopenharmony_ci return -EPERM; 53262306a36Sopenharmony_ci if (ext4_has_feature_project(inode->i_sb) && 53362306a36Sopenharmony_ci __kprojid_val(ei->i_projid) != new_projid) 53462306a36Sopenharmony_ci return -EPERM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void ext4_dax_dontcache(struct inode *inode, unsigned int flags) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (test_opt2(inode->i_sb, DAX_NEVER) || 54762306a36Sopenharmony_ci test_opt(inode->i_sb, DAX_ALWAYS)) 54862306a36Sopenharmony_ci return; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if ((ei->i_flags ^ flags) & EXT4_DAX_FL) 55162306a36Sopenharmony_ci d_mark_dontcache(inode); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic bool dax_compatible(struct inode *inode, unsigned int oldflags, 55562306a36Sopenharmony_ci unsigned int flags) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci /* Allow the DAX flag to be changed on inline directories */ 55862306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 55962306a36Sopenharmony_ci flags &= ~EXT4_INLINE_DATA_FL; 56062306a36Sopenharmony_ci oldflags &= ~EXT4_INLINE_DATA_FL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (flags & EXT4_DAX_FL) { 56462306a36Sopenharmony_ci if ((oldflags & EXT4_DAX_MUT_EXCL) || 56562306a36Sopenharmony_ci ext4_test_inode_state(inode, 56662306a36Sopenharmony_ci EXT4_STATE_VERITY_IN_PROGRESS)) { 56762306a36Sopenharmony_ci return false; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if ((flags & EXT4_DAX_MUT_EXCL) && (oldflags & EXT4_DAX_FL)) 57262306a36Sopenharmony_ci return false; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return true; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int ext4_ioctl_setflags(struct inode *inode, 57862306a36Sopenharmony_ci unsigned int flags) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 58162306a36Sopenharmony_ci handle_t *handle = NULL; 58262306a36Sopenharmony_ci int err = -EPERM, migrate = 0; 58362306a36Sopenharmony_ci struct ext4_iloc iloc; 58462306a36Sopenharmony_ci unsigned int oldflags, mask, i; 58562306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Is it quota file? Do not allow user to mess with it */ 58862306a36Sopenharmony_ci if (ext4_is_quota_file(inode)) 58962306a36Sopenharmony_ci goto flags_out; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci oldflags = ei->i_flags; 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * The JOURNAL_DATA flag can only be changed by 59462306a36Sopenharmony_ci * the relevant capability. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci if ((flags ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { 59762306a36Sopenharmony_ci if (!capable(CAP_SYS_RESOURCE)) 59862306a36Sopenharmony_ci goto flags_out; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!dax_compatible(inode, oldflags, flags)) { 60262306a36Sopenharmony_ci err = -EOPNOTSUPP; 60362306a36Sopenharmony_ci goto flags_out; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if ((flags ^ oldflags) & EXT4_EXTENTS_FL) 60762306a36Sopenharmony_ci migrate = 1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if ((flags ^ oldflags) & EXT4_CASEFOLD_FL) { 61062306a36Sopenharmony_ci if (!ext4_has_feature_casefold(sb)) { 61162306a36Sopenharmony_ci err = -EOPNOTSUPP; 61262306a36Sopenharmony_ci goto flags_out; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 61662306a36Sopenharmony_ci err = -ENOTDIR; 61762306a36Sopenharmony_ci goto flags_out; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (!ext4_empty_dir(inode)) { 62162306a36Sopenharmony_ci err = -ENOTEMPTY; 62262306a36Sopenharmony_ci goto flags_out; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* 62762306a36Sopenharmony_ci * Wait for all pending directio and then flush all the dirty pages 62862306a36Sopenharmony_ci * for this file. The flush marks all the pages readonly, so any 62962306a36Sopenharmony_ci * subsequent attempt to write to the file (particularly mmap pages) 63062306a36Sopenharmony_ci * will come through the filesystem and fail. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci if (S_ISREG(inode->i_mode) && !IS_IMMUTABLE(inode) && 63362306a36Sopenharmony_ci (flags & EXT4_IMMUTABLE_FL)) { 63462306a36Sopenharmony_ci inode_dio_wait(inode); 63562306a36Sopenharmony_ci err = filemap_write_and_wait(inode->i_mapping); 63662306a36Sopenharmony_ci if (err) 63762306a36Sopenharmony_ci goto flags_out; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); 64162306a36Sopenharmony_ci if (IS_ERR(handle)) { 64262306a36Sopenharmony_ci err = PTR_ERR(handle); 64362306a36Sopenharmony_ci goto flags_out; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci if (IS_SYNC(inode)) 64662306a36Sopenharmony_ci ext4_handle_sync(handle); 64762306a36Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 64862306a36Sopenharmony_ci if (err) 64962306a36Sopenharmony_ci goto flags_err; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ext4_dax_dontcache(inode, flags); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (i = 0, mask = 1; i < 32; i++, mask <<= 1) { 65462306a36Sopenharmony_ci if (!(mask & EXT4_FL_USER_MODIFIABLE)) 65562306a36Sopenharmony_ci continue; 65662306a36Sopenharmony_ci /* These flags get special treatment later */ 65762306a36Sopenharmony_ci if (mask == EXT4_JOURNAL_DATA_FL || mask == EXT4_EXTENTS_FL) 65862306a36Sopenharmony_ci continue; 65962306a36Sopenharmony_ci if (mask & flags) 66062306a36Sopenharmony_ci ext4_set_inode_flag(inode, i); 66162306a36Sopenharmony_ci else 66262306a36Sopenharmony_ci ext4_clear_inode_flag(inode, i); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ext4_set_inode_flags(inode, false); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci inode_set_ctime_current(inode); 66862306a36Sopenharmony_ci inode_inc_iversion(inode); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci err = ext4_mark_iloc_dirty(handle, inode, &iloc); 67162306a36Sopenharmony_ciflags_err: 67262306a36Sopenharmony_ci ext4_journal_stop(handle); 67362306a36Sopenharmony_ci if (err) 67462306a36Sopenharmony_ci goto flags_out; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if ((flags ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { 67762306a36Sopenharmony_ci /* 67862306a36Sopenharmony_ci * Changes to the journaling mode can cause unsafe changes to 67962306a36Sopenharmony_ci * S_DAX if the inode is DAX 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci if (IS_DAX(inode)) { 68262306a36Sopenharmony_ci err = -EBUSY; 68362306a36Sopenharmony_ci goto flags_out; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci err = ext4_change_inode_journal_flag(inode, 68762306a36Sopenharmony_ci flags & EXT4_JOURNAL_DATA_FL); 68862306a36Sopenharmony_ci if (err) 68962306a36Sopenharmony_ci goto flags_out; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci if (migrate) { 69262306a36Sopenharmony_ci if (flags & EXT4_EXTENTS_FL) 69362306a36Sopenharmony_ci err = ext4_ext_migrate(inode); 69462306a36Sopenharmony_ci else 69562306a36Sopenharmony_ci err = ext4_ind_migrate(inode); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ciflags_out: 69962306a36Sopenharmony_ci return err; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci#ifdef CONFIG_QUOTA 70362306a36Sopenharmony_cistatic int ext4_ioctl_setproject(struct inode *inode, __u32 projid) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 70662306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 70762306a36Sopenharmony_ci int err, rc; 70862306a36Sopenharmony_ci handle_t *handle; 70962306a36Sopenharmony_ci kprojid_t kprojid; 71062306a36Sopenharmony_ci struct ext4_iloc iloc; 71162306a36Sopenharmony_ci struct ext4_inode *raw_inode; 71262306a36Sopenharmony_ci struct dquot *transfer_to[MAXQUOTAS] = { }; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!ext4_has_feature_project(sb)) { 71562306a36Sopenharmony_ci if (projid != EXT4_DEF_PROJID) 71662306a36Sopenharmony_ci return -EOPNOTSUPP; 71762306a36Sopenharmony_ci else 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE) 72262306a36Sopenharmony_ci return -EOPNOTSUPP; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci kprojid = make_kprojid(&init_user_ns, (projid_t)projid); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (projid_eq(kprojid, EXT4_I(inode)->i_projid)) 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci err = -EPERM; 73062306a36Sopenharmony_ci /* Is it quota file? Do not allow user to mess with it */ 73162306a36Sopenharmony_ci if (ext4_is_quota_file(inode)) 73262306a36Sopenharmony_ci return err; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci err = dquot_initialize(inode); 73562306a36Sopenharmony_ci if (err) 73662306a36Sopenharmony_ci return err; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci err = ext4_get_inode_loc(inode, &iloc); 73962306a36Sopenharmony_ci if (err) 74062306a36Sopenharmony_ci return err; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci raw_inode = ext4_raw_inode(&iloc); 74362306a36Sopenharmony_ci if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) { 74462306a36Sopenharmony_ci err = ext4_expand_extra_isize(inode, 74562306a36Sopenharmony_ci EXT4_SB(sb)->s_want_extra_isize, 74662306a36Sopenharmony_ci &iloc); 74762306a36Sopenharmony_ci if (err) 74862306a36Sopenharmony_ci return err; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci brelse(iloc.bh); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci handle = ext4_journal_start(inode, EXT4_HT_QUOTA, 75462306a36Sopenharmony_ci EXT4_QUOTA_INIT_BLOCKS(sb) + 75562306a36Sopenharmony_ci EXT4_QUOTA_DEL_BLOCKS(sb) + 3); 75662306a36Sopenharmony_ci if (IS_ERR(handle)) 75762306a36Sopenharmony_ci return PTR_ERR(handle); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 76062306a36Sopenharmony_ci if (err) 76162306a36Sopenharmony_ci goto out_stop; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); 76462306a36Sopenharmony_ci if (!IS_ERR(transfer_to[PRJQUOTA])) { 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* __dquot_transfer() calls back ext4_get_inode_usage() which 76762306a36Sopenharmony_ci * counts xattr inode references. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci down_read(&EXT4_I(inode)->xattr_sem); 77062306a36Sopenharmony_ci err = __dquot_transfer(inode, transfer_to); 77162306a36Sopenharmony_ci up_read(&EXT4_I(inode)->xattr_sem); 77262306a36Sopenharmony_ci dqput(transfer_to[PRJQUOTA]); 77362306a36Sopenharmony_ci if (err) 77462306a36Sopenharmony_ci goto out_dirty; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci EXT4_I(inode)->i_projid = kprojid; 77862306a36Sopenharmony_ci inode_set_ctime_current(inode); 77962306a36Sopenharmony_ci inode_inc_iversion(inode); 78062306a36Sopenharmony_ciout_dirty: 78162306a36Sopenharmony_ci rc = ext4_mark_iloc_dirty(handle, inode, &iloc); 78262306a36Sopenharmony_ci if (!err) 78362306a36Sopenharmony_ci err = rc; 78462306a36Sopenharmony_ciout_stop: 78562306a36Sopenharmony_ci ext4_journal_stop(handle); 78662306a36Sopenharmony_ci return err; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci#else 78962306a36Sopenharmony_cistatic int ext4_ioctl_setproject(struct inode *inode, __u32 projid) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci if (projid != EXT4_DEF_PROJID) 79262306a36Sopenharmony_ci return -EOPNOTSUPP; 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci#endif 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciint ext4_force_shutdown(struct super_block *sb, u32 flags) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 80062306a36Sopenharmony_ci int ret; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (flags > EXT4_GOING_FLAGS_NOLOGFLUSH) 80362306a36Sopenharmony_ci return -EINVAL; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (ext4_forced_shutdown(sb)) 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ext4_msg(sb, KERN_ALERT, "shut down requested (%d)", flags); 80962306a36Sopenharmony_ci trace_ext4_shutdown(sb, flags); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci switch (flags) { 81262306a36Sopenharmony_ci case EXT4_GOING_FLAGS_DEFAULT: 81362306a36Sopenharmony_ci ret = freeze_bdev(sb->s_bdev); 81462306a36Sopenharmony_ci if (ret) 81562306a36Sopenharmony_ci return ret; 81662306a36Sopenharmony_ci set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); 81762306a36Sopenharmony_ci thaw_bdev(sb->s_bdev); 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case EXT4_GOING_FLAGS_LOGFLUSH: 82062306a36Sopenharmony_ci set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); 82162306a36Sopenharmony_ci if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) { 82262306a36Sopenharmony_ci (void) ext4_force_commit(sb); 82362306a36Sopenharmony_ci jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci case EXT4_GOING_FLAGS_NOLOGFLUSH: 82762306a36Sopenharmony_ci set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); 82862306a36Sopenharmony_ci if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) 82962306a36Sopenharmony_ci jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN); 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci default: 83262306a36Sopenharmony_ci return -EINVAL; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci clear_opt(sb, DISCARD); 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int ext4_ioctl_shutdown(struct super_block *sb, unsigned long arg) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci u32 flags; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 84362306a36Sopenharmony_ci return -EPERM; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (get_user(flags, (__u32 __user *)arg)) 84662306a36Sopenharmony_ci return -EFAULT; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return ext4_force_shutdown(sb, flags); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistruct getfsmap_info { 85262306a36Sopenharmony_ci struct super_block *gi_sb; 85362306a36Sopenharmony_ci struct fsmap_head __user *gi_data; 85462306a36Sopenharmony_ci unsigned int gi_idx; 85562306a36Sopenharmony_ci __u32 gi_last_flags; 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int ext4_getfsmap_format(struct ext4_fsmap *xfm, void *priv) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct getfsmap_info *info = priv; 86162306a36Sopenharmony_ci struct fsmap fm; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci trace_ext4_getfsmap_mapping(info->gi_sb, xfm); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci info->gi_last_flags = xfm->fmr_flags; 86662306a36Sopenharmony_ci ext4_fsmap_from_internal(info->gi_sb, &fm, xfm); 86762306a36Sopenharmony_ci if (copy_to_user(&info->gi_data->fmh_recs[info->gi_idx++], &fm, 86862306a36Sopenharmony_ci sizeof(struct fsmap))) 86962306a36Sopenharmony_ci return -EFAULT; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return 0; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int ext4_ioc_getfsmap(struct super_block *sb, 87562306a36Sopenharmony_ci struct fsmap_head __user *arg) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct getfsmap_info info = { NULL }; 87862306a36Sopenharmony_ci struct ext4_fsmap_head xhead = {0}; 87962306a36Sopenharmony_ci struct fsmap_head head; 88062306a36Sopenharmony_ci bool aborted = false; 88162306a36Sopenharmony_ci int error; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (copy_from_user(&head, arg, sizeof(struct fsmap_head))) 88462306a36Sopenharmony_ci return -EFAULT; 88562306a36Sopenharmony_ci if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) || 88662306a36Sopenharmony_ci memchr_inv(head.fmh_keys[0].fmr_reserved, 0, 88762306a36Sopenharmony_ci sizeof(head.fmh_keys[0].fmr_reserved)) || 88862306a36Sopenharmony_ci memchr_inv(head.fmh_keys[1].fmr_reserved, 0, 88962306a36Sopenharmony_ci sizeof(head.fmh_keys[1].fmr_reserved))) 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci /* 89262306a36Sopenharmony_ci * ext4 doesn't report file extents at all, so the only valid 89362306a36Sopenharmony_ci * file offsets are the magic ones (all zeroes or all ones). 89462306a36Sopenharmony_ci */ 89562306a36Sopenharmony_ci if (head.fmh_keys[0].fmr_offset || 89662306a36Sopenharmony_ci (head.fmh_keys[1].fmr_offset != 0 && 89762306a36Sopenharmony_ci head.fmh_keys[1].fmr_offset != -1ULL)) 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci xhead.fmh_iflags = head.fmh_iflags; 90162306a36Sopenharmony_ci xhead.fmh_count = head.fmh_count; 90262306a36Sopenharmony_ci ext4_fsmap_to_internal(sb, &xhead.fmh_keys[0], &head.fmh_keys[0]); 90362306a36Sopenharmony_ci ext4_fsmap_to_internal(sb, &xhead.fmh_keys[1], &head.fmh_keys[1]); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci trace_ext4_getfsmap_low_key(sb, &xhead.fmh_keys[0]); 90662306a36Sopenharmony_ci trace_ext4_getfsmap_high_key(sb, &xhead.fmh_keys[1]); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci info.gi_sb = sb; 90962306a36Sopenharmony_ci info.gi_data = arg; 91062306a36Sopenharmony_ci error = ext4_getfsmap(sb, &xhead, ext4_getfsmap_format, &info); 91162306a36Sopenharmony_ci if (error == EXT4_QUERY_RANGE_ABORT) 91262306a36Sopenharmony_ci aborted = true; 91362306a36Sopenharmony_ci else if (error) 91462306a36Sopenharmony_ci return error; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* If we didn't abort, set the "last" flag in the last fmx */ 91762306a36Sopenharmony_ci if (!aborted && info.gi_idx) { 91862306a36Sopenharmony_ci info.gi_last_flags |= FMR_OF_LAST; 91962306a36Sopenharmony_ci if (copy_to_user(&info.gi_data->fmh_recs[info.gi_idx - 1].fmr_flags, 92062306a36Sopenharmony_ci &info.gi_last_flags, 92162306a36Sopenharmony_ci sizeof(info.gi_last_flags))) 92262306a36Sopenharmony_ci return -EFAULT; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* copy back header */ 92662306a36Sopenharmony_ci head.fmh_entries = xhead.fmh_entries; 92762306a36Sopenharmony_ci head.fmh_oflags = xhead.fmh_oflags; 92862306a36Sopenharmony_ci if (copy_to_user(arg, &head, sizeof(struct fsmap_head))) 92962306a36Sopenharmony_ci return -EFAULT; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic long ext4_ioctl_group_add(struct file *file, 93562306a36Sopenharmony_ci struct ext4_new_group_data *input) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct super_block *sb = file_inode(file)->i_sb; 93862306a36Sopenharmony_ci int err, err2=0; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci err = ext4_resize_begin(sb); 94162306a36Sopenharmony_ci if (err) 94262306a36Sopenharmony_ci return err; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (ext4_has_feature_bigalloc(sb)) { 94562306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, 94662306a36Sopenharmony_ci "Online resizing not supported with bigalloc"); 94762306a36Sopenharmony_ci err = -EOPNOTSUPP; 94862306a36Sopenharmony_ci goto group_add_out; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci err = mnt_want_write_file(file); 95262306a36Sopenharmony_ci if (err) 95362306a36Sopenharmony_ci goto group_add_out; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci err = ext4_group_add(sb, input); 95662306a36Sopenharmony_ci if (EXT4_SB(sb)->s_journal) { 95762306a36Sopenharmony_ci jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 95862306a36Sopenharmony_ci err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0); 95962306a36Sopenharmony_ci jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci if (err == 0) 96262306a36Sopenharmony_ci err = err2; 96362306a36Sopenharmony_ci mnt_drop_write_file(file); 96462306a36Sopenharmony_ci if (!err && ext4_has_group_desc_csum(sb) && 96562306a36Sopenharmony_ci test_opt(sb, INIT_INODE_TABLE)) 96662306a36Sopenharmony_ci err = ext4_register_li_request(sb, input->group); 96762306a36Sopenharmony_cigroup_add_out: 96862306a36Sopenharmony_ci err2 = ext4_resize_end(sb, false); 96962306a36Sopenharmony_ci if (err == 0) 97062306a36Sopenharmony_ci err = err2; 97162306a36Sopenharmony_ci return err; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ciint ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 97762306a36Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 97862306a36Sopenharmony_ci u32 flags = ei->i_flags & EXT4_FL_USER_VISIBLE; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) 98162306a36Sopenharmony_ci flags &= ~FS_PROJINHERIT_FL; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci fileattr_fill_flags(fa, flags); 98462306a36Sopenharmony_ci if (ext4_has_feature_project(inode->i_sb)) 98562306a36Sopenharmony_ci fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ciint ext4_fileattr_set(struct mnt_idmap *idmap, 99162306a36Sopenharmony_ci struct dentry *dentry, struct fileattr *fa) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 99462306a36Sopenharmony_ci u32 flags = fa->flags; 99562306a36Sopenharmony_ci int err = -EOPNOTSUPP; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (flags & ~EXT4_FL_USER_VISIBLE) 99862306a36Sopenharmony_ci goto out; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /* 100162306a36Sopenharmony_ci * chattr(1) grabs flags via GETFLAGS, modifies the result and 100262306a36Sopenharmony_ci * passes that to SETFLAGS. So we cannot easily make SETFLAGS 100362306a36Sopenharmony_ci * more restrictive than just silently masking off visible but 100462306a36Sopenharmony_ci * not settable flags as we always did. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci flags &= EXT4_FL_USER_MODIFIABLE; 100762306a36Sopenharmony_ci if (ext4_mask_flags(inode->i_mode, flags) != flags) 100862306a36Sopenharmony_ci goto out; 100962306a36Sopenharmony_ci err = ext4_ioctl_check_immutable(inode, fa->fsx_projid, flags); 101062306a36Sopenharmony_ci if (err) 101162306a36Sopenharmony_ci goto out; 101262306a36Sopenharmony_ci err = ext4_ioctl_setflags(inode, flags); 101362306a36Sopenharmony_ci if (err) 101462306a36Sopenharmony_ci goto out; 101562306a36Sopenharmony_ci err = ext4_ioctl_setproject(inode, fa->fsx_projid); 101662306a36Sopenharmony_ciout: 101762306a36Sopenharmony_ci return err; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci/* So that the fiemap access checks can't overflow on 32 bit machines. */ 102162306a36Sopenharmony_ci#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct fiemap fiemap; 102662306a36Sopenharmony_ci struct fiemap __user *ufiemap = (struct fiemap __user *) arg; 102762306a36Sopenharmony_ci struct fiemap_extent_info fieinfo = { 0, }; 102862306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 102962306a36Sopenharmony_ci int error; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap))) 103262306a36Sopenharmony_ci return -EFAULT; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci fieinfo.fi_flags = fiemap.fm_flags; 103862306a36Sopenharmony_ci fieinfo.fi_extents_max = fiemap.fm_extent_count; 103962306a36Sopenharmony_ci fieinfo.fi_extents_start = ufiemap->fm_extents; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start, 104262306a36Sopenharmony_ci fiemap.fm_length); 104362306a36Sopenharmony_ci fiemap.fm_flags = fieinfo.fi_flags; 104462306a36Sopenharmony_ci fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; 104562306a36Sopenharmony_ci if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap))) 104662306a36Sopenharmony_ci error = -EFAULT; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return error; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci int err = 0; 105462306a36Sopenharmony_ci __u32 flags = 0; 105562306a36Sopenharmony_ci unsigned int flush_flags = 0; 105662306a36Sopenharmony_ci struct super_block *sb = file_inode(filp)->i_sb; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (copy_from_user(&flags, (__u32 __user *)arg, 105962306a36Sopenharmony_ci sizeof(__u32))) 106062306a36Sopenharmony_ci return -EFAULT; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 106362306a36Sopenharmony_ci return -EPERM; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* check for invalid bits set */ 106662306a36Sopenharmony_ci if ((flags & ~EXT4_IOC_CHECKPOINT_FLAG_VALID) || 106762306a36Sopenharmony_ci ((flags & JBD2_JOURNAL_FLUSH_DISCARD) && 106862306a36Sopenharmony_ci (flags & JBD2_JOURNAL_FLUSH_ZEROOUT))) 106962306a36Sopenharmony_ci return -EINVAL; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (!EXT4_SB(sb)->s_journal) 107262306a36Sopenharmony_ci return -ENODEV; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if ((flags & JBD2_JOURNAL_FLUSH_DISCARD) && 107562306a36Sopenharmony_ci !bdev_max_discard_sectors(EXT4_SB(sb)->s_journal->j_dev)) 107662306a36Sopenharmony_ci return -EOPNOTSUPP; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (flags & EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN) 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (flags & EXT4_IOC_CHECKPOINT_FLAG_DISCARD) 108262306a36Sopenharmony_ci flush_flags |= JBD2_JOURNAL_FLUSH_DISCARD; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (flags & EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT) { 108562306a36Sopenharmony_ci flush_flags |= JBD2_JOURNAL_FLUSH_ZEROOUT; 108662306a36Sopenharmony_ci pr_info_ratelimited("warning: checkpointing journal with EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT can be slow"); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 109062306a36Sopenharmony_ci err = jbd2_journal_flush(EXT4_SB(sb)->s_journal, flush_flags); 109162306a36Sopenharmony_ci jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return err; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int ext4_ioctl_setlabel(struct file *filp, const char __user *user_label) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci size_t len; 109962306a36Sopenharmony_ci int ret = 0; 110062306a36Sopenharmony_ci char new_label[EXT4_LABEL_MAX + 1]; 110162306a36Sopenharmony_ci struct super_block *sb = file_inode(filp)->i_sb; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 110462306a36Sopenharmony_ci return -EPERM; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * Copy the maximum length allowed for ext4 label with one more to 110862306a36Sopenharmony_ci * find the required terminating null byte in order to test the 110962306a36Sopenharmony_ci * label length. The on disk label doesn't need to be null terminated. 111062306a36Sopenharmony_ci */ 111162306a36Sopenharmony_ci if (copy_from_user(new_label, user_label, EXT4_LABEL_MAX + 1)) 111262306a36Sopenharmony_ci return -EFAULT; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci len = strnlen(new_label, EXT4_LABEL_MAX + 1); 111562306a36Sopenharmony_ci if (len > EXT4_LABEL_MAX) 111662306a36Sopenharmony_ci return -EINVAL; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* 111962306a36Sopenharmony_ci * Clear the buffer after the new label 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci memset(new_label + len, 0, EXT4_LABEL_MAX - len); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci ret = mnt_want_write_file(filp); 112462306a36Sopenharmony_ci if (ret) 112562306a36Sopenharmony_ci return ret; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci ret = ext4_update_superblocks_fn(sb, ext4_sb_setlabel, new_label); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci mnt_drop_write_file(filp); 113062306a36Sopenharmony_ci return ret; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic int ext4_ioctl_getlabel(struct ext4_sb_info *sbi, char __user *user_label) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci char label[EXT4_LABEL_MAX + 1]; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci * EXT4_LABEL_MAX must always be smaller than FSLABEL_MAX because 113962306a36Sopenharmony_ci * FSLABEL_MAX must include terminating null byte, while s_volume_name 114062306a36Sopenharmony_ci * does not have to. 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci BUILD_BUG_ON(EXT4_LABEL_MAX >= FSLABEL_MAX); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci memset(label, 0, sizeof(label)); 114562306a36Sopenharmony_ci lock_buffer(sbi->s_sbh); 114662306a36Sopenharmony_ci strncpy(label, sbi->s_es->s_volume_name, EXT4_LABEL_MAX); 114762306a36Sopenharmony_ci unlock_buffer(sbi->s_sbh); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (copy_to_user(user_label, label, sizeof(label))) 115062306a36Sopenharmony_ci return -EFAULT; 115162306a36Sopenharmony_ci return 0; 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int ext4_ioctl_getuuid(struct ext4_sb_info *sbi, 115562306a36Sopenharmony_ci struct fsuuid __user *ufsuuid) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci struct fsuuid fsuuid; 115862306a36Sopenharmony_ci __u8 uuid[UUID_SIZE]; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid))) 116162306a36Sopenharmony_ci return -EFAULT; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (fsuuid.fsu_len == 0) { 116462306a36Sopenharmony_ci fsuuid.fsu_len = UUID_SIZE; 116562306a36Sopenharmony_ci if (copy_to_user(&ufsuuid->fsu_len, &fsuuid.fsu_len, 116662306a36Sopenharmony_ci sizeof(fsuuid.fsu_len))) 116762306a36Sopenharmony_ci return -EFAULT; 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (fsuuid.fsu_len < UUID_SIZE || fsuuid.fsu_flags != 0) 117262306a36Sopenharmony_ci return -EINVAL; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci lock_buffer(sbi->s_sbh); 117562306a36Sopenharmony_ci memcpy(uuid, sbi->s_es->s_uuid, UUID_SIZE); 117662306a36Sopenharmony_ci unlock_buffer(sbi->s_sbh); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci fsuuid.fsu_len = UUID_SIZE; 117962306a36Sopenharmony_ci if (copy_to_user(ufsuuid, &fsuuid, sizeof(fsuuid)) || 118062306a36Sopenharmony_ci copy_to_user(&ufsuuid->fsu_uuid[0], uuid, UUID_SIZE)) 118162306a36Sopenharmony_ci return -EFAULT; 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int ext4_ioctl_setuuid(struct file *filp, 118662306a36Sopenharmony_ci const struct fsuuid __user *ufsuuid) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci int ret = 0; 118962306a36Sopenharmony_ci struct super_block *sb = file_inode(filp)->i_sb; 119062306a36Sopenharmony_ci struct fsuuid fsuuid; 119162306a36Sopenharmony_ci __u8 uuid[UUID_SIZE]; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 119462306a36Sopenharmony_ci return -EPERM; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* 119762306a36Sopenharmony_ci * If any checksums (group descriptors or metadata) are being used 119862306a36Sopenharmony_ci * then the checksum seed feature is required to change the UUID. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (((ext4_has_feature_gdt_csum(sb) || ext4_has_metadata_csum(sb)) 120162306a36Sopenharmony_ci && !ext4_has_feature_csum_seed(sb)) 120262306a36Sopenharmony_ci || ext4_has_feature_stable_inodes(sb)) 120362306a36Sopenharmony_ci return -EOPNOTSUPP; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid))) 120662306a36Sopenharmony_ci return -EFAULT; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (fsuuid.fsu_len != UUID_SIZE || fsuuid.fsu_flags != 0) 120962306a36Sopenharmony_ci return -EINVAL; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (copy_from_user(uuid, &ufsuuid->fsu_uuid[0], UUID_SIZE)) 121262306a36Sopenharmony_ci return -EFAULT; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci ret = mnt_want_write_file(filp); 121562306a36Sopenharmony_ci if (ret) 121662306a36Sopenharmony_ci return ret; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci ret = ext4_update_superblocks_fn(sb, ext4_sb_setuuid, &uuid); 121962306a36Sopenharmony_ci mnt_drop_write_file(filp); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return ret; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 122762306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 122862306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(filp); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci switch (cmd) { 123362306a36Sopenharmony_ci case FS_IOC_GETFSMAP: 123462306a36Sopenharmony_ci return ext4_ioc_getfsmap(sb, (void __user *)arg); 123562306a36Sopenharmony_ci case EXT4_IOC_GETVERSION: 123662306a36Sopenharmony_ci case EXT4_IOC_GETVERSION_OLD: 123762306a36Sopenharmony_ci return put_user(inode->i_generation, (int __user *) arg); 123862306a36Sopenharmony_ci case EXT4_IOC_SETVERSION: 123962306a36Sopenharmony_ci case EXT4_IOC_SETVERSION_OLD: { 124062306a36Sopenharmony_ci handle_t *handle; 124162306a36Sopenharmony_ci struct ext4_iloc iloc; 124262306a36Sopenharmony_ci __u32 generation; 124362306a36Sopenharmony_ci int err; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 124662306a36Sopenharmony_ci return -EPERM; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) { 124962306a36Sopenharmony_ci ext4_warning(sb, "Setting inode version is not " 125062306a36Sopenharmony_ci "supported with metadata_csum enabled."); 125162306a36Sopenharmony_ci return -ENOTTY; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci err = mnt_want_write_file(filp); 125562306a36Sopenharmony_ci if (err) 125662306a36Sopenharmony_ci return err; 125762306a36Sopenharmony_ci if (get_user(generation, (int __user *) arg)) { 125862306a36Sopenharmony_ci err = -EFAULT; 125962306a36Sopenharmony_ci goto setversion_out; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci inode_lock(inode); 126362306a36Sopenharmony_ci handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); 126462306a36Sopenharmony_ci if (IS_ERR(handle)) { 126562306a36Sopenharmony_ci err = PTR_ERR(handle); 126662306a36Sopenharmony_ci goto unlock_out; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 126962306a36Sopenharmony_ci if (err == 0) { 127062306a36Sopenharmony_ci inode_set_ctime_current(inode); 127162306a36Sopenharmony_ci inode_inc_iversion(inode); 127262306a36Sopenharmony_ci inode->i_generation = generation; 127362306a36Sopenharmony_ci err = ext4_mark_iloc_dirty(handle, inode, &iloc); 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci ext4_journal_stop(handle); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ciunlock_out: 127862306a36Sopenharmony_ci inode_unlock(inode); 127962306a36Sopenharmony_cisetversion_out: 128062306a36Sopenharmony_ci mnt_drop_write_file(filp); 128162306a36Sopenharmony_ci return err; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci case EXT4_IOC_GROUP_EXTEND: { 128462306a36Sopenharmony_ci ext4_fsblk_t n_blocks_count; 128562306a36Sopenharmony_ci int err, err2=0; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci err = ext4_resize_begin(sb); 128862306a36Sopenharmony_ci if (err) 128962306a36Sopenharmony_ci return err; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (get_user(n_blocks_count, (__u32 __user *)arg)) { 129262306a36Sopenharmony_ci err = -EFAULT; 129362306a36Sopenharmony_ci goto group_extend_out; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci if (ext4_has_feature_bigalloc(sb)) { 129762306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, 129862306a36Sopenharmony_ci "Online resizing not supported with bigalloc"); 129962306a36Sopenharmony_ci err = -EOPNOTSUPP; 130062306a36Sopenharmony_ci goto group_extend_out; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci err = mnt_want_write_file(filp); 130462306a36Sopenharmony_ci if (err) 130562306a36Sopenharmony_ci goto group_extend_out; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); 130862306a36Sopenharmony_ci if (EXT4_SB(sb)->s_journal) { 130962306a36Sopenharmony_ci jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 131062306a36Sopenharmony_ci err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0); 131162306a36Sopenharmony_ci jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci if (err == 0) 131462306a36Sopenharmony_ci err = err2; 131562306a36Sopenharmony_ci mnt_drop_write_file(filp); 131662306a36Sopenharmony_cigroup_extend_out: 131762306a36Sopenharmony_ci err2 = ext4_resize_end(sb, false); 131862306a36Sopenharmony_ci if (err == 0) 131962306a36Sopenharmony_ci err = err2; 132062306a36Sopenharmony_ci return err; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci case EXT4_IOC_MOVE_EXT: { 132462306a36Sopenharmony_ci struct move_extent me; 132562306a36Sopenharmony_ci struct fd donor; 132662306a36Sopenharmony_ci int err; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (!(filp->f_mode & FMODE_READ) || 132962306a36Sopenharmony_ci !(filp->f_mode & FMODE_WRITE)) 133062306a36Sopenharmony_ci return -EBADF; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (copy_from_user(&me, 133362306a36Sopenharmony_ci (struct move_extent __user *)arg, sizeof(me))) 133462306a36Sopenharmony_ci return -EFAULT; 133562306a36Sopenharmony_ci me.moved_len = 0; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci donor = fdget(me.donor_fd); 133862306a36Sopenharmony_ci if (!donor.file) 133962306a36Sopenharmony_ci return -EBADF; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci if (!(donor.file->f_mode & FMODE_WRITE)) { 134262306a36Sopenharmony_ci err = -EBADF; 134362306a36Sopenharmony_ci goto mext_out; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (ext4_has_feature_bigalloc(sb)) { 134762306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, 134862306a36Sopenharmony_ci "Online defrag not supported with bigalloc"); 134962306a36Sopenharmony_ci err = -EOPNOTSUPP; 135062306a36Sopenharmony_ci goto mext_out; 135162306a36Sopenharmony_ci } else if (IS_DAX(inode)) { 135262306a36Sopenharmony_ci ext4_msg(sb, KERN_ERR, 135362306a36Sopenharmony_ci "Online defrag not supported with DAX"); 135462306a36Sopenharmony_ci err = -EOPNOTSUPP; 135562306a36Sopenharmony_ci goto mext_out; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci err = mnt_want_write_file(filp); 135962306a36Sopenharmony_ci if (err) 136062306a36Sopenharmony_ci goto mext_out; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci err = ext4_move_extents(filp, donor.file, me.orig_start, 136362306a36Sopenharmony_ci me.donor_start, me.len, &me.moved_len); 136462306a36Sopenharmony_ci mnt_drop_write_file(filp); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (copy_to_user((struct move_extent __user *)arg, 136762306a36Sopenharmony_ci &me, sizeof(me))) 136862306a36Sopenharmony_ci err = -EFAULT; 136962306a36Sopenharmony_cimext_out: 137062306a36Sopenharmony_ci fdput(donor); 137162306a36Sopenharmony_ci return err; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci case EXT4_IOC_GROUP_ADD: { 137562306a36Sopenharmony_ci struct ext4_new_group_data input; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, 137862306a36Sopenharmony_ci sizeof(input))) 137962306a36Sopenharmony_ci return -EFAULT; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return ext4_ioctl_group_add(filp, &input); 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci case EXT4_IOC_MIGRATE: 138562306a36Sopenharmony_ci { 138662306a36Sopenharmony_ci int err; 138762306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 138862306a36Sopenharmony_ci return -EACCES; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci err = mnt_want_write_file(filp); 139162306a36Sopenharmony_ci if (err) 139262306a36Sopenharmony_ci return err; 139362306a36Sopenharmony_ci /* 139462306a36Sopenharmony_ci * inode_mutex prevent write and truncate on the file. 139562306a36Sopenharmony_ci * Read still goes through. We take i_data_sem in 139662306a36Sopenharmony_ci * ext4_ext_swap_inode_data before we switch the 139762306a36Sopenharmony_ci * inode format to prevent read. 139862306a36Sopenharmony_ci */ 139962306a36Sopenharmony_ci inode_lock((inode)); 140062306a36Sopenharmony_ci err = ext4_ext_migrate(inode); 140162306a36Sopenharmony_ci inode_unlock((inode)); 140262306a36Sopenharmony_ci mnt_drop_write_file(filp); 140362306a36Sopenharmony_ci return err; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci case EXT4_IOC_ALLOC_DA_BLKS: 140762306a36Sopenharmony_ci { 140862306a36Sopenharmony_ci int err; 140962306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 141062306a36Sopenharmony_ci return -EACCES; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci err = mnt_want_write_file(filp); 141362306a36Sopenharmony_ci if (err) 141462306a36Sopenharmony_ci return err; 141562306a36Sopenharmony_ci err = ext4_alloc_da_blocks(inode); 141662306a36Sopenharmony_ci mnt_drop_write_file(filp); 141762306a36Sopenharmony_ci return err; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci case EXT4_IOC_SWAP_BOOT: 142162306a36Sopenharmony_ci { 142262306a36Sopenharmony_ci int err; 142362306a36Sopenharmony_ci if (!(filp->f_mode & FMODE_WRITE)) 142462306a36Sopenharmony_ci return -EBADF; 142562306a36Sopenharmony_ci err = mnt_want_write_file(filp); 142662306a36Sopenharmony_ci if (err) 142762306a36Sopenharmony_ci return err; 142862306a36Sopenharmony_ci err = swap_inode_boot_loader(sb, idmap, inode); 142962306a36Sopenharmony_ci mnt_drop_write_file(filp); 143062306a36Sopenharmony_ci return err; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci case EXT4_IOC_RESIZE_FS: { 143462306a36Sopenharmony_ci ext4_fsblk_t n_blocks_count; 143562306a36Sopenharmony_ci int err = 0, err2 = 0; 143662306a36Sopenharmony_ci ext4_group_t o_group = EXT4_SB(sb)->s_groups_count; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (copy_from_user(&n_blocks_count, (__u64 __user *)arg, 143962306a36Sopenharmony_ci sizeof(__u64))) { 144062306a36Sopenharmony_ci return -EFAULT; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci err = ext4_resize_begin(sb); 144462306a36Sopenharmony_ci if (err) 144562306a36Sopenharmony_ci return err; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci err = mnt_want_write_file(filp); 144862306a36Sopenharmony_ci if (err) 144962306a36Sopenharmony_ci goto resizefs_out; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci err = ext4_resize_fs(sb, n_blocks_count); 145262306a36Sopenharmony_ci if (EXT4_SB(sb)->s_journal) { 145362306a36Sopenharmony_ci ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE, NULL); 145462306a36Sopenharmony_ci jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 145562306a36Sopenharmony_ci err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0); 145662306a36Sopenharmony_ci jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci if (err == 0) 145962306a36Sopenharmony_ci err = err2; 146062306a36Sopenharmony_ci mnt_drop_write_file(filp); 146162306a36Sopenharmony_ci if (!err && (o_group < EXT4_SB(sb)->s_groups_count) && 146262306a36Sopenharmony_ci ext4_has_group_desc_csum(sb) && 146362306a36Sopenharmony_ci test_opt(sb, INIT_INODE_TABLE)) 146462306a36Sopenharmony_ci err = ext4_register_li_request(sb, o_group); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ciresizefs_out: 146762306a36Sopenharmony_ci err2 = ext4_resize_end(sb, true); 146862306a36Sopenharmony_ci if (err == 0) 146962306a36Sopenharmony_ci err = err2; 147062306a36Sopenharmony_ci return err; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci case FITRIM: 147462306a36Sopenharmony_ci { 147562306a36Sopenharmony_ci struct fstrim_range range; 147662306a36Sopenharmony_ci int ret = 0; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 147962306a36Sopenharmony_ci return -EPERM; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (!bdev_max_discard_sectors(sb->s_bdev)) 148262306a36Sopenharmony_ci return -EOPNOTSUPP; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* 148562306a36Sopenharmony_ci * We haven't replayed the journal, so we cannot use our 148662306a36Sopenharmony_ci * block-bitmap-guided storage zapping commands. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_ci if (test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) 148962306a36Sopenharmony_ci return -EROFS; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (copy_from_user(&range, (struct fstrim_range __user *)arg, 149262306a36Sopenharmony_ci sizeof(range))) 149362306a36Sopenharmony_ci return -EFAULT; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci ret = ext4_trim_fs(sb, &range); 149662306a36Sopenharmony_ci if (ret < 0) 149762306a36Sopenharmony_ci return ret; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (copy_to_user((struct fstrim_range __user *)arg, &range, 150062306a36Sopenharmony_ci sizeof(range))) 150162306a36Sopenharmony_ci return -EFAULT; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci return 0; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci case EXT4_IOC_PRECACHE_EXTENTS: 150662306a36Sopenharmony_ci return ext4_ext_precache(inode); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci case FS_IOC_SET_ENCRYPTION_POLICY: 150962306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 151062306a36Sopenharmony_ci return -EOPNOTSUPP; 151162306a36Sopenharmony_ci return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_PWSALT: 151462306a36Sopenharmony_ci return ext4_ioctl_get_encryption_pwsalt(filp, (void __user *)arg); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_POLICY: 151762306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 151862306a36Sopenharmony_ci return -EOPNOTSUPP; 151962306a36Sopenharmony_ci return fscrypt_ioctl_get_policy(filp, (void __user *)arg); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_POLICY_EX: 152262306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 152362306a36Sopenharmony_ci return -EOPNOTSUPP; 152462306a36Sopenharmony_ci return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci case FS_IOC_ADD_ENCRYPTION_KEY: 152762306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 152862306a36Sopenharmony_ci return -EOPNOTSUPP; 152962306a36Sopenharmony_ci return fscrypt_ioctl_add_key(filp, (void __user *)arg); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci case FS_IOC_REMOVE_ENCRYPTION_KEY: 153262306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 153362306a36Sopenharmony_ci return -EOPNOTSUPP; 153462306a36Sopenharmony_ci return fscrypt_ioctl_remove_key(filp, (void __user *)arg); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: 153762306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 153862306a36Sopenharmony_ci return -EOPNOTSUPP; 153962306a36Sopenharmony_ci return fscrypt_ioctl_remove_key_all_users(filp, 154062306a36Sopenharmony_ci (void __user *)arg); 154162306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_KEY_STATUS: 154262306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 154362306a36Sopenharmony_ci return -EOPNOTSUPP; 154462306a36Sopenharmony_ci return fscrypt_ioctl_get_key_status(filp, (void __user *)arg); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_NONCE: 154762306a36Sopenharmony_ci if (!ext4_has_feature_encrypt(sb)) 154862306a36Sopenharmony_ci return -EOPNOTSUPP; 154962306a36Sopenharmony_ci return fscrypt_ioctl_get_nonce(filp, (void __user *)arg); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci case EXT4_IOC_CLEAR_ES_CACHE: 155262306a36Sopenharmony_ci { 155362306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 155462306a36Sopenharmony_ci return -EACCES; 155562306a36Sopenharmony_ci ext4_clear_inode_es(inode); 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci case EXT4_IOC_GETSTATE: 156062306a36Sopenharmony_ci { 156162306a36Sopenharmony_ci __u32 state = 0; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_EXT_PRECACHED)) 156462306a36Sopenharmony_ci state |= EXT4_STATE_FLAG_EXT_PRECACHED; 156562306a36Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_NEW)) 156662306a36Sopenharmony_ci state |= EXT4_STATE_FLAG_NEW; 156762306a36Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) 156862306a36Sopenharmony_ci state |= EXT4_STATE_FLAG_NEWENTRY; 156962306a36Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE)) 157062306a36Sopenharmony_ci state |= EXT4_STATE_FLAG_DA_ALLOC_CLOSE; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return put_user(state, (__u32 __user *) arg); 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci case EXT4_IOC_GET_ES_CACHE: 157662306a36Sopenharmony_ci return ext4_ioctl_get_es_cache(filp, arg); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci case EXT4_IOC_SHUTDOWN: 157962306a36Sopenharmony_ci return ext4_ioctl_shutdown(sb, arg); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci case FS_IOC_ENABLE_VERITY: 158262306a36Sopenharmony_ci if (!ext4_has_feature_verity(sb)) 158362306a36Sopenharmony_ci return -EOPNOTSUPP; 158462306a36Sopenharmony_ci return fsverity_ioctl_enable(filp, (const void __user *)arg); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci case FS_IOC_MEASURE_VERITY: 158762306a36Sopenharmony_ci if (!ext4_has_feature_verity(sb)) 158862306a36Sopenharmony_ci return -EOPNOTSUPP; 158962306a36Sopenharmony_ci return fsverity_ioctl_measure(filp, (void __user *)arg); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci case FS_IOC_READ_VERITY_METADATA: 159262306a36Sopenharmony_ci if (!ext4_has_feature_verity(sb)) 159362306a36Sopenharmony_ci return -EOPNOTSUPP; 159462306a36Sopenharmony_ci return fsverity_ioctl_read_metadata(filp, 159562306a36Sopenharmony_ci (const void __user *)arg); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci case EXT4_IOC_CHECKPOINT: 159862306a36Sopenharmony_ci return ext4_ioctl_checkpoint(filp, arg); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci case FS_IOC_GETFSLABEL: 160162306a36Sopenharmony_ci return ext4_ioctl_getlabel(EXT4_SB(sb), (void __user *)arg); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci case FS_IOC_SETFSLABEL: 160462306a36Sopenharmony_ci return ext4_ioctl_setlabel(filp, 160562306a36Sopenharmony_ci (const void __user *)arg); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci case EXT4_IOC_GETFSUUID: 160862306a36Sopenharmony_ci return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg); 160962306a36Sopenharmony_ci case EXT4_IOC_SETFSUUID: 161062306a36Sopenharmony_ci return ext4_ioctl_setuuid(filp, (const void __user *)arg); 161162306a36Sopenharmony_ci default: 161262306a36Sopenharmony_ci return -ENOTTY; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cilong ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci return __ext4_ioctl(filp, cmd, arg); 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 162262306a36Sopenharmony_cilong ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci /* These are just misnamed, they actually get/put from/to user an int */ 162562306a36Sopenharmony_ci switch (cmd) { 162662306a36Sopenharmony_ci case EXT4_IOC32_GETVERSION: 162762306a36Sopenharmony_ci cmd = EXT4_IOC_GETVERSION; 162862306a36Sopenharmony_ci break; 162962306a36Sopenharmony_ci case EXT4_IOC32_SETVERSION: 163062306a36Sopenharmony_ci cmd = EXT4_IOC_SETVERSION; 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci case EXT4_IOC32_GROUP_EXTEND: 163362306a36Sopenharmony_ci cmd = EXT4_IOC_GROUP_EXTEND; 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci case EXT4_IOC32_GETVERSION_OLD: 163662306a36Sopenharmony_ci cmd = EXT4_IOC_GETVERSION_OLD; 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci case EXT4_IOC32_SETVERSION_OLD: 163962306a36Sopenharmony_ci cmd = EXT4_IOC_SETVERSION_OLD; 164062306a36Sopenharmony_ci break; 164162306a36Sopenharmony_ci case EXT4_IOC32_GETRSVSZ: 164262306a36Sopenharmony_ci cmd = EXT4_IOC_GETRSVSZ; 164362306a36Sopenharmony_ci break; 164462306a36Sopenharmony_ci case EXT4_IOC32_SETRSVSZ: 164562306a36Sopenharmony_ci cmd = EXT4_IOC_SETRSVSZ; 164662306a36Sopenharmony_ci break; 164762306a36Sopenharmony_ci case EXT4_IOC32_GROUP_ADD: { 164862306a36Sopenharmony_ci struct compat_ext4_new_group_input __user *uinput; 164962306a36Sopenharmony_ci struct ext4_new_group_data input; 165062306a36Sopenharmony_ci int err; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci uinput = compat_ptr(arg); 165362306a36Sopenharmony_ci err = get_user(input.group, &uinput->group); 165462306a36Sopenharmony_ci err |= get_user(input.block_bitmap, &uinput->block_bitmap); 165562306a36Sopenharmony_ci err |= get_user(input.inode_bitmap, &uinput->inode_bitmap); 165662306a36Sopenharmony_ci err |= get_user(input.inode_table, &uinput->inode_table); 165762306a36Sopenharmony_ci err |= get_user(input.blocks_count, &uinput->blocks_count); 165862306a36Sopenharmony_ci err |= get_user(input.reserved_blocks, 165962306a36Sopenharmony_ci &uinput->reserved_blocks); 166062306a36Sopenharmony_ci if (err) 166162306a36Sopenharmony_ci return -EFAULT; 166262306a36Sopenharmony_ci return ext4_ioctl_group_add(file, &input); 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci case EXT4_IOC_MOVE_EXT: 166562306a36Sopenharmony_ci case EXT4_IOC_RESIZE_FS: 166662306a36Sopenharmony_ci case FITRIM: 166762306a36Sopenharmony_ci case EXT4_IOC_PRECACHE_EXTENTS: 166862306a36Sopenharmony_ci case FS_IOC_SET_ENCRYPTION_POLICY: 166962306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_PWSALT: 167062306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_POLICY: 167162306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_POLICY_EX: 167262306a36Sopenharmony_ci case FS_IOC_ADD_ENCRYPTION_KEY: 167362306a36Sopenharmony_ci case FS_IOC_REMOVE_ENCRYPTION_KEY: 167462306a36Sopenharmony_ci case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: 167562306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_KEY_STATUS: 167662306a36Sopenharmony_ci case FS_IOC_GET_ENCRYPTION_NONCE: 167762306a36Sopenharmony_ci case EXT4_IOC_SHUTDOWN: 167862306a36Sopenharmony_ci case FS_IOC_GETFSMAP: 167962306a36Sopenharmony_ci case FS_IOC_ENABLE_VERITY: 168062306a36Sopenharmony_ci case FS_IOC_MEASURE_VERITY: 168162306a36Sopenharmony_ci case FS_IOC_READ_VERITY_METADATA: 168262306a36Sopenharmony_ci case EXT4_IOC_CLEAR_ES_CACHE: 168362306a36Sopenharmony_ci case EXT4_IOC_GETSTATE: 168462306a36Sopenharmony_ci case EXT4_IOC_GET_ES_CACHE: 168562306a36Sopenharmony_ci case EXT4_IOC_CHECKPOINT: 168662306a36Sopenharmony_ci case FS_IOC_GETFSLABEL: 168762306a36Sopenharmony_ci case FS_IOC_SETFSLABEL: 168862306a36Sopenharmony_ci case EXT4_IOC_GETFSUUID: 168962306a36Sopenharmony_ci case EXT4_IOC_SETFSUUID: 169062306a36Sopenharmony_ci break; 169162306a36Sopenharmony_ci default: 169262306a36Sopenharmony_ci return -ENOIOCTLCMD; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci#endif 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_cistatic void set_overhead(struct ext4_super_block *es, const void *arg) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg)); 170162306a36Sopenharmony_ci} 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ciint ext4_update_overhead(struct super_block *sb, bool force) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (sb_rdonly(sb)) 170862306a36Sopenharmony_ci return 0; 170962306a36Sopenharmony_ci if (!force && 171062306a36Sopenharmony_ci (sbi->s_overhead == 0 || 171162306a36Sopenharmony_ci sbi->s_overhead == le32_to_cpu(sbi->s_es->s_overhead_clusters))) 171262306a36Sopenharmony_ci return 0; 171362306a36Sopenharmony_ci return ext4_update_superblocks_fn(sb, set_overhead, &sbi->s_overhead); 171462306a36Sopenharmony_ci} 1715