f2fs: add F2FS_INLINE_DOTS to recover missing dot dentries
If f2fs was corrupted with missing dot dentries, it needs to recover them after fsck.f2fs detection. The underlying precedure is: 1. The fsck.f2fs remains F2FS_INLINE_DOTS flag in directory inode, if it detects missing dot dentries. 2. When f2fs looks up the corrupted directory, it triggers f2fs_add_link with proper inode numbers and their dot and dotdot names. 3. Once f2fs recovers the directory without errors, it removes F2FS_INLINE_DOTS finally. Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
c9ef481097
commit
510022a858
|
@ -59,9 +59,8 @@ static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|||
[S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK,
|
||||
};
|
||||
|
||||
void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
|
||||
void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
|
||||
{
|
||||
umode_t mode = inode->i_mode;
|
||||
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
|
||||
}
|
||||
|
||||
|
@ -282,7 +281,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
|
|||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, type);
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
set_de_type(de, inode);
|
||||
set_de_type(de, inode->i_mode);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
set_page_dirty(page);
|
||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
|
@ -328,14 +327,14 @@ void do_make_empty_dir(struct inode *inode, struct inode *parent,
|
|||
de->hash_code = 0;
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
memcpy(d->filename[0], ".", 1);
|
||||
set_de_type(de, inode);
|
||||
set_de_type(de, inode->i_mode);
|
||||
|
||||
de = &d->dentry[1];
|
||||
de->hash_code = 0;
|
||||
de->name_len = cpu_to_le16(2);
|
||||
de->ino = cpu_to_le32(parent->i_ino);
|
||||
memcpy(d->filename[1], "..", 2);
|
||||
set_de_type(de, inode);
|
||||
set_de_type(de, inode->i_mode);
|
||||
|
||||
test_and_set_bit_le(0, (void *)d->bitmap);
|
||||
test_and_set_bit_le(1, (void *)d->bitmap);
|
||||
|
@ -432,7 +431,7 @@ error:
|
|||
void update_parent_metadata(struct inode *dir, struct inode *inode,
|
||||
unsigned int current_depth)
|
||||
{
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
|
||||
if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
inc_nlink(dir);
|
||||
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
|
||||
|
@ -447,7 +446,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode,
|
|||
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
|
||||
if (inode && is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
|
||||
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
|
||||
}
|
||||
|
||||
|
@ -471,7 +470,7 @@ next:
|
|||
goto next;
|
||||
}
|
||||
|
||||
void f2fs_update_dentry(struct inode *inode, struct f2fs_dentry_ptr *d,
|
||||
void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
|
||||
const struct qstr *name, f2fs_hash_t name_hash,
|
||||
unsigned int bit_pos)
|
||||
{
|
||||
|
@ -483,8 +482,8 @@ void f2fs_update_dentry(struct inode *inode, struct f2fs_dentry_ptr *d,
|
|||
de->hash_code = name_hash;
|
||||
de->name_len = cpu_to_le16(name->len);
|
||||
memcpy(d->filename[bit_pos], name->name, name->len);
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
set_de_type(de, inode);
|
||||
de->ino = cpu_to_le32(ino);
|
||||
set_de_type(de, mode);
|
||||
for (i = 0; i < slots; i++)
|
||||
test_and_set_bit_le(bit_pos + i, (void *)d->bitmap);
|
||||
}
|
||||
|
@ -494,7 +493,7 @@ void f2fs_update_dentry(struct inode *inode, struct f2fs_dentry_ptr *d,
|
|||
* f2fs_unlock_op().
|
||||
*/
|
||||
int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
||||
struct inode *inode)
|
||||
struct inode *inode, nid_t ino, umode_t mode)
|
||||
{
|
||||
unsigned int bit_pos;
|
||||
unsigned int level;
|
||||
|
@ -507,11 +506,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
|||
struct f2fs_dentry_block *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page;
|
||||
struct page *page = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_has_inline_dentry(dir)) {
|
||||
err = f2fs_add_inline_entry(dir, name, inode);
|
||||
err = f2fs_add_inline_entry(dir, name, inode, ino, mode);
|
||||
if (!err || err != -EAGAIN)
|
||||
return err;
|
||||
else
|
||||
|
@ -561,25 +560,30 @@ start:
|
|||
add_dentry:
|
||||
f2fs_wait_on_page_writeback(dentry_page, DATA);
|
||||
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, name, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 1);
|
||||
f2fs_update_dentry(inode, &d, name, dentry_hash, bit_pos);
|
||||
f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos);
|
||||
|
||||
set_page_dirty(dentry_page);
|
||||
|
||||
if (inode) {
|
||||
/* we don't need to mark_inode_dirty now */
|
||||
F2FS_I(inode)->i_pino = dir->i_ino;
|
||||
update_inode(inode, page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
update_parent_metadata(dir, inode, current_depth);
|
||||
fail:
|
||||
if (inode)
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
|
||||
|
|
|
@ -1246,6 +1246,7 @@ enum {
|
|||
FI_FIRST_BLOCK_WRITTEN, /* indicate #0 data block was written */
|
||||
FI_DROP_CACHE, /* drop dirty page cache */
|
||||
FI_DATA_EXIST, /* indicate data exists */
|
||||
FI_INLINE_DOTS, /* indicate inline dot dentries */
|
||||
};
|
||||
|
||||
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
|
||||
|
@ -1282,6 +1283,8 @@ static inline void get_inline_info(struct f2fs_inode_info *fi,
|
|||
set_inode_flag(fi, FI_INLINE_DENTRY);
|
||||
if (ri->i_inline & F2FS_DATA_EXIST)
|
||||
set_inode_flag(fi, FI_DATA_EXIST);
|
||||
if (ri->i_inline & F2FS_INLINE_DOTS)
|
||||
set_inode_flag(fi, FI_INLINE_DOTS);
|
||||
}
|
||||
|
||||
static inline void set_raw_inline(struct f2fs_inode_info *fi,
|
||||
|
@ -1297,6 +1300,8 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi,
|
|||
ri->i_inline |= F2FS_INLINE_DENTRY;
|
||||
if (is_inode_flag_set(fi, FI_DATA_EXIST))
|
||||
ri->i_inline |= F2FS_DATA_EXIST;
|
||||
if (is_inode_flag_set(fi, FI_INLINE_DOTS))
|
||||
ri->i_inline |= F2FS_INLINE_DOTS;
|
||||
}
|
||||
|
||||
static inline int f2fs_has_inline_xattr(struct inode *inode)
|
||||
|
@ -1342,6 +1347,11 @@ static inline int f2fs_exist_data(struct inode *inode)
|
|||
return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST);
|
||||
}
|
||||
|
||||
static inline int f2fs_has_inline_dots(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DOTS);
|
||||
}
|
||||
|
||||
static inline bool f2fs_is_atomic_file(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||
|
@ -1440,7 +1450,7 @@ struct dentry *f2fs_get_parent(struct dentry *child);
|
|||
* dir.c
|
||||
*/
|
||||
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
|
||||
void set_de_type(struct f2fs_dir_entry *, struct inode *);
|
||||
void set_de_type(struct f2fs_dir_entry *, umode_t);
|
||||
struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
|
||||
struct f2fs_dentry_ptr *);
|
||||
bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
|
||||
|
@ -1459,9 +1469,10 @@ ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
|
|||
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
|
||||
struct page *, struct inode *);
|
||||
int update_dent_inode(struct inode *, const struct qstr *);
|
||||
void f2fs_update_dentry(struct inode *, struct f2fs_dentry_ptr *,
|
||||
void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *,
|
||||
const struct qstr *, f2fs_hash_t , unsigned int);
|
||||
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
|
||||
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t,
|
||||
umode_t);
|
||||
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *,
|
||||
struct inode *);
|
||||
int f2fs_do_tmpfile(struct inode *, struct inode *);
|
||||
|
@ -1471,7 +1482,7 @@ bool f2fs_empty_dir(struct inode *);
|
|||
static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name,
|
||||
inode);
|
||||
inode, inode->i_ino, inode->i_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1792,7 +1803,8 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
|
|||
struct page **);
|
||||
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
|
||||
int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
|
||||
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *);
|
||||
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
|
||||
nid_t, umode_t);
|
||||
void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
|
||||
struct inode *, struct inode *);
|
||||
bool f2fs_empty_inline_dir(struct inode *);
|
||||
|
|
|
@ -390,7 +390,7 @@ out:
|
|||
}
|
||||
|
||||
int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
||||
struct inode *inode)
|
||||
struct inode *inode, nid_t ino, umode_t mode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct page *ipage;
|
||||
|
@ -400,7 +400,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
|||
struct f2fs_inline_dentry *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page;
|
||||
struct page *page = NULL;
|
||||
int err = 0;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
|
@ -417,28 +417,33 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, name, ipage);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
|
||||
name_hash = f2fs_dentry_hash(name);
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(inode, &d, name, name_hash, bit_pos);
|
||||
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
||||
/* we don't need to mark_inode_dirty now */
|
||||
if (inode) {
|
||||
F2FS_I(inode)->i_pino = dir->i_ino;
|
||||
update_inode(inode, page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
update_parent_metadata(dir, inode, 0);
|
||||
fail:
|
||||
if (inode)
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
|
||||
|
|
|
@ -187,6 +187,44 @@ struct dentry *f2fs_get_parent(struct dentry *child)
|
|||
return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino));
|
||||
}
|
||||
|
||||
static int __recover_dot_dentries(struct inode *dir, nid_t pino)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct qstr dot = QSTR_INIT(".", 1);
|
||||
struct qstr dotdot = QSTR_INIT("..", 2);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
int err = 0;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
de = f2fs_find_entry(dir, &dot, &page);
|
||||
if (de) {
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
} else {
|
||||
err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
de = f2fs_find_entry(dir, &dotdot, &page);
|
||||
if (de) {
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
} else {
|
||||
err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
|
||||
}
|
||||
out:
|
||||
if (!err) {
|
||||
clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
@ -206,6 +244,16 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
inode = f2fs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
int err;
|
||||
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err) {
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
|
|
@ -115,7 +115,7 @@ retry:
|
|||
iput(einode);
|
||||
goto retry;
|
||||
}
|
||||
err = __f2fs_add_link(dir, &name, inode);
|
||||
err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
|
|
|
@ -178,6 +178,7 @@ struct f2fs_extent {
|
|||
#define F2FS_INLINE_DATA 0x02 /* file inline data flag */
|
||||
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
|
||||
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
|
||||
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
|
||||
|
||||
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
|
||||
F2FS_INLINE_XATTR_ADDRS - 1))
|
||||
|
|
Loading…
Reference in New Issue