License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 14:07:57 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2006-10-11 08:20:50 +00:00
|
|
|
/*
|
2006-10-11 08:20:53 +00:00
|
|
|
* linux/fs/ext4/ioctl.c
|
2006-10-11 08:20:50 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 1993, 1994, 1995
|
|
|
|
* Remy Card (card@masi.ibp.fr)
|
|
|
|
* Laboratoire MASI - Institut Blaise Pascal
|
|
|
|
* Universite Pierre et Marie Curie (Paris VI)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/capability.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/compat.h>
|
2008-02-15 22:37:46 +00:00
|
|
|
#include <linux/mount.h>
|
2009-06-17 23:24:03 +00:00
|
|
|
#include <linux/file.h>
|
2016-01-08 21:01:22 +00:00
|
|
|
#include <linux/quotaops.h>
|
2017-11-09 03:23:20 +00:00
|
|
|
#include <linux/random.h>
|
2016-12-24 19:46:01 +00:00
|
|
|
#include <linux/uaccess.h>
|
2017-02-06 00:47:14 +00:00
|
|
|
#include <linux/delay.h>
|
2018-01-09 13:21:39 +00:00
|
|
|
#include <linux/iversion.h>
|
2021-04-07 12:36:43 +00:00
|
|
|
#include <linux/fileattr.h>
|
2022-07-21 22:44:22 +00:00
|
|
|
#include <linux/uuid.h>
|
2008-04-29 22:13:32 +00:00
|
|
|
#include "ext4_jbd2.h"
|
|
|
|
#include "ext4.h"
|
2017-04-30 04:36:53 +00:00
|
|
|
#include <linux/fsmap.h>
|
|
|
|
#include "fsmap.h"
|
|
|
|
#include <trace/events/ext4.h>
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2021-12-13 13:56:18 +00:00
|
|
|
typedef void ext4_update_sb_callback(struct ext4_super_block *es,
|
|
|
|
const void *arg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Superblock modification callback function for changing file system
|
|
|
|
* label
|
|
|
|
*/
|
|
|
|
static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg)
|
|
|
|
{
|
|
|
|
/* Sanity check, this should never happen */
|
|
|
|
BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX);
|
|
|
|
|
|
|
|
memcpy(es->s_volume_name, (char *)arg, EXT4_LABEL_MAX);
|
|
|
|
}
|
|
|
|
|
2022-07-21 22:44:22 +00:00
|
|
|
/*
|
|
|
|
* Superblock modification callback function for changing file system
|
|
|
|
* UUID.
|
|
|
|
*/
|
|
|
|
static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg)
|
|
|
|
{
|
|
|
|
memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE);
|
|
|
|
}
|
|
|
|
|
2021-12-13 13:56:18 +00:00
|
|
|
static
|
|
|
|
int ext4_update_primary_sb(struct super_block *sb, handle_t *handle,
|
|
|
|
ext4_update_sb_callback func,
|
|
|
|
const void *arg)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
|
struct buffer_head *bh = sbi->s_sbh;
|
|
|
|
struct ext4_super_block *es = sbi->s_es;
|
|
|
|
|
|
|
|
trace_ext4_update_sb(sb, bh->b_blocknr, 1);
|
|
|
|
|
|
|
|
BUFFER_TRACE(bh, "get_write_access");
|
|
|
|
err = ext4_journal_get_write_access(handle, sb,
|
|
|
|
bh,
|
|
|
|
EXT4_JTR_NONE);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
lock_buffer(bh);
|
|
|
|
func(es, arg);
|
|
|
|
ext4_superblock_csum_set(sb);
|
|
|
|
unlock_buffer(bh);
|
|
|
|
|
|
|
|
if (buffer_write_io_error(bh) || !buffer_uptodate(bh)) {
|
|
|
|
ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to "
|
|
|
|
"superblock detected");
|
|
|
|
clear_buffer_write_io_error(bh);
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ext4_handle_dirty_metadata(handle, NULL, bh);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
err = sync_dirty_buffer(bh);
|
|
|
|
out_err:
|
|
|
|
ext4_std_error(sb, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update one backup superblock in the group 'grp' using the callback
|
|
|
|
* function 'func' and argument 'arg'. If the handle is NULL the
|
|
|
|
* modification is not journalled.
|
|
|
|
*
|
|
|
|
* Returns: 0 when no modification was done (no superblock in the group)
|
|
|
|
* 1 when the modification was successful
|
|
|
|
* <0 on error
|
|
|
|
*/
|
|
|
|
static int ext4_update_backup_sb(struct super_block *sb,
|
|
|
|
handle_t *handle, ext4_group_t grp,
|
|
|
|
ext4_update_sb_callback func, const void *arg)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
ext4_fsblk_t sb_block;
|
|
|
|
struct buffer_head *bh;
|
|
|
|
unsigned long offset = 0;
|
|
|
|
struct ext4_super_block *es;
|
|
|
|
|
|
|
|
if (!ext4_bg_has_super(sb, grp))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the group 0 there is always 1k padding, so we have
|
|
|
|
* either adjust offset, or sb_block depending on blocksize
|
|
|
|
*/
|
|
|
|
if (grp == 0) {
|
|
|
|
sb_block = 1 * EXT4_MIN_BLOCK_SIZE;
|
|
|
|
offset = do_div(sb_block, sb->s_blocksize);
|
|
|
|
} else {
|
|
|
|
sb_block = ext4_group_first_block_no(sb, grp);
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_ext4_update_sb(sb, sb_block, handle ? 1 : 0);
|
|
|
|
|
|
|
|
bh = ext4_sb_bread(sb, sb_block, 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
|
|
|
|
|
|
|
if (handle) {
|
|
|
|
BUFFER_TRACE(bh, "get_write_access");
|
|
|
|
err = ext4_journal_get_write_access(handle, sb,
|
|
|
|
bh,
|
|
|
|
EXT4_JTR_NONE);
|
|
|
|
if (err)
|
|
|
|
goto out_bh;
|
|
|
|
}
|
|
|
|
|
|
|
|
es = (struct ext4_super_block *) (bh->b_data + offset);
|
|
|
|
lock_buffer(bh);
|
|
|
|
if (ext4_has_metadata_csum(sb) &&
|
|
|
|
es->s_checksum != ext4_superblock_csum(sb, es)) {
|
|
|
|
ext4_msg(sb, KERN_ERR, "Invalid checksum for backup "
|
ext4: update the backup superblock's at the end of the online resize
When expanding a file system using online resize, various fields in
the superblock (e.g., s_blocks_count, s_inodes_count, etc.) change.
To update the backup superblocks, the online resize uses the function
update_backups() in fs/ext4/resize.c. This function was not updating
the checksum field in the backup superblocks. This wasn't a big deal
previously, because e2fsck didn't care about the checksum field in the
backup superblock. (And indeed, update_backups() goes all the way
back to the ext3 days, well before we had support for metadata
checksums.)
However, there is an alternate, more general way of updating
superblock fields, ext4_update_primary_sb() in fs/ext4/ioctl.c. This
function does check the checksum of the backup superblock, and if it
doesn't match will mark the file system as corrupted. That was
clearly not the intent, so avoid to aborting the resize when a bad
superblock is found.
In addition, teach update_backups() to properly update the checksum in
the backup superblocks. We will eventually want to unify
updapte_backups() with the infrasture in ext4_update_primary_sb(), but
that's for another day.
Note: The problem has been around for a while; it just didn't really
matter until ext4_update_primary_sb() was added by commit bbc605cdb1e1
("ext4: implement support for get/set fs label"). And it became
trivially easy to reproduce after commit 827891a38acc ("ext4: update
the s_overhead_clusters in the backup sb's when resizing") in v6.0.
Cc: stable@kernel.org # 5.17+
Fixes: bbc605cdb1e1 ("ext4: implement support for get/set fs label")
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2022-10-27 20:04:36 +00:00
|
|
|
"superblock %llu", sb_block);
|
2021-12-13 13:56:18 +00:00
|
|
|
unlock_buffer(bh);
|
|
|
|
goto out_bh;
|
|
|
|
}
|
|
|
|
func(es, arg);
|
|
|
|
if (ext4_has_metadata_csum(sb))
|
|
|
|
es->s_checksum = ext4_superblock_csum(sb, es);
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
unlock_buffer(bh);
|
|
|
|
|
|
|
|
if (handle) {
|
|
|
|
err = ext4_handle_dirty_metadata(handle, NULL, bh);
|
|
|
|
if (err)
|
|
|
|
goto out_bh;
|
|
|
|
} else {
|
|
|
|
BUFFER_TRACE(bh, "marking dirty");
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
}
|
|
|
|
err = sync_dirty_buffer(bh);
|
|
|
|
|
|
|
|
out_bh:
|
|
|
|
brelse(bh);
|
|
|
|
ext4_std_error(sb, err);
|
|
|
|
return (err) ? err : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update primary and backup superblocks using the provided function
|
|
|
|
* func and argument arg.
|
|
|
|
*
|
|
|
|
* Only the primary superblock and at most two backup superblock
|
|
|
|
* modifications are journalled; the rest is modified without journal.
|
|
|
|
* This is safe because e2fsck will re-write them if there is a problem,
|
|
|
|
* and we're very unlikely to ever need more than two backups.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int ext4_update_superblocks_fn(struct super_block *sb,
|
|
|
|
ext4_update_sb_callback func,
|
|
|
|
const void *arg)
|
|
|
|
{
|
|
|
|
handle_t *handle;
|
|
|
|
ext4_group_t ngroups;
|
|
|
|
unsigned int three = 1;
|
|
|
|
unsigned int five = 5;
|
|
|
|
unsigned int seven = 7;
|
|
|
|
int err = 0, ret, i;
|
|
|
|
ext4_group_t grp, primary_grp;
|
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can't update superblocks while the online resize is running
|
|
|
|
*/
|
|
|
|
if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING,
|
|
|
|
&sbi->s_ext4_flags)) {
|
|
|
|
ext4_msg(sb, KERN_ERR, "Can't modify superblock while"
|
|
|
|
"performing online resize");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're only going to update primary superblock and two
|
|
|
|
* backup superblocks in this transaction.
|
|
|
|
*/
|
|
|
|
handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 3);
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
err = PTR_ERR(handle);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update primary superblock */
|
|
|
|
err = ext4_update_primary_sb(sb, handle, func, arg);
|
|
|
|
if (err) {
|
|
|
|
ext4_msg(sb, KERN_ERR, "Failed to update primary "
|
|
|
|
"superblock");
|
|
|
|
goto out_journal;
|
|
|
|
}
|
|
|
|
|
|
|
|
primary_grp = ext4_get_group_number(sb, sbi->s_sbh->b_blocknr);
|
|
|
|
ngroups = ext4_get_groups_count(sb);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update backup superblocks. We have to start from group 0
|
|
|
|
* because it might not be where the primary superblock is
|
|
|
|
* if the fs is mounted with -o sb=<backup_sb_block>
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
grp = 0;
|
|
|
|
while (grp < ngroups) {
|
|
|
|
/* Skip primary superblock */
|
|
|
|
if (grp == primary_grp)
|
|
|
|
goto next_grp;
|
|
|
|
|
|
|
|
ret = ext4_update_backup_sb(sb, handle, grp, func, arg);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* Ignore bad checksum; try to update next sb */
|
|
|
|
if (ret == -EFSBADCRC)
|
|
|
|
goto next_grp;
|
|
|
|
err = ret;
|
|
|
|
goto out_journal;
|
|
|
|
}
|
|
|
|
|
|
|
|
i += ret;
|
|
|
|
if (handle && i > 1) {
|
|
|
|
/*
|
|
|
|
* We're only journalling primary superblock and
|
|
|
|
* two backup superblocks; the rest is not
|
|
|
|
* journalled.
|
|
|
|
*/
|
|
|
|
err = ext4_journal_stop(handle);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
handle = NULL;
|
|
|
|
}
|
|
|
|
next_grp:
|
|
|
|
grp = ext4_list_backups(sb, &three, &five, &seven);
|
|
|
|
}
|
|
|
|
|
|
|
|
out_journal:
|
|
|
|
if (handle) {
|
|
|
|
ret = ext4_journal_stop(handle);
|
|
|
|
if (ret && !err)
|
|
|
|
err = ret;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
clear_bit_unlock(EXT4_FLAGS_RESIZING, &sbi->s_ext4_flags);
|
|
|
|
smp_mb__after_atomic();
|
|
|
|
return err ? err : 0;
|
|
|
|
}
|
|
|
|
|
2022-03-13 02:39:35 +00:00
|
|
|
/*
|
2013-04-08 16:54:05 +00:00
|
|
|
* Swap memory between @a and @b for @len bytes.
|
|
|
|
*
|
|
|
|
* @a: pointer to first memory area
|
|
|
|
* @b: pointer to second memory area
|
|
|
|
* @len: number of bytes to swap
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void memswap(void *a, void *b, size_t len)
|
|
|
|
{
|
|
|
|
unsigned char *ap, *bp;
|
|
|
|
|
|
|
|
ap = (unsigned char *)a;
|
|
|
|
bp = (unsigned char *)b;
|
|
|
|
while (len-- > 0) {
|
2015-06-13 03:46:33 +00:00
|
|
|
swap(*ap, *bp);
|
2013-04-08 16:54:05 +00:00
|
|
|
ap++;
|
|
|
|
bp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-13 02:39:35 +00:00
|
|
|
/*
|
2013-04-08 16:54:05 +00:00
|
|
|
* Swap i_data and associated attributes between @inode1 and @inode2.
|
|
|
|
* This function is used for the primary swap between inode1 and inode2
|
|
|
|
* and also to revert this primary swap in case of errors.
|
|
|
|
*
|
|
|
|
* Therefore you have to make sure, that calling this method twice
|
|
|
|
* will revert all changes.
|
|
|
|
*
|
|
|
|
* @inode1: pointer to first inode
|
|
|
|
* @inode2: pointer to second inode
|
|
|
|
*/
|
|
|
|
static void swap_inode_data(struct inode *inode1, struct inode *inode2)
|
|
|
|
{
|
|
|
|
loff_t isize;
|
|
|
|
struct ext4_inode_info *ei1;
|
|
|
|
struct ext4_inode_info *ei2;
|
2019-02-11 05:35:06 +00:00
|
|
|
unsigned long tmp;
|
2023-10-04 18:52:20 +00:00
|
|
|
struct timespec64 ts1, ts2;
|
2013-04-08 16:54:05 +00:00
|
|
|
|
|
|
|
ei1 = EXT4_I(inode1);
|
|
|
|
ei2 = EXT4_I(inode2);
|
|
|
|
|
2017-07-31 04:55:34 +00:00
|
|
|
swap(inode1->i_version, inode2->i_version);
|
2023-10-04 18:52:20 +00:00
|
|
|
|
|
|
|
ts1 = inode_get_atime(inode1);
|
|
|
|
ts2 = inode_get_atime(inode2);
|
|
|
|
inode_set_atime_to_ts(inode1, ts2);
|
|
|
|
inode_set_atime_to_ts(inode2, ts1);
|
|
|
|
|
|
|
|
ts1 = inode_get_mtime(inode1);
|
|
|
|
ts2 = inode_get_mtime(inode2);
|
|
|
|
inode_set_mtime_to_ts(inode1, ts2);
|
|
|
|
inode_set_mtime_to_ts(inode2, ts1);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
|
|
|
memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data));
|
2019-02-11 05:35:06 +00:00
|
|
|
tmp = ei1->i_flags & EXT4_FL_SHOULD_SWAP;
|
|
|
|
ei1->i_flags = (ei2->i_flags & EXT4_FL_SHOULD_SWAP) |
|
|
|
|
(ei1->i_flags & ~EXT4_FL_SHOULD_SWAP);
|
|
|
|
ei2->i_flags = tmp | (ei2->i_flags & ~EXT4_FL_SHOULD_SWAP);
|
2017-07-31 04:55:34 +00:00
|
|
|
swap(ei1->i_disksize, ei2->i_disksize);
|
2013-08-12 13:29:30 +00:00
|
|
|
ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS);
|
|
|
|
ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
|
|
|
isize = i_size_read(inode1);
|
|
|
|
i_size_write(inode1, i_size_read(inode2));
|
|
|
|
i_size_write(inode2, isize);
|
|
|
|
}
|
|
|
|
|
2020-10-15 20:37:59 +00:00
|
|
|
void ext4_reset_inode_seed(struct inode *inode)
|
2018-10-02 22:21:19 +00:00
|
|
|
{
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
|
|
__le32 inum = cpu_to_le32(inode->i_ino);
|
|
|
|
__le32 gen = cpu_to_le32(inode->i_generation);
|
|
|
|
__u32 csum;
|
|
|
|
|
|
|
|
if (!ext4_has_metadata_csum(inode->i_sb))
|
|
|
|
return;
|
|
|
|
|
|
|
|
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, sizeof(inum));
|
|
|
|
ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, sizeof(gen));
|
|
|
|
}
|
|
|
|
|
2022-03-13 02:39:35 +00:00
|
|
|
/*
|
2013-04-08 16:54:05 +00:00
|
|
|
* Swap the information from the given @inode and the inode
|
|
|
|
* EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other
|
|
|
|
* important fields of the inodes.
|
|
|
|
*
|
|
|
|
* @sb: the super block of the filesystem
|
2023-01-13 11:49:26 +00:00
|
|
|
* @idmap: idmap of the mount the inode was found from
|
2013-04-08 16:54:05 +00:00
|
|
|
* @inode: the inode to swap with EXT4_BOOT_LOADER_INO
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static long swap_inode_boot_loader(struct super_block *sb,
|
2023-01-13 11:49:26 +00:00
|
|
|
struct mnt_idmap *idmap,
|
2013-04-08 16:54:05 +00:00
|
|
|
struct inode *inode)
|
|
|
|
{
|
|
|
|
handle_t *handle;
|
|
|
|
int err;
|
|
|
|
struct inode *inode_bl;
|
|
|
|
struct ext4_inode_info *ei_bl;
|
2019-02-11 05:14:02 +00:00
|
|
|
qsize_t size, size_bl, diff;
|
|
|
|
blkcnt_t blocks;
|
|
|
|
unsigned short bytes;
|
2013-04-08 16:54:05 +00:00
|
|
|
|
2022-10-26 04:23:09 +00:00
|
|
|
inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO,
|
|
|
|
EXT4_IGET_SPECIAL | EXT4_IGET_BAD);
|
2014-02-18 01:44:36 +00:00
|
|
|
if (IS_ERR(inode_bl))
|
|
|
|
return PTR_ERR(inode_bl);
|
2013-04-08 16:54:05 +00:00
|
|
|
ei_bl = EXT4_I(inode_bl);
|
|
|
|
|
|
|
|
/* Protect orig inodes against a truncate and make sure,
|
|
|
|
* that only 1 swap_inode_boot_loader is running. */
|
2012-04-18 19:16:33 +00:00
|
|
|
lock_two_nondirectories(inode, inode_bl);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
2019-02-11 05:02:05 +00:00
|
|
|
if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) ||
|
|
|
|
IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) ||
|
2019-02-11 06:07:10 +00:00
|
|
|
(EXT4_I(inode)->i_flags & EXT4_JOURNAL_DATA_FL) ||
|
2019-02-11 05:02:05 +00:00
|
|
|
ext4_has_inline_data(inode)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto journal_err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) ||
|
2023-01-13 11:49:26 +00:00
|
|
|
!inode_owner_or_capable(idmap, inode) ||
|
2021-01-21 13:19:25 +00:00
|
|
|
!capable(CAP_SYS_ADMIN)) {
|
2019-02-11 05:02:05 +00:00
|
|
|
err = -EPERM;
|
|
|
|
goto journal_err_out;
|
|
|
|
}
|
|
|
|
|
2021-02-04 17:05:42 +00:00
|
|
|
filemap_invalidate_lock(inode->i_mapping);
|
2019-02-11 05:05:24 +00:00
|
|
|
err = filemap_write_and_wait(inode->i_mapping);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
err = filemap_write_and_wait(inode_bl->i_mapping);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
2013-04-08 16:54:05 +00:00
|
|
|
/* Wait for all existing dio workers */
|
|
|
|
inode_dio_wait(inode);
|
|
|
|
inode_dio_wait(inode_bl);
|
|
|
|
|
2018-10-02 22:21:19 +00:00
|
|
|
truncate_inode_pages(&inode->i_data, 0);
|
|
|
|
truncate_inode_pages(&inode_bl->i_data, 0);
|
|
|
|
|
2013-04-08 16:54:05 +00:00
|
|
|
handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2);
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
err = -EINVAL;
|
2019-02-11 05:05:24 +00:00
|
|
|
goto err_out;
|
2013-04-08 16:54:05 +00:00
|
|
|
}
|
2022-01-17 09:36:54 +00:00
|
|
|
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT, handle);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
|
|
|
/* Protect extent tree against block allocations via delalloc */
|
|
|
|
ext4_double_down_write_data_sem(inode, inode_bl);
|
|
|
|
|
ext4: fix bug_on in __es_tree_search caused by bad boot loader inode
We got a issue as fllows:
==================================================================
kernel BUG at fs/ext4/extents_status.c:203!
invalid opcode: 0000 [#1] PREEMPT SMP
CPU: 1 PID: 945 Comm: cat Not tainted 6.0.0-next-20221007-dirty #349
RIP: 0010:ext4_es_end.isra.0+0x34/0x42
RSP: 0018:ffffc9000143b768 EFLAGS: 00010203
RAX: 0000000000000000 RBX: ffff8881769cd0b8 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff8fc27cf7 RDI: 00000000ffffffff
RBP: ffff8881769cd0bc R08: 0000000000000000 R09: ffffc9000143b5f8
R10: 0000000000000001 R11: 0000000000000001 R12: ffff8881769cd0a0
R13: ffff8881768e5668 R14: 00000000768e52f0 R15: 0000000000000000
FS: 00007f359f7f05c0(0000)GS:ffff88842fd00000(0000)knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f359f5a2000 CR3: 000000017130c000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
__es_tree_search.isra.0+0x6d/0xf5
ext4_es_cache_extent+0xfa/0x230
ext4_cache_extents+0xd2/0x110
ext4_find_extent+0x5d5/0x8c0
ext4_ext_map_blocks+0x9c/0x1d30
ext4_map_blocks+0x431/0xa50
ext4_mpage_readpages+0x48e/0xe40
ext4_readahead+0x47/0x50
read_pages+0x82/0x530
page_cache_ra_unbounded+0x199/0x2a0
do_page_cache_ra+0x47/0x70
page_cache_ra_order+0x242/0x400
ondemand_readahead+0x1e8/0x4b0
page_cache_sync_ra+0xf4/0x110
filemap_get_pages+0x131/0xb20
filemap_read+0xda/0x4b0
generic_file_read_iter+0x13a/0x250
ext4_file_read_iter+0x59/0x1d0
vfs_read+0x28f/0x460
ksys_read+0x73/0x160
__x64_sys_read+0x1e/0x30
do_syscall_64+0x35/0x80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
</TASK>
==================================================================
In the above issue, ioctl invokes the swap_inode_boot_loader function to
swap inode<5> and inode<12>. However, inode<5> contain incorrect imode and
disordered extents, and i_nlink is set to 1. The extents check for inode in
the ext4_iget function can be bypassed bacause 5 is EXT4_BOOT_LOADER_INO.
While links_count is set to 1, the extents are not initialized in
swap_inode_boot_loader. After the ioctl command is executed successfully,
the extents are swapped to inode<12>, in this case, run the `cat` command
to view inode<12>. And Bug_ON is triggered due to the incorrect extents.
When the boot loader inode is not initialized, its imode can be one of the
following:
1) the imode is a bad type, which is marked as bad_inode in ext4_iget and
set to S_IFREG.
2) the imode is good type but not S_IFREG.
3) the imode is S_IFREG.
The BUG_ON may be triggered by bypassing the check in cases 1 and 2.
Therefore, when the boot loader inode is bad_inode or its imode is not
S_IFREG, initialize the inode to avoid triggering the BUG.
Signed-off-by: Baokun Li <libaokun1@huawei.com>
Reviewed-by: Jason Yan <yanaijie@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20221026042310.3839669-5-libaokun1@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
2022-10-26 04:23:10 +00:00
|
|
|
if (is_bad_inode(inode_bl) || !S_ISREG(inode_bl->i_mode)) {
|
2013-04-08 16:54:05 +00:00
|
|
|
/* this inode has never been used as a BOOT_LOADER */
|
|
|
|
set_nlink(inode_bl, 1);
|
|
|
|
i_uid_write(inode_bl, 0);
|
|
|
|
i_gid_write(inode_bl, 0);
|
|
|
|
inode_bl->i_flags = 0;
|
|
|
|
ei_bl->i_flags = 0;
|
2018-01-09 13:21:39 +00:00
|
|
|
inode_set_iversion(inode_bl, 1);
|
2013-04-08 16:54:05 +00:00
|
|
|
i_size_write(inode_bl, 0);
|
ext4: zero i_disksize when initializing the bootloader inode
If the boot loader inode has never been used before, the
EXT4_IOC_SWAP_BOOT inode will initialize it, including setting the
i_size to 0. However, if the "never before used" boot loader has a
non-zero i_size, then i_disksize will be non-zero, and the
inconsistency between i_size and i_disksize can trigger a kernel
warning:
WARNING: CPU: 0 PID: 2580 at fs/ext4/file.c:319
CPU: 0 PID: 2580 Comm: bb Not tainted 6.3.0-rc1-00004-g703695902cfa
RIP: 0010:ext4_file_write_iter+0xbc7/0xd10
Call Trace:
vfs_write+0x3b1/0x5c0
ksys_write+0x77/0x160
__x64_sys_write+0x22/0x30
do_syscall_64+0x39/0x80
Reproducer:
1. create corrupted image and mount it:
mke2fs -t ext4 /tmp/foo.img 200
debugfs -wR "sif <5> size 25700" /tmp/foo.img
mount -t ext4 /tmp/foo.img /mnt
cd /mnt
echo 123 > file
2. Run the reproducer program:
posix_memalign(&buf, 1024, 1024)
fd = open("file", O_RDWR | O_DIRECT);
ioctl(fd, EXT4_IOC_SWAP_BOOT);
write(fd, buf, 1024);
Fix this by setting i_disksize as well as i_size to zero when
initiaizing the boot loader inode.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217159
Cc: stable@kernel.org
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Link: https://lore.kernel.org/r/20230308032643.641113-1-chengzhihao1@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2023-03-08 03:26:43 +00:00
|
|
|
EXT4_I(inode_bl)->i_disksize = inode_bl->i_size;
|
2013-04-08 16:54:05 +00:00
|
|
|
inode_bl->i_mode = S_IFREG;
|
2015-10-17 20:18:43 +00:00
|
|
|
if (ext4_has_feature_extents(sb)) {
|
2013-04-08 16:54:05 +00:00
|
|
|
ext4_set_inode_flag(inode_bl, EXT4_INODE_EXTENTS);
|
|
|
|
ext4_ext_tree_init(handle, inode_bl);
|
|
|
|
} else
|
|
|
|
memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data));
|
|
|
|
}
|
|
|
|
|
2019-02-11 05:14:02 +00:00
|
|
|
err = dquot_initialize(inode);
|
|
|
|
if (err)
|
|
|
|
goto err_out1;
|
|
|
|
|
|
|
|
size = (qsize_t)(inode->i_blocks) * (1 << 9) + inode->i_bytes;
|
|
|
|
size_bl = (qsize_t)(inode_bl->i_blocks) * (1 << 9) + inode_bl->i_bytes;
|
|
|
|
diff = size - size_bl;
|
2013-04-08 16:54:05 +00:00
|
|
|
swap_inode_data(inode, inode_bl);
|
|
|
|
|
2023-07-05 19:01:07 +00:00
|
|
|
inode_set_ctime_current(inode);
|
|
|
|
inode_set_ctime_current(inode_bl);
|
2022-09-08 17:24:42 +00:00
|
|
|
inode_inc_iversion(inode);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
2022-10-05 15:43:22 +00:00
|
|
|
inode->i_generation = get_random_u32();
|
|
|
|
inode_bl->i_generation = get_random_u32();
|
2020-10-15 20:37:59 +00:00
|
|
|
ext4_reset_inode_seed(inode);
|
|
|
|
ext4_reset_inode_seed(inode_bl);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
2024-01-05 09:21:01 +00:00
|
|
|
ext4_discard_preallocations(inode);
|
2013-04-08 16:54:05 +00:00
|
|
|
|
|
|
|
err = ext4_mark_inode_dirty(handle, inode);
|
|
|
|
if (err < 0) {
|
2019-02-11 05:14:02 +00:00
|
|
|
/* No need to update quota information. */
|
2013-04-08 16:54:05 +00:00
|
|
|
ext4_warning(inode->i_sb,
|
|
|
|
"couldn't mark inode #%lu dirty (err %d)",
|
|
|
|
inode->i_ino, err);
|
|
|
|
/* Revert all changes: */
|
|
|
|
swap_inode_data(inode, inode_bl);
|
2018-10-02 22:21:19 +00:00
|
|
|
ext4_mark_inode_dirty(handle, inode);
|
2019-02-11 05:14:02 +00:00
|
|
|
goto err_out1;
|
|
|
|
}
|
|
|
|
|
|
|
|
blocks = inode_bl->i_blocks;
|
|
|
|
bytes = inode_bl->i_bytes;
|
|
|
|
inode_bl->i_blocks = inode->i_blocks;
|
|
|
|
inode_bl->i_bytes = inode->i_bytes;
|
|
|
|
err = ext4_mark_inode_dirty(handle, inode_bl);
|
|
|
|
if (err < 0) {
|
|
|
|
/* No need to update quota information. */
|
|
|
|
ext4_warning(inode_bl->i_sb,
|
|
|
|
"couldn't mark inode #%lu dirty (err %d)",
|
|
|
|
inode_bl->i_ino, err);
|
|
|
|
goto revert;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bootloader inode should not be counted into quota information. */
|
|
|
|
if (diff > 0)
|
|
|
|
dquot_free_space(inode, diff);
|
|
|
|
else
|
|
|
|
err = dquot_alloc_space(inode, -1 * diff);
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
revert:
|
|
|
|
/* Revert all changes: */
|
|
|
|
inode_bl->i_blocks = blocks;
|
|
|
|
inode_bl->i_bytes = bytes;
|
|
|
|
swap_inode_data(inode, inode_bl);
|
|
|
|
ext4_mark_inode_dirty(handle, inode);
|
|
|
|
ext4_mark_inode_dirty(handle, inode_bl);
|
2013-04-08 16:54:05 +00:00
|
|
|
}
|
2019-02-11 05:14:02 +00:00
|
|
|
|
|
|
|
err_out1:
|
2013-04-08 16:54:05 +00:00
|
|
|
ext4_journal_stop(handle);
|
|
|
|
ext4_double_up_write_data_sem(inode, inode_bl);
|
|
|
|
|
2019-02-11 05:05:24 +00:00
|
|
|
err_out:
|
2021-02-04 17:05:42 +00:00
|
|
|
filemap_invalidate_unlock(inode->i_mapping);
|
2014-02-12 16:48:31 +00:00
|
|
|
journal_err_out:
|
2012-04-18 19:16:33 +00:00
|
|
|
unlock_two_nondirectories(inode, inode_bl);
|
2013-04-08 16:54:05 +00:00
|
|
|
iput(inode_bl);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-06-10 01:41:41 +00:00
|
|
|
/*
|
|
|
|
* If immutable is set and we are not clearing it, we're not allowed to change
|
|
|
|
* anything else in the inode. Don't error out if we're only trying to set
|
|
|
|
* immutable on an immutable file.
|
|
|
|
*/
|
|
|
|
static int ext4_ioctl_check_immutable(struct inode *inode, __u32 new_projid,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
unsigned int oldflags = ei->i_flags;
|
|
|
|
|
|
|
|
if (!(oldflags & EXT4_IMMUTABLE_FL) || !(flags & EXT4_IMMUTABLE_FL))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((oldflags & ~EXT4_IMMUTABLE_FL) != (flags & ~EXT4_IMMUTABLE_FL))
|
|
|
|
return -EPERM;
|
|
|
|
if (ext4_has_feature_project(inode->i_sb) &&
|
|
|
|
__kprojid_val(ei->i_projid) != new_projid)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-28 15:00:02 +00:00
|
|
|
static void ext4_dax_dontcache(struct inode *inode, unsigned int flags)
|
|
|
|
{
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (test_opt2(inode->i_sb, DAX_NEVER) ||
|
|
|
|
test_opt(inode->i_sb, DAX_ALWAYS))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((ei->i_flags ^ flags) & EXT4_DAX_FL)
|
|
|
|
d_mark_dontcache(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dax_compatible(struct inode *inode, unsigned int oldflags,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
2021-04-12 21:19:00 +00:00
|
|
|
/* Allow the DAX flag to be changed on inline directories */
|
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
flags &= ~EXT4_INLINE_DATA_FL;
|
|
|
|
oldflags &= ~EXT4_INLINE_DATA_FL;
|
|
|
|
}
|
|
|
|
|
2020-05-28 15:00:02 +00:00
|
|
|
if (flags & EXT4_DAX_FL) {
|
|
|
|
if ((oldflags & EXT4_DAX_MUT_EXCL) ||
|
|
|
|
ext4_test_inode_state(inode,
|
|
|
|
EXT4_STATE_VERITY_IN_PROGRESS)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & EXT4_DAX_MUT_EXCL) && (oldflags & EXT4_DAX_FL))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
static int ext4_ioctl_setflags(struct inode *inode,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
handle_t *handle = NULL;
|
ext4: ioctl: fix erroneous return value
The ext4_ioctl_setflags() function which is used in the ioctls
EXT4_IOC_SETFLAGS and EXT4_IOC_FSSETXATTR may return the positive value
EPERM instead of -EPERM in case of error. This bug was introduced by a
recent commit 9b7365fc.
The following program can be used to illustrate the wrong behavior:
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>
#define FS_IOC_GETFLAGS _IOR('f', 1, long)
#define FS_IOC_SETFLAGS _IOW('f', 2, long)
#define FS_IMMUTABLE_FL 0x00000010
int main(void)
{
int fd;
long flags;
fd = open("file", O_RDWR|O_CREAT, 0600);
if (fd < 0)
err(1, "open");
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
err(1, "ioctl: FS_IOC_GETFLAGS");
flags |= FS_IMMUTABLE_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) < 0)
err(1, "ioctl: FS_IOC_SETFLAGS");
warnx("ioctl returned no error");
return 0;
}
Running it gives the following result:
$ strace -e ioctl ./test
ioctl(3, FS_IOC_GETFLAGS, 0x7ffdbd8bfd38) = 0
ioctl(3, FS_IOC_SETFLAGS, 0x7ffdbd8bfd38) = 1
test: ioctl returned no error
+++ exited with 0 +++
Running the program on a kernel with the bug fixed gives the proper result:
$ strace -e ioctl ./test
ioctl(3, FS_IOC_GETFLAGS, 0x7ffdd2768258) = 0
ioctl(3, FS_IOC_SETFLAGS, 0x7ffdd2768258) = -1 EPERM (Operation not permitted)
test: ioctl: FS_IOC_SETFLAGS: Operation not permitted
+++ exited with 1 +++
Signed-off-by: Anton Protopopov <a.s.protopopov@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2016-02-12 04:57:21 +00:00
|
|
|
int err = -EPERM, migrate = 0;
|
2016-01-08 21:01:22 +00:00
|
|
|
struct ext4_iloc iloc;
|
|
|
|
unsigned int oldflags, mask, i;
|
ext4: Support case-insensitive file name lookups
This patch implements the actual support for case-insensitive file name
lookups in ext4, based on the feature bit and the encoding stored in the
superblock.
A filesystem that has the casefold feature set is able to configure
directories with the +F (EXT4_CASEFOLD_FL) attribute, enabling lookups
to succeed in that directory in a case-insensitive fashion, i.e: match
a directory entry even if the name used by userspace is not a byte per
byte match with the disk name, but is an equivalent case-insensitive
version of the Unicode string. This operation is called a
case-insensitive file name lookup.
The feature is configured as an inode attribute applied to directories
and inherited by its children. This attribute can only be enabled on
empty directories for filesystems that support the encoding feature,
thus preventing collision of file names that only differ by case.
* dcache handling:
For a +F directory, Ext4 only stores the first equivalent name dentry
used in the dcache. This is done to prevent unintentional duplication of
dentries in the dcache, while also allowing the VFS code to quickly find
the right entry in the cache despite which equivalent string was used in
a previous lookup, without having to resort to ->lookup().
d_hash() of casefolded directories is implemented as the hash of the
casefolded string, such that we always have a well-known bucket for all
the equivalencies of the same string. d_compare() uses the
utf8_strncasecmp() infrastructure, which handles the comparison of
equivalent, same case, names as well.
For now, negative lookups are not inserted in the dcache, since they
would need to be invalidated anyway, because we can't trust missing file
dentries. This is bad for performance but requires some leveraging of
the vfs layer to fix. We can live without that for now, and so does
everyone else.
* on-disk data:
Despite using a specific version of the name as the internal
representation within the dcache, the name stored and fetched from the
disk is a byte-per-byte match with what the user requested, making this
implementation 'name-preserving'. i.e. no actual information is lost
when writing to storage.
DX is supported by modifying the hashes used in +F directories to make
them case/encoding-aware. The new disk hashes are calculated as the
hash of the full casefolded string, instead of the string directly.
This allows us to efficiently search for file names in the htree without
requiring the user to provide an exact name.
* Dealing with invalid sequences:
By default, when a invalid UTF-8 sequence is identified, ext4 will treat
it as an opaque byte sequence, ignoring the encoding and reverting to
the old behavior for that unique file. This means that case-insensitive
file name lookup will not work only for that file. An optional bit can
be set in the superblock telling the filesystem code and userspace tools
to enforce the encoding. When that optional bit is set, any attempt to
create a file name using an invalid UTF-8 sequence will fail and return
an error to userspace.
* Normalization algorithm:
The UTF-8 algorithms used to compare strings in ext4 is implemented
lives in fs/unicode, and is based on a previous version developed by
SGI. It implements the Canonical decomposition (NFD) algorithm
described by the Unicode specification 12.1, or higher, combined with
the elimination of ignorable code points (NFDi) and full
case-folding (CF) as documented in fs/unicode/utf8_norm.c.
NFD seems to be the best normalization method for EXT4 because:
- It has a lower cost than NFC/NFKC (which requires
decomposing to NFD as an intermediary step)
- It doesn't eliminate important semantic meaning like
compatibility decompositions.
Although:
- This implementation is not completely linguistic accurate, because
different languages have conflicting rules, which would require the
specialization of the filesystem to a given locale, which brings all
sorts of problems for removable media and for users who use more than
one language.
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2019-04-25 18:12:08 +00:00
|
|
|
struct super_block *sb = inode->i_sb;
|
2016-01-08 21:01:22 +00:00
|
|
|
|
|
|
|
/* Is it quota file? Do not allow user to mess with it */
|
2017-06-22 15:31:25 +00:00
|
|
|
if (ext4_is_quota_file(inode))
|
2016-01-08 21:01:22 +00:00
|
|
|
goto flags_out;
|
|
|
|
|
|
|
|
oldflags = ei->i_flags;
|
|
|
|
/*
|
|
|
|
* The JOURNAL_DATA flag can only be changed by
|
|
|
|
* the relevant capability.
|
|
|
|
*/
|
2020-05-28 15:00:01 +00:00
|
|
|
if ((flags ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
|
2016-01-08 21:01:22 +00:00
|
|
|
if (!capable(CAP_SYS_RESOURCE))
|
|
|
|
goto flags_out;
|
|
|
|
}
|
2020-05-28 15:00:02 +00:00
|
|
|
|
|
|
|
if (!dax_compatible(inode, oldflags, flags)) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
|
|
|
|
migrate = 1;
|
|
|
|
|
ext4: Support case-insensitive file name lookups
This patch implements the actual support for case-insensitive file name
lookups in ext4, based on the feature bit and the encoding stored in the
superblock.
A filesystem that has the casefold feature set is able to configure
directories with the +F (EXT4_CASEFOLD_FL) attribute, enabling lookups
to succeed in that directory in a case-insensitive fashion, i.e: match
a directory entry even if the name used by userspace is not a byte per
byte match with the disk name, but is an equivalent case-insensitive
version of the Unicode string. This operation is called a
case-insensitive file name lookup.
The feature is configured as an inode attribute applied to directories
and inherited by its children. This attribute can only be enabled on
empty directories for filesystems that support the encoding feature,
thus preventing collision of file names that only differ by case.
* dcache handling:
For a +F directory, Ext4 only stores the first equivalent name dentry
used in the dcache. This is done to prevent unintentional duplication of
dentries in the dcache, while also allowing the VFS code to quickly find
the right entry in the cache despite which equivalent string was used in
a previous lookup, without having to resort to ->lookup().
d_hash() of casefolded directories is implemented as the hash of the
casefolded string, such that we always have a well-known bucket for all
the equivalencies of the same string. d_compare() uses the
utf8_strncasecmp() infrastructure, which handles the comparison of
equivalent, same case, names as well.
For now, negative lookups are not inserted in the dcache, since they
would need to be invalidated anyway, because we can't trust missing file
dentries. This is bad for performance but requires some leveraging of
the vfs layer to fix. We can live without that for now, and so does
everyone else.
* on-disk data:
Despite using a specific version of the name as the internal
representation within the dcache, the name stored and fetched from the
disk is a byte-per-byte match with what the user requested, making this
implementation 'name-preserving'. i.e. no actual information is lost
when writing to storage.
DX is supported by modifying the hashes used in +F directories to make
them case/encoding-aware. The new disk hashes are calculated as the
hash of the full casefolded string, instead of the string directly.
This allows us to efficiently search for file names in the htree without
requiring the user to provide an exact name.
* Dealing with invalid sequences:
By default, when a invalid UTF-8 sequence is identified, ext4 will treat
it as an opaque byte sequence, ignoring the encoding and reverting to
the old behavior for that unique file. This means that case-insensitive
file name lookup will not work only for that file. An optional bit can
be set in the superblock telling the filesystem code and userspace tools
to enforce the encoding. When that optional bit is set, any attempt to
create a file name using an invalid UTF-8 sequence will fail and return
an error to userspace.
* Normalization algorithm:
The UTF-8 algorithms used to compare strings in ext4 is implemented
lives in fs/unicode, and is based on a previous version developed by
SGI. It implements the Canonical decomposition (NFD) algorithm
described by the Unicode specification 12.1, or higher, combined with
the elimination of ignorable code points (NFDi) and full
case-folding (CF) as documented in fs/unicode/utf8_norm.c.
NFD seems to be the best normalization method for EXT4 because:
- It has a lower cost than NFC/NFKC (which requires
decomposing to NFD as an intermediary step)
- It doesn't eliminate important semantic meaning like
compatibility decompositions.
Although:
- This implementation is not completely linguistic accurate, because
different languages have conflicting rules, which would require the
specialization of the filesystem to a given locale, which brings all
sorts of problems for removable media and for users who use more than
one language.
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2019-04-25 18:12:08 +00:00
|
|
|
if ((flags ^ oldflags) & EXT4_CASEFOLD_FL) {
|
|
|
|
if (!ext4_has_feature_casefold(sb)) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!S_ISDIR(inode->i_mode)) {
|
|
|
|
err = -ENOTDIR;
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ext4_empty_dir(inode)) {
|
|
|
|
err = -ENOTEMPTY;
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-10 01:41:41 +00:00
|
|
|
/*
|
|
|
|
* Wait for all pending directio and then flush all the dirty pages
|
|
|
|
* for this file. The flush marks all the pages readonly, so any
|
|
|
|
* subsequent attempt to write to the file (particularly mmap pages)
|
|
|
|
* will come through the filesystem and fail.
|
|
|
|
*/
|
|
|
|
if (S_ISREG(inode->i_mode) && !IS_IMMUTABLE(inode) &&
|
|
|
|
(flags & EXT4_IMMUTABLE_FL)) {
|
|
|
|
inode_dio_wait(inode);
|
|
|
|
err = filemap_write_and_wait(inode->i_mapping);
|
|
|
|
if (err)
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
|
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
err = PTR_ERR(handle);
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
if (IS_SYNC(inode))
|
|
|
|
ext4_handle_sync(handle);
|
|
|
|
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
|
|
|
if (err)
|
|
|
|
goto flags_err;
|
|
|
|
|
2020-05-28 15:00:02 +00:00
|
|
|
ext4_dax_dontcache(inode, flags);
|
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
|
|
|
|
if (!(mask & EXT4_FL_USER_MODIFIABLE))
|
|
|
|
continue;
|
2016-11-29 16:13:13 +00:00
|
|
|
/* These flags get special treatment later */
|
|
|
|
if (mask == EXT4_JOURNAL_DATA_FL || mask == EXT4_EXTENTS_FL)
|
|
|
|
continue;
|
2016-01-08 21:01:22 +00:00
|
|
|
if (mask & flags)
|
|
|
|
ext4_set_inode_flag(inode, i);
|
|
|
|
else
|
|
|
|
ext4_clear_inode_flag(inode, i);
|
|
|
|
}
|
|
|
|
|
2020-05-28 14:59:59 +00:00
|
|
|
ext4_set_inode_flags(inode, false);
|
|
|
|
|
2023-07-05 19:01:07 +00:00
|
|
|
inode_set_ctime_current(inode);
|
2022-09-08 17:24:42 +00:00
|
|
|
inode_inc_iversion(inode);
|
2016-01-08 21:01:22 +00:00
|
|
|
|
|
|
|
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
|
|
|
flags_err:
|
|
|
|
ext4_journal_stop(handle);
|
|
|
|
if (err)
|
|
|
|
goto flags_out;
|
|
|
|
|
2020-05-28 15:00:01 +00:00
|
|
|
if ((flags ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
|
2017-10-12 15:54:08 +00:00
|
|
|
/*
|
|
|
|
* Changes to the journaling mode can cause unsafe changes to
|
2020-05-28 14:59:55 +00:00
|
|
|
* S_DAX if the inode is DAX
|
2017-10-12 15:54:08 +00:00
|
|
|
*/
|
2020-05-28 14:59:55 +00:00
|
|
|
if (IS_DAX(inode)) {
|
2017-10-12 15:54:08 +00:00
|
|
|
err = -EBUSY;
|
|
|
|
goto flags_out;
|
|
|
|
}
|
|
|
|
|
2020-05-28 15:00:01 +00:00
|
|
|
err = ext4_change_inode_journal_flag(inode,
|
|
|
|
flags & EXT4_JOURNAL_DATA_FL);
|
2017-10-12 15:54:08 +00:00
|
|
|
if (err)
|
|
|
|
goto flags_out;
|
|
|
|
}
|
2016-01-08 21:01:22 +00:00
|
|
|
if (migrate) {
|
|
|
|
if (flags & EXT4_EXTENTS_FL)
|
|
|
|
err = ext4_ext_migrate(inode);
|
|
|
|
else
|
|
|
|
err = ext4_ind_migrate(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
flags_out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_QUOTA
|
2021-04-07 12:36:43 +00:00
|
|
|
static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
|
2016-01-08 21:01:22 +00:00
|
|
|
{
|
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
int err, rc;
|
|
|
|
handle_t *handle;
|
|
|
|
kprojid_t kprojid;
|
|
|
|
struct ext4_iloc iloc;
|
|
|
|
struct ext4_inode *raw_inode;
|
2016-07-06 01:33:52 +00:00
|
|
|
struct dquot *transfer_to[MAXQUOTAS] = { };
|
2016-01-08 21:01:22 +00:00
|
|
|
|
2016-09-06 03:11:58 +00:00
|
|
|
if (!ext4_has_feature_project(sb)) {
|
2016-01-08 21:01:22 +00:00
|
|
|
if (projid != EXT4_DEF_PROJID)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
|
|
|
|
|
|
|
|
if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err = -EPERM;
|
|
|
|
/* Is it quota file? Do not allow user to mess with it */
|
2017-06-22 15:31:25 +00:00
|
|
|
if (ext4_is_quota_file(inode))
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
return err;
|
2016-01-08 21:01:22 +00:00
|
|
|
|
2022-12-07 11:59:27 +00:00
|
|
|
err = dquot_initialize(inode);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
err = ext4_get_inode_loc(inode, &iloc);
|
|
|
|
if (err)
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
return err;
|
2016-01-08 21:01:22 +00:00
|
|
|
|
|
|
|
raw_inode = ext4_raw_inode(&iloc);
|
|
|
|
if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
|
2017-08-06 05:00:49 +00:00
|
|
|
err = ext4_expand_extra_isize(inode,
|
|
|
|
EXT4_SB(sb)->s_want_extra_isize,
|
|
|
|
&iloc);
|
|
|
|
if (err)
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
return err;
|
2017-08-06 05:00:49 +00:00
|
|
|
} else {
|
2016-01-08 21:01:22 +00:00
|
|
|
brelse(iloc.bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
|
|
|
|
EXT4_QUOTA_INIT_BLOCKS(sb) +
|
|
|
|
EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
if (IS_ERR(handle))
|
|
|
|
return PTR_ERR(handle);
|
2016-01-08 21:01:22 +00:00
|
|
|
|
|
|
|
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
|
|
|
if (err)
|
|
|
|
goto out_stop;
|
|
|
|
|
2016-07-06 01:33:52 +00:00
|
|
|
transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
|
|
|
|
if (!IS_ERR(transfer_to[PRJQUOTA])) {
|
2017-06-22 15:46:48 +00:00
|
|
|
|
|
|
|
/* __dquot_transfer() calls back ext4_get_inode_usage() which
|
|
|
|
* counts xattr inode references.
|
|
|
|
*/
|
|
|
|
down_read(&EXT4_I(inode)->xattr_sem);
|
2016-07-06 01:33:52 +00:00
|
|
|
err = __dquot_transfer(inode, transfer_to);
|
2017-06-22 15:46:48 +00:00
|
|
|
up_read(&EXT4_I(inode)->xattr_sem);
|
2016-07-06 01:33:52 +00:00
|
|
|
dqput(transfer_to[PRJQUOTA]);
|
|
|
|
if (err)
|
|
|
|
goto out_dirty;
|
2016-01-08 21:01:22 +00:00
|
|
|
}
|
2016-07-06 01:33:52 +00:00
|
|
|
|
2016-01-08 21:01:22 +00:00
|
|
|
EXT4_I(inode)->i_projid = kprojid;
|
2023-07-05 19:01:07 +00:00
|
|
|
inode_set_ctime_current(inode);
|
2022-09-08 17:24:42 +00:00
|
|
|
inode_inc_iversion(inode);
|
2016-01-08 21:01:22 +00:00
|
|
|
out_dirty:
|
|
|
|
rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
|
|
|
if (!err)
|
|
|
|
err = rc;
|
|
|
|
out_stop:
|
|
|
|
ext4_journal_stop(handle);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#else
|
2021-04-07 12:36:43 +00:00
|
|
|
static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
|
2016-01-08 21:01:22 +00:00
|
|
|
{
|
|
|
|
if (projid != EXT4_DEF_PROJID)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-06-01 09:44:57 +00:00
|
|
|
int ext4_force_shutdown(struct super_block *sb, u32 flags)
|
2017-02-06 00:47:14 +00:00
|
|
|
{
|
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
2023-06-06 07:32:03 +00:00
|
|
|
int ret;
|
2017-02-06 00:47:14 +00:00
|
|
|
|
|
|
|
if (flags > EXT4_GOING_FLAGS_NOLOGFLUSH)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2023-06-16 16:50:49 +00:00
|
|
|
if (ext4_forced_shutdown(sb))
|
2017-02-06 00:47:14 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
ext4_msg(sb, KERN_ALERT, "shut down requested (%d)", flags);
|
2018-02-19 01:53:23 +00:00
|
|
|
trace_ext4_shutdown(sb, flags);
|
2017-02-06 00:47:14 +00:00
|
|
|
|
|
|
|
switch (flags) {
|
|
|
|
case EXT4_GOING_FLAGS_DEFAULT:
|
2023-10-24 13:01:08 +00:00
|
|
|
ret = bdev_freeze(sb->s_bdev);
|
2023-06-06 07:32:03 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-02-06 00:47:14 +00:00
|
|
|
set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
|
2023-10-24 13:01:08 +00:00
|
|
|
bdev_thaw(sb->s_bdev);
|
2017-02-06 00:47:14 +00:00
|
|
|
break;
|
|
|
|
case EXT4_GOING_FLAGS_LOGFLUSH:
|
|
|
|
set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
|
|
|
|
if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) {
|
|
|
|
(void) ext4_force_commit(sb);
|
2018-02-19 04:45:18 +00:00
|
|
|
jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN);
|
2017-02-06 00:47:14 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EXT4_GOING_FLAGS_NOLOGFLUSH:
|
|
|
|
set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
|
2018-02-19 04:16:28 +00:00
|
|
|
if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
|
2018-02-19 04:45:18 +00:00
|
|
|
jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN);
|
2017-02-06 00:47:14 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
clear_opt(sb, DISCARD);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-01 09:44:57 +00:00
|
|
|
static int ext4_ioctl_shutdown(struct super_block *sb, unsigned long arg)
|
|
|
|
{
|
|
|
|
u32 flags;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (get_user(flags, (__u32 __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return ext4_force_shutdown(sb, flags);
|
|
|
|
}
|
|
|
|
|
2017-04-30 04:36:53 +00:00
|
|
|
struct getfsmap_info {
|
|
|
|
struct super_block *gi_sb;
|
|
|
|
struct fsmap_head __user *gi_data;
|
|
|
|
unsigned int gi_idx;
|
|
|
|
__u32 gi_last_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ext4_getfsmap_format(struct ext4_fsmap *xfm, void *priv)
|
|
|
|
{
|
|
|
|
struct getfsmap_info *info = priv;
|
|
|
|
struct fsmap fm;
|
|
|
|
|
|
|
|
trace_ext4_getfsmap_mapping(info->gi_sb, xfm);
|
|
|
|
|
|
|
|
info->gi_last_flags = xfm->fmr_flags;
|
|
|
|
ext4_fsmap_from_internal(info->gi_sb, &fm, xfm);
|
|
|
|
if (copy_to_user(&info->gi_data->fmh_recs[info->gi_idx++], &fm,
|
|
|
|
sizeof(struct fsmap)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ext4_ioc_getfsmap(struct super_block *sb,
|
|
|
|
struct fsmap_head __user *arg)
|
|
|
|
{
|
2019-05-12 08:49:47 +00:00
|
|
|
struct getfsmap_info info = { NULL };
|
2017-04-30 04:36:53 +00:00
|
|
|
struct ext4_fsmap_head xhead = {0};
|
|
|
|
struct fsmap_head head;
|
|
|
|
bool aborted = false;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
|
|
|
|
memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
|
|
|
|
sizeof(head.fmh_keys[0].fmr_reserved)) ||
|
|
|
|
memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
|
|
|
|
sizeof(head.fmh_keys[1].fmr_reserved)))
|
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* ext4 doesn't report file extents at all, so the only valid
|
|
|
|
* file offsets are the magic ones (all zeroes or all ones).
|
|
|
|
*/
|
|
|
|
if (head.fmh_keys[0].fmr_offset ||
|
|
|
|
(head.fmh_keys[1].fmr_offset != 0 &&
|
|
|
|
head.fmh_keys[1].fmr_offset != -1ULL))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
xhead.fmh_iflags = head.fmh_iflags;
|
|
|
|
xhead.fmh_count = head.fmh_count;
|
|
|
|
ext4_fsmap_to_internal(sb, &xhead.fmh_keys[0], &head.fmh_keys[0]);
|
|
|
|
ext4_fsmap_to_internal(sb, &xhead.fmh_keys[1], &head.fmh_keys[1]);
|
|
|
|
|
|
|
|
trace_ext4_getfsmap_low_key(sb, &xhead.fmh_keys[0]);
|
|
|
|
trace_ext4_getfsmap_high_key(sb, &xhead.fmh_keys[1]);
|
|
|
|
|
|
|
|
info.gi_sb = sb;
|
|
|
|
info.gi_data = arg;
|
|
|
|
error = ext4_getfsmap(sb, &xhead, ext4_getfsmap_format, &info);
|
2021-04-29 10:16:49 +00:00
|
|
|
if (error == EXT4_QUERY_RANGE_ABORT)
|
2017-04-30 04:36:53 +00:00
|
|
|
aborted = true;
|
2021-04-29 10:16:49 +00:00
|
|
|
else if (error)
|
2017-04-30 04:36:53 +00:00
|
|
|
return error;
|
|
|
|
|
|
|
|
/* If we didn't abort, set the "last" flag in the last fmx */
|
|
|
|
if (!aborted && info.gi_idx) {
|
|
|
|
info.gi_last_flags |= FMR_OF_LAST;
|
|
|
|
if (copy_to_user(&info.gi_data->fmh_recs[info.gi_idx - 1].fmr_flags,
|
|
|
|
&info.gi_last_flags,
|
|
|
|
sizeof(info.gi_last_flags)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy back header */
|
|
|
|
head.fmh_entries = xhead.fmh_entries;
|
|
|
|
head.fmh_oflags = xhead.fmh_oflags;
|
|
|
|
if (copy_to_user(arg, &head, sizeof(struct fsmap_head)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-30 01:39:59 +00:00
|
|
|
static long ext4_ioctl_group_add(struct file *file,
|
|
|
|
struct ext4_new_group_data *input)
|
|
|
|
{
|
|
|
|
struct super_block *sb = file_inode(file)->i_sb;
|
|
|
|
int err, err2=0;
|
|
|
|
|
|
|
|
err = ext4_resize_begin(sb);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2021-07-01 00:54:22 +00:00
|
|
|
if (ext4_has_feature_bigalloc(sb)) {
|
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
|
|
"Online resizing not supported with bigalloc");
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto group_add_out;
|
|
|
|
}
|
|
|
|
|
2017-09-30 01:39:59 +00:00
|
|
|
err = mnt_want_write_file(file);
|
|
|
|
if (err)
|
|
|
|
goto group_add_out;
|
|
|
|
|
|
|
|
err = ext4_group_add(sb, input);
|
|
|
|
if (EXT4_SB(sb)->s_journal) {
|
|
|
|
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
2021-05-18 15:13:25 +00:00
|
|
|
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0);
|
2017-09-30 01:39:59 +00:00
|
|
|
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
|
|
|
}
|
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
|
|
|
mnt_drop_write_file(file);
|
|
|
|
if (!err && ext4_has_group_desc_csum(sb) &&
|
|
|
|
test_opt(sb, INIT_INODE_TABLE))
|
|
|
|
err = ext4_register_li_request(sb, input->group);
|
|
|
|
group_add_out:
|
2022-06-29 04:00:26 +00:00
|
|
|
err2 = ext4_resize_end(sb, false);
|
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
2017-09-30 01:39:59 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
{
|
2021-04-07 12:36:43 +00:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2019-07-01 15:25:35 +00:00
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
2021-04-07 12:36:43 +00:00
|
|
|
u32 flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
if (S_ISREG(inode->i_mode))
|
|
|
|
flags &= ~FS_PROJINHERIT_FL;
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
fileattr_fill_flags(fa, flags);
|
2019-07-01 15:25:35 +00:00
|
|
|
if (ext4_has_feature_project(inode->i_sb))
|
|
|
|
fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
|
2021-04-07 12:36:43 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-13 11:49:21 +00:00
|
|
|
int ext4_fileattr_set(struct mnt_idmap *idmap,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct dentry *dentry, struct fileattr *fa)
|
|
|
|
{
|
|
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
u32 flags = fa->flags;
|
|
|
|
int err = -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (flags & ~EXT4_FL_USER_VISIBLE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* chattr(1) grabs flags via GETFLAGS, modifies the result and
|
|
|
|
* passes that to SETFLAGS. So we cannot easily make SETFLAGS
|
|
|
|
* more restrictive than just silently masking off visible but
|
|
|
|
* not settable flags as we always did.
|
|
|
|
*/
|
|
|
|
flags &= EXT4_FL_USER_MODIFIABLE;
|
|
|
|
if (ext4_mask_flags(inode->i_mode, flags) != flags)
|
|
|
|
goto out;
|
|
|
|
err = ext4_ioctl_check_immutable(inode, fa->fsx_projid, flags);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = ext4_ioctl_setflags(inode, flags);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
err = ext4_ioctl_setproject(inode, fa->fsx_projid);
|
|
|
|
out:
|
|
|
|
return err;
|
ext4: fix setattr project check in fssetxattr ioctl
Currently, project quota could be changed by fssetxattr
ioctl, and existed permission check inode_owner_or_capable()
is obviously not enough, just think that common users could
change project id of file, that could make users to
break project quota easily.
This patch try to follow same regular of xfs project
quota:
"Project Quota ID state is only allowed to change from
within the init namespace. Enforce that restriction only
if we are trying to change the quota ID state.
Everything else is allowed in user namespaces."
Besides that, check and set project id'state should
be an atomic operation, protect whole operation with
inode lock, ext4_ioctl_setproject() is only used for
ioctl EXT4_IOC_FSSETXATTR, we have held mnt_want_write_file()
before ext4_ioctl_setflags(), and ext4_ioctl_setproject()
is called after ext4_ioctl_setflags(), we could share
codes, so remove it inside ext4_ioctl_setproject().
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Cc: stable@kernel.org
2018-10-03 14:33:32 +00:00
|
|
|
}
|
|
|
|
|
2019-08-11 20:32:41 +00:00
|
|
|
/* So that the fiemap access checks can't overflow on 32 bit machines. */
|
|
|
|
#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
|
|
|
|
|
|
|
|
static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct fiemap fiemap;
|
|
|
|
struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
|
|
|
|
struct fiemap_extent_info fieinfo = { 0, };
|
|
|
|
struct inode *inode = file_inode(filp);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
fieinfo.fi_flags = fiemap.fm_flags;
|
|
|
|
fieinfo.fi_extents_max = fiemap.fm_extent_count;
|
|
|
|
fieinfo.fi_extents_start = ufiemap->fm_extents;
|
|
|
|
|
2020-05-05 15:43:15 +00:00
|
|
|
error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start,
|
|
|
|
fiemap.fm_length);
|
2019-08-11 20:32:41 +00:00
|
|
|
fiemap.fm_flags = fieinfo.fi_flags;
|
|
|
|
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
|
|
|
|
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
|
|
|
|
error = -EFAULT;
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
ext4: add ioctl EXT4_IOC_CHECKPOINT
ioctl EXT4_IOC_CHECKPOINT checkpoints and flushes the journal. This
includes forcing all the transactions to the log, checkpointing the
transactions, and flushing the log to disk. This ioctl takes u32 "flags"
as an argument. Three flags are supported. EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
can be used to verify input to the ioctl. It returns error if there is any
invalid input, otherwise it returns success without performing
any checkpointing. The other two flags, EXT4_IOC_CHECKPOINT_FLAG_DISCARD
and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT, can be used to issue requests to
discard or zeroout the journal logs blocks, respectively. At this
point, EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT is primarily added to enable
testing of this codepath on devices that don't support discard.
EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
cannot both be set.
Systems that wish to achieve content deletion SLO can set up a daemon
that calls this ioctl at a regular interval such that it matches with the
SLO requirement. Thus, with this patch, the ext4_dir_entry2 wipeout
patch[1], and the Ext4 "-o discard" mount option set, Ext4 can now
guarantee that all file contents, file metatdata, and filenames will not
be accessible through the filesystem and will have had discard or
zeroout requests issued for corresponding device blocks.
The __jbd2_journal_erase function could also be used to discard or
zero-fill the journal during journal load after recovery. This would
provide a potential solution to a journal replay bug reported earlier this
year[2]. After a successful journal recovery, e2fsck can call this ioctl to
discard the journal as well.
[1] https://lore.kernel.org/linux-ext4/YIHknqxngB1sUdie@mit.edu/
[2] https://lore.kernel.org/linux-ext4/YDZoaacIYStFQT8g@mit.edu/
Link: https://lore.kernel.org/r/20210518151327.130198-2-leah.rumancik@gmail.com
Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2021-05-18 15:13:26 +00:00
|
|
|
static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
__u32 flags = 0;
|
|
|
|
unsigned int flush_flags = 0;
|
|
|
|
struct super_block *sb = file_inode(filp)->i_sb;
|
|
|
|
|
|
|
|
if (copy_from_user(&flags, (__u32 __user *)arg,
|
|
|
|
sizeof(__u32)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/* check for invalid bits set */
|
|
|
|
if ((flags & ~EXT4_IOC_CHECKPOINT_FLAG_VALID) ||
|
|
|
|
((flags & JBD2_JOURNAL_FLUSH_DISCARD) &&
|
|
|
|
(flags & JBD2_JOURNAL_FLUSH_ZEROOUT)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!EXT4_SB(sb)->s_journal)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2022-04-15 04:52:55 +00:00
|
|
|
if ((flags & JBD2_JOURNAL_FLUSH_DISCARD) &&
|
|
|
|
!bdev_max_discard_sectors(EXT4_SB(sb)->s_journal->j_dev))
|
ext4: add ioctl EXT4_IOC_CHECKPOINT
ioctl EXT4_IOC_CHECKPOINT checkpoints and flushes the journal. This
includes forcing all the transactions to the log, checkpointing the
transactions, and flushing the log to disk. This ioctl takes u32 "flags"
as an argument. Three flags are supported. EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
can be used to verify input to the ioctl. It returns error if there is any
invalid input, otherwise it returns success without performing
any checkpointing. The other two flags, EXT4_IOC_CHECKPOINT_FLAG_DISCARD
and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT, can be used to issue requests to
discard or zeroout the journal logs blocks, respectively. At this
point, EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT is primarily added to enable
testing of this codepath on devices that don't support discard.
EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
cannot both be set.
Systems that wish to achieve content deletion SLO can set up a daemon
that calls this ioctl at a regular interval such that it matches with the
SLO requirement. Thus, with this patch, the ext4_dir_entry2 wipeout
patch[1], and the Ext4 "-o discard" mount option set, Ext4 can now
guarantee that all file contents, file metatdata, and filenames will not
be accessible through the filesystem and will have had discard or
zeroout requests issued for corresponding device blocks.
The __jbd2_journal_erase function could also be used to discard or
zero-fill the journal during journal load after recovery. This would
provide a potential solution to a journal replay bug reported earlier this
year[2]. After a successful journal recovery, e2fsck can call this ioctl to
discard the journal as well.
[1] https://lore.kernel.org/linux-ext4/YIHknqxngB1sUdie@mit.edu/
[2] https://lore.kernel.org/linux-ext4/YDZoaacIYStFQT8g@mit.edu/
Link: https://lore.kernel.org/r/20210518151327.130198-2-leah.rumancik@gmail.com
Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2021-05-18 15:13:26 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (flags & EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (flags & EXT4_IOC_CHECKPOINT_FLAG_DISCARD)
|
|
|
|
flush_flags |= JBD2_JOURNAL_FLUSH_DISCARD;
|
|
|
|
|
|
|
|
if (flags & EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT) {
|
|
|
|
flush_flags |= JBD2_JOURNAL_FLUSH_ZEROOUT;
|
|
|
|
pr_info_ratelimited("warning: checkpointing journal with EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT can be slow");
|
|
|
|
}
|
|
|
|
|
|
|
|
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
|
|
|
err = jbd2_journal_flush(EXT4_SB(sb)->s_journal, flush_flags);
|
|
|
|
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-12-13 13:56:18 +00:00
|
|
|
static int ext4_ioctl_setlabel(struct file *filp, const char __user *user_label)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
int ret = 0;
|
|
|
|
char new_label[EXT4_LABEL_MAX + 1];
|
|
|
|
struct super_block *sb = file_inode(filp)->i_sb;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy the maximum length allowed for ext4 label with one more to
|
|
|
|
* find the required terminating null byte in order to test the
|
|
|
|
* label length. The on disk label doesn't need to be null terminated.
|
|
|
|
*/
|
|
|
|
if (copy_from_user(new_label, user_label, EXT4_LABEL_MAX + 1))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
len = strnlen(new_label, EXT4_LABEL_MAX + 1);
|
|
|
|
if (len > EXT4_LABEL_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the buffer after the new label
|
|
|
|
*/
|
|
|
|
memset(new_label + len, 0, EXT4_LABEL_MAX - len);
|
|
|
|
|
|
|
|
ret = mnt_want_write_file(filp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ext4_update_superblocks_fn(sb, ext4_sb_setlabel, new_label);
|
|
|
|
|
|
|
|
mnt_drop_write_file(filp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ext4_ioctl_getlabel(struct ext4_sb_info *sbi, char __user *user_label)
|
|
|
|
{
|
|
|
|
char label[EXT4_LABEL_MAX + 1];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EXT4_LABEL_MAX must always be smaller than FSLABEL_MAX because
|
|
|
|
* FSLABEL_MAX must include terminating null byte, while s_volume_name
|
|
|
|
* does not have to.
|
|
|
|
*/
|
|
|
|
BUILD_BUG_ON(EXT4_LABEL_MAX >= FSLABEL_MAX);
|
|
|
|
|
|
|
|
memset(label, 0, sizeof(label));
|
|
|
|
lock_buffer(sbi->s_sbh);
|
|
|
|
strncpy(label, sbi->s_es->s_volume_name, EXT4_LABEL_MAX);
|
|
|
|
unlock_buffer(sbi->s_sbh);
|
|
|
|
|
|
|
|
if (copy_to_user(user_label, label, sizeof(label)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-21 22:44:22 +00:00
|
|
|
static int ext4_ioctl_getuuid(struct ext4_sb_info *sbi,
|
|
|
|
struct fsuuid __user *ufsuuid)
|
|
|
|
{
|
|
|
|
struct fsuuid fsuuid;
|
|
|
|
__u8 uuid[UUID_SIZE];
|
|
|
|
|
|
|
|
if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (fsuuid.fsu_len == 0) {
|
|
|
|
fsuuid.fsu_len = UUID_SIZE;
|
2022-11-10 20:16:29 +00:00
|
|
|
if (copy_to_user(&ufsuuid->fsu_len, &fsuuid.fsu_len,
|
|
|
|
sizeof(fsuuid.fsu_len)))
|
2022-07-21 22:44:22 +00:00
|
|
|
return -EFAULT;
|
2022-11-10 20:16:29 +00:00
|
|
|
return 0;
|
2022-07-21 22:44:22 +00:00
|
|
|
}
|
|
|
|
|
2022-11-10 20:16:34 +00:00
|
|
|
if (fsuuid.fsu_len < UUID_SIZE || fsuuid.fsu_flags != 0)
|
2022-07-21 22:44:22 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
lock_buffer(sbi->s_sbh);
|
|
|
|
memcpy(uuid, sbi->s_es->s_uuid, UUID_SIZE);
|
|
|
|
unlock_buffer(sbi->s_sbh);
|
|
|
|
|
2022-11-10 20:16:34 +00:00
|
|
|
fsuuid.fsu_len = UUID_SIZE;
|
|
|
|
if (copy_to_user(ufsuuid, &fsuuid, sizeof(fsuuid)) ||
|
|
|
|
copy_to_user(&ufsuuid->fsu_uuid[0], uuid, UUID_SIZE))
|
2022-07-21 22:44:22 +00:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ext4_ioctl_setuuid(struct file *filp,
|
|
|
|
const struct fsuuid __user *ufsuuid)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct super_block *sb = file_inode(filp)->i_sb;
|
|
|
|
struct fsuuid fsuuid;
|
|
|
|
__u8 uuid[UUID_SIZE];
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If any checksums (group descriptors or metadata) are being used
|
|
|
|
* then the checksum seed feature is required to change the UUID.
|
|
|
|
*/
|
|
|
|
if (((ext4_has_feature_gdt_csum(sb) || ext4_has_metadata_csum(sb))
|
|
|
|
&& !ext4_has_feature_csum_seed(sb))
|
|
|
|
|| ext4_has_feature_stable_inodes(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (fsuuid.fsu_len != UUID_SIZE || fsuuid.fsu_flags != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (copy_from_user(uuid, &ufsuuid->fsu_uuid[0], UUID_SIZE))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
ret = mnt_want_write_file(filp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ext4_update_superblocks_fn(sb, ext4_sb_setuuid, &uuid);
|
|
|
|
mnt_drop_write_file(filp);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-10-15 20:37:57 +00:00
|
|
|
static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
2006-10-11 08:20:50 +00:00
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
struct inode *inode = file_inode(filp);
|
2011-09-09 22:36:51 +00:00
|
|
|
struct super_block *sb = inode->i_sb;
|
2023-01-13 11:49:26 +00:00
|
|
|
struct mnt_idmap *idmap = file_mnt_idmap(filp);
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2008-09-09 02:25:24 +00:00
|
|
|
ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
|
2006-10-11 08:20:50 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
2017-04-30 04:36:53 +00:00
|
|
|
case FS_IOC_GETFSMAP:
|
|
|
|
return ext4_ioc_getfsmap(sb, (void __user *)arg);
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC_GETVERSION:
|
|
|
|
case EXT4_IOC_GETVERSION_OLD:
|
2006-10-11 08:20:50 +00:00
|
|
|
return put_user(inode->i_generation, (int __user *) arg);
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC_SETVERSION:
|
|
|
|
case EXT4_IOC_SETVERSION_OLD: {
|
2006-10-11 08:20:50 +00:00
|
|
|
handle_t *handle;
|
2006-10-11 08:20:53 +00:00
|
|
|
struct ext4_iloc iloc;
|
2006-10-11 08:20:50 +00:00
|
|
|
__u32 generation;
|
|
|
|
int err;
|
|
|
|
|
2023-01-13 11:49:26 +00:00
|
|
|
if (!inode_owner_or_capable(idmap, inode))
|
2006-10-11 08:20:50 +00:00
|
|
|
return -EPERM;
|
2008-02-15 22:37:46 +00:00
|
|
|
|
2014-10-13 07:36:16 +00:00
|
|
|
if (ext4_has_metadata_csum(inode->i_sb)) {
|
2012-04-29 22:31:10 +00:00
|
|
|
ext4_warning(sb, "Setting inode version is not "
|
|
|
|
"supported with metadata_csum enabled.");
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
|
2011-11-23 16:57:51 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2008-02-15 22:37:46 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (get_user(generation, (int __user *) arg)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto setversion_out;
|
|
|
|
}
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2016-01-22 20:40:57 +00:00
|
|
|
inode_lock(inode);
|
2013-02-09 02:59:22 +00:00
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
|
2008-02-15 22:37:46 +00:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
err = PTR_ERR(handle);
|
2012-01-03 01:31:52 +00:00
|
|
|
goto unlock_out;
|
2008-02-15 22:37:46 +00:00
|
|
|
}
|
2006-10-11 08:20:53 +00:00
|
|
|
err = ext4_reserve_inode_write(handle, inode, &iloc);
|
2006-10-11 08:20:50 +00:00
|
|
|
if (err == 0) {
|
2023-07-05 19:01:07 +00:00
|
|
|
inode_set_ctime_current(inode);
|
2022-09-08 17:24:42 +00:00
|
|
|
inode_inc_iversion(inode);
|
2006-10-11 08:20:50 +00:00
|
|
|
inode->i_generation = generation;
|
2006-10-11 08:20:53 +00:00
|
|
|
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
|
2006-10-11 08:20:50 +00:00
|
|
|
}
|
2006-10-11 08:20:53 +00:00
|
|
|
ext4_journal_stop(handle);
|
2012-01-03 01:31:52 +00:00
|
|
|
|
|
|
|
unlock_out:
|
2016-01-22 20:40:57 +00:00
|
|
|
inode_unlock(inode);
|
2008-02-15 22:37:46 +00:00
|
|
|
setversion_out:
|
2011-12-09 13:06:57 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2006-10-11 08:20:50 +00:00
|
|
|
return err;
|
|
|
|
}
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC_GROUP_EXTEND: {
|
|
|
|
ext4_fsblk_t n_blocks_count;
|
2009-07-13 13:30:17 +00:00
|
|
|
int err, err2=0;
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2011-07-27 01:35:44 +00:00
|
|
|
err = ext4_resize_begin(sb);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2012-01-04 22:09:52 +00:00
|
|
|
if (get_user(n_blocks_count, (__u32 __user *)arg)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto group_extend_out;
|
|
|
|
}
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2021-07-01 00:54:22 +00:00
|
|
|
if (ext4_has_feature_bigalloc(sb)) {
|
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
|
|
"Online resizing not supported with bigalloc");
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto group_extend_out;
|
|
|
|
}
|
|
|
|
|
2011-11-23 16:57:51 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2008-02-15 22:37:46 +00:00
|
|
|
if (err)
|
2012-01-04 22:09:52 +00:00
|
|
|
goto group_extend_out;
|
2008-02-15 22:37:46 +00:00
|
|
|
|
2006-10-11 08:20:53 +00:00
|
|
|
err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
|
2009-07-13 13:30:17 +00:00
|
|
|
if (EXT4_SB(sb)->s_journal) {
|
|
|
|
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
2021-05-18 15:13:25 +00:00
|
|
|
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0);
|
2009-07-13 13:30:17 +00:00
|
|
|
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
|
|
|
}
|
2008-10-11 00:29:21 +00:00
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
2011-12-09 13:06:57 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2012-01-04 22:09:52 +00:00
|
|
|
group_extend_out:
|
2022-06-29 04:00:26 +00:00
|
|
|
err2 = ext4_resize_end(sb, false);
|
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
2006-10-11 08:20:50 +00:00
|
|
|
return err;
|
|
|
|
}
|
2009-06-17 23:24:03 +00:00
|
|
|
|
|
|
|
case EXT4_IOC_MOVE_EXT: {
|
|
|
|
struct move_extent me;
|
2012-08-28 16:52:22 +00:00
|
|
|
struct fd donor;
|
|
|
|
int err;
|
2009-06-17 23:24:03 +00:00
|
|
|
|
2009-12-07 04:38:31 +00:00
|
|
|
if (!(filp->f_mode & FMODE_READ) ||
|
|
|
|
!(filp->f_mode & FMODE_WRITE))
|
|
|
|
return -EBADF;
|
|
|
|
|
2009-06-17 23:24:03 +00:00
|
|
|
if (copy_from_user(&me,
|
|
|
|
(struct move_extent __user *)arg, sizeof(me)))
|
|
|
|
return -EFAULT;
|
2009-12-07 04:38:31 +00:00
|
|
|
me.moved_len = 0;
|
2009-06-17 23:24:03 +00:00
|
|
|
|
2012-08-28 16:52:22 +00:00
|
|
|
donor = fdget(me.donor_fd);
|
|
|
|
if (!donor.file)
|
2009-06-17 23:24:03 +00:00
|
|
|
return -EBADF;
|
|
|
|
|
2012-08-28 16:52:22 +00:00
|
|
|
if (!(donor.file->f_mode & FMODE_WRITE)) {
|
2009-12-07 04:38:31 +00:00
|
|
|
err = -EBADF;
|
|
|
|
goto mext_out;
|
2009-06-17 23:24:03 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:18:43 +00:00
|
|
|
if (ext4_has_feature_bigalloc(sb)) {
|
2011-09-09 22:36:51 +00:00
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
|
|
"Online defrag not supported with bigalloc");
|
2012-08-27 01:00:03 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto mext_out;
|
2016-02-26 23:19:49 +00:00
|
|
|
} else if (IS_DAX(inode)) {
|
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
|
|
"Online defrag not supported with DAX");
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto mext_out;
|
2011-09-09 22:36:51 +00:00
|
|
|
}
|
|
|
|
|
2011-11-23 16:57:51 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2009-12-07 04:38:31 +00:00
|
|
|
if (err)
|
|
|
|
goto mext_out;
|
|
|
|
|
2012-08-28 16:52:22 +00:00
|
|
|
err = ext4_move_extents(filp, donor.file, me.orig_start,
|
2009-06-17 23:24:03 +00:00
|
|
|
me.donor_start, me.len, &me.moved_len);
|
2011-12-09 13:06:57 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2009-06-17 23:24:03 +00:00
|
|
|
|
2010-05-17 11:00:00 +00:00
|
|
|
if (copy_to_user((struct move_extent __user *)arg,
|
2010-03-04 05:39:24 +00:00
|
|
|
&me, sizeof(me)))
|
2009-12-07 04:38:31 +00:00
|
|
|
err = -EFAULT;
|
|
|
|
mext_out:
|
2012-08-28 16:52:22 +00:00
|
|
|
fdput(donor);
|
2009-06-17 23:24:03 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC_GROUP_ADD: {
|
|
|
|
struct ext4_new_group_data input;
|
2006-10-11 08:20:50 +00:00
|
|
|
|
2006-10-11 08:20:53 +00:00
|
|
|
if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
|
2017-09-30 01:39:59 +00:00
|
|
|
sizeof(input)))
|
|
|
|
return -EFAULT;
|
2008-02-15 22:37:46 +00:00
|
|
|
|
2017-09-30 01:39:59 +00:00
|
|
|
return ext4_ioctl_group_add(filp, &input);
|
2006-10-11 08:20:50 +00:00
|
|
|
}
|
|
|
|
|
2008-01-29 04:58:26 +00:00
|
|
|
case EXT4_IOC_MIGRATE:
|
2008-09-13 16:52:26 +00:00
|
|
|
{
|
|
|
|
int err;
|
2023-01-13 11:49:26 +00:00
|
|
|
if (!inode_owner_or_capable(idmap, inode))
|
2008-09-13 16:52:26 +00:00
|
|
|
return -EACCES;
|
|
|
|
|
2011-11-23 16:57:51 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2008-09-13 16:52:26 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
/*
|
|
|
|
* inode_mutex prevent write and truncate on the file.
|
|
|
|
* Read still goes through. We take i_data_sem in
|
|
|
|
* ext4_ext_swap_inode_data before we switch the
|
|
|
|
* inode format to prevent read.
|
|
|
|
*/
|
2016-01-22 20:40:57 +00:00
|
|
|
inode_lock((inode));
|
2008-09-13 16:52:26 +00:00
|
|
|
err = ext4_ext_migrate(inode);
|
2016-01-22 20:40:57 +00:00
|
|
|
inode_unlock((inode));
|
2011-12-09 13:06:57 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2008-09-13 16:52:26 +00:00
|
|
|
return err;
|
|
|
|
}
|
2008-01-29 04:58:26 +00:00
|
|
|
|
2009-02-26 06:04:07 +00:00
|
|
|
case EXT4_IOC_ALLOC_DA_BLKS:
|
|
|
|
{
|
|
|
|
int err;
|
2023-01-13 11:49:26 +00:00
|
|
|
if (!inode_owner_or_capable(idmap, inode))
|
2009-02-26 06:04:07 +00:00
|
|
|
return -EACCES;
|
|
|
|
|
2011-11-23 16:57:51 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2009-02-26 06:04:07 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
err = ext4_alloc_da_blocks(inode);
|
2011-12-09 13:06:57 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2009-02-26 06:04:07 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-04-08 16:54:05 +00:00
|
|
|
case EXT4_IOC_SWAP_BOOT:
|
2014-10-03 16:47:23 +00:00
|
|
|
{
|
|
|
|
int err;
|
2013-04-08 16:54:05 +00:00
|
|
|
if (!(filp->f_mode & FMODE_WRITE))
|
|
|
|
return -EBADF;
|
2014-10-03 16:47:23 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2023-01-13 11:49:26 +00:00
|
|
|
err = swap_inode_boot_loader(sb, idmap, inode);
|
2014-10-03 16:47:23 +00:00
|
|
|
mnt_drop_write_file(filp);
|
|
|
|
return err;
|
|
|
|
}
|
2013-04-08 16:54:05 +00:00
|
|
|
|
2012-01-04 22:09:44 +00:00
|
|
|
case EXT4_IOC_RESIZE_FS: {
|
|
|
|
ext4_fsblk_t n_blocks_count;
|
|
|
|
int err = 0, err2 = 0;
|
2013-01-13 13:41:45 +00:00
|
|
|
ext4_group_t o_group = EXT4_SB(sb)->s_groups_count;
|
2012-01-04 22:09:44 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&n_blocks_count, (__u64 __user *)arg,
|
|
|
|
sizeof(__u64))) {
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ext4_resize_begin(sb);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2012-07-19 07:19:07 +00:00
|
|
|
err = mnt_want_write_file(filp);
|
2012-01-04 22:09:44 +00:00
|
|
|
if (err)
|
|
|
|
goto resizefs_out;
|
|
|
|
|
|
|
|
err = ext4_resize_fs(sb, n_blocks_count);
|
|
|
|
if (EXT4_SB(sb)->s_journal) {
|
2022-01-17 09:36:54 +00:00
|
|
|
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE, NULL);
|
2012-01-04 22:09:44 +00:00
|
|
|
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
2021-05-18 15:13:25 +00:00
|
|
|
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0);
|
2012-01-04 22:09:44 +00:00
|
|
|
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
|
|
|
}
|
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
2012-07-19 07:19:07 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2019-04-25 17:06:18 +00:00
|
|
|
if (!err && (o_group < EXT4_SB(sb)->s_groups_count) &&
|
2013-01-13 13:41:45 +00:00
|
|
|
ext4_has_group_desc_csum(sb) &&
|
|
|
|
test_opt(sb, INIT_INODE_TABLE))
|
|
|
|
err = ext4_register_li_request(sb, o_group);
|
|
|
|
|
2012-01-04 22:09:44 +00:00
|
|
|
resizefs_out:
|
2022-06-29 04:00:26 +00:00
|
|
|
err2 = ext4_resize_end(sb, true);
|
|
|
|
if (err == 0)
|
|
|
|
err = err2;
|
2012-01-04 22:09:44 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-11-20 02:47:07 +00:00
|
|
|
case FITRIM:
|
|
|
|
{
|
|
|
|
struct fstrim_range range;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2022-04-15 04:52:55 +00:00
|
|
|
if (!bdev_max_discard_sectors(sb->s_bdev))
|
2011-02-23 17:42:32 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2019-03-23 16:10:29 +00:00
|
|
|
/*
|
|
|
|
* We haven't replayed the journal, so we cannot use our
|
|
|
|
* block-bitmap-guided storage zapping commands.
|
|
|
|
*/
|
|
|
|
if (test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb))
|
|
|
|
return -EROFS;
|
|
|
|
|
2011-10-18 14:59:51 +00:00
|
|
|
if (copy_from_user(&range, (struct fstrim_range __user *)arg,
|
2010-11-20 02:47:07 +00:00
|
|
|
sizeof(range)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
ret = ext4_trim_fs(sb, &range);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2011-10-18 14:59:51 +00:00
|
|
|
if (copy_to_user((struct fstrim_range __user *)arg, &range,
|
2010-11-20 02:47:07 +00:00
|
|
|
sizeof(range)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-17 02:05:14 +00:00
|
|
|
case EXT4_IOC_PRECACHE_EXTENTS:
|
|
|
|
return ext4_ext_precache(inode);
|
2015-04-11 11:48:01 +00:00
|
|
|
|
2020-07-14 23:09:09 +00:00
|
|
|
case FS_IOC_SET_ENCRYPTION_POLICY:
|
2016-09-30 05:49:55 +00:00
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
2016-11-27 00:07:49 +00:00
|
|
|
return fscrypt_ioctl_set_policy(filp, (const void __user *)arg);
|
2016-09-30 05:49:55 +00:00
|
|
|
|
2022-05-15 06:37:48 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_PWSALT:
|
|
|
|
return ext4_ioctl_get_encryption_pwsalt(filp, (void __user *)arg);
|
2015-04-11 11:48:01 +00:00
|
|
|
|
2020-07-14 23:09:09 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_POLICY:
|
2019-08-04 09:56:43 +00:00
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
2016-11-27 00:07:49 +00:00
|
|
|
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
|
2015-04-11 11:48:01 +00:00
|
|
|
|
2019-08-05 02:35:48 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg);
|
|
|
|
|
|
|
|
case FS_IOC_ADD_ENCRYPTION_KEY:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_add_key(filp, (void __user *)arg);
|
|
|
|
|
|
|
|
case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_remove_key(filp, (void __user *)arg);
|
|
|
|
|
|
|
|
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_remove_key_all_users(filp,
|
|
|
|
(void __user *)arg);
|
|
|
|
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_get_key_status(filp, (void __user *)arg);
|
|
|
|
|
2020-03-14 20:50:50 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_NONCE:
|
|
|
|
if (!ext4_has_feature_encrypt(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fscrypt_ioctl_get_nonce(filp, (void __user *)arg);
|
|
|
|
|
2019-08-11 20:30:41 +00:00
|
|
|
case EXT4_IOC_CLEAR_ES_CACHE:
|
|
|
|
{
|
2023-01-13 11:49:26 +00:00
|
|
|
if (!inode_owner_or_capable(idmap, inode))
|
2019-08-11 20:30:41 +00:00
|
|
|
return -EACCES;
|
|
|
|
ext4_clear_inode_es(inode);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-11 20:31:41 +00:00
|
|
|
case EXT4_IOC_GETSTATE:
|
|
|
|
{
|
|
|
|
__u32 state = 0;
|
|
|
|
|
|
|
|
if (ext4_test_inode_state(inode, EXT4_STATE_EXT_PRECACHED))
|
|
|
|
state |= EXT4_STATE_FLAG_EXT_PRECACHED;
|
|
|
|
if (ext4_test_inode_state(inode, EXT4_STATE_NEW))
|
|
|
|
state |= EXT4_STATE_FLAG_NEW;
|
|
|
|
if (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY))
|
|
|
|
state |= EXT4_STATE_FLAG_NEWENTRY;
|
|
|
|
if (ext4_test_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE))
|
|
|
|
state |= EXT4_STATE_FLAG_DA_ALLOC_CLOSE;
|
|
|
|
|
|
|
|
return put_user(state, (__u32 __user *) arg);
|
|
|
|
}
|
|
|
|
|
2019-08-11 20:32:41 +00:00
|
|
|
case EXT4_IOC_GET_ES_CACHE:
|
|
|
|
return ext4_ioctl_get_es_cache(filp, arg);
|
|
|
|
|
2017-02-20 20:34:59 +00:00
|
|
|
case EXT4_IOC_SHUTDOWN:
|
2023-06-01 09:44:57 +00:00
|
|
|
return ext4_ioctl_shutdown(sb, arg);
|
2019-07-22 16:26:24 +00:00
|
|
|
|
|
|
|
case FS_IOC_ENABLE_VERITY:
|
|
|
|
if (!ext4_has_feature_verity(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fsverity_ioctl_enable(filp, (const void __user *)arg);
|
|
|
|
|
|
|
|
case FS_IOC_MEASURE_VERITY:
|
|
|
|
if (!ext4_has_feature_verity(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fsverity_ioctl_measure(filp, (void __user *)arg);
|
|
|
|
|
fs-verity: add FS_IOC_READ_VERITY_METADATA ioctl
Add an ioctl FS_IOC_READ_VERITY_METADATA which will allow reading verity
metadata from a file that has fs-verity enabled, including:
- The Merkle tree
- The fsverity_descriptor (not including the signature if present)
- The built-in signature, if present
This ioctl has similar semantics to pread(). It is passed the type of
metadata to read (one of the above three), and a buffer, offset, and
size. It returns the number of bytes read or an error.
Separate patches will add support for each of the above metadata types.
This patch just adds the ioctl itself.
This ioctl doesn't make any assumption about where the metadata is
stored on-disk. It does assume the metadata is in a stable format, but
that's basically already the case:
- The Merkle tree and fsverity_descriptor are defined by how fs-verity
file digests are computed; see the "File digest computation" section
of Documentation/filesystems/fsverity.rst. Technically, the way in
which the levels of the tree are ordered relative to each other wasn't
previously specified, but it's logical to put the root level first.
- The built-in signature is the value passed to FS_IOC_ENABLE_VERITY.
This ioctl is useful because it allows writing a server program that
takes a verity file and serves it to a client program, such that the
client can do its own fs-verity compatible verification of the file.
This only makes sense if the client doesn't trust the server and if the
server needs to provide the storage for the client.
More concretely, there is interest in using this ability in Android to
export APK files (which are protected by fs-verity) to "protected VMs".
This would use Protected KVM (https://lwn.net/Articles/836693), which
provides an isolated execution environment without having to trust the
traditional "host". A "guest" VM can boot from a signed image and
perform specific tasks in a minimum trusted environment using files that
have fs-verity enabled on the host, without trusting the host or
requiring that the guest has its own trusted storage.
Technically, it would be possible to duplicate the metadata and store it
in separate files for serving. However, that would be less efficient
and would require extra care in userspace to maintain file consistency.
In addition to the above, the ability to read the built-in signatures is
useful because it allows a system that is using the in-kernel signature
verification to migrate to userspace signature verification.
Link: https://lore.kernel.org/r/20210115181819.34732-4-ebiggers@kernel.org
Reviewed-by: Victor Hsieh <victorhsieh@google.com>
Acked-by: Jaegeuk Kim <jaegeuk@kernel.org>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
2021-01-15 18:18:16 +00:00
|
|
|
case FS_IOC_READ_VERITY_METADATA:
|
|
|
|
if (!ext4_has_feature_verity(sb))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return fsverity_ioctl_read_metadata(filp,
|
|
|
|
(const void __user *)arg);
|
|
|
|
|
ext4: add ioctl EXT4_IOC_CHECKPOINT
ioctl EXT4_IOC_CHECKPOINT checkpoints and flushes the journal. This
includes forcing all the transactions to the log, checkpointing the
transactions, and flushing the log to disk. This ioctl takes u32 "flags"
as an argument. Three flags are supported. EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
can be used to verify input to the ioctl. It returns error if there is any
invalid input, otherwise it returns success without performing
any checkpointing. The other two flags, EXT4_IOC_CHECKPOINT_FLAG_DISCARD
and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT, can be used to issue requests to
discard or zeroout the journal logs blocks, respectively. At this
point, EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT is primarily added to enable
testing of this codepath on devices that don't support discard.
EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
cannot both be set.
Systems that wish to achieve content deletion SLO can set up a daemon
that calls this ioctl at a regular interval such that it matches with the
SLO requirement. Thus, with this patch, the ext4_dir_entry2 wipeout
patch[1], and the Ext4 "-o discard" mount option set, Ext4 can now
guarantee that all file contents, file metatdata, and filenames will not
be accessible through the filesystem and will have had discard or
zeroout requests issued for corresponding device blocks.
The __jbd2_journal_erase function could also be used to discard or
zero-fill the journal during journal load after recovery. This would
provide a potential solution to a journal replay bug reported earlier this
year[2]. After a successful journal recovery, e2fsck can call this ioctl to
discard the journal as well.
[1] https://lore.kernel.org/linux-ext4/YIHknqxngB1sUdie@mit.edu/
[2] https://lore.kernel.org/linux-ext4/YDZoaacIYStFQT8g@mit.edu/
Link: https://lore.kernel.org/r/20210518151327.130198-2-leah.rumancik@gmail.com
Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2021-05-18 15:13:26 +00:00
|
|
|
case EXT4_IOC_CHECKPOINT:
|
|
|
|
return ext4_ioctl_checkpoint(filp, arg);
|
|
|
|
|
2021-12-13 13:56:18 +00:00
|
|
|
case FS_IOC_GETFSLABEL:
|
|
|
|
return ext4_ioctl_getlabel(EXT4_SB(sb), (void __user *)arg);
|
|
|
|
|
|
|
|
case FS_IOC_SETFSLABEL:
|
|
|
|
return ext4_ioctl_setlabel(filp,
|
|
|
|
(const void __user *)arg);
|
|
|
|
|
2022-07-21 22:44:22 +00:00
|
|
|
case EXT4_IOC_GETFSUUID:
|
|
|
|
return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg);
|
|
|
|
case EXT4_IOC_SETFSUUID:
|
|
|
|
return ext4_ioctl_setuuid(filp, (const void __user *)arg);
|
2006-10-11 08:20:50 +00:00
|
|
|
default:
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-15 20:37:57 +00:00
|
|
|
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
2021-12-23 20:21:37 +00:00
|
|
|
return __ext4_ioctl(filp, cmd, arg);
|
2020-10-15 20:37:57 +00:00
|
|
|
}
|
|
|
|
|
2006-10-11 08:20:50 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
2006-10-11 08:20:53 +00:00
|
|
|
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
2006-10-11 08:20:50 +00:00
|
|
|
{
|
|
|
|
/* These are just misnamed, they actually get/put from/to user an int */
|
|
|
|
switch (cmd) {
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_GETVERSION:
|
|
|
|
cmd = EXT4_IOC_GETVERSION;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_SETVERSION:
|
|
|
|
cmd = EXT4_IOC_SETVERSION;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_GROUP_EXTEND:
|
|
|
|
cmd = EXT4_IOC_GROUP_EXTEND;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_GETVERSION_OLD:
|
|
|
|
cmd = EXT4_IOC_GETVERSION_OLD;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_SETVERSION_OLD:
|
|
|
|
cmd = EXT4_IOC_SETVERSION_OLD;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_GETRSVSZ:
|
|
|
|
cmd = EXT4_IOC_GETRSVSZ;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2006-10-11 08:20:53 +00:00
|
|
|
case EXT4_IOC32_SETRSVSZ:
|
|
|
|
cmd = EXT4_IOC_SETRSVSZ;
|
2006-10-11 08:20:50 +00:00
|
|
|
break;
|
2010-05-17 10:00:00 +00:00
|
|
|
case EXT4_IOC32_GROUP_ADD: {
|
|
|
|
struct compat_ext4_new_group_input __user *uinput;
|
2017-09-30 01:39:59 +00:00
|
|
|
struct ext4_new_group_data input;
|
2010-05-17 10:00:00 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
uinput = compat_ptr(arg);
|
|
|
|
err = get_user(input.group, &uinput->group);
|
|
|
|
err |= get_user(input.block_bitmap, &uinput->block_bitmap);
|
|
|
|
err |= get_user(input.inode_bitmap, &uinput->inode_bitmap);
|
|
|
|
err |= get_user(input.inode_table, &uinput->inode_table);
|
|
|
|
err |= get_user(input.blocks_count, &uinput->blocks_count);
|
|
|
|
err |= get_user(input.reserved_blocks,
|
|
|
|
&uinput->reserved_blocks);
|
|
|
|
if (err)
|
|
|
|
return -EFAULT;
|
2017-09-30 01:39:59 +00:00
|
|
|
return ext4_ioctl_group_add(file, &input);
|
2010-05-17 10:00:00 +00:00
|
|
|
}
|
2010-05-15 04:00:00 +00:00
|
|
|
case EXT4_IOC_MOVE_EXT:
|
2012-01-04 22:09:44 +00:00
|
|
|
case EXT4_IOC_RESIZE_FS:
|
2019-06-03 11:51:58 +00:00
|
|
|
case FITRIM:
|
2013-08-17 02:05:14 +00:00
|
|
|
case EXT4_IOC_PRECACHE_EXTENTS:
|
2020-07-14 23:09:09 +00:00
|
|
|
case FS_IOC_SET_ENCRYPTION_POLICY:
|
|
|
|
case FS_IOC_GET_ENCRYPTION_PWSALT:
|
|
|
|
case FS_IOC_GET_ENCRYPTION_POLICY:
|
2019-08-05 02:35:48 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
|
|
|
case FS_IOC_ADD_ENCRYPTION_KEY:
|
|
|
|
case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
|
|
|
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
|
|
|
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
2020-03-14 20:50:50 +00:00
|
|
|
case FS_IOC_GET_ENCRYPTION_NONCE:
|
2017-02-20 20:34:59 +00:00
|
|
|
case EXT4_IOC_SHUTDOWN:
|
2017-04-30 04:36:53 +00:00
|
|
|
case FS_IOC_GETFSMAP:
|
2019-07-22 16:26:24 +00:00
|
|
|
case FS_IOC_ENABLE_VERITY:
|
|
|
|
case FS_IOC_MEASURE_VERITY:
|
fs-verity: add FS_IOC_READ_VERITY_METADATA ioctl
Add an ioctl FS_IOC_READ_VERITY_METADATA which will allow reading verity
metadata from a file that has fs-verity enabled, including:
- The Merkle tree
- The fsverity_descriptor (not including the signature if present)
- The built-in signature, if present
This ioctl has similar semantics to pread(). It is passed the type of
metadata to read (one of the above three), and a buffer, offset, and
size. It returns the number of bytes read or an error.
Separate patches will add support for each of the above metadata types.
This patch just adds the ioctl itself.
This ioctl doesn't make any assumption about where the metadata is
stored on-disk. It does assume the metadata is in a stable format, but
that's basically already the case:
- The Merkle tree and fsverity_descriptor are defined by how fs-verity
file digests are computed; see the "File digest computation" section
of Documentation/filesystems/fsverity.rst. Technically, the way in
which the levels of the tree are ordered relative to each other wasn't
previously specified, but it's logical to put the root level first.
- The built-in signature is the value passed to FS_IOC_ENABLE_VERITY.
This ioctl is useful because it allows writing a server program that
takes a verity file and serves it to a client program, such that the
client can do its own fs-verity compatible verification of the file.
This only makes sense if the client doesn't trust the server and if the
server needs to provide the storage for the client.
More concretely, there is interest in using this ability in Android to
export APK files (which are protected by fs-verity) to "protected VMs".
This would use Protected KVM (https://lwn.net/Articles/836693), which
provides an isolated execution environment without having to trust the
traditional "host". A "guest" VM can boot from a signed image and
perform specific tasks in a minimum trusted environment using files that
have fs-verity enabled on the host, without trusting the host or
requiring that the guest has its own trusted storage.
Technically, it would be possible to duplicate the metadata and store it
in separate files for serving. However, that would be less efficient
and would require extra care in userspace to maintain file consistency.
In addition to the above, the ability to read the built-in signatures is
useful because it allows a system that is using the in-kernel signature
verification to migrate to userspace signature verification.
Link: https://lore.kernel.org/r/20210115181819.34732-4-ebiggers@kernel.org
Reviewed-by: Victor Hsieh <victorhsieh@google.com>
Acked-by: Jaegeuk Kim <jaegeuk@kernel.org>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
2021-01-15 18:18:16 +00:00
|
|
|
case FS_IOC_READ_VERITY_METADATA:
|
2019-08-11 20:30:41 +00:00
|
|
|
case EXT4_IOC_CLEAR_ES_CACHE:
|
2019-08-11 20:31:41 +00:00
|
|
|
case EXT4_IOC_GETSTATE:
|
2019-08-11 20:32:41 +00:00
|
|
|
case EXT4_IOC_GET_ES_CACHE:
|
ext4: add ioctl EXT4_IOC_CHECKPOINT
ioctl EXT4_IOC_CHECKPOINT checkpoints and flushes the journal. This
includes forcing all the transactions to the log, checkpointing the
transactions, and flushing the log to disk. This ioctl takes u32 "flags"
as an argument. Three flags are supported. EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
can be used to verify input to the ioctl. It returns error if there is any
invalid input, otherwise it returns success without performing
any checkpointing. The other two flags, EXT4_IOC_CHECKPOINT_FLAG_DISCARD
and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT, can be used to issue requests to
discard or zeroout the journal logs blocks, respectively. At this
point, EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT is primarily added to enable
testing of this codepath on devices that don't support discard.
EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
cannot both be set.
Systems that wish to achieve content deletion SLO can set up a daemon
that calls this ioctl at a regular interval such that it matches with the
SLO requirement. Thus, with this patch, the ext4_dir_entry2 wipeout
patch[1], and the Ext4 "-o discard" mount option set, Ext4 can now
guarantee that all file contents, file metatdata, and filenames will not
be accessible through the filesystem and will have had discard or
zeroout requests issued for corresponding device blocks.
The __jbd2_journal_erase function could also be used to discard or
zero-fill the journal during journal load after recovery. This would
provide a potential solution to a journal replay bug reported earlier this
year[2]. After a successful journal recovery, e2fsck can call this ioctl to
discard the journal as well.
[1] https://lore.kernel.org/linux-ext4/YIHknqxngB1sUdie@mit.edu/
[2] https://lore.kernel.org/linux-ext4/YDZoaacIYStFQT8g@mit.edu/
Link: https://lore.kernel.org/r/20210518151327.130198-2-leah.rumancik@gmail.com
Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2021-05-18 15:13:26 +00:00
|
|
|
case EXT4_IOC_CHECKPOINT:
|
2021-12-13 13:56:18 +00:00
|
|
|
case FS_IOC_GETFSLABEL:
|
|
|
|
case FS_IOC_SETFSLABEL:
|
2022-07-21 22:44:22 +00:00
|
|
|
case EXT4_IOC_GETFSUUID:
|
|
|
|
case EXT4_IOC_SETFSUUID:
|
2010-05-15 04:00:00 +00:00
|
|
|
break;
|
2006-10-11 08:20:50 +00:00
|
|
|
default:
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
2008-04-30 02:03:54 +00:00
|
|
|
return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
|
2006-10-11 08:20:50 +00:00
|
|
|
}
|
|
|
|
#endif
|
2022-04-15 02:39:00 +00:00
|
|
|
|
|
|
|
static void set_overhead(struct ext4_super_block *es, const void *arg)
|
|
|
|
{
|
|
|
|
es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg));
|
|
|
|
}
|
|
|
|
|
2022-06-29 04:00:26 +00:00
|
|
|
int ext4_update_overhead(struct super_block *sb, bool force)
|
2022-04-15 02:39:00 +00:00
|
|
|
{
|
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
|
|
2022-06-29 04:00:26 +00:00
|
|
|
if (sb_rdonly(sb))
|
|
|
|
return 0;
|
|
|
|
if (!force &&
|
|
|
|
(sbi->s_overhead == 0 ||
|
|
|
|
sbi->s_overhead == le32_to_cpu(sbi->s_es->s_overhead_clusters)))
|
2022-04-15 02:39:00 +00:00
|
|
|
return 0;
|
|
|
|
return ext4_update_superblocks_fn(sb, set_overhead, &sbi->s_overhead);
|
|
|
|
}
|