2012-11-29 08:28:09 +04:00
/*
2012-11-02 12:10:40 +04:00
* fs / f2fs / inode . c
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com/
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/fs.h>
# include <linux/f2fs_fs.h>
# include <linux/buffer_head.h>
# include <linux/writeback.h>
# include "f2fs.h"
# include "node.h"
void f2fs_set_inode_flags ( struct inode * inode )
{
unsigned int flags = F2FS_I ( inode ) - > i_flags ;
inode - > i_flags & = ~ ( S_SYNC | S_APPEND | S_IMMUTABLE |
S_NOATIME | S_DIRSYNC ) ;
if ( flags & FS_SYNC_FL )
inode - > i_flags | = S_SYNC ;
if ( flags & FS_APPEND_FL )
inode - > i_flags | = S_APPEND ;
if ( flags & FS_IMMUTABLE_FL )
inode - > i_flags | = S_IMMUTABLE ;
if ( flags & FS_NOATIME_FL )
inode - > i_flags | = S_NOATIME ;
if ( flags & FS_DIRSYNC_FL )
inode - > i_flags | = S_DIRSYNC ;
}
static int do_read_inode ( struct inode * inode )
{
struct f2fs_sb_info * sbi = F2FS_SB ( inode - > i_sb ) ;
struct f2fs_inode_info * fi = F2FS_I ( inode ) ;
struct page * node_page ;
struct f2fs_node * rn ;
struct f2fs_inode * ri ;
/* Check if ino is within scope */
check_nid_range ( sbi , inode - > i_ino ) ;
node_page = get_node_page ( sbi , inode - > i_ino ) ;
if ( IS_ERR ( node_page ) )
return PTR_ERR ( node_page ) ;
rn = page_address ( node_page ) ;
ri = & ( rn - > i ) ;
inode - > i_mode = le16_to_cpu ( ri - > i_mode ) ;
i_uid_write ( inode , le32_to_cpu ( ri - > i_uid ) ) ;
i_gid_write ( inode , le32_to_cpu ( ri - > i_gid ) ) ;
set_nlink ( inode , le32_to_cpu ( ri - > i_links ) ) ;
inode - > i_size = le64_to_cpu ( ri - > i_size ) ;
inode - > i_blocks = le64_to_cpu ( ri - > i_blocks ) ;
inode - > i_atime . tv_sec = le64_to_cpu ( ri - > i_atime ) ;
inode - > i_ctime . tv_sec = le64_to_cpu ( ri - > i_ctime ) ;
inode - > i_mtime . tv_sec = le64_to_cpu ( ri - > i_mtime ) ;
inode - > i_atime . tv_nsec = le32_to_cpu ( ri - > i_atime_nsec ) ;
inode - > i_ctime . tv_nsec = le32_to_cpu ( ri - > i_ctime_nsec ) ;
inode - > i_mtime . tv_nsec = le32_to_cpu ( ri - > i_mtime_nsec ) ;
inode - > i_generation = le32_to_cpu ( ri - > i_generation ) ;
f2fs: save device node number into f2fs_inode
This patch stores inode->i_rdev into on-disk inode structure.
Alun reported that:
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # mknod mnt/sda1 b 8 1
aspire tmp # mknod mnt/null c 1 3
aspire tmp # mknod mnt/console c 5 1
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console
crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null
brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1
aspire tmp # umount mnt
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null
brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1
In this report, f2fs lost the major/minor numbers of device files after umount.
The reason was revealed that f2fs does not store the inode->i_rdev to the
on-disk inode data structure.
So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields
in on-disk inode structure without any on-disk layout changes.
Note that, this bug is limited to device files made by mknod().
Reported-and-Tested-by: Alun Jones <alun.linux@ty-penguin.org.uk>
Signed-off-by: Changman Lee <cm224.lee@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2013-01-23 04:40:23 +04:00
if ( ri - > i_addr [ 0 ] )
inode - > i_rdev = old_decode_dev ( le32_to_cpu ( ri - > i_addr [ 0 ] ) ) ;
else
inode - > i_rdev = new_decode_dev ( le32_to_cpu ( ri - > i_addr [ 1 ] ) ) ;
2012-11-02 12:10:40 +04:00
fi - > i_current_depth = le32_to_cpu ( ri - > i_current_depth ) ;
fi - > i_xattr_nid = le32_to_cpu ( ri - > i_xattr_nid ) ;
fi - > i_flags = le32_to_cpu ( ri - > i_flags ) ;
fi - > flags = 0 ;
fi - > data_version = le64_to_cpu ( F2FS_CKPT ( sbi ) - > checkpoint_ver ) - 1 ;
fi - > i_advise = ri - > i_advise ;
f2fs: fix tracking parent inode number
Previously, f2fs didn't track the parent inode number correctly which is stored
in each f2fs_inode. In the case of the following scenario, a bug can be occured.
Let's suppose there are one directory, "/b", and two files, "/a" and "/b/a".
- pino of "/a" is ROOT_INO.
- pino of "/b/a" is DIR_B_INO.
Then,
# sync
: The inode pages of "/a" and "/b/a" contain the parent inode numbers as
ROOT_INO and DIR_B_INO respectively.
# mv /a /b/a
: The parent inode number of "/a" should be changed to DIR_B_INO, but f2fs
didn't do that. Ref. f2fs_set_link().
In order to fix this clearly, I added i_pino in f2fs_inode_info, and whenever
it needs to be changed like in f2fs_add_link() and f2fs_set_link(), it is
updated temporarily in f2fs_inode_info.
And later, f2fs_write_inode() stores the latest information to the inode pages.
For power-off-recovery, f2fs_sync_file() triggers simply f2fs_write_inode().
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-10 12:52:48 +04:00
fi - > i_pino = le32_to_cpu ( ri - > i_pino ) ;
2012-11-02 12:10:40 +04:00
get_extent_info ( & fi - > ext , ri - > i_ext ) ;
f2fs_put_page ( node_page , 1 ) ;
return 0 ;
}
struct inode * f2fs_iget ( struct super_block * sb , unsigned long ino )
{
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * inode ;
int ret ;
inode = iget_locked ( sb , ino ) ;
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
if ( ! ( inode - > i_state & I_NEW ) )
return inode ;
if ( ino = = F2FS_NODE_INO ( sbi ) | | ino = = F2FS_META_INO ( sbi ) )
goto make_now ;
ret = do_read_inode ( inode ) ;
if ( ret )
goto bad_inode ;
if ( ! sbi - > por_doing & & inode - > i_nlink = = 0 ) {
ret = - ENOENT ;
goto bad_inode ;
}
make_now :
if ( ino = = F2FS_NODE_INO ( sbi ) ) {
inode - > i_mapping - > a_ops = & f2fs_node_aops ;
mapping_set_gfp_mask ( inode - > i_mapping , GFP_F2FS_ZERO ) ;
} else if ( ino = = F2FS_META_INO ( sbi ) ) {
inode - > i_mapping - > a_ops = & f2fs_meta_aops ;
mapping_set_gfp_mask ( inode - > i_mapping , GFP_F2FS_ZERO ) ;
} else if ( S_ISREG ( inode - > i_mode ) ) {
inode - > i_op = & f2fs_file_inode_operations ;
inode - > i_fop = & f2fs_file_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
} else if ( S_ISDIR ( inode - > i_mode ) ) {
inode - > i_op = & f2fs_dir_inode_operations ;
inode - > i_fop = & f2fs_dir_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
mapping_set_gfp_mask ( inode - > i_mapping , GFP_HIGHUSER_MOVABLE |
__GFP_ZERO ) ;
} else if ( S_ISLNK ( inode - > i_mode ) ) {
inode - > i_op = & f2fs_symlink_inode_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
} else if ( S_ISCHR ( inode - > i_mode ) | | S_ISBLK ( inode - > i_mode ) | |
S_ISFIFO ( inode - > i_mode ) | | S_ISSOCK ( inode - > i_mode ) ) {
inode - > i_op = & f2fs_special_inode_operations ;
init_special_inode ( inode , inode - > i_mode , inode - > i_rdev ) ;
} else {
ret = - EIO ;
goto bad_inode ;
}
unlock_new_inode ( inode ) ;
return inode ;
bad_inode :
iget_failed ( inode ) ;
return ERR_PTR ( ret ) ;
}
void update_inode ( struct inode * inode , struct page * node_page )
{
struct f2fs_node * rn ;
struct f2fs_inode * ri ;
wait_on_page_writeback ( node_page ) ;
rn = page_address ( node_page ) ;
ri = & ( rn - > i ) ;
ri - > i_mode = cpu_to_le16 ( inode - > i_mode ) ;
ri - > i_advise = F2FS_I ( inode ) - > i_advise ;
ri - > i_uid = cpu_to_le32 ( i_uid_read ( inode ) ) ;
ri - > i_gid = cpu_to_le32 ( i_gid_read ( inode ) ) ;
ri - > i_links = cpu_to_le32 ( inode - > i_nlink ) ;
ri - > i_size = cpu_to_le64 ( i_size_read ( inode ) ) ;
ri - > i_blocks = cpu_to_le64 ( inode - > i_blocks ) ;
set_raw_extent ( & F2FS_I ( inode ) - > ext , & ri - > i_ext ) ;
ri - > i_atime = cpu_to_le64 ( inode - > i_atime . tv_sec ) ;
ri - > i_ctime = cpu_to_le64 ( inode - > i_ctime . tv_sec ) ;
ri - > i_mtime = cpu_to_le64 ( inode - > i_mtime . tv_sec ) ;
ri - > i_atime_nsec = cpu_to_le32 ( inode - > i_atime . tv_nsec ) ;
ri - > i_ctime_nsec = cpu_to_le32 ( inode - > i_ctime . tv_nsec ) ;
ri - > i_mtime_nsec = cpu_to_le32 ( inode - > i_mtime . tv_nsec ) ;
ri - > i_current_depth = cpu_to_le32 ( F2FS_I ( inode ) - > i_current_depth ) ;
ri - > i_xattr_nid = cpu_to_le32 ( F2FS_I ( inode ) - > i_xattr_nid ) ;
ri - > i_flags = cpu_to_le32 ( F2FS_I ( inode ) - > i_flags ) ;
f2fs: fix tracking parent inode number
Previously, f2fs didn't track the parent inode number correctly which is stored
in each f2fs_inode. In the case of the following scenario, a bug can be occured.
Let's suppose there are one directory, "/b", and two files, "/a" and "/b/a".
- pino of "/a" is ROOT_INO.
- pino of "/b/a" is DIR_B_INO.
Then,
# sync
: The inode pages of "/a" and "/b/a" contain the parent inode numbers as
ROOT_INO and DIR_B_INO respectively.
# mv /a /b/a
: The parent inode number of "/a" should be changed to DIR_B_INO, but f2fs
didn't do that. Ref. f2fs_set_link().
In order to fix this clearly, I added i_pino in f2fs_inode_info, and whenever
it needs to be changed like in f2fs_add_link() and f2fs_set_link(), it is
updated temporarily in f2fs_inode_info.
And later, f2fs_write_inode() stores the latest information to the inode pages.
For power-off-recovery, f2fs_sync_file() triggers simply f2fs_write_inode().
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-10 12:52:48 +04:00
ri - > i_pino = cpu_to_le32 ( F2FS_I ( inode ) - > i_pino ) ;
2012-11-02 12:10:40 +04:00
ri - > i_generation = cpu_to_le32 ( inode - > i_generation ) ;
f2fs: save device node number into f2fs_inode
This patch stores inode->i_rdev into on-disk inode structure.
Alun reported that:
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # mknod mnt/sda1 b 8 1
aspire tmp # mknod mnt/null c 1 3
aspire tmp # mknod mnt/console c 5 1
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console
crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null
brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1
aspire tmp # umount mnt
aspire tmp # mount -t f2fs /dev/sdb mnt
aspire tmp # ls -l mnt
total 2
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console
crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null
brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1
In this report, f2fs lost the major/minor numbers of device files after umount.
The reason was revealed that f2fs does not store the inode->i_rdev to the
on-disk inode data structure.
So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields
in on-disk inode structure without any on-disk layout changes.
Note that, this bug is limited to device files made by mknod().
Reported-and-Tested-by: Alun Jones <alun.linux@ty-penguin.org.uk>
Signed-off-by: Changman Lee <cm224.lee@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2013-01-23 04:40:23 +04:00
if ( S_ISCHR ( inode - > i_mode ) | | S_ISBLK ( inode - > i_mode ) ) {
if ( old_valid_dev ( inode - > i_rdev ) ) {
ri - > i_addr [ 0 ] =
cpu_to_le32 ( old_encode_dev ( inode - > i_rdev ) ) ;
ri - > i_addr [ 1 ] = 0 ;
} else {
ri - > i_addr [ 0 ] = 0 ;
ri - > i_addr [ 1 ] =
cpu_to_le32 ( new_encode_dev ( inode - > i_rdev ) ) ;
ri - > i_addr [ 2 ] = 0 ;
}
}
f2fs: fix handling errors got by f2fs_write_inode
Ruslan reported that f2fs hangs with an infinite loop in f2fs_sync_file():
while (sync_node_pages(sbi, inode->i_ino, &wbc) == 0)
f2fs_write_inode(inode, NULL);
The reason was revealed that the cold flag is not set even thought this inode is
a normal file. Therefore, sync_node_pages() skips to write node blocks since it
only writes cold node blocks.
The cold flag is stored to the node_footer in node block, and whenever a new
node page is allocated, it is set according to its file type, file or directory.
But, after sudden-power-off, when recovering the inode page, f2fs doesn't recover
its cold flag.
So, let's assign the cold flag in more right places.
One more thing:
If f2fs_write_inode() returns an error due to whatever situations, there would
be no dirty node pages so that sync_node_pages() returns zero.
(i.e., zero means nothing was written.)
Reported-by: Ruslan N. Marchenko <me@ruff.mobi>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
2012-12-19 10:28:39 +04:00
set_cold_node ( inode , node_page ) ;
2012-11-02 12:10:40 +04:00
set_page_dirty ( node_page ) ;
}
int f2fs_write_inode ( struct inode * inode , struct writeback_control * wbc )
{
struct f2fs_sb_info * sbi = F2FS_SB ( inode - > i_sb ) ;
struct page * node_page ;
bool need_lock = false ;
if ( inode - > i_ino = = F2FS_NODE_INO ( sbi ) | |
inode - > i_ino = = F2FS_META_INO ( sbi ) )
return 0 ;
2013-01-11 08:10:49 +04:00
if ( wbc )
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:10:40 +04:00
node_page = get_node_page ( sbi , inode - > i_ino ) ;
if ( IS_ERR ( node_page ) )
return PTR_ERR ( node_page ) ;
if ( ! PageDirty ( node_page ) ) {
need_lock = true ;
f2fs_put_page ( node_page , 1 ) ;
mutex_lock ( & sbi - > write_inode ) ;
node_page = get_node_page ( sbi , inode - > i_ino ) ;
if ( IS_ERR ( node_page ) ) {
mutex_unlock ( & sbi - > write_inode ) ;
return PTR_ERR ( node_page ) ;
}
}
update_inode ( inode , node_page ) ;
f2fs_put_page ( node_page , 1 ) ;
if ( need_lock )
mutex_unlock ( & sbi - > write_inode ) ;
return 0 ;
}
2012-11-29 08:28:09 +04:00
/*
2012-11-02 12:10:40 +04:00
* Called at the last iput ( ) if i_nlink is zero
*/
void f2fs_evict_inode ( struct inode * inode )
{
struct f2fs_sb_info * sbi = F2FS_SB ( inode - > i_sb ) ;
truncate_inode_pages ( & inode - > i_data , 0 ) ;
if ( inode - > i_ino = = F2FS_NODE_INO ( sbi ) | |
inode - > i_ino = = F2FS_META_INO ( sbi ) )
goto no_delete ;
BUG_ON ( atomic_read ( & F2FS_I ( inode ) - > dirty_dents ) ) ;
remove_dirty_dir_inode ( inode ) ;
if ( inode - > i_nlink | | is_bad_inode ( inode ) )
goto no_delete ;
2013-01-29 13:30:07 +04:00
sb_start_intwrite ( inode - > i_sb ) ;
2012-11-02 12:10:40 +04:00
set_inode_flag ( F2FS_I ( inode ) , FI_NO_ALLOC ) ;
i_size_write ( inode , 0 ) ;
if ( F2FS_HAS_BLOCKS ( inode ) )
f2fs_truncate ( inode ) ;
remove_inode_page ( inode ) ;
2013-01-29 13:30:07 +04:00
sb_end_intwrite ( inode - > i_sb ) ;
2012-11-02 12:10:40 +04:00
no_delete :
clear_inode ( inode ) ;
}