2009-04-07 06:01:34 +04:00
/*
* namei . c - NILFS pathname lookup operations .
*
* Copyright ( C ) 2005 - 2008 Nippon Telegraph and Telephone Corporation .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* Modified for NILFS by Amagai Yoshiji < amagai @ osrg . net > ,
* Ryusuke Konishi < ryusuke @ osrg . net >
*/
/*
* linux / fs / ext2 / namei . c
*
* Copyright ( C ) 1992 , 1993 , 1994 , 1995
* Remy Card ( card @ masi . ibp . fr )
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie ( Paris VI )
*
* from
*
* linux / fs / minix / namei . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* Big - endian to little - endian byte - swapping / bitmaps by
* David S . Miller ( davem @ caip . rutgers . edu ) , 1995
*/
# include <linux/pagemap.h>
# include "nilfs.h"
2010-08-26 19:23:02 +04:00
# include "export.h"
2009-04-07 06:01:34 +04:00
2010-08-26 19:23:02 +04:00
# define NILFS_FID_SIZE_NON_CONNECTABLE \
( offsetof ( struct nilfs_fid , parent_gen ) / 4 )
# define NILFS_FID_SIZE_CONNECTABLE (sizeof(struct nilfs_fid) / 4)
2009-04-07 06:01:34 +04:00
static inline int nilfs_add_nondir ( struct dentry * dentry , struct inode * inode )
{
int err = nilfs_add_link ( dentry , inode ) ;
if ( ! err ) {
d_instantiate ( dentry , inode ) ;
return 0 ;
}
inode_dec_link_count ( inode ) ;
iput ( inode ) ;
return err ;
}
/*
* Methods themselves .
*/
static struct dentry *
nilfs_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
struct inode * inode ;
ino_t ino ;
if ( dentry - > d_name . len > NILFS_NAME_LEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
2010-02-01 05:02:09 +03:00
ino = nilfs_inode_by_name ( dir , & dentry - > d_name ) ;
2011-07-09 05:20:11 +04:00
inode = ino ? nilfs_iget ( dir - > i_sb , NILFS_I ( dir ) - > i_root , ino ) : NULL ;
2009-04-07 06:01:34 +04:00
return d_splice_alias ( inode , dentry ) ;
}
/*
* By the time this is called , we already have created
* the directory cache entry for the new file , but it
* is so far negative - it has no inode .
*
* If the create succeeds , we fill in the inode information
* with d_instantiate ( ) .
*/
2011-07-26 09:42:34 +04:00
static int nilfs_create ( struct inode * dir , struct dentry * dentry , umode_t mode ,
2009-04-07 06:01:34 +04:00
struct nameidata * nd )
{
struct inode * inode ;
struct nilfs_transaction_info ti ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 1 ) ;
if ( err )
return err ;
inode = nilfs_new_inode ( dir , mode ) ;
err = PTR_ERR ( inode ) ;
if ( ! IS_ERR ( inode ) ) {
inode - > i_op = & nilfs_file_inode_operations ;
inode - > i_fop = & nilfs_file_operations ;
inode - > i_mapping - > a_ops = & nilfs_aops ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-04-07 06:01:34 +04:00
err = nilfs_add_nondir ( dentry , inode ) ;
}
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
}
static int
2011-07-26 09:52:52 +04:00
nilfs_mknod ( struct inode * dir , struct dentry * dentry , umode_t mode , dev_t rdev )
2009-04-07 06:01:34 +04:00
{
struct inode * inode ;
struct nilfs_transaction_info ti ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
if ( ! new_valid_dev ( rdev ) )
return - EINVAL ;
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 1 ) ;
if ( err )
return err ;
inode = nilfs_new_inode ( dir , mode ) ;
err = PTR_ERR ( inode ) ;
if ( ! IS_ERR ( inode ) ) {
init_special_inode ( inode , inode - > i_mode , rdev ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-04-07 06:01:34 +04:00
err = nilfs_add_nondir ( dentry , inode ) ;
}
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
}
static int nilfs_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
struct nilfs_transaction_info ti ;
struct super_block * sb = dir - > i_sb ;
unsigned l = strlen ( symname ) + 1 ;
struct inode * inode ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
if ( l > sb - > s_blocksize )
return - ENAMETOOLONG ;
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 1 ) ;
if ( err )
return err ;
inode = nilfs_new_inode ( dir , S_IFLNK | S_IRWXUGO ) ;
err = PTR_ERR ( inode ) ;
if ( IS_ERR ( inode ) )
goto out ;
/* slow symlink */
inode - > i_op = & nilfs_symlink_inode_operations ;
inode - > i_mapping - > a_ops = & nilfs_aops ;
err = page_symlink ( inode , symname , l ) ;
if ( err )
goto out_fail ;
/* mark_inode_dirty(inode); */
2009-11-27 13:41:06 +03:00
/* page_symlink() do this */
2009-04-07 06:01:34 +04:00
err = nilfs_add_nondir ( dentry , inode ) ;
out :
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
out_fail :
2009-11-27 13:41:08 +03:00
drop_nlink ( inode ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-04-07 06:01:34 +04:00
iput ( inode ) ;
goto out ;
}
static int nilfs_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
struct inode * inode = old_dentry - > d_inode ;
struct nilfs_transaction_info ti ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 1 ) ;
if ( err )
return err ;
inode - > i_ctime = CURRENT_TIME ;
inode_inc_link_count ( inode ) ;
2010-10-23 19:11:40 +04:00
ihold ( inode ) ;
2009-04-07 06:01:34 +04:00
err = nilfs_add_nondir ( dentry , inode ) ;
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
}
2011-07-26 09:41:39 +04:00
static int nilfs_mkdir ( struct inode * dir , struct dentry * dentry , umode_t mode )
2009-04-07 06:01:34 +04:00
{
struct inode * inode ;
struct nilfs_transaction_info ti ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 1 ) ;
if ( err )
return err ;
2009-11-27 13:41:08 +03:00
inc_nlink ( dir ) ;
2009-04-07 06:01:34 +04:00
inode = nilfs_new_inode ( dir , S_IFDIR | mode ) ;
err = PTR_ERR ( inode ) ;
if ( IS_ERR ( inode ) )
goto out_dir ;
inode - > i_op = & nilfs_dir_inode_operations ;
inode - > i_fop = & nilfs_dir_operations ;
inode - > i_mapping - > a_ops = & nilfs_aops ;
2009-11-27 13:41:08 +03:00
inc_nlink ( inode ) ;
2009-04-07 06:01:34 +04:00
err = nilfs_make_empty ( inode , dir ) ;
if ( err )
goto out_fail ;
err = nilfs_add_link ( dentry , inode ) ;
if ( err )
goto out_fail ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-04-07 06:01:34 +04:00
d_instantiate ( dentry , inode ) ;
out :
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
out_fail :
2009-11-27 13:41:08 +03:00
drop_nlink ( inode ) ;
drop_nlink ( inode ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-04-07 06:01:34 +04:00
iput ( inode ) ;
out_dir :
2009-11-27 13:41:08 +03:00
drop_nlink ( dir ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( dir ) ;
2009-04-07 06:01:34 +04:00
goto out ;
}
2009-11-27 13:41:10 +03:00
static int nilfs_do_unlink ( struct inode * dir , struct dentry * dentry )
2009-04-07 06:01:34 +04:00
{
struct inode * inode ;
struct nilfs_dir_entry * de ;
struct page * page ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
err = - ENOENT ;
2010-02-01 05:02:09 +03:00
de = nilfs_find_entry ( dir , & dentry - > d_name , & page ) ;
2009-04-07 06:01:34 +04:00
if ( ! de )
goto out ;
inode = dentry - > d_inode ;
err = - EIO ;
if ( le64_to_cpu ( de - > inode ) ! = inode - > i_ino )
goto out ;
if ( ! inode - > i_nlink ) {
nilfs_warning ( inode - > i_sb , __func__ ,
" deleting nonexistent file (%lu), %d \n " ,
inode - > i_ino , inode - > i_nlink ) ;
2011-10-28 16:13:29 +04:00
set_nlink ( inode , 1 ) ;
2009-04-07 06:01:34 +04:00
}
err = nilfs_delete_entry ( de , page ) ;
if ( err )
goto out ;
inode - > i_ctime = dir - > i_ctime ;
2009-11-27 13:41:08 +03:00
drop_nlink ( inode ) ;
2009-04-07 06:01:34 +04:00
err = 0 ;
out :
2009-11-27 13:41:10 +03:00
return err ;
}
static int nilfs_unlink ( struct inode * dir , struct dentry * dentry )
{
struct nilfs_transaction_info ti ;
int err ;
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 0 ) ;
if ( err )
return err ;
err = nilfs_do_unlink ( dir , dentry ) ;
if ( ! err ) {
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( dir ) ;
nilfs_mark_inode_dirty ( dentry - > d_inode ) ;
2009-04-07 06:01:45 +04:00
err = nilfs_transaction_commit ( dir - > i_sb ) ;
2009-11-27 13:41:10 +03:00
} else
2009-04-07 06:01:45 +04:00
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
}
static int nilfs_rmdir ( struct inode * dir , struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
struct nilfs_transaction_info ti ;
2009-04-07 06:01:45 +04:00
int err ;
2009-04-07 06:01:34 +04:00
err = nilfs_transaction_begin ( dir - > i_sb , & ti , 0 ) ;
if ( err )
return err ;
err = - ENOTEMPTY ;
if ( nilfs_empty_dir ( inode ) ) {
2009-11-27 13:41:10 +03:00
err = nilfs_do_unlink ( dir , dentry ) ;
2009-04-07 06:01:34 +04:00
if ( ! err ) {
inode - > i_size = 0 ;
2009-11-27 13:41:08 +03:00
drop_nlink ( inode ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( inode ) ;
2009-11-27 13:41:08 +03:00
drop_nlink ( dir ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( dir ) ;
2009-04-07 06:01:34 +04:00
}
}
2009-04-07 06:01:45 +04:00
if ( ! err )
err = nilfs_transaction_commit ( dir - > i_sb ) ;
else
nilfs_transaction_abort ( dir - > i_sb ) ;
return err ;
2009-04-07 06:01:34 +04:00
}
static int nilfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct inode * old_inode = old_dentry - > d_inode ;
struct inode * new_inode = new_dentry - > d_inode ;
struct page * dir_page = NULL ;
struct nilfs_dir_entry * dir_de = NULL ;
struct page * old_page ;
struct nilfs_dir_entry * old_de ;
struct nilfs_transaction_info ti ;
int err ;
err = nilfs_transaction_begin ( old_dir - > i_sb , & ti , 1 ) ;
if ( unlikely ( err ) )
return err ;
err = - ENOENT ;
2010-02-01 05:02:09 +03:00
old_de = nilfs_find_entry ( old_dir , & old_dentry - > d_name , & old_page ) ;
2009-04-07 06:01:34 +04:00
if ( ! old_de )
goto out ;
if ( S_ISDIR ( old_inode - > i_mode ) ) {
err = - EIO ;
dir_de = nilfs_dotdot ( old_inode , & dir_page ) ;
if ( ! dir_de )
goto out_old ;
}
if ( new_inode ) {
struct page * new_page ;
struct nilfs_dir_entry * new_de ;
err = - ENOTEMPTY ;
if ( dir_de & & ! nilfs_empty_dir ( new_inode ) )
goto out_dir ;
err = - ENOENT ;
2010-02-01 05:02:09 +03:00
new_de = nilfs_find_entry ( new_dir , & new_dentry - > d_name , & new_page ) ;
2009-04-07 06:01:34 +04:00
if ( ! new_de )
goto out_dir ;
nilfs_set_link ( new_dir , new_de , new_page , old_inode ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( new_dir ) ;
2009-04-07 06:01:34 +04:00
new_inode - > i_ctime = CURRENT_TIME ;
if ( dir_de )
drop_nlink ( new_inode ) ;
2009-11-27 13:41:08 +03:00
drop_nlink ( new_inode ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( new_inode ) ;
2009-04-07 06:01:34 +04:00
} else {
err = nilfs_add_link ( new_dentry , old_inode ) ;
2011-03-02 20:01:13 +03:00
if ( err )
2009-04-07 06:01:34 +04:00
goto out_dir ;
2009-11-27 13:41:08 +03:00
if ( dir_de ) {
inc_nlink ( new_dir ) ;
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( new_dir ) ;
2009-11-27 13:41:08 +03:00
}
2009-04-07 06:01:34 +04:00
}
/*
* Like most other Unix systems , set the ctime for inodes on a
* rename .
*/
old_inode - > i_ctime = CURRENT_TIME ;
nilfs_delete_entry ( old_de , old_page ) ;
if ( dir_de ) {
nilfs_set_link ( old_inode , dir_de , dir_page , new_dir ) ;
2009-11-27 13:41:08 +03:00
drop_nlink ( old_dir ) ;
2009-04-07 06:01:34 +04:00
}
2009-11-27 13:41:14 +03:00
nilfs_mark_inode_dirty ( old_dir ) ;
nilfs_mark_inode_dirty ( old_inode ) ;
2009-04-07 06:01:34 +04:00
2009-04-07 06:01:45 +04:00
err = nilfs_transaction_commit ( old_dir - > i_sb ) ;
2009-04-07 06:01:34 +04:00
return err ;
out_dir :
if ( dir_de ) {
kunmap ( dir_page ) ;
page_cache_release ( dir_page ) ;
}
out_old :
kunmap ( old_page ) ;
page_cache_release ( old_page ) ;
out :
2009-04-07 06:01:45 +04:00
nilfs_transaction_abort ( old_dir - > i_sb ) ;
2009-04-07 06:01:34 +04:00
return err ;
}
2010-08-26 19:23:02 +04:00
/*
* Export operations
*/
static struct dentry * nilfs_get_parent ( struct dentry * child )
{
unsigned long ino ;
struct inode * inode ;
2012-05-11 00:14:12 +04:00
struct qstr dotdot = QSTR_INIT ( " .. " , 2 ) ;
2010-08-26 19:23:02 +04:00
struct nilfs_root * root ;
ino = nilfs_inode_by_name ( child - > d_inode , & dotdot ) ;
if ( ! ino )
return ERR_PTR ( - ENOENT ) ;
root = NILFS_I ( child - > d_inode ) - > i_root ;
inode = nilfs_iget ( child - > d_inode - > i_sb , root , ino ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
return d_obtain_alias ( inode ) ;
}
static struct dentry * nilfs_get_dentry ( struct super_block * sb , u64 cno ,
u64 ino , u32 gen )
{
struct nilfs_root * root ;
struct inode * inode ;
if ( ino < NILFS_FIRST_INO ( sb ) & & ino ! = NILFS_ROOT_INO )
return ERR_PTR ( - ESTALE ) ;
2011-03-09 05:05:08 +03:00
root = nilfs_lookup_root ( sb - > s_fs_info , cno ) ;
2010-08-26 19:23:02 +04:00
if ( ! root )
return ERR_PTR ( - ESTALE ) ;
inode = nilfs_iget ( sb , root , ino ) ;
nilfs_put_root ( root ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
if ( gen & & inode - > i_generation ! = gen ) {
iput ( inode ) ;
return ERR_PTR ( - ESTALE ) ;
}
return d_obtain_alias ( inode ) ;
}
static struct dentry * nilfs_fh_to_dentry ( struct super_block * sb , struct fid * fh ,
int fh_len , int fh_type )
{
struct nilfs_fid * fid = ( struct nilfs_fid * ) fh ;
if ( ( fh_len ! = NILFS_FID_SIZE_NON_CONNECTABLE & &
fh_len ! = NILFS_FID_SIZE_CONNECTABLE ) | |
( fh_type ! = FILEID_NILFS_WITH_PARENT & &
fh_type ! = FILEID_NILFS_WITHOUT_PARENT ) )
return NULL ;
return nilfs_get_dentry ( sb , fid - > cno , fid - > ino , fid - > gen ) ;
}
static struct dentry * nilfs_fh_to_parent ( struct super_block * sb , struct fid * fh ,
int fh_len , int fh_type )
{
struct nilfs_fid * fid = ( struct nilfs_fid * ) fh ;
if ( fh_len ! = NILFS_FID_SIZE_CONNECTABLE | |
fh_type ! = FILEID_NILFS_WITH_PARENT )
return NULL ;
return nilfs_get_dentry ( sb , fid - > cno , fid - > parent_ino , fid - > parent_gen ) ;
}
2012-04-02 22:34:06 +04:00
static int nilfs_encode_fh ( struct inode * inode , __u32 * fh , int * lenp ,
struct inode * parent )
2010-08-26 19:23:02 +04:00
{
struct nilfs_fid * fid = ( struct nilfs_fid * ) fh ;
struct nilfs_root * root = NILFS_I ( inode ) - > i_root ;
int type ;
2012-04-02 22:34:06 +04:00
if ( parent & & * lenp < NILFS_FID_SIZE_CONNECTABLE ) {
* lenp = NILFS_FID_SIZE_CONNECTABLE ;
return 255 ;
}
if ( * lenp < NILFS_FID_SIZE_NON_CONNECTABLE ) {
* lenp = NILFS_FID_SIZE_NON_CONNECTABLE ;
2010-08-26 19:23:02 +04:00
return 255 ;
2012-04-02 22:34:06 +04:00
}
2010-08-26 19:23:02 +04:00
fid - > cno = root - > cno ;
fid - > ino = inode - > i_ino ;
fid - > gen = inode - > i_generation ;
2012-04-02 22:34:06 +04:00
if ( parent ) {
2010-08-26 19:23:02 +04:00
fid - > parent_ino = parent - > i_ino ;
fid - > parent_gen = parent - > i_generation ;
type = FILEID_NILFS_WITH_PARENT ;
* lenp = NILFS_FID_SIZE_CONNECTABLE ;
} else {
type = FILEID_NILFS_WITHOUT_PARENT ;
* lenp = NILFS_FID_SIZE_NON_CONNECTABLE ;
}
return type ;
}
2009-09-22 04:01:11 +04:00
const struct inode_operations nilfs_dir_inode_operations = {
2009-04-07 06:01:34 +04:00
. create = nilfs_create ,
. lookup = nilfs_lookup ,
. link = nilfs_link ,
. unlink = nilfs_unlink ,
. symlink = nilfs_symlink ,
. mkdir = nilfs_mkdir ,
. rmdir = nilfs_rmdir ,
. mknod = nilfs_mknod ,
. rename = nilfs_rename ,
. setattr = nilfs_setattr ,
. permission = nilfs_permission ,
2010-12-26 10:38:43 +03:00
. fiemap = nilfs_fiemap ,
2009-04-07 06:01:34 +04:00
} ;
2009-09-22 04:01:11 +04:00
const struct inode_operations nilfs_special_inode_operations = {
2009-04-07 06:01:34 +04:00
. setattr = nilfs_setattr ,
. permission = nilfs_permission ,
} ;
2009-09-22 04:01:11 +04:00
const struct inode_operations nilfs_symlink_inode_operations = {
2009-04-07 06:01:34 +04:00
. readlink = generic_readlink ,
. follow_link = page_follow_link_light ,
. put_link = page_put_link ,
2010-08-15 18:33:57 +04:00
. permission = nilfs_permission ,
2009-04-07 06:01:34 +04:00
} ;
2010-08-26 19:23:02 +04:00
const struct export_operations nilfs_export_ops = {
. encode_fh = nilfs_encode_fh ,
. fh_to_dentry = nilfs_fh_to_dentry ,
. fh_to_parent = nilfs_fh_to_parent ,
. get_parent = nilfs_get_parent ,
} ;