f2fs-for-5.4-rc1
In this round, we introduced casefolding support in f2fs, and fixed various bugs in individual features such as IO alignment, checkpoint=disable, quota, and swapfile. Enhancement: - support casefolding w/ enhancement in ext4 - support fiemap for directory - support FS_IO_GET|SET_FSLABEL Bug fix: - fix IO stuck during checkpoint=disable - avoid infinite GC loop - fix panic/overflow related to IO alignment feature - fix livelock in swap file - fix discard command leak - disallow dio for atomic_write -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE00UqedjCtOrGVvQiQBSofoJIUNIFAl2FL1wACgkQQBSofoJI UNIRdg/+M6QNiAazIbqzPXyUUkyZQYR5YKhu2abd49o/g38xjTq1afH0PZpQyDrA 9RncR4xsW8F241vPLVoCSanfaa+MxN6xHi3TrD8zYtZWxOcPF6v1ETHeUXGTHuJ2 gqlk+mm+CnY02M6rxW7XwixuXwttT3bF9+cf1YBWRpNoVrR+SjNqgeJS7FmJwXKd nGKb+94OxuygL1NUop+LDUo3qRQjc0Sxv/7qj/K4lhqgTjhAxMYT2KvUP/1MZ7U0 Kh9WIayDXnpoioxMPnt4VEb+JgXfLLFELvQzNjwulk15GIweuJzwVYCBXcRoX0cK eRBRmRy/kRp/e0R1gvl3kYrXQC2AC5QTlBVH/0ESwnaukFiUBKB509vH4aqE/vpB Krldjfg+uMHkc7XiNBf1boDp713vJ76iRKUDWoVb6H/sPbdJ+jtrnUNeBP8CVpWh u31SY1MppnmKhhsoCHQRbhbXO/Z29imBQgF9Tm3IFWImyLY3IU40vFj2fR15gJkL X3x/HWxQynSqyqEOwAZrvhCRTvBAIGIVy5292Di1RkqIoh8saxcqiaywgLz1+eVE 0DCOoh8R6sSbfN/EEh+yZqTxmjo0VGVTw30XVI6QEo4cY5Vfc9u6dN6SRWVRvbjb kPb3dKcMrttgbn3fcXU8Jbw1AOor9N6afHaqs0swQJyci2RwJyc= =oonf -----END PGP SIGNATURE----- Merge tag 'f2fs-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs Pull f2fs updates from Jaegeuk Kim: "In this round, we introduced casefolding support in f2fs, and fixed various bugs in individual features such as IO alignment, checkpoint=disable, quota, and swapfile. Enhancement: - support casefolding w/ enhancement in ext4 - support fiemap for directory - support FS_IO_GET|SET_FSLABEL Bug fix: - fix IO stuck during checkpoint=disable - avoid infinite GC loop - fix panic/overflow related to IO alignment feature - fix livelock in swap file - fix discard command leak - disallow dio for atomic_write" * tag 'f2fs-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (51 commits) f2fs: add a condition to detect overflow in f2fs_ioc_gc_range() f2fs: fix to add missing F2FS_IO_ALIGNED() condition f2fs: fix to fallback to buffered IO in IO aligned mode f2fs: fix to handle error path correctly in f2fs_map_blocks f2fs: fix extent corrupotion during directIO in LFS mode f2fs: check all the data segments against all node ones f2fs: Add a small clarification to CONFIG_FS_F2FS_FS_SECURITY f2fs: fix inode rwsem regression f2fs: fix to avoid accessing uninitialized field of inode page in is_alive() f2fs: avoid infinite GC loop due to stale atomic files f2fs: Fix indefinite loop in f2fs_gc() f2fs: convert inline_data in prior to i_size_write f2fs: fix error path of f2fs_convert_inline_page() f2fs: add missing documents of reserve_root/resuid/resgid f2fs: fix flushing node pages when checkpoint is disabled f2fs: enhance f2fs_is_checkpoint_ready()'s readability f2fs: clean up __bio_alloc()'s parameter f2fs: fix wrong error injection path in inc_valid_block_count() f2fs: fix to writeout dirty inode during node flush f2fs: optimize case-insensitive lookups ...
This commit is contained in:
commit
fbc246a12a
|
@ -251,3 +251,10 @@ Description:
|
|||
If checkpoint=disable, it displays the number of blocks that are unusable.
|
||||
If checkpoint=enable it displays the enumber of blocks that would be unusable
|
||||
if checkpoint=disable were to be set.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/encoding
|
||||
Date July 2019
|
||||
Contact: "Daniel Rosenberg" <drosen@google.com>
|
||||
Description:
|
||||
Displays name and version of the encoding set for the filesystem.
|
||||
If no encoding is set, displays (none)
|
||||
|
|
|
@ -157,6 +157,11 @@ noinline_data Disable the inline data feature, inline data feature is
|
|||
enabled by default.
|
||||
data_flush Enable data flushing before checkpoint in order to
|
||||
persist data of regular and symlink.
|
||||
reserve_root=%d Support configuring reserved space which is used for
|
||||
allocation from a privileged user with specified uid or
|
||||
gid, unit: 4KB, the default limit is 0.2% of user blocks.
|
||||
resuid=%d The user ID which may use the reserved blocks.
|
||||
resgid=%d The group ID which may use the reserved blocks.
|
||||
fault_injection=%d Enable fault injection in all supported types with
|
||||
specified injection rate.
|
||||
fault_type=%d Support configuring fault injection type, should be
|
||||
|
@ -413,6 +418,9 @@ Files in /sys/fs/f2fs/<devname>
|
|||
that would be unusable if checkpoint=disable were
|
||||
to be set.
|
||||
|
||||
encoding This shows the encoding used for casefolding.
|
||||
If casefolding is not enabled, returns (none)
|
||||
|
||||
================================================================================
|
||||
USAGE
|
||||
================================================================================
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
config F2FS_FS
|
||||
tristate "F2FS filesystem support"
|
||||
depends on BLOCK
|
||||
select NLS
|
||||
select CRYPTO
|
||||
select CRYPTO_CRC32
|
||||
select F2FS_FS_XATTR if FS_ENCRYPTION
|
||||
|
@ -60,7 +61,9 @@ config F2FS_FS_SECURITY
|
|||
Security Models (LSMs) accepted by AppArmor, SELinux, Smack and TOMOYO
|
||||
Linux. This option enables an extended attribute handler for file
|
||||
security labels in the f2fs filesystem, so that it requires enabling
|
||||
the extended attribute support in advance.
|
||||
the extended attribute support in advance. In particular you need this
|
||||
option if you use the setcap command to assign initial process capabi-
|
||||
lities to executables (the security.* extended attributes).
|
||||
|
||||
If you are not using a security module, say N.
|
||||
|
||||
|
|
104
fs/f2fs/data.c
104
fs/f2fs/data.c
|
@ -283,26 +283,25 @@ static bool __same_bdev(struct f2fs_sb_info *sbi,
|
|||
/*
|
||||
* Low-level block read/write IO operations.
|
||||
*/
|
||||
static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr,
|
||||
struct writeback_control *wbc,
|
||||
int npages, bool is_read,
|
||||
enum page_type type, enum temp_type temp)
|
||||
static struct bio *__bio_alloc(struct f2fs_io_info *fio, int npages)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
struct bio *bio;
|
||||
|
||||
bio = f2fs_bio_alloc(sbi, npages, true);
|
||||
|
||||
f2fs_target_device(sbi, blk_addr, bio);
|
||||
if (is_read) {
|
||||
f2fs_target_device(sbi, fio->new_blkaddr, bio);
|
||||
if (is_read_io(fio->op)) {
|
||||
bio->bi_end_io = f2fs_read_end_io;
|
||||
bio->bi_private = NULL;
|
||||
} else {
|
||||
bio->bi_end_io = f2fs_write_end_io;
|
||||
bio->bi_private = sbi;
|
||||
bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, type, temp);
|
||||
bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi,
|
||||
fio->type, fio->temp);
|
||||
}
|
||||
if (wbc)
|
||||
wbc_init_bio(wbc, bio);
|
||||
if (fio->io_wbc)
|
||||
wbc_init_bio(fio->io_wbc, bio);
|
||||
|
||||
return bio;
|
||||
}
|
||||
|
@ -319,6 +318,9 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi,
|
|||
if (test_opt(sbi, LFS) && current->plug)
|
||||
blk_finish_plug(current->plug);
|
||||
|
||||
if (F2FS_IO_ALIGNED(sbi))
|
||||
goto submit_io;
|
||||
|
||||
start = bio->bi_iter.bi_size >> F2FS_BLKSIZE_BITS;
|
||||
start %= F2FS_IO_SIZE(sbi);
|
||||
|
||||
|
@ -485,8 +487,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
|||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
/* Allocate a new bio */
|
||||
bio = __bio_alloc(fio->sbi, fio->new_blkaddr, fio->io_wbc,
|
||||
1, is_read_io(fio->op), fio->type, fio->temp);
|
||||
bio = __bio_alloc(fio, 1);
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
||||
bio_put(bio);
|
||||
|
@ -505,6 +506,43 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool page_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio,
|
||||
block_t last_blkaddr, block_t cur_blkaddr)
|
||||
{
|
||||
if (last_blkaddr + 1 != cur_blkaddr)
|
||||
return false;
|
||||
return __same_bdev(sbi, cur_blkaddr, bio);
|
||||
}
|
||||
|
||||
static bool io_type_is_mergeable(struct f2fs_bio_info *io,
|
||||
struct f2fs_io_info *fio)
|
||||
{
|
||||
if (io->fio.op != fio->op)
|
||||
return false;
|
||||
return io->fio.op_flags == fio->op_flags;
|
||||
}
|
||||
|
||||
static bool io_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio,
|
||||
struct f2fs_bio_info *io,
|
||||
struct f2fs_io_info *fio,
|
||||
block_t last_blkaddr,
|
||||
block_t cur_blkaddr)
|
||||
{
|
||||
if (F2FS_IO_ALIGNED(sbi) && (fio->type == DATA || fio->type == NODE)) {
|
||||
unsigned int filled_blocks =
|
||||
F2FS_BYTES_TO_BLK(bio->bi_iter.bi_size);
|
||||
unsigned int io_size = F2FS_IO_SIZE(sbi);
|
||||
unsigned int left_vecs = bio->bi_max_vecs - bio->bi_vcnt;
|
||||
|
||||
/* IOs in bio is aligned and left space of vectors is not enough */
|
||||
if (!(filled_blocks % io_size) && left_vecs < io_size)
|
||||
return false;
|
||||
}
|
||||
if (!page_is_mergeable(sbi, bio, last_blkaddr, cur_blkaddr))
|
||||
return false;
|
||||
return io_type_is_mergeable(io, fio);
|
||||
}
|
||||
|
||||
int f2fs_merge_page_bio(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct bio *bio = *fio->bio;
|
||||
|
@ -518,15 +556,14 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
|
|||
trace_f2fs_submit_page_bio(page, fio);
|
||||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
if (bio && (*fio->last_block + 1 != fio->new_blkaddr ||
|
||||
!__same_bdev(fio->sbi, fio->new_blkaddr, bio))) {
|
||||
if (bio && !page_is_mergeable(fio->sbi, bio, *fio->last_block,
|
||||
fio->new_blkaddr)) {
|
||||
__submit_bio(fio->sbi, bio, fio->type);
|
||||
bio = NULL;
|
||||
}
|
||||
alloc_new:
|
||||
if (!bio) {
|
||||
bio = __bio_alloc(fio->sbi, fio->new_blkaddr, fio->io_wbc,
|
||||
BIO_MAX_PAGES, false, fio->type, fio->temp);
|
||||
bio = __bio_alloc(fio, BIO_MAX_PAGES);
|
||||
bio_set_op_attrs(bio, fio->op, fio->op_flags);
|
||||
}
|
||||
|
||||
|
@ -592,21 +629,19 @@ next:
|
|||
|
||||
inc_page_count(sbi, WB_DATA_TYPE(bio_page));
|
||||
|
||||
if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 ||
|
||||
(io->fio.op != fio->op || io->fio.op_flags != fio->op_flags) ||
|
||||
!__same_bdev(sbi, fio->new_blkaddr, io->bio)))
|
||||
if (io->bio && !io_is_mergeable(sbi, io->bio, io, fio,
|
||||
io->last_block_in_bio, fio->new_blkaddr))
|
||||
__submit_merged_bio(io);
|
||||
alloc_new:
|
||||
if (io->bio == NULL) {
|
||||
if ((fio->type == DATA || fio->type == NODE) &&
|
||||
if (F2FS_IO_ALIGNED(sbi) &&
|
||||
(fio->type == DATA || fio->type == NODE) &&
|
||||
fio->new_blkaddr & F2FS_IO_SIZE_MASK(sbi)) {
|
||||
dec_page_count(sbi, WB_DATA_TYPE(bio_page));
|
||||
fio->retry = true;
|
||||
goto skip;
|
||||
}
|
||||
io->bio = __bio_alloc(sbi, fio->new_blkaddr, fio->io_wbc,
|
||||
BIO_MAX_PAGES, false,
|
||||
fio->type, fio->temp);
|
||||
io->bio = __bio_alloc(fio, BIO_MAX_PAGES);
|
||||
io->fio = *fio;
|
||||
}
|
||||
|
||||
|
@ -627,7 +662,7 @@ skip:
|
|||
goto next;
|
||||
out:
|
||||
if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN) ||
|
||||
f2fs_is_checkpoint_ready(sbi))
|
||||
!f2fs_is_checkpoint_ready(sbi))
|
||||
__submit_merged_bio(io);
|
||||
up_write(&io->io_rwsem);
|
||||
}
|
||||
|
@ -1022,7 +1057,7 @@ alloc:
|
|||
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
old_blkaddr, old_blkaddr);
|
||||
f2fs_set_data_blkaddr(dn);
|
||||
f2fs_update_data_blkaddr(dn, dn->data_blkaddr);
|
||||
|
||||
/*
|
||||
* i_size will be updated by direct_IO. Otherwise, we'll get stale
|
||||
|
@ -1199,10 +1234,10 @@ next_block:
|
|||
if (test_opt(sbi, LFS) && flag == F2FS_GET_BLOCK_DIO &&
|
||||
map->m_may_create) {
|
||||
err = __allocate_data_block(&dn, map->m_seg_type);
|
||||
if (!err) {
|
||||
blkaddr = dn.data_blkaddr;
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
}
|
||||
if (err)
|
||||
goto sync_out;
|
||||
blkaddr = dn.data_blkaddr;
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
}
|
||||
} else {
|
||||
if (create) {
|
||||
|
@ -1407,7 +1442,7 @@ static int get_data_block_dio_write(struct inode *inode, sector_t iblock,
|
|||
return __get_data_block(inode, iblock, bh_result, create,
|
||||
F2FS_GET_BLOCK_DIO, NULL,
|
||||
f2fs_rw_hint_to_seg_type(inode->i_write_hint),
|
||||
true);
|
||||
IS_SWAPFILE(inode) ? false : true);
|
||||
}
|
||||
|
||||
static int get_data_block_dio(struct inode *inode, sector_t iblock,
|
||||
|
@ -1538,7 +1573,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) {
|
||||
ret = f2fs_inline_data_fiemap(inode, fieinfo, start, len);
|
||||
if (ret != -EAGAIN)
|
||||
goto out;
|
||||
|
@ -1691,8 +1726,8 @@ zero_out:
|
|||
* This page will go to BIO. Do we need to send this
|
||||
* BIO off first?
|
||||
*/
|
||||
if (bio && (*last_block_in_bio != block_nr - 1 ||
|
||||
!__same_bdev(F2FS_I_SB(inode), block_nr, bio))) {
|
||||
if (bio && !page_is_mergeable(F2FS_I_SB(inode), bio,
|
||||
*last_block_in_bio, block_nr)) {
|
||||
submit_and_realloc:
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
bio = NULL;
|
||||
|
@ -2590,9 +2625,10 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
|||
|
||||
trace_f2fs_write_begin(inode, pos, len, flags);
|
||||
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
if (!f2fs_is_checkpoint_ready(sbi)) {
|
||||
err = -ENOSPC;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((f2fs_is_atomic_file(inode) &&
|
||||
!f2fs_available_free_memory(sbi, INMEM_PAGES)) ||
|
||||
|
|
|
@ -67,7 +67,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->nr_rd_data = get_pages(sbi, F2FS_RD_DATA);
|
||||
si->nr_rd_node = get_pages(sbi, F2FS_RD_NODE);
|
||||
si->nr_rd_meta = get_pages(sbi, F2FS_RD_META);
|
||||
if (SM_I(sbi) && SM_I(sbi)->fcc_info) {
|
||||
if (SM_I(sbi)->fcc_info) {
|
||||
si->nr_flushed =
|
||||
atomic_read(&SM_I(sbi)->fcc_info->issued_flush);
|
||||
si->nr_flushing =
|
||||
|
@ -75,7 +75,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->flush_list_empty =
|
||||
llist_empty(&SM_I(sbi)->fcc_info->issue_list);
|
||||
}
|
||||
if (SM_I(sbi) && SM_I(sbi)->dcc_info) {
|
||||
if (SM_I(sbi)->dcc_info) {
|
||||
si->nr_discarded =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->issued_discard);
|
||||
si->nr_discarding =
|
||||
|
|
184
fs/f2fs/dir.c
184
fs/f2fs/dir.c
|
@ -8,6 +8,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/unicode.h>
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "acl.h"
|
||||
|
@ -81,7 +82,8 @@ static unsigned long dir_block_index(unsigned int level,
|
|||
return bidx;
|
||||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
static struct f2fs_dir_entry *find_in_block(struct inode *dir,
|
||||
struct page *dentry_page,
|
||||
struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash,
|
||||
int *max_slots,
|
||||
|
@ -93,7 +95,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
|||
|
||||
dentry_blk = (struct f2fs_dentry_block *)page_address(dentry_page);
|
||||
|
||||
make_dentry_ptr_block(NULL, &d, dentry_blk);
|
||||
make_dentry_ptr_block(dir, &d, dentry_blk);
|
||||
de = f2fs_find_target_dentry(fname, namehash, max_slots, &d);
|
||||
if (de)
|
||||
*res_page = dentry_page;
|
||||
|
@ -101,14 +103,116 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
|||
return de;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
/*
|
||||
* Test whether a case-insensitive directory entry matches the filename
|
||||
* being searched for.
|
||||
*
|
||||
* Returns: 0 if the directory entry matches, more than 0 if it
|
||||
* doesn't match or less than zero on error.
|
||||
*/
|
||||
int f2fs_ci_compare(const struct inode *parent, const struct qstr *name,
|
||||
const struct qstr *entry, bool quick)
|
||||
{
|
||||
const struct f2fs_sb_info *sbi = F2FS_SB(parent->i_sb);
|
||||
const struct unicode_map *um = sbi->s_encoding;
|
||||
int ret;
|
||||
|
||||
if (quick)
|
||||
ret = utf8_strncasecmp_folded(um, name, entry);
|
||||
else
|
||||
ret = utf8_strncasecmp(um, name, entry);
|
||||
|
||||
if (ret < 0) {
|
||||
/* Handle invalid character sequence as either an error
|
||||
* or as an opaque byte sequence.
|
||||
*/
|
||||
if (f2fs_has_strict_mode(sbi))
|
||||
return -EINVAL;
|
||||
|
||||
if (name->len != entry->len)
|
||||
return 1;
|
||||
|
||||
return !!memcmp(name->name, entry->name, name->len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void f2fs_fname_setup_ci_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *cf_name)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
|
||||
if (!IS_CASEFOLDED(dir)) {
|
||||
cf_name->name = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
cf_name->name = f2fs_kmalloc(sbi, F2FS_NAME_LEN, GFP_NOFS);
|
||||
if (!cf_name->name)
|
||||
return;
|
||||
|
||||
cf_name->len = utf8_casefold(sbi->s_encoding,
|
||||
iname, cf_name->name,
|
||||
F2FS_NAME_LEN);
|
||||
if ((int)cf_name->len <= 0) {
|
||||
kvfree(cf_name->name);
|
||||
cf_name->name = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool f2fs_match_name(struct f2fs_dentry_ptr *d,
|
||||
struct f2fs_dir_entry *de,
|
||||
struct fscrypt_name *fname,
|
||||
struct fscrypt_str *cf_str,
|
||||
unsigned long bit_pos,
|
||||
f2fs_hash_t namehash)
|
||||
{
|
||||
#ifdef CONFIG_UNICODE
|
||||
struct inode *parent = d->inode;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(parent);
|
||||
struct qstr entry;
|
||||
#endif
|
||||
|
||||
if (de->hash_code != namehash)
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
entry.name = d->filename[bit_pos];
|
||||
entry.len = de->name_len;
|
||||
|
||||
if (sbi->s_encoding && IS_CASEFOLDED(parent)) {
|
||||
if (cf_str->name) {
|
||||
struct qstr cf = {.name = cf_str->name,
|
||||
.len = cf_str->len};
|
||||
return !f2fs_ci_compare(parent, &cf, &entry, true);
|
||||
}
|
||||
return !f2fs_ci_compare(parent, fname->usr_fname, &entry,
|
||||
false);
|
||||
}
|
||||
#endif
|
||||
if (fscrypt_match_name(fname, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *f2fs_find_target_dentry(struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash, int *max_slots,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
struct fscrypt_str cf_str = { .name = NULL, .len = 0 };
|
||||
unsigned long bit_pos = 0;
|
||||
int max_len = 0;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
f2fs_fname_setup_ci_filename(d->inode, fname->usr_fname, &cf_str);
|
||||
#endif
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
while (bit_pos < d->max) {
|
||||
|
@ -125,9 +229,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(struct fscrypt_name *fname,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (de->hash_code == namehash &&
|
||||
fscrypt_match_name(fname, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len)))
|
||||
if (f2fs_match_name(d, de, fname, &cf_str, bit_pos, namehash))
|
||||
goto found;
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
|
@ -141,6 +243,10 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(struct fscrypt_name *fname,
|
|||
found:
|
||||
if (max_slots && max_len > *max_slots)
|
||||
*max_slots = max_len;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
kvfree(cf_str.name);
|
||||
#endif
|
||||
return de;
|
||||
}
|
||||
|
||||
|
@ -157,7 +263,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
|||
struct f2fs_dir_entry *de = NULL;
|
||||
bool room = false;
|
||||
int max_slots;
|
||||
f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname);
|
||||
f2fs_hash_t namehash = f2fs_dentry_hash(dir, &name, fname);
|
||||
|
||||
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
|
||||
nblock = bucket_blocks(level);
|
||||
|
@ -179,8 +285,8 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
|||
}
|
||||
}
|
||||
|
||||
de = find_in_block(dentry_page, fname, namehash, &max_slots,
|
||||
res_page);
|
||||
de = find_in_block(dir, dentry_page, fname, namehash,
|
||||
&max_slots, res_page);
|
||||
if (de)
|
||||
break;
|
||||
|
||||
|
@ -250,6 +356,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
|||
struct fscrypt_name fname;
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (f2fs_has_strict_mode(F2FS_I_SB(dir)) && IS_CASEFOLDED(dir) &&
|
||||
utf8_validate(F2FS_I_SB(dir)->s_encoding, child)) {
|
||||
*res_page = ERR_PTR(-EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = fscrypt_setup_filename(dir, child, 1, &fname);
|
||||
if (err) {
|
||||
if (err == -ENOENT)
|
||||
|
@ -504,7 +618,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
|
|||
|
||||
level = 0;
|
||||
slots = GET_DENTRY_SLOTS(new_name->len);
|
||||
dentry_hash = f2fs_dentry_hash(new_name, NULL);
|
||||
dentry_hash = f2fs_dentry_hash(dir, new_name, NULL);
|
||||
|
||||
current_depth = F2FS_I(dir)->i_current_depth;
|
||||
if (F2FS_I(dir)->chash == dentry_hash) {
|
||||
|
@ -568,6 +682,11 @@ add_dentry:
|
|||
|
||||
if (inode) {
|
||||
f2fs_i_pino_write(inode, dir->i_ino);
|
||||
|
||||
/* synchronize inode page's data from inode cache */
|
||||
if (is_inode_flag_set(inode, FI_NEW_INODE))
|
||||
f2fs_update_inode(inode, page);
|
||||
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
|
@ -943,3 +1062,50 @@ const struct file_operations f2fs_dir_operations = {
|
|||
.compat_ioctl = f2fs_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
static int f2fs_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
const char *str, const struct qstr *name)
|
||||
{
|
||||
struct qstr qstr = {.name = str, .len = len };
|
||||
|
||||
if (!IS_CASEFOLDED(dentry->d_parent->d_inode)) {
|
||||
if (len != name->len)
|
||||
return -1;
|
||||
return memcmp(str, name, len);
|
||||
}
|
||||
|
||||
return f2fs_ci_compare(dentry->d_parent->d_inode, name, &qstr, false);
|
||||
}
|
||||
|
||||
static int f2fs_d_hash(const struct dentry *dentry, struct qstr *str)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
|
||||
const struct unicode_map *um = sbi->s_encoding;
|
||||
unsigned char *norm;
|
||||
int len, ret = 0;
|
||||
|
||||
if (!IS_CASEFOLDED(dentry->d_inode))
|
||||
return 0;
|
||||
|
||||
norm = f2fs_kmalloc(sbi, PATH_MAX, GFP_ATOMIC);
|
||||
if (!norm)
|
||||
return -ENOMEM;
|
||||
|
||||
len = utf8_casefold(um, str, norm, PATH_MAX);
|
||||
if (len < 0) {
|
||||
if (f2fs_has_strict_mode(sbi))
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
str->hash = full_name_hash(dentry, norm, len);
|
||||
out:
|
||||
kvfree(norm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct dentry_operations f2fs_dentry_ops = {
|
||||
.d_hash = f2fs_d_hash,
|
||||
.d_compare = f2fs_d_compare,
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -154,6 +154,7 @@ struct f2fs_mount_info {
|
|||
#define F2FS_FEATURE_LOST_FOUND 0x0200
|
||||
#define F2FS_FEATURE_VERITY 0x0400
|
||||
#define F2FS_FEATURE_SB_CHKSUM 0x0800
|
||||
#define F2FS_FEATURE_CASEFOLD 0x1000
|
||||
|
||||
#define __F2FS_HAS_FEATURE(raw_super, mask) \
|
||||
((raw_super->feature & cpu_to_le32(mask)) != 0)
|
||||
|
@ -418,6 +419,9 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
|
|||
#define F2FS_IOC_PRECACHE_EXTENTS _IO(F2FS_IOCTL_MAGIC, 15)
|
||||
#define F2FS_IOC_RESIZE_FS _IOW(F2FS_IOCTL_MAGIC, 16, __u64)
|
||||
|
||||
#define F2FS_IOC_GET_VOLUME_NAME FS_IOC_GETFSLABEL
|
||||
#define F2FS_IOC_SET_VOLUME_NAME FS_IOC_SETFSLABEL
|
||||
|
||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
|
||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
|
||||
#define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT
|
||||
|
@ -1172,6 +1176,10 @@ struct f2fs_sb_info {
|
|||
int valid_super_block; /* valid super block no */
|
||||
unsigned long s_flag; /* flags for sbi */
|
||||
struct mutex writepages; /* mutex for writepages() */
|
||||
#ifdef CONFIG_UNICODE
|
||||
struct unicode_map *s_encoding;
|
||||
__u16 s_encoding_flags;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
unsigned int blocks_per_blkz; /* F2FS blocks per zone */
|
||||
|
@ -1643,6 +1651,7 @@ static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f)
|
|||
static inline void disable_nat_bits(struct f2fs_sb_info *sbi, bool lock)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char *nat_bits;
|
||||
|
||||
/*
|
||||
* In order to re-enable nat_bits we need to call fsck.f2fs by
|
||||
|
@ -1653,10 +1662,12 @@ static inline void disable_nat_bits(struct f2fs_sb_info *sbi, bool lock)
|
|||
if (lock)
|
||||
spin_lock_irqsave(&sbi->cp_lock, flags);
|
||||
__clear_ckpt_flags(F2FS_CKPT(sbi), CP_NAT_BITS_FLAG);
|
||||
kvfree(NM_I(sbi)->nat_bits);
|
||||
nat_bits = NM_I(sbi)->nat_bits;
|
||||
NM_I(sbi)->nat_bits = NULL;
|
||||
if (lock)
|
||||
spin_unlock_irqrestore(&sbi->cp_lock, flags);
|
||||
|
||||
kvfree(nat_bits);
|
||||
}
|
||||
|
||||
static inline bool enabled_nat_bits(struct f2fs_sb_info *sbi,
|
||||
|
@ -1763,7 +1774,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
|
|||
if (time_to_inject(sbi, FAULT_BLOCK)) {
|
||||
f2fs_show_injection_info(FAULT_BLOCK);
|
||||
release = *count;
|
||||
goto enospc;
|
||||
goto release_quota;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1808,6 +1819,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
|
|||
|
||||
enospc:
|
||||
percpu_counter_sub(&sbi->alloc_valid_block_count, release);
|
||||
release_quota:
|
||||
dquot_release_reservation_block(inode, release);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
@ -2362,13 +2374,16 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
|
|||
#define F2FS_INDEX_FL 0x00001000 /* hash-indexed directory */
|
||||
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
|
||||
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
|
||||
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
|
||||
|
||||
/* Flags that should be inherited by new inodes from their parent. */
|
||||
#define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \
|
||||
F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL)
|
||||
F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
|
||||
F2FS_CASEFOLD_FL)
|
||||
|
||||
/* Flags that are appropriate for regular files (all but dir-specific ones). */
|
||||
#define F2FS_REG_FLMASK (~(F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL))
|
||||
#define F2FS_REG_FLMASK (~(F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
|
||||
F2FS_CASEFOLD_FL))
|
||||
|
||||
/* Flags that are appropriate for non-directories/regular files. */
|
||||
#define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL)
|
||||
|
@ -2935,6 +2950,11 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name,
|
|||
bool hot, bool set);
|
||||
struct dentry *f2fs_get_parent(struct dentry *child);
|
||||
|
||||
extern int f2fs_ci_compare(const struct inode *parent,
|
||||
const struct qstr *name,
|
||||
const struct qstr *entry,
|
||||
bool quick);
|
||||
|
||||
/*
|
||||
* dir.c
|
||||
*/
|
||||
|
@ -2998,8 +3018,8 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
|||
/*
|
||||
* hash.c
|
||||
*/
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
|
||||
struct fscrypt_name *fname);
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct inode *dir,
|
||||
const struct qstr *name_info, struct fscrypt_name *fname);
|
||||
|
||||
/*
|
||||
* node.c
|
||||
|
@ -3442,6 +3462,9 @@ static inline void f2fs_destroy_root_stats(void) { }
|
|||
#endif
|
||||
|
||||
extern const struct file_operations f2fs_dir_operations;
|
||||
#ifdef CONFIG_UNICODE
|
||||
extern const struct dentry_operations f2fs_dentry_ops;
|
||||
#endif
|
||||
extern const struct file_operations f2fs_file_operations;
|
||||
extern const struct inode_operations f2fs_file_inode_operations;
|
||||
extern const struct address_space_operations f2fs_dblock_aops;
|
||||
|
@ -3576,6 +3599,7 @@ F2FS_FEATURE_FUNCS(inode_crtime, INODE_CRTIME);
|
|||
F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND);
|
||||
F2FS_FEATURE_FUNCS(verity, VERITY);
|
||||
F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
|
||||
F2FS_FEATURE_FUNCS(casefold, CASEFOLD);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,
|
||||
|
@ -3694,11 +3718,14 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
|||
*/
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
return true;
|
||||
if (test_opt(sbi, LFS) && (rw == WRITE) &&
|
||||
block_unaligned_IO(inode, iocb, iter))
|
||||
return true;
|
||||
if (test_opt(sbi, LFS) && (rw == WRITE)) {
|
||||
if (block_unaligned_IO(inode, iocb, iter))
|
||||
return true;
|
||||
if (F2FS_IO_ALIGNED(sbi))
|
||||
return true;
|
||||
}
|
||||
if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_CP_DISABLED) &&
|
||||
!(inode->i_flags & S_SWAPFILE))
|
||||
!IS_SWAPFILE(inode))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
162
fs/f2fs/file.c
162
fs/f2fs/file.c
|
@ -20,6 +20,7 @@
|
|||
#include <linux/uio.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -57,6 +58,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (!f2fs_is_checkpoint_ready(sbi)) {
|
||||
err = -ENOSPC;
|
||||
goto err;
|
||||
}
|
||||
|
||||
sb_start_pagefault(inode->i_sb);
|
||||
|
||||
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
|
||||
|
@ -819,14 +825,24 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
}
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
bool to_smaller = (attr->ia_size <= i_size_read(inode));
|
||||
loff_t old_size = i_size_read(inode);
|
||||
|
||||
if (attr->ia_size > MAX_INLINE_DATA(inode)) {
|
||||
/*
|
||||
* should convert inline inode before i_size_write to
|
||||
* keep smaller than inline_data size with inline flag.
|
||||
*/
|
||||
err = f2fs_convert_inline_inode(inode);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
down_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
|
||||
if (to_smaller)
|
||||
if (attr->ia_size <= old_size)
|
||||
err = f2fs_truncate(inode);
|
||||
/*
|
||||
* do not trim all blocks after i_size if target size is
|
||||
|
@ -834,21 +850,11 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
*/
|
||||
up_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!to_smaller) {
|
||||
/* should convert inline inode here */
|
||||
if (!f2fs_may_inline_data(inode)) {
|
||||
err = f2fs_convert_inline_inode(inode);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
F2FS_I(inode)->last_disk_size = i_size_read(inode);
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
}
|
||||
|
@ -1041,7 +1047,7 @@ next_dnode:
|
|||
|
||||
if (test_opt(sbi, LFS)) {
|
||||
f2fs_put_dnode(&dn);
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* do not invalidate this block address */
|
||||
|
@ -1578,6 +1584,8 @@ static long f2fs_fallocate(struct file *file, int mode,
|
|||
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
|
||||
return -EIO;
|
||||
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
|
||||
return -ENOSPC;
|
||||
|
||||
/* f2fs only support ->fallocate for regular file */
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
|
@ -1669,6 +1677,13 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
|
|||
if (IS_NOQUOTA(inode))
|
||||
return -EPERM;
|
||||
|
||||
if ((iflags ^ fi->i_flags) & F2FS_CASEFOLD_FL) {
|
||||
if (!f2fs_sb_has_casefold(F2FS_I_SB(inode)))
|
||||
return -EOPNOTSUPP;
|
||||
if (!f2fs_empty_dir(inode))
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
|
||||
fi->i_flags = iflags | (fi->i_flags & ~mask);
|
||||
|
||||
if (fi->i_flags & F2FS_PROJINHERIT_FL)
|
||||
|
@ -1703,6 +1718,7 @@ static const struct {
|
|||
{ F2FS_INDEX_FL, FS_INDEX_FL },
|
||||
{ F2FS_DIRSYNC_FL, FS_DIRSYNC_FL },
|
||||
{ F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL },
|
||||
{ F2FS_CASEFOLD_FL, FS_CASEFOLD_FL },
|
||||
};
|
||||
|
||||
#define F2FS_GETTABLE_FS_FL ( \
|
||||
|
@ -1717,7 +1733,8 @@ static const struct {
|
|||
FS_ENCRYPT_FL | \
|
||||
FS_INLINE_DATA_FL | \
|
||||
FS_NOCOW_FL | \
|
||||
FS_VERITY_FL)
|
||||
FS_VERITY_FL | \
|
||||
FS_CASEFOLD_FL)
|
||||
|
||||
#define F2FS_SETTABLE_FS_FL ( \
|
||||
FS_SYNC_FL | \
|
||||
|
@ -1726,7 +1743,8 @@ static const struct {
|
|||
FS_NODUMP_FL | \
|
||||
FS_NOATIME_FL | \
|
||||
FS_DIRSYNC_FL | \
|
||||
FS_PROJINHERIT_FL)
|
||||
FS_PROJINHERIT_FL | \
|
||||
FS_CASEFOLD_FL)
|
||||
|
||||
/* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */
|
||||
static inline u32 f2fs_iflags_to_fsflags(u32 iflags)
|
||||
|
@ -1825,6 +1843,8 @@ static int f2fs_ioc_getversion(struct file *filp, unsigned long arg)
|
|||
static int f2fs_ioc_start_atomic_write(struct file *filp)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int ret;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
|
@ -1833,6 +1853,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
|
|||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (filp->f_flags & O_DIRECT)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1864,6 +1887,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
|
|||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (list_empty(&fi->inmem_ilist))
|
||||
list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
|
||||
/* add inode in inmem_list first and set atomic_file */
|
||||
set_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
|
@ -1905,11 +1934,8 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
|
|||
goto err_out;
|
||||
|
||||
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true);
|
||||
if (!ret) {
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
F2FS_I(inode)->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
|
||||
stat_dec_atomic_write(inode);
|
||||
}
|
||||
if (!ret)
|
||||
f2fs_drop_inmem_pages(inode);
|
||||
} else {
|
||||
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false);
|
||||
}
|
||||
|
@ -2295,9 +2321,9 @@ static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg)
|
|||
return -EROFS;
|
||||
|
||||
end = range.start + range.len;
|
||||
if (range.start < MAIN_BLKADDR(sbi) || end >= MAX_BLKADDR(sbi)) {
|
||||
if (end < range.start || range.start < MAIN_BLKADDR(sbi) ||
|
||||
end >= MAX_BLKADDR(sbi))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
|
@ -2421,8 +2447,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
|||
map.m_lblk += map.m_len;
|
||||
}
|
||||
|
||||
if (!fragmented)
|
||||
if (!fragmented) {
|
||||
total = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sec_num = DIV_ROUND_UP(total, BLKS_PER_SEC(sbi));
|
||||
|
||||
|
@ -2452,7 +2480,7 @@ do_map:
|
|||
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
|
||||
map.m_lblk = next_pgofs;
|
||||
continue;
|
||||
goto check;
|
||||
}
|
||||
|
||||
set_inode_flag(inode, FI_DO_DEFRAG);
|
||||
|
@ -2476,8 +2504,8 @@ do_map:
|
|||
}
|
||||
|
||||
map.m_lblk = idx;
|
||||
|
||||
if (idx < pg_end && cnt < blk_per_seg)
|
||||
check:
|
||||
if (map.m_lblk < pg_end && cnt < blk_per_seg)
|
||||
goto do_map;
|
||||
|
||||
clear_inode_flag(inode, FI_DO_DEFRAG);
|
||||
|
@ -3141,10 +3169,74 @@ static int f2fs_ioc_measure_verity(struct file *filp, unsigned long arg)
|
|||
return fsverity_ioctl_measure(filp, (void __user *)arg);
|
||||
}
|
||||
|
||||
static int f2fs_get_volume_name(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
char *vbuf;
|
||||
int count;
|
||||
int err = 0;
|
||||
|
||||
vbuf = f2fs_kzalloc(sbi, MAX_VOLUME_NAME, GFP_KERNEL);
|
||||
if (!vbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
down_read(&sbi->sb_lock);
|
||||
count = utf16s_to_utf8s(sbi->raw_super->volume_name,
|
||||
ARRAY_SIZE(sbi->raw_super->volume_name),
|
||||
UTF16_LITTLE_ENDIAN, vbuf, MAX_VOLUME_NAME);
|
||||
up_read(&sbi->sb_lock);
|
||||
|
||||
if (copy_to_user((char __user *)arg, vbuf,
|
||||
min(FSLABEL_MAX, count)))
|
||||
err = -EFAULT;
|
||||
|
||||
kvfree(vbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_set_volume_name(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
char *vbuf;
|
||||
int err = 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
vbuf = strndup_user((const char __user *)arg, FSLABEL_MAX);
|
||||
if (IS_ERR(vbuf))
|
||||
return PTR_ERR(vbuf);
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
down_write(&sbi->sb_lock);
|
||||
|
||||
memset(sbi->raw_super->volume_name, 0,
|
||||
sizeof(sbi->raw_super->volume_name));
|
||||
utf8s_to_utf16s(vbuf, strlen(vbuf), UTF16_LITTLE_ENDIAN,
|
||||
sbi->raw_super->volume_name,
|
||||
ARRAY_SIZE(sbi->raw_super->volume_name));
|
||||
|
||||
err = f2fs_commit_super(sbi, false);
|
||||
|
||||
up_write(&sbi->sb_lock);
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
out:
|
||||
kfree(vbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
|
||||
return -EIO;
|
||||
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(file_inode(filp))))
|
||||
return -ENOSPC;
|
||||
|
||||
switch (cmd) {
|
||||
case F2FS_IOC_GETFLAGS:
|
||||
|
@ -3213,6 +3305,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
return f2fs_ioc_enable_verity(filp, arg);
|
||||
case FS_IOC_MEASURE_VERITY:
|
||||
return f2fs_ioc_measure_verity(filp, arg);
|
||||
case F2FS_IOC_GET_VOLUME_NAME:
|
||||
return f2fs_get_volume_name(filp, arg);
|
||||
case F2FS_IOC_SET_VOLUME_NAME:
|
||||
return f2fs_set_volume_name(filp, arg);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
@ -3229,16 +3325,12 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!inode_trylock(inode)) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
if (!inode_trylock(inode)) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
inode_lock(inode);
|
||||
}
|
||||
|
||||
|
@ -3334,6 +3426,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
case F2FS_IOC_RESIZE_FS:
|
||||
case FS_IOC_ENABLE_VERITY:
|
||||
case FS_IOC_MEASURE_VERITY:
|
||||
case F2FS_IOC_GET_VOLUME_NAME:
|
||||
case F2FS_IOC_SET_VOLUME_NAME:
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
|
27
fs/f2fs/gc.c
27
fs/f2fs/gc.c
|
@ -382,6 +382,16 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
nsearched++;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
/*
|
||||
* skip selecting the invalid segno (that is failed due to block
|
||||
* validity check failure during GC) to avoid endless GC loop in
|
||||
* such cases.
|
||||
*/
|
||||
if (test_bit(segno, sm->invalid_segmap))
|
||||
goto next;
|
||||
#endif
|
||||
|
||||
secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
|
@ -627,8 +637,21 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
source_blkaddr = datablock_addr(NULL, node_page, ofs_in_node);
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
if (source_blkaddr != blkaddr)
|
||||
if (source_blkaddr != blkaddr) {
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
unsigned int segno = GET_SEGNO(sbi, blkaddr);
|
||||
unsigned long offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
|
||||
|
||||
if (unlikely(check_valid_map(sbi, segno, offset))) {
|
||||
if (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) {
|
||||
f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u\n",
|
||||
blkaddr, source_blkaddr, segno);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1303,7 +1326,7 @@ gc_more:
|
|||
round++;
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
if (gc_type == FG_GC && seg_freed)
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
|
||||
if (sync)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/unicode.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
|
||||
|
@ -67,7 +68,7 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
|
|||
*buf++ = pad;
|
||||
}
|
||||
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
|
||||
static f2fs_hash_t __f2fs_dentry_hash(const struct qstr *name_info,
|
||||
struct fscrypt_name *fname)
|
||||
{
|
||||
__u32 hash;
|
||||
|
@ -103,3 +104,37 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
|
|||
f2fs_hash = cpu_to_le32(hash & ~F2FS_HASH_COL_BIT);
|
||||
return f2fs_hash;
|
||||
}
|
||||
|
||||
f2fs_hash_t f2fs_dentry_hash(const struct inode *dir,
|
||||
const struct qstr *name_info, struct fscrypt_name *fname)
|
||||
{
|
||||
#ifdef CONFIG_UNICODE
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
const struct unicode_map *um = sbi->s_encoding;
|
||||
int r, dlen;
|
||||
unsigned char *buff;
|
||||
struct qstr folded;
|
||||
|
||||
if (!name_info->len || !IS_CASEFOLDED(dir))
|
||||
goto opaque_seq;
|
||||
|
||||
buff = f2fs_kzalloc(sbi, sizeof(char) * PATH_MAX, GFP_KERNEL);
|
||||
if (!buff)
|
||||
return -ENOMEM;
|
||||
|
||||
dlen = utf8_casefold(um, name_info, buff, PATH_MAX);
|
||||
if (dlen < 0) {
|
||||
kvfree(buff);
|
||||
goto opaque_seq;
|
||||
}
|
||||
folded.name = buff;
|
||||
folded.len = dlen;
|
||||
r = __f2fs_dentry_hash(&folded, fname);
|
||||
|
||||
kvfree(buff);
|
||||
return r;
|
||||
|
||||
opaque_seq:
|
||||
#endif
|
||||
return __f2fs_dentry_hash(name_info, fname);
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
|||
|
||||
err = f2fs_get_node_info(fio.sbi, dn->nid, &ni);
|
||||
if (err) {
|
||||
f2fs_truncate_data_blocks_range(dn, 1);
|
||||
f2fs_put_dnode(dn);
|
||||
return err;
|
||||
}
|
||||
|
@ -320,7 +321,7 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
namehash = f2fs_dentry_hash(&name, fname);
|
||||
namehash = f2fs_dentry_hash(dir, &name, fname);
|
||||
|
||||
inline_dentry = inline_data_addr(dir, ipage);
|
||||
|
||||
|
@ -580,7 +581,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
|||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(new_name, NULL);
|
||||
name_hash = f2fs_dentry_hash(dir, new_name, NULL);
|
||||
f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
@ -588,6 +589,11 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name,
|
|||
/* we don't need to mark_inode_dirty now */
|
||||
if (inode) {
|
||||
f2fs_i_pino_write(inode, dir->i_ino);
|
||||
|
||||
/* synchronize inode page's data from inode cache */
|
||||
if (is_inode_flag_set(inode, FI_NEW_INODE))
|
||||
f2fs_update_inode(inode, page);
|
||||
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
|
@ -704,7 +710,13 @@ int f2fs_inline_data_fiemap(struct inode *inode,
|
|||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
if ((S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!f2fs_has_inline_data(inode)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISDIR(inode->i_mode) && !f2fs_has_inline_dentry(inode)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -48,9 +48,11 @@ void f2fs_set_inode_flags(struct inode *inode)
|
|||
new_fl |= S_ENCRYPTED;
|
||||
if (file_is_verity(inode))
|
||||
new_fl |= S_VERITY;
|
||||
if (flags & F2FS_CASEFOLD_FL)
|
||||
new_fl |= S_CASEFOLD;
|
||||
inode_set_flags(inode, new_fl,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|
|
||||
S_ENCRYPTED|S_VERITY);
|
||||
S_ENCRYPTED|S_VERITY|S_CASEFOLD);
|
||||
}
|
||||
|
||||
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
|
@ -616,7 +618,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
|
||||
return 0;
|
||||
|
||||
if (f2fs_is_checkpoint_ready(sbi))
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
/*
|
||||
|
@ -695,7 +697,8 @@ retry:
|
|||
|
||||
if (err) {
|
||||
f2fs_update_inode_page(inode);
|
||||
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
|
||||
if (dquot_initialize_needed(inode))
|
||||
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
|
||||
}
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
no_delete:
|
||||
|
@ -705,7 +708,7 @@ no_delete:
|
|||
stat_dec_inline_dir(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
|
||||
if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG) &&
|
||||
if (likely(!f2fs_cp_error(sbi) &&
|
||||
!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
|
||||
f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
|
||||
else
|
||||
|
|
|
@ -272,9 +272,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
|
@ -321,9 +320,8 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
err = fscrypt_prepare_link(old_dentry, dir, dentry);
|
||||
if (err)
|
||||
|
@ -489,6 +487,17 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
goto out_iput;
|
||||
}
|
||||
out_splice:
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (!inode && IS_CASEFOLDED(dir)) {
|
||||
/* Eventually we want to call d_add_ci(dentry, NULL)
|
||||
* for negative dentries in the encoding case as
|
||||
* well. For now, prevent the negative dentry
|
||||
* from being cached.
|
||||
*/
|
||||
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
new = d_splice_alias(inode, dentry);
|
||||
err = PTR_ERR_OR_ZERO(new);
|
||||
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
||||
|
@ -537,6 +546,16 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
goto fail;
|
||||
}
|
||||
f2fs_delete_entry(de, page, dir, inode);
|
||||
#ifdef CONFIG_UNICODE
|
||||
/* VFS negative dentries are incompatible with Encoding and
|
||||
* Case-insensitiveness. Eventually we'll want avoid
|
||||
* invalidating the dentries here, alongside with returning the
|
||||
* negative dentries at f2fs_lookup(), when it is better
|
||||
* supported by the VFS for the CI case.
|
||||
*/
|
||||
if (IS_CASEFOLDED(dir))
|
||||
d_invalidate(dentry);
|
||||
#endif
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
|
@ -571,9 +590,8 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
|
||||
&disk_link);
|
||||
|
@ -703,9 +721,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
|
@ -804,6 +821,8 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
if (IS_ENCRYPTED(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) {
|
||||
int err = fscrypt_get_encryption_info(dir);
|
||||
|
@ -840,9 +859,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
||||
(!projid_eq(F2FS_I(new_dir)->i_projid,
|
||||
|
@ -1035,9 +1053,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
err = f2fs_is_checkpoint_ready(sbi);
|
||||
if (err)
|
||||
return err;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
|
||||
!projid_eq(F2FS_I(new_dir)->i_projid,
|
||||
|
@ -1250,6 +1267,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
|
|||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
.fiemap = f2fs_fiemap,
|
||||
};
|
||||
|
||||
const struct inode_operations f2fs_symlink_inode_operations = {
|
||||
|
|
|
@ -1524,7 +1524,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
|||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto redirty_out;
|
||||
|
||||
if (wbc->sync_mode == WB_SYNC_NONE &&
|
||||
if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
|
||||
wbc->sync_mode == WB_SYNC_NONE &&
|
||||
IS_DNODE(page) && is_cold_node(page))
|
||||
goto redirty_out;
|
||||
|
||||
|
@ -1762,6 +1763,47 @@ out:
|
|||
return ret ? -EIO: 0;
|
||||
}
|
||||
|
||||
static int f2fs_match_ino(struct inode *inode, unsigned long ino, void *data)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
bool clean;
|
||||
|
||||
if (inode->i_ino != ino)
|
||||
return 0;
|
||||
|
||||
if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
|
||||
return 0;
|
||||
|
||||
spin_lock(&sbi->inode_lock[DIRTY_META]);
|
||||
clean = list_empty(&F2FS_I(inode)->gdirty_list);
|
||||
spin_unlock(&sbi->inode_lock[DIRTY_META]);
|
||||
|
||||
if (clean)
|
||||
return 0;
|
||||
|
||||
inode = igrab(inode);
|
||||
if (!inode)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool flush_dirty_inode(struct page *page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
struct inode *inode;
|
||||
nid_t ino = ino_of_node(page);
|
||||
|
||||
inode = find_inode_nowait(sbi->sb, ino, f2fs_match_ino, NULL);
|
||||
if (!inode)
|
||||
return false;
|
||||
|
||||
f2fs_update_inode(inode, page);
|
||||
unlock_page(page);
|
||||
|
||||
iput(inode);
|
||||
return true;
|
||||
}
|
||||
|
||||
int f2fs_sync_node_pages(struct f2fs_sb_info *sbi,
|
||||
struct writeback_control *wbc,
|
||||
bool do_balance, enum iostat_type io_type)
|
||||
|
@ -1785,6 +1827,7 @@ next_step:
|
|||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
bool submitted = false;
|
||||
bool may_dirty = true;
|
||||
|
||||
/* give a priority to WB_SYNC threads */
|
||||
if (atomic_read(&sbi->wb_sync_req[NODE]) &&
|
||||
|
@ -1832,6 +1875,13 @@ continue_unlock:
|
|||
goto lock_node;
|
||||
}
|
||||
|
||||
/* flush dirty inode */
|
||||
if (IS_INODE(page) && may_dirty) {
|
||||
may_dirty = false;
|
||||
if (flush_dirty_inode(page))
|
||||
goto lock_node;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, NODE, true, true);
|
||||
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
|
@ -1860,7 +1910,8 @@ continue_unlock:
|
|||
}
|
||||
|
||||
if (step < 2) {
|
||||
if (wbc->sync_mode == WB_SYNC_NONE && step == 1)
|
||||
if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
|
||||
wbc->sync_mode == WB_SYNC_NONE && step == 1)
|
||||
goto out;
|
||||
step++;
|
||||
goto next_step;
|
||||
|
@ -2964,7 +3015,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
|
|||
|
||||
/* not used nids: 0, node, meta, (and root counted as valid node) */
|
||||
nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count -
|
||||
sbi->nquota_files - F2FS_RESERVED_NODE_NUM;
|
||||
F2FS_RESERVED_NODE_NUM;
|
||||
nm_i->nid_cnt[FREE_NID] = 0;
|
||||
nm_i->nid_cnt[PREALLOC_NID] = 0;
|
||||
nm_i->nat_cnt = 0;
|
||||
|
|
|
@ -185,8 +185,6 @@ bool f2fs_need_SSR(struct f2fs_sb_info *sbi)
|
|||
|
||||
void f2fs_register_inmem_page(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct inmem_pages *new;
|
||||
|
||||
f2fs_trace_pid(page);
|
||||
|
@ -200,15 +198,11 @@ void f2fs_register_inmem_page(struct inode *inode, struct page *page)
|
|||
INIT_LIST_HEAD(&new->list);
|
||||
|
||||
/* increase reference count with clean state */
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
get_page(page);
|
||||
list_add_tail(&new->list, &fi->inmem_pages);
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (list_empty(&fi->inmem_ilist))
|
||||
list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
mutex_lock(&F2FS_I(inode)->inmem_lock);
|
||||
list_add_tail(&new->list, &F2FS_I(inode)->inmem_pages);
|
||||
inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
mutex_unlock(&F2FS_I(inode)->inmem_lock);
|
||||
|
||||
trace_f2fs_register_inmem_page(page, INMEM);
|
||||
}
|
||||
|
@ -330,19 +324,17 @@ void f2fs_drop_inmem_pages(struct inode *inode)
|
|||
mutex_lock(&fi->inmem_lock);
|
||||
__revoke_inmem_pages(inode, &fi->inmem_pages,
|
||||
true, false, true);
|
||||
|
||||
if (list_empty(&fi->inmem_pages)) {
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (!list_empty(&fi->inmem_ilist))
|
||||
list_del_init(&fi->inmem_ilist);
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
}
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
}
|
||||
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
|
||||
stat_dec_atomic_write(inode);
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (!list_empty(&fi->inmem_ilist))
|
||||
list_del_init(&fi->inmem_ilist);
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
}
|
||||
|
||||
void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
|
||||
|
@ -471,11 +463,6 @@ int f2fs_commit_inmem_pages(struct inode *inode)
|
|||
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
err = __f2fs_commit_inmem_pages(inode);
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (!list_empty(&fi->inmem_ilist))
|
||||
list_del_init(&fi->inmem_ilist);
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
|
||||
clear_inode_flag(inode, FI_ATOMIC_COMMIT);
|
||||
|
@ -501,7 +488,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
|||
if (need && excess_cached_nats(sbi))
|
||||
f2fs_balance_fs_bg(sbi);
|
||||
|
||||
if (f2fs_is_checkpoint_ready(sbi))
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -817,9 +804,13 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
|
|||
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
|
||||
dirty_i->nr_dirty[t]--;
|
||||
|
||||
if (get_valid_blocks(sbi, segno, true) == 0)
|
||||
if (get_valid_blocks(sbi, segno, true) == 0) {
|
||||
clear_bit(GET_SEC_FROM_SEG(sbi, segno),
|
||||
dirty_i->victim_secmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
clear_bit(segno, SIT_I(sbi)->invalid_segmap);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2084,6 +2075,13 @@ static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi)
|
|||
|
||||
f2fs_stop_discard_thread(sbi);
|
||||
|
||||
/*
|
||||
* Recovery can cache discard commands, so in error path of
|
||||
* fill_super(), it needs to give a chance to handle them.
|
||||
*/
|
||||
if (unlikely(atomic_read(&dcc->discard_cmd_cnt)))
|
||||
f2fs_issue_discard_timeout(sbi);
|
||||
|
||||
kvfree(dcc);
|
||||
SM_I(sbi)->dcc_info = NULL;
|
||||
}
|
||||
|
@ -2156,9 +2154,11 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
|
|||
if (!f2fs_test_and_set_bit(offset, se->discard_map))
|
||||
sbi->discard_blks--;
|
||||
|
||||
/* don't overwrite by SSR to keep node chain */
|
||||
if (IS_NODESEG(se->type) &&
|
||||
!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
|
||||
/*
|
||||
* SSR should never reuse block which is checkpointed
|
||||
* or newly invalidated.
|
||||
*/
|
||||
if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
|
||||
if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
|
||||
se->ckpt_valid_blocks++;
|
||||
}
|
||||
|
@ -3116,12 +3116,14 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
|
|||
f2fs_inode_chksum_set(sbi, page);
|
||||
}
|
||||
|
||||
if (F2FS_IO_ALIGNED(sbi))
|
||||
fio->retry = false;
|
||||
|
||||
if (add_list) {
|
||||
struct f2fs_bio_info *io;
|
||||
|
||||
INIT_LIST_HEAD(&fio->list);
|
||||
fio->in_list = true;
|
||||
fio->retry = false;
|
||||
io = sbi->write_io[fio->type] + fio->temp;
|
||||
spin_lock(&io->io_lock);
|
||||
list_add_tail(&fio->list, &io->io_list);
|
||||
|
@ -3447,11 +3449,6 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|||
seg_i = CURSEG_I(sbi, i);
|
||||
segno = le32_to_cpu(ckpt->cur_data_segno[i]);
|
||||
blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]);
|
||||
if (blk_off > ENTRIES_IN_SUM) {
|
||||
f2fs_bug_on(sbi, 1);
|
||||
f2fs_put_page(page, 1);
|
||||
return -EFAULT;
|
||||
}
|
||||
seg_i->next_segno = segno;
|
||||
reset_curseg(sbi, i, 0);
|
||||
seg_i->alloc_type = ckpt->alloc_type[i];
|
||||
|
@ -3941,8 +3938,8 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
|||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||
struct sit_info *sit_i;
|
||||
unsigned int sit_segs, start;
|
||||
char *src_bitmap;
|
||||
unsigned int bitmap_size;
|
||||
char *src_bitmap, *bitmap;
|
||||
unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size;
|
||||
|
||||
/* allocate memory for SIT information */
|
||||
sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL);
|
||||
|
@ -3958,33 +3955,37 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
|||
if (!sit_i->sentries)
|
||||
return -ENOMEM;
|
||||
|
||||
bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
|
||||
sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, bitmap_size,
|
||||
main_bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
|
||||
sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, main_bitmap_size,
|
||||
GFP_KERNEL);
|
||||
if (!sit_i->dirty_sentries_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * 4;
|
||||
#else
|
||||
bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * 3;
|
||||
#endif
|
||||
sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
bitmap = sit_i->bitmap;
|
||||
|
||||
for (start = 0; start < MAIN_SEGS(sbi); start++) {
|
||||
sit_i->sentries[start].cur_valid_map
|
||||
= f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
sit_i->sentries[start].ckpt_valid_map
|
||||
= f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
if (!sit_i->sentries[start].cur_valid_map ||
|
||||
!sit_i->sentries[start].ckpt_valid_map)
|
||||
return -ENOMEM;
|
||||
sit_i->sentries[start].cur_valid_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
|
||||
sit_i->sentries[start].ckpt_valid_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
sit_i->sentries[start].cur_valid_map_mir
|
||||
= f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
if (!sit_i->sentries[start].cur_valid_map_mir)
|
||||
return -ENOMEM;
|
||||
sit_i->sentries[start].cur_valid_map_mir = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
#endif
|
||||
|
||||
sit_i->sentries[start].discard_map
|
||||
= f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE,
|
||||
GFP_KERNEL);
|
||||
if (!sit_i->sentries[start].discard_map)
|
||||
return -ENOMEM;
|
||||
sit_i->sentries[start].discard_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
}
|
||||
|
||||
sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
|
@ -4004,17 +4005,23 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
|||
sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1;
|
||||
|
||||
/* setup SIT bitmap from ckeckpoint pack */
|
||||
bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
|
||||
sit_bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
|
||||
src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP);
|
||||
|
||||
sit_i->sit_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL);
|
||||
sit_i->sit_bitmap = kmemdup(src_bitmap, sit_bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->sit_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
sit_i->sit_bitmap_mir = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL);
|
||||
sit_i->sit_bitmap_mir = kmemdup(src_bitmap,
|
||||
sit_bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->sit_bitmap_mir)
|
||||
return -ENOMEM;
|
||||
|
||||
sit_i->invalid_segmap = f2fs_kvzalloc(sbi,
|
||||
main_bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->invalid_segmap)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
|
||||
/* init SIT information */
|
||||
|
@ -4023,7 +4030,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
|||
sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr);
|
||||
sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg;
|
||||
sit_i->written_valid_blocks = 0;
|
||||
sit_i->bitmap_size = bitmap_size;
|
||||
sit_i->bitmap_size = sit_bitmap_size;
|
||||
sit_i->dirty_sentries = 0;
|
||||
sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK;
|
||||
sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time);
|
||||
|
@ -4161,7 +4168,6 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
|||
if (start >= MAIN_SEGS(sbi)) {
|
||||
f2fs_err(sbi, "Wrong journal entry on segno %u",
|
||||
start);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
err = -EFSCORRUPTED;
|
||||
break;
|
||||
}
|
||||
|
@ -4201,7 +4207,6 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
|||
if (!err && total_node_blocks != valid_node_count(sbi)) {
|
||||
f2fs_err(sbi, "SIT is corrupted node# %u vs %u",
|
||||
total_node_blocks, valid_node_count(sbi));
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
err = -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
|
@ -4492,21 +4497,12 @@ static void destroy_free_segmap(struct f2fs_sb_info *sbi)
|
|||
static void destroy_sit_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
unsigned int start;
|
||||
|
||||
if (!sit_i)
|
||||
return;
|
||||
|
||||
if (sit_i->sentries) {
|
||||
for (start = 0; start < MAIN_SEGS(sbi); start++) {
|
||||
kvfree(sit_i->sentries[start].cur_valid_map);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
kvfree(sit_i->sentries[start].cur_valid_map_mir);
|
||||
#endif
|
||||
kvfree(sit_i->sentries[start].ckpt_valid_map);
|
||||
kvfree(sit_i->sentries[start].discard_map);
|
||||
}
|
||||
}
|
||||
if (sit_i->sentries)
|
||||
kvfree(sit_i->bitmap);
|
||||
kvfree(sit_i->tmp_map);
|
||||
|
||||
kvfree(sit_i->sentries);
|
||||
|
@ -4517,6 +4513,7 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
|
|||
kvfree(sit_i->sit_bitmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
kvfree(sit_i->sit_bitmap_mir);
|
||||
kvfree(sit_i->invalid_segmap);
|
||||
#endif
|
||||
kvfree(sit_i);
|
||||
}
|
||||
|
|
|
@ -226,9 +226,13 @@ struct sit_info {
|
|||
block_t sit_base_addr; /* start block address of SIT area */
|
||||
block_t sit_blocks; /* # of blocks used by SIT area */
|
||||
block_t written_valid_blocks; /* # of valid blocks in main area */
|
||||
char *bitmap; /* all bitmaps pointer */
|
||||
char *sit_bitmap; /* SIT bitmap pointer */
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
char *sit_bitmap_mir; /* SIT bitmap mirror */
|
||||
|
||||
/* bitmap of segments to be ignored by GC in case of errors */
|
||||
unsigned long *invalid_segmap;
|
||||
#endif
|
||||
unsigned int bitmap_size; /* SIT bitmap size */
|
||||
|
||||
|
@ -582,13 +586,13 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
|
|||
reserved_sections(sbi) + needed);
|
||||
}
|
||||
|
||||
static inline int f2fs_is_checkpoint_ready(struct f2fs_sb_info *sbi)
|
||||
static inline bool f2fs_is_checkpoint_ready(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
|
||||
return 0;
|
||||
return true;
|
||||
if (likely(!has_not_enough_free_secs(sbi, 0, 0)))
|
||||
return 0;
|
||||
return -ENOSPC;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi)
|
||||
|
|
156
fs/f2fs/super.c
156
fs/f2fs/super.c
|
@ -23,6 +23,7 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/quota.h>
|
||||
#include <linux/unicode.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -222,6 +223,36 @@ void f2fs_printk(struct f2fs_sb_info *sbi, const char *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
static const struct f2fs_sb_encodings {
|
||||
__u16 magic;
|
||||
char *name;
|
||||
char *version;
|
||||
} f2fs_sb_encoding_map[] = {
|
||||
{F2FS_ENC_UTF8_12_1, "utf8", "12.1.0"},
|
||||
};
|
||||
|
||||
static int f2fs_sb_read_encoding(const struct f2fs_super_block *sb,
|
||||
const struct f2fs_sb_encodings **encoding,
|
||||
__u16 *flags)
|
||||
{
|
||||
__u16 magic = le16_to_cpu(sb->s_encoding);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(f2fs_sb_encoding_map); i++)
|
||||
if (magic == f2fs_sb_encoding_map[i].magic)
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(f2fs_sb_encoding_map))
|
||||
return -EINVAL;
|
||||
|
||||
*encoding = &f2fs_sb_encoding_map[i];
|
||||
*flags = le16_to_cpu(sb->s_encoding_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
block_t limit = min((sbi->user_block_count << 1) / 1000,
|
||||
|
@ -798,6 +829,13 @@ static int parse_options(struct super_block *sb, char *options)
|
|||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_UNICODE
|
||||
if (f2fs_sb_has_casefold(sbi)) {
|
||||
f2fs_err(sbi,
|
||||
"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) {
|
||||
f2fs_err(sbi, "Should set mode=lfs with %uKB-sized IO",
|
||||
|
@ -873,7 +911,21 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
|
|||
|
||||
static int f2fs_drop_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* during filesystem shutdown, if checkpoint is disabled,
|
||||
* drop useless meta/node dirty pages.
|
||||
*/
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
|
||||
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
|
||||
inode->i_ino == F2FS_META_INO(sbi)) {
|
||||
trace_f2fs_drop_inode(inode, 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is to avoid a deadlock condition like below.
|
||||
* writeback_single_inode(inode)
|
||||
|
@ -1091,6 +1143,9 @@ static void f2fs_put_super(struct super_block *sb)
|
|||
destroy_percpu_info(sbi);
|
||||
for (i = 0; i < NR_PAGE_TYPE; i++)
|
||||
kvfree(sbi->write_io[i]);
|
||||
#ifdef CONFIG_UNICODE
|
||||
utf8_unload(sbi->s_encoding);
|
||||
#endif
|
||||
kvfree(sbi);
|
||||
}
|
||||
|
||||
|
@ -1216,8 +1271,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
else
|
||||
buf->f_bavail = 0;
|
||||
|
||||
avail_node_count = sbi->total_node_count - sbi->nquota_files -
|
||||
F2FS_RESERVED_NODE_NUM;
|
||||
avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
|
||||
|
||||
if (avail_node_count > user_block_count) {
|
||||
buf->f_files = user_block_count;
|
||||
|
@ -1524,6 +1578,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
bool need_stop_gc = false;
|
||||
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
||||
bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
|
||||
bool no_io_align = !F2FS_IO_ALIGNED(sbi);
|
||||
bool checkpoint_changed;
|
||||
#ifdef CONFIG_QUOTA
|
||||
int i, j;
|
||||
|
@ -1603,6 +1658,12 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
goto restore_opts;
|
||||
}
|
||||
|
||||
if (no_io_align == !!F2FS_IO_ALIGNED(sbi)) {
|
||||
err = -EINVAL;
|
||||
f2fs_warn(sbi, "switch io_bits option is not allowed");
|
||||
goto restore_opts;
|
||||
}
|
||||
|
||||
if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
|
||||
err = -EINVAL;
|
||||
f2fs_warn(sbi, "disabling checkpoint not compatible with read-only");
|
||||
|
@ -1981,6 +2042,12 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
|
|||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
/* if quota sysfile exists, deny enabling quota with specific file */
|
||||
if (f2fs_sb_has_quota_ino(F2FS_SB(sb))) {
|
||||
f2fs_err(F2FS_SB(sb), "quota sysfile already exists");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
err = f2fs_quota_sync(sb, type);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -2000,7 +2067,7 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_quota_off(struct super_block *sb, int type)
|
||||
static int __f2fs_quota_off(struct super_block *sb, int type)
|
||||
{
|
||||
struct inode *inode = sb_dqopt(sb)->files[type];
|
||||
int err;
|
||||
|
@ -2026,13 +2093,30 @@ out_put:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_quota_off(struct super_block *sb, int type)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
int err;
|
||||
|
||||
err = __f2fs_quota_off(sb, type);
|
||||
|
||||
/*
|
||||
* quotactl can shutdown journalled quota, result in inconsistence
|
||||
* between quota record and fs data by following updates, tag the
|
||||
* flag to let fsck be aware of it.
|
||||
*/
|
||||
if (is_journalled_quota(sbi))
|
||||
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
|
||||
return err;
|
||||
}
|
||||
|
||||
void f2fs_quota_off_umount(struct super_block *sb)
|
||||
{
|
||||
int type;
|
||||
int err;
|
||||
|
||||
for (type = 0; type < MAXQUOTAS; type++) {
|
||||
err = f2fs_quota_off(sb, type);
|
||||
err = __f2fs_quota_off(sb, type);
|
||||
if (err) {
|
||||
int ret = dquot_quota_off(sb, type);
|
||||
|
||||
|
@ -2617,8 +2701,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
|||
}
|
||||
|
||||
valid_node_count = le32_to_cpu(ckpt->valid_node_count);
|
||||
avail_node_count = sbi->total_node_count - sbi->nquota_files -
|
||||
F2FS_RESERVED_NODE_NUM;
|
||||
avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
|
||||
if (valid_node_count > avail_node_count) {
|
||||
f2fs_err(sbi, "Wrong valid_node_count: %u, avail_node_count: %u",
|
||||
valid_node_count, avail_node_count);
|
||||
|
@ -2657,10 +2740,10 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
|||
}
|
||||
}
|
||||
for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) {
|
||||
for (j = i; j < NR_CURSEG_DATA_TYPE; j++) {
|
||||
for (j = 0; j < NR_CURSEG_DATA_TYPE; j++) {
|
||||
if (le32_to_cpu(ckpt->cur_node_segno[i]) ==
|
||||
le32_to_cpu(ckpt->cur_data_segno[j])) {
|
||||
f2fs_err(sbi, "Data segment (%u) and Data segment (%u) has the same segno: %u",
|
||||
f2fs_err(sbi, "Node segment (%u) and Data segment (%u) has the same segno: %u",
|
||||
i, j,
|
||||
le32_to_cpu(ckpt->cur_node_segno[i]));
|
||||
return 1;
|
||||
|
@ -3033,6 +3116,53 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_setup_casefold(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (f2fs_sb_has_casefold(sbi) && !sbi->s_encoding) {
|
||||
const struct f2fs_sb_encodings *encoding_info;
|
||||
struct unicode_map *encoding;
|
||||
__u16 encoding_flags;
|
||||
|
||||
if (f2fs_sb_has_encrypt(sbi)) {
|
||||
f2fs_err(sbi,
|
||||
"Can't mount with encoding and encryption");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info,
|
||||
&encoding_flags)) {
|
||||
f2fs_err(sbi,
|
||||
"Encoding requested by superblock is unknown");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
encoding = utf8_load(encoding_info->version);
|
||||
if (IS_ERR(encoding)) {
|
||||
f2fs_err(sbi,
|
||||
"can't mount with superblock charset: %s-%s "
|
||||
"not supported by the kernel. flags: 0x%x.",
|
||||
encoding_info->name, encoding_info->version,
|
||||
encoding_flags);
|
||||
return PTR_ERR(encoding);
|
||||
}
|
||||
f2fs_info(sbi, "Using encoding defined by superblock: "
|
||||
"%s-%s with flags 0x%hx", encoding_info->name,
|
||||
encoding_info->version?:"\b", encoding_flags);
|
||||
|
||||
sbi->s_encoding = encoding;
|
||||
sbi->s_encoding_flags = encoding_flags;
|
||||
sbi->sb->s_d_op = &f2fs_dentry_ops;
|
||||
}
|
||||
#else
|
||||
if (f2fs_sb_has_casefold(sbi)) {
|
||||
f2fs_err(sbi, "Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void f2fs_tuning_parameters(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_sm_info *sm_i = SM_I(sbi);
|
||||
|
@ -3129,6 +3259,10 @@ try_onemore:
|
|||
le32_to_cpu(raw_super->log_blocksize);
|
||||
sb->s_max_links = F2FS_LINK_MAX;
|
||||
|
||||
err = f2fs_setup_casefold(sbi);
|
||||
if (err)
|
||||
goto free_options;
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
sb->dq_op = &f2fs_quota_operations;
|
||||
sb->s_qcop = &f2fs_quotactl_ops;
|
||||
|
@ -3207,7 +3341,7 @@ try_onemore:
|
|||
if (err)
|
||||
goto free_bio_info;
|
||||
|
||||
if (F2FS_IO_SIZE(sbi) > 1) {
|
||||
if (F2FS_IO_ALIGNED(sbi)) {
|
||||
sbi->write_io_dummy =
|
||||
mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0);
|
||||
if (!sbi->write_io_dummy) {
|
||||
|
@ -3482,6 +3616,10 @@ free_percpu:
|
|||
free_bio_info:
|
||||
for (i = 0; i < NR_PAGE_TYPE; i++)
|
||||
kvfree(sbi->write_io[i]);
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
utf8_unload(sbi->s_encoding);
|
||||
#endif
|
||||
free_options:
|
||||
#ifdef CONFIG_QUOTA
|
||||
for (i = 0; i < MAXQUOTAS; i++)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/proc_fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/unicode.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "segment.h"
|
||||
|
@ -81,6 +82,19 @@ static ssize_t unusable_show(struct f2fs_attr *a,
|
|||
(unsigned long long)unusable);
|
||||
}
|
||||
|
||||
static ssize_t encoding_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
#ifdef CONFIG_UNICODE
|
||||
if (f2fs_sb_has_casefold(sbi))
|
||||
return snprintf(buf, PAGE_SIZE, "%s (%d.%d.%d)\n",
|
||||
sbi->s_encoding->charset,
|
||||
(sbi->s_encoding->version >> 16) & 0xff,
|
||||
(sbi->s_encoding->version >> 8) & 0xff,
|
||||
sbi->s_encoding->version & 0xff);
|
||||
#endif
|
||||
return snprintf(buf, PAGE_SIZE, "(none)");
|
||||
}
|
||||
|
||||
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
|
@ -137,6 +151,9 @@ static ssize_t features_show(struct f2fs_attr *a,
|
|||
if (f2fs_sb_has_sb_chksum(sbi))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "sb_checksum");
|
||||
if (f2fs_sb_has_casefold(sbi))
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "casefold");
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
return len;
|
||||
}
|
||||
|
@ -369,6 +386,7 @@ enum feat_id {
|
|||
FEAT_LOST_FOUND,
|
||||
FEAT_VERITY,
|
||||
FEAT_SB_CHECKSUM,
|
||||
FEAT_CASEFOLD,
|
||||
};
|
||||
|
||||
static ssize_t f2fs_feature_show(struct f2fs_attr *a,
|
||||
|
@ -387,6 +405,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
|
|||
case FEAT_LOST_FOUND:
|
||||
case FEAT_VERITY:
|
||||
case FEAT_SB_CHECKSUM:
|
||||
case FEAT_CASEFOLD:
|
||||
return snprintf(buf, PAGE_SIZE, "supported\n");
|
||||
}
|
||||
return 0;
|
||||
|
@ -460,6 +479,7 @@ F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
|||
F2FS_GENERAL_RO_ATTR(features);
|
||||
F2FS_GENERAL_RO_ATTR(current_reserved_blocks);
|
||||
F2FS_GENERAL_RO_ATTR(unusable);
|
||||
F2FS_GENERAL_RO_ATTR(encoding);
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
|
||||
|
@ -479,6 +499,7 @@ F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND);
|
|||
F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM);
|
||||
F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
|
@ -523,6 +544,7 @@ static struct attribute *f2fs_attrs[] = {
|
|||
ATTR_LIST(features),
|
||||
ATTR_LIST(reserved_blocks),
|
||||
ATTR_LIST(current_reserved_blocks),
|
||||
ATTR_LIST(encoding),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(f2fs);
|
||||
|
@ -546,6 +568,7 @@ static struct attribute *f2fs_feat_attrs[] = {
|
|||
ATTR_LIST(verity),
|
||||
#endif
|
||||
ATTR_LIST(sb_checksum),
|
||||
ATTR_LIST(casefold),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(f2fs_feat);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/posix_acl_xattr.h>
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
#include "segment.h"
|
||||
|
||||
static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
|
@ -729,6 +730,11 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
|||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int err;
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
if (!f2fs_is_checkpoint_ready(sbi))
|
||||
return -ENOSPC;
|
||||
|
||||
err = dquot_initialize(inode);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -36,11 +36,17 @@
|
|||
|
||||
#define F2FS_MAX_QUOTAS 3
|
||||
|
||||
#define F2FS_ENC_UTF8_12_1 1
|
||||
#define F2FS_ENC_STRICT_MODE_FL (1 << 0)
|
||||
#define f2fs_has_strict_mode(sbi) \
|
||||
(sbi->s_encoding_flags & F2FS_ENC_STRICT_MODE_FL)
|
||||
|
||||
#define F2FS_IO_SIZE(sbi) (1 << F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */
|
||||
#define F2FS_IO_SIZE_KB(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 2)) /* KB */
|
||||
#define F2FS_IO_SIZE_BYTES(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 12)) /* B */
|
||||
#define F2FS_IO_SIZE_BITS(sbi) (F2FS_OPTION(sbi).write_io_size_bits) /* power of 2 */
|
||||
#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1)
|
||||
#define F2FS_IO_ALIGNED(sbi) (F2FS_IO_SIZE(sbi) > 1)
|
||||
|
||||
/* This flag is used by node and meta inodes, and by recovery */
|
||||
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
|
||||
|
@ -109,7 +115,9 @@ struct f2fs_super_block {
|
|||
struct f2fs_device devs[MAX_DEVICES]; /* device list */
|
||||
__le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */
|
||||
__u8 hot_ext_count; /* # of hot file extension */
|
||||
__u8 reserved[310]; /* valid reserved region */
|
||||
__le16 s_encoding; /* Filename charset encoding */
|
||||
__le16 s_encoding_flags; /* Filename charset encoding flags */
|
||||
__u8 reserved[306]; /* valid reserved region */
|
||||
__le32 crc; /* checksum of superblock */
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -264,6 +264,7 @@ struct fsxattr {
|
|||
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
|
||||
#define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */
|
||||
#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
|
||||
#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */
|
||||
#define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
|
||||
|
||||
#define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
|
||||
|
|
|
@ -311,6 +311,7 @@ struct fscrypt_key {
|
|||
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
|
||||
#define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */
|
||||
#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
|
||||
#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */
|
||||
#define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
|
||||
|
||||
#define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
|
||||
|
|
Loading…
Reference in New Issue