2012-11-29 08:28:09 +04:00
/*
2012-11-02 12:11:10 +04:00
* fs / f2fs / namei . 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/pagemap.h>
# include <linux/sched.h>
# include <linux/ctype.h>
# include "f2fs.h"
# include "xattr.h"
# include "acl.h"
static struct inode * f2fs_new_inode ( struct inode * dir , umode_t mode )
{
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
nid_t ino ;
struct inode * inode ;
bool nid_free = false ;
int err ;
inode = new_inode ( sb ) ;
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
mutex_lock_op ( sbi , NODE_NEW ) ;
if ( ! alloc_nid ( sbi , & ino ) ) {
mutex_unlock_op ( sbi , NODE_NEW ) ;
err = - ENOSPC ;
goto fail ;
}
mutex_unlock_op ( sbi , NODE_NEW ) ;
inode - > i_uid = current_fsuid ( ) ;
if ( dir - > i_mode & S_ISGID ) {
inode - > i_gid = dir - > i_gid ;
if ( S_ISDIR ( mode ) )
mode | = S_ISGID ;
} else {
inode - > i_gid = current_fsgid ( ) ;
}
inode - > i_ino = ino ;
inode - > i_mode = mode ;
inode - > i_blocks = 0 ;
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
inode - > i_generation = sbi - > s_next_generation + + ;
err = insert_inode_locked ( inode ) ;
if ( err ) {
err = - EINVAL ;
nid_free = true ;
goto out ;
}
mark_inode_dirty ( inode ) ;
return inode ;
out :
clear_nlink ( inode ) ;
unlock_new_inode ( inode ) ;
fail :
iput ( inode ) ;
if ( nid_free )
alloc_nid_failed ( sbi , ino ) ;
return ERR_PTR ( err ) ;
}
static int is_multimedia_file ( const unsigned char * s , const char * sub )
{
2012-12-27 21:55:46 +04:00
size_t slen = strlen ( s ) ;
size_t sublen = strlen ( sub ) ;
2012-11-02 12:11:10 +04:00
int ret ;
if ( sublen > slen )
return 1 ;
ret = memcmp ( s + slen - sublen , sub , sublen ) ;
if ( ret ) { /* compare upper case */
int i ;
char upper_sub [ 8 ] ;
for ( i = 0 ; i < sublen & & i < sizeof ( upper_sub ) ; i + + )
upper_sub [ i ] = toupper ( sub [ i ] ) ;
return memcmp ( s + slen - sublen , upper_sub , sublen ) ;
}
return ret ;
}
2012-11-29 08:28:09 +04:00
/*
2012-11-02 12:11:10 +04:00
* Set multimedia files as cold files for hot / cold data separation
*/
static inline void set_cold_file ( struct f2fs_sb_info * sbi , struct inode * inode ,
const unsigned char * name )
{
int i ;
__u8 ( * extlist ) [ 8 ] = sbi - > raw_super - > extension_list ;
int count = le32_to_cpu ( sbi - > raw_super - > extension_count ) ;
for ( i = 0 ; i < count ; i + + ) {
if ( ! is_multimedia_file ( name , extlist [ i ] ) ) {
F2FS_I ( inode ) - > i_advise | = FADVISE_COLD_BIT ;
break ;
}
}
}
static int f2fs_create ( struct inode * dir , struct dentry * dentry , umode_t mode ,
bool excl )
{
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * inode ;
nid_t ino = 0 ;
int err ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
inode = f2fs_new_inode ( dir , mode ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
if ( ! test_opt ( sbi , DISABLE_EXT_IDENTIFY ) )
set_cold_file ( sbi , inode , dentry - > d_name . name ) ;
inode - > i_op = & f2fs_file_inode_operations ;
inode - > i_fop = & f2fs_file_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
ino = inode - > i_ino ;
err = f2fs_add_link ( dentry , inode ) ;
if ( err )
goto out ;
alloc_nid_done ( sbi , ino ) ;
if ( ! sbi - > por_doing )
d_instantiate ( dentry , inode ) ;
unlock_new_inode ( inode ) ;
return 0 ;
out :
clear_nlink ( inode ) ;
unlock_new_inode ( inode ) ;
iput ( inode ) ;
alloc_nid_failed ( sbi , ino ) ;
return err ;
}
static int f2fs_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
struct inode * inode = old_dentry - > d_inode ;
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
int err ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
inode - > i_ctime = CURRENT_TIME ;
atomic_inc ( & inode - > i_count ) ;
set_inode_flag ( F2FS_I ( inode ) , FI_INC_LINK ) ;
err = f2fs_add_link ( dentry , inode ) ;
if ( err )
goto out ;
d_instantiate ( dentry , inode ) ;
return 0 ;
out :
clear_inode_flag ( F2FS_I ( inode ) , FI_INC_LINK ) ;
iput ( inode ) ;
return err ;
}
struct dentry * f2fs_get_parent ( struct dentry * child )
{
struct qstr dotdot = QSTR_INIT ( " .. " , 2 ) ;
unsigned long ino = f2fs_inode_by_name ( child - > d_inode , & dotdot ) ;
if ( ! ino )
return ERR_PTR ( - ENOENT ) ;
return d_obtain_alias ( f2fs_iget ( child - > d_inode - > i_sb , ino ) ) ;
}
static struct dentry * f2fs_lookup ( struct inode * dir , struct dentry * dentry ,
unsigned int flags )
{
struct inode * inode = NULL ;
struct f2fs_dir_entry * de ;
struct page * page ;
if ( dentry - > d_name . len > F2FS_MAX_NAME_LEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
de = f2fs_find_entry ( dir , & dentry - > d_name , & page ) ;
if ( de ) {
nid_t ino = le32_to_cpu ( de - > ino ) ;
kunmap ( page ) ;
f2fs_put_page ( page , 0 ) ;
inode = f2fs_iget ( dir - > i_sb , ino ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
}
return d_splice_alias ( inode , dentry ) ;
}
static int f2fs_unlink ( struct inode * dir , struct dentry * dentry )
{
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * inode = dentry - > d_inode ;
struct f2fs_dir_entry * de ;
struct page * page ;
int err = - ENOENT ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
de = f2fs_find_entry ( dir , & dentry - > d_name , & page ) ;
if ( ! de )
goto fail ;
err = check_orphan_space ( sbi ) ;
if ( err ) {
kunmap ( page ) ;
f2fs_put_page ( page , 0 ) ;
goto fail ;
}
f2fs_delete_entry ( de , page , inode ) ;
/* In order to evict this inode, we set it dirty */
mark_inode_dirty ( inode ) ;
fail :
return err ;
}
static int f2fs_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * inode ;
2012-12-27 21:55:46 +04:00
size_t symlen = strlen ( symname ) + 1 ;
2012-11-02 12:11:10 +04:00
int err ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
inode = f2fs_new_inode ( dir , S_IFLNK | S_IRWXUGO ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
inode - > i_op = & f2fs_symlink_inode_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
err = f2fs_add_link ( dentry , inode ) ;
if ( err )
goto out ;
err = page_symlink ( inode , symname , symlen ) ;
alloc_nid_done ( sbi , inode - > i_ino ) ;
d_instantiate ( dentry , inode ) ;
unlock_new_inode ( inode ) ;
return err ;
out :
clear_nlink ( inode ) ;
unlock_new_inode ( inode ) ;
iput ( inode ) ;
alloc_nid_failed ( sbi , inode - > i_ino ) ;
return err ;
}
static int f2fs_mkdir ( struct inode * dir , struct dentry * dentry , umode_t mode )
{
struct f2fs_sb_info * sbi = F2FS_SB ( dir - > i_sb ) ;
struct inode * inode ;
int err ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
inode = f2fs_new_inode ( dir , S_IFDIR | mode ) ;
if ( IS_ERR ( inode ) )
2012-12-01 05:56:25 +04:00
return PTR_ERR ( inode ) ;
2012-11-02 12:11:10 +04:00
inode - > i_op = & f2fs_dir_inode_operations ;
inode - > i_fop = & f2fs_dir_operations ;
inode - > i_mapping - > a_ops = & f2fs_dblock_aops ;
2012-12-08 09:54:18 +04:00
mapping_set_gfp_mask ( inode - > i_mapping , GFP_F2FS_ZERO ) ;
2012-11-02 12:11:10 +04:00
set_inode_flag ( F2FS_I ( inode ) , FI_INC_LINK ) ;
err = f2fs_add_link ( dentry , inode ) ;
if ( err )
goto out_fail ;
alloc_nid_done ( sbi , inode - > i_ino ) ;
d_instantiate ( dentry , inode ) ;
unlock_new_inode ( inode ) ;
return 0 ;
out_fail :
clear_inode_flag ( F2FS_I ( inode ) , FI_INC_LINK ) ;
clear_nlink ( inode ) ;
unlock_new_inode ( inode ) ;
iput ( inode ) ;
alloc_nid_failed ( sbi , inode - > i_ino ) ;
return err ;
}
static int f2fs_rmdir ( struct inode * dir , struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
if ( f2fs_empty_dir ( inode ) )
return f2fs_unlink ( dir , dentry ) ;
return - ENOTEMPTY ;
}
static int f2fs_mknod ( struct inode * dir , struct dentry * dentry ,
umode_t mode , dev_t rdev )
{
struct super_block * sb = dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * inode ;
int err = 0 ;
if ( ! new_valid_dev ( rdev ) )
return - EINVAL ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
inode = f2fs_new_inode ( dir , mode ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
init_special_inode ( inode , inode - > i_mode , rdev ) ;
inode - > i_op = & f2fs_special_inode_operations ;
err = f2fs_add_link ( dentry , inode ) ;
if ( err )
goto out ;
alloc_nid_done ( sbi , inode - > i_ino ) ;
d_instantiate ( dentry , inode ) ;
unlock_new_inode ( inode ) ;
return 0 ;
out :
clear_nlink ( inode ) ;
unlock_new_inode ( inode ) ;
iput ( inode ) ;
alloc_nid_failed ( sbi , inode - > i_ino ) ;
return err ;
}
static int f2fs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct super_block * sb = old_dir - > i_sb ;
struct f2fs_sb_info * sbi = F2FS_SB ( sb ) ;
struct inode * old_inode = old_dentry - > d_inode ;
struct inode * new_inode = new_dentry - > d_inode ;
struct page * old_dir_page ;
struct page * old_page ;
struct f2fs_dir_entry * old_dir_entry = NULL ;
struct f2fs_dir_entry * old_entry ;
struct f2fs_dir_entry * new_entry ;
int err = - ENOENT ;
2012-12-19 11:25:21 +04:00
f2fs_balance_fs ( sbi ) ;
2012-11-02 12:11:10 +04:00
old_entry = f2fs_find_entry ( old_dir , & old_dentry - > d_name , & old_page ) ;
if ( ! old_entry )
goto out ;
if ( S_ISDIR ( old_inode - > i_mode ) ) {
err = - EIO ;
old_dir_entry = f2fs_parent_dir ( old_inode , & old_dir_page ) ;
if ( ! old_dir_entry )
goto out_old ;
}
mutex_lock_op ( sbi , RENAME ) ;
if ( new_inode ) {
struct page * new_page ;
err = - ENOTEMPTY ;
if ( old_dir_entry & & ! f2fs_empty_dir ( new_inode ) )
goto out_dir ;
err = - ENOENT ;
new_entry = f2fs_find_entry ( new_dir , & new_dentry - > d_name ,
& new_page ) ;
if ( ! new_entry )
goto out_dir ;
f2fs_set_link ( new_dir , new_entry , new_page , old_inode ) ;
new_inode - > i_ctime = CURRENT_TIME ;
if ( old_dir_entry )
drop_nlink ( new_inode ) ;
drop_nlink ( new_inode ) ;
if ( ! new_inode - > i_nlink )
add_orphan_inode ( sbi , new_inode - > i_ino ) ;
f2fs_write_inode ( new_inode , NULL ) ;
} else {
err = f2fs_add_link ( new_dentry , old_inode ) ;
if ( err )
goto out_dir ;
if ( old_dir_entry ) {
inc_nlink ( new_dir ) ;
f2fs_write_inode ( new_dir , NULL ) ;
}
}
old_inode - > i_ctime = CURRENT_TIME ;
set_inode_flag ( F2FS_I ( old_inode ) , FI_NEED_CP ) ;
mark_inode_dirty ( old_inode ) ;
f2fs_delete_entry ( old_entry , old_page , NULL ) ;
if ( old_dir_entry ) {
if ( old_dir ! = new_dir ) {
f2fs_set_link ( old_inode , old_dir_entry ,
old_dir_page , new_dir ) ;
} else {
kunmap ( old_dir_page ) ;
f2fs_put_page ( old_dir_page , 0 ) ;
}
drop_nlink ( old_dir ) ;
f2fs_write_inode ( old_dir , NULL ) ;
}
mutex_unlock_op ( sbi , RENAME ) ;
return 0 ;
out_dir :
if ( old_dir_entry ) {
kunmap ( old_dir_page ) ;
f2fs_put_page ( old_dir_page , 0 ) ;
}
mutex_unlock_op ( sbi , RENAME ) ;
out_old :
kunmap ( old_page ) ;
f2fs_put_page ( old_page , 0 ) ;
out :
return err ;
}
const struct inode_operations f2fs_dir_inode_operations = {
. create = f2fs_create ,
. lookup = f2fs_lookup ,
. link = f2fs_link ,
. unlink = f2fs_unlink ,
. symlink = f2fs_symlink ,
. mkdir = f2fs_mkdir ,
. rmdir = f2fs_rmdir ,
. mknod = f2fs_mknod ,
. rename = f2fs_rename ,
. setattr = f2fs_setattr ,
. get_acl = f2fs_get_acl ,
# ifdef CONFIG_F2FS_FS_XATTR
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. listxattr = f2fs_listxattr ,
. removexattr = generic_removexattr ,
# endif
} ;
const struct inode_operations f2fs_symlink_inode_operations = {
. readlink = generic_readlink ,
. follow_link = page_follow_link_light ,
. put_link = page_put_link ,
. setattr = f2fs_setattr ,
# ifdef CONFIG_F2FS_FS_XATTR
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. listxattr = f2fs_listxattr ,
. removexattr = generic_removexattr ,
# endif
} ;
const struct inode_operations f2fs_special_inode_operations = {
. setattr = f2fs_setattr ,
. get_acl = f2fs_get_acl ,
# ifdef CONFIG_F2FS_FS_XATTR
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. listxattr = f2fs_listxattr ,
. removexattr = generic_removexattr ,
# endif
} ;