2009-11-20 22:13:39 +03:00
/*
* fs / logfs / dir . c - directory - related code
*
* As should be obvious for Linux kernel code , license is GPLv2
*
* Copyright ( c ) 2005 - 2008 Joern Engel < joern @ logfs . org >
*/
# include "logfs.h"
/*
* Atomic dir operations
*
* Directory operations are by default not atomic . Dentries and Inodes are
* created / removed / altered in seperate operations . Therefore we need to do
* a small amount of journaling .
*
* Create , link , mkdir , mknod and symlink all share the same function to do
* the work : __logfs_create . This function works in two atomic steps :
* 1. allocate inode ( remember in journal )
* 2. allocate dentry ( clear journal )
*
* As we can only get interrupted between the two , when the inode we just
* created is simply stored in the anchor . On next mount , if we were
* interrupted , we delete the inode . From a users point of view the
* operation never happened .
*
* Unlink and rmdir also share the same function : unlink . Again , this
* function works in two atomic steps
* 1. remove dentry ( remember inode in journal )
* 2. unlink inode ( clear journal )
*
* And again , on the next mount , if we were interrupted , we delete the inode .
* From a users point of view the operation succeeded .
*
* Rename is the real pain to deal with , harder than all the other methods
* combined . Depending on the circumstances we can run into three cases .
* A " target rename " where the target dentry already existed , a " local
* rename " where both parent directories are identical or a " cross - directory
* rename " in the remaining case.
*
* Local rename is atomic , as the old dentry is simply rewritten with a new
* name .
*
* Cross - directory rename works in two steps , similar to __logfs_create and
* logfs_unlink :
* 1. Write new dentry ( remember old dentry in journal )
* 2. Remove old dentry ( clear journal )
*
* Here we remember a dentry instead of an inode . On next mount , if we were
* interrupted , we delete the dentry . From a users point of view , the
* operation succeeded .
*
* Target rename works in three atomic steps :
* 1. Attach old inode to new dentry ( remember old dentry and new inode )
* 2. Remove old dentry ( still remember the new inode )
* 3. Remove victim inode
*
* Here we remember both an inode an a dentry . If we get interrupted
* between steps 1 and 2 , we delete both the dentry and the inode . If
* we get interrupted between steps 2 and 3 , we delete just the inode .
* In either case , the remaining objects are deleted on next mount . From
* a users point of view , the operation succeeded .
*/
static int write_dir ( struct inode * dir , struct logfs_disk_dentry * dd ,
loff_t pos )
{
return logfs_inode_write ( dir , dd , sizeof ( * dd ) , pos , WF_LOCK , NULL ) ;
}
static int write_inode ( struct inode * inode )
{
return __logfs_write_inode ( inode , WF_LOCK ) ;
}
static s64 dir_seek_data ( struct inode * inode , s64 pos )
{
s64 new_pos = logfs_seek_data ( inode , pos ) ;
return max ( pos , new_pos - 1 ) ;
}
static int beyond_eof ( struct inode * inode , loff_t bix )
{
loff_t pos = bix < < inode - > i_sb - > s_blocksize_bits ;
return pos > = i_size_read ( inode ) ;
}
/*
* Prime value was chosen to be roughly 256 + 26. r5 hash uses 11 ,
* so short names ( len < = 9 ) don ' t even occupy the complete 32 bit name
* space . A prime > 256 ensures short names quickly spread the 32 bit
* name space . Add about 26 for the estimated amount of information
* of each character and pick a prime nearby , preferrably a bit - sparse
* one .
*/
static u32 hash_32 ( const char * s , int len , u32 seed )
{
u32 hash = seed ;
int i ;
for ( i = 0 ; i < len ; i + + )
hash = hash * 293 + s [ i ] ;
return hash ;
}
/*
* We have to satisfy several conflicting requirements here . Small
* directories should stay fairly compact and not require too many
* indirect blocks . The number of possible locations for a given hash
* should be small to make lookup ( ) fast . And we should try hard not
* to overflow the 32 bit name space or nfs and 32 bit host systems will
* be unhappy .
*
* So we use the following scheme . First we reduce the hash to 0. .15
* and try a direct block . If that is occupied we reduce the hash to
* 16. .255 and try an indirect block . Same for 2 x and 3 x indirect
* blocks . Lastly we reduce the hash to 0x800 _0000 . . 0xffff _ffff ,
* but use buckets containing eight entries instead of a single one .
*
* Using 16 entries should allow for a reasonable amount of hash
* collisions , so the 32 bit name space can be packed fairly tight
* before overflowing . Oh and currently we don ' t overflow but return
* and error .
*
* How likely are collisions ? Doing the appropriate math is beyond me
* and the Bronstein textbook . But running a test program to brute
* force collisions for a couple of days showed that on average the
* first collision occurs after 598 M entries , with 290 M being the
* smallest result . Obviously 21 entries could already cause a
* collision if all entries are carefully chosen .
*/
static pgoff_t hash_index ( u32 hash , int round )
{
switch ( round ) {
case 0 :
return hash % I0_BLOCKS ;
case 1 :
return I0_BLOCKS + hash % ( I1_BLOCKS - I0_BLOCKS ) ;
case 2 :
return I1_BLOCKS + hash % ( I2_BLOCKS - I1_BLOCKS ) ;
case 3 :
return I2_BLOCKS + hash % ( I3_BLOCKS - I2_BLOCKS ) ;
case 4 . . . 19 :
return I3_BLOCKS + 16 * ( hash % ( ( ( 1 < < 31 ) - I3_BLOCKS ) / 16 ) )
+ round - 4 ;
}
BUG ( ) ;
}
static struct page * logfs_get_dd_page ( struct inode * dir , struct dentry * dentry )
{
struct qstr * name = & dentry - > d_name ;
struct page * page ;
struct logfs_disk_dentry * dd ;
u32 hash = hash_32 ( name - > name , name - > len , 0 ) ;
pgoff_t index ;
int round ;
if ( name - > len > LOGFS_MAX_NAMELEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
for ( round = 0 ; round < 20 ; round + + ) {
index = hash_index ( hash , round ) ;
if ( beyond_eof ( dir , index ) )
return NULL ;
if ( ! logfs_exist_block ( dir , index ) )
continue ;
page = read_cache_page ( dir - > i_mapping , index ,
( filler_t * ) logfs_readpage , NULL ) ;
if ( IS_ERR ( page ) )
return page ;
dd = kmap_atomic ( page , KM_USER0 ) ;
BUG_ON ( dd - > namelen = = 0 ) ;
if ( name - > len ! = be16_to_cpu ( dd - > namelen ) | |
memcmp ( name - > name , dd - > name , name - > len ) ) {
kunmap_atomic ( dd , KM_USER0 ) ;
page_cache_release ( page ) ;
continue ;
}
kunmap_atomic ( dd , KM_USER0 ) ;
return page ;
}
return NULL ;
}
static int logfs_remove_inode ( struct inode * inode )
{
int ret ;
inode - > i_nlink - - ;
ret = write_inode ( inode ) ;
LOGFS_BUG_ON ( ret , inode - > i_sb ) ;
return ret ;
}
static void abort_transaction ( struct inode * inode , struct logfs_transaction * ta )
{
if ( logfs_inode ( inode ) - > li_block )
logfs_inode ( inode ) - > li_block - > ta = NULL ;
kfree ( ta ) ;
}
static int logfs_unlink ( struct inode * dir , struct dentry * dentry )
{
struct logfs_super * super = logfs_super ( dir - > i_sb ) ;
struct inode * inode = dentry - > d_inode ;
struct logfs_transaction * ta ;
struct page * page ;
pgoff_t index ;
int ret ;
ta = kzalloc ( sizeof ( * ta ) , GFP_KERNEL ) ;
if ( ! ta )
return - ENOMEM ;
ta - > state = UNLINK_1 ;
ta - > ino = inode - > i_ino ;
inode - > i_ctime = dir - > i_ctime = dir - > i_mtime = CURRENT_TIME ;
page = logfs_get_dd_page ( dir , dentry ) ;
2009-11-23 16:29:12 +03:00
if ( ! page ) {
kfree ( ta ) ;
2009-11-20 22:13:39 +03:00
return - ENOENT ;
2009-11-23 16:29:12 +03:00
}
if ( IS_ERR ( page ) ) {
kfree ( ta ) ;
2009-11-20 22:13:39 +03:00
return PTR_ERR ( page ) ;
2009-11-23 16:29:12 +03:00
}
2009-11-20 22:13:39 +03:00
index = page - > index ;
page_cache_release ( page ) ;
mutex_lock ( & super - > s_dirop_mutex ) ;
logfs_add_transaction ( dir , ta ) ;
ret = logfs_delete ( dir , index , NULL ) ;
if ( ! ret )
ret = write_inode ( dir ) ;
if ( ret ) {
abort_transaction ( dir , ta ) ;
printk ( KERN_ERR " LOGFS: unable to delete inode \n " ) ;
goto out ;
}
ta - > state = UNLINK_2 ;
logfs_add_transaction ( inode , ta ) ;
ret = logfs_remove_inode ( inode ) ;
out :
mutex_unlock ( & super - > s_dirop_mutex ) ;
return ret ;
}
static inline int logfs_empty_dir ( struct inode * dir )
{
u64 data ;
data = logfs_seek_data ( dir , 0 ) < < dir - > i_sb - > s_blocksize_bits ;
return data > = i_size_read ( dir ) ;
}
static int logfs_rmdir ( struct inode * dir , struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
if ( ! logfs_empty_dir ( inode ) )
return - ENOTEMPTY ;
return logfs_unlink ( dir , dentry ) ;
}
/* FIXME: readdir currently has it's own dir_walk code. I don't see a good
* way to combine the two copies */
# define IMPLICIT_NODES 2
static int __logfs_readdir ( struct file * file , void * buf , filldir_t filldir )
{
struct inode * dir = file - > f_dentry - > d_inode ;
loff_t pos = file - > f_pos - IMPLICIT_NODES ;
struct page * page ;
struct logfs_disk_dentry * dd ;
int full ;
BUG_ON ( pos < 0 ) ;
for ( ; ; pos + + ) {
if ( beyond_eof ( dir , pos ) )
break ;
if ( ! logfs_exist_block ( dir , pos ) ) {
/* deleted dentry */
pos = dir_seek_data ( dir , pos ) ;
continue ;
}
page = read_cache_page ( dir - > i_mapping , pos ,
( filler_t * ) logfs_readpage , NULL ) ;
if ( IS_ERR ( page ) )
return PTR_ERR ( page ) ;
dd = kmap_atomic ( page , KM_USER0 ) ;
BUG_ON ( dd - > namelen = = 0 ) ;
full = filldir ( buf , ( char * ) dd - > name , be16_to_cpu ( dd - > namelen ) ,
pos , be64_to_cpu ( dd - > ino ) , dd - > type ) ;
kunmap_atomic ( dd , KM_USER0 ) ;
page_cache_release ( page ) ;
if ( full )
break ;
}
file - > f_pos = pos + IMPLICIT_NODES ;
return 0 ;
}
static int logfs_readdir ( struct file * file , void * buf , filldir_t filldir )
{
struct inode * inode = file - > f_dentry - > d_inode ;
ino_t pino = parent_ino ( file - > f_dentry ) ;
int err ;
if ( file - > f_pos < 0 )
return - EINVAL ;
if ( file - > f_pos = = 0 ) {
if ( filldir ( buf , " . " , 1 , 1 , inode - > i_ino , DT_DIR ) < 0 )
return 0 ;
file - > f_pos + + ;
}
if ( file - > f_pos = = 1 ) {
if ( filldir ( buf , " .. " , 2 , 2 , pino , DT_DIR ) < 0 )
return 0 ;
file - > f_pos + + ;
}
err = __logfs_readdir ( file , buf , filldir ) ;
return err ;
}
static void logfs_set_name ( struct logfs_disk_dentry * dd , struct qstr * name )
{
dd - > namelen = cpu_to_be16 ( name - > len ) ;
memcpy ( dd - > name , name - > name , name - > len ) ;
}
static struct dentry * logfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
struct page * page ;
struct logfs_disk_dentry * dd ;
pgoff_t index ;
u64 ino = 0 ;
struct inode * inode ;
page = logfs_get_dd_page ( dir , dentry ) ;
if ( IS_ERR ( page ) )
return ERR_CAST ( page ) ;
if ( ! page ) {
d_add ( dentry , NULL ) ;
return NULL ;
}
index = page - > index ;
dd = kmap_atomic ( page , KM_USER0 ) ;
ino = be64_to_cpu ( dd - > ino ) ;
kunmap_atomic ( dd , KM_USER0 ) ;
page_cache_release ( page ) ;
inode = logfs_iget ( dir - > i_sb , ino ) ;
if ( IS_ERR ( inode ) ) {
printk ( KERN_ERR " LogFS: Cannot read inode #%llx for dentry (%lx, %lx)n " ,
ino , dir - > i_ino , index ) ;
return ERR_CAST ( inode ) ;
}
return d_splice_alias ( inode , dentry ) ;
}
static void grow_dir ( struct inode * dir , loff_t index )
{
index = ( index + 1 ) < < dir - > i_sb - > s_blocksize_bits ;
if ( i_size_read ( dir ) < index )
i_size_write ( dir , index ) ;
}
static int logfs_write_dir ( struct inode * dir , struct dentry * dentry ,
struct inode * inode )
{
struct page * page ;
struct logfs_disk_dentry * dd ;
u32 hash = hash_32 ( dentry - > d_name . name , dentry - > d_name . len , 0 ) ;
pgoff_t index ;
int round , err ;
for ( round = 0 ; round < 20 ; round + + ) {
index = hash_index ( hash , round ) ;
if ( logfs_exist_block ( dir , index ) )
continue ;
page = find_or_create_page ( dir - > i_mapping , index , GFP_KERNEL ) ;
if ( ! page )
return - ENOMEM ;
dd = kmap_atomic ( page , KM_USER0 ) ;
memset ( dd , 0 , sizeof ( * dd ) ) ;
dd - > ino = cpu_to_be64 ( inode - > i_ino ) ;
dd - > type = logfs_type ( inode ) ;
logfs_set_name ( dd , & dentry - > d_name ) ;
kunmap_atomic ( dd , KM_USER0 ) ;
err = logfs_write_buf ( dir , page , WF_LOCK ) ;
unlock_page ( page ) ;
page_cache_release ( page ) ;
if ( ! err )
grow_dir ( dir , index ) ;
return err ;
}
/* FIXME: Is there a better return value? In most cases neither
* the filesystem nor the directory are full . But we have had
* too many collisions for this particular hash and no fallback .
*/
return - ENOSPC ;
}
static int __logfs_create ( struct inode * dir , struct dentry * dentry ,
struct inode * inode , const char * dest , long destlen )
{
struct logfs_super * super = logfs_super ( dir - > i_sb ) ;
struct logfs_inode * li = logfs_inode ( inode ) ;
struct logfs_transaction * ta ;
int ret ;
ta = kzalloc ( sizeof ( * ta ) , GFP_KERNEL ) ;
if ( ! ta )
return - ENOMEM ;
ta - > state = CREATE_1 ;
ta - > ino = inode - > i_ino ;
mutex_lock ( & super - > s_dirop_mutex ) ;
logfs_add_transaction ( inode , ta ) ;
if ( dest ) {
/* symlink */
ret = logfs_inode_write ( inode , dest , destlen , 0 , WF_LOCK , NULL ) ;
if ( ! ret )
ret = write_inode ( inode ) ;
} else {
/* creat/mkdir/mknod */
ret = write_inode ( inode ) ;
}
if ( ret ) {
abort_transaction ( inode , ta ) ;
li - > li_flags | = LOGFS_IF_STILLBORN ;
/* FIXME: truncate symlink */
inode - > i_nlink - - ;
iput ( inode ) ;
goto out ;
}
ta - > state = CREATE_2 ;
logfs_add_transaction ( dir , ta ) ;
ret = logfs_write_dir ( dir , dentry , inode ) ;
/* sync directory */
if ( ! ret )
ret = write_inode ( dir ) ;
if ( ret ) {
logfs_del_transaction ( dir , ta ) ;
ta - > state = CREATE_2 ;
logfs_add_transaction ( inode , ta ) ;
logfs_remove_inode ( inode ) ;
iput ( inode ) ;
goto out ;
}
d_instantiate ( dentry , inode ) ;
out :
mutex_unlock ( & super - > s_dirop_mutex ) ;
return ret ;
}
static int logfs_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
struct inode * inode ;
/*
* FIXME : why do we have to fill in S_IFDIR , while the mode is
* correct for mknod , creat , etc . ? Smells like the vfs * should *
* do it for us but for some reason fails to do so .
*/
inode = logfs_new_inode ( dir , S_IFDIR | mode ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
inode - > i_op = & logfs_dir_iops ;
inode - > i_fop = & logfs_dir_fops ;
return __logfs_create ( dir , dentry , inode , NULL , 0 ) ;
}
static int logfs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
struct inode * inode ;
inode = logfs_new_inode ( dir , mode ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
inode - > i_op = & logfs_reg_iops ;
inode - > i_fop = & logfs_reg_fops ;
inode - > i_mapping - > a_ops = & logfs_reg_aops ;
return __logfs_create ( dir , dentry , inode , NULL , 0 ) ;
}
static int logfs_mknod ( struct inode * dir , struct dentry * dentry , int mode ,
dev_t rdev )
{
struct inode * inode ;
if ( dentry - > d_name . len > LOGFS_MAX_NAMELEN )
return - ENAMETOOLONG ;
inode = logfs_new_inode ( dir , mode ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
init_special_inode ( inode , mode , rdev ) ;
return __logfs_create ( dir , dentry , inode , NULL , 0 ) ;
}
static int logfs_symlink ( struct inode * dir , struct dentry * dentry ,
const char * target )
{
struct inode * inode ;
size_t destlen = strlen ( target ) + 1 ;
if ( destlen > dir - > i_sb - > s_blocksize )
return - ENAMETOOLONG ;
inode = logfs_new_inode ( dir , S_IFLNK | 0777 ) ;
if ( IS_ERR ( inode ) )
return PTR_ERR ( inode ) ;
inode - > i_op = & logfs_symlink_iops ;
inode - > i_mapping - > a_ops = & logfs_reg_aops ;
return __logfs_create ( dir , dentry , inode , target , destlen ) ;
}
static int logfs_permission ( struct inode * inode , int mask )
{
return generic_permission ( inode , mask , NULL ) ;
}
static int logfs_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
struct inode * inode = old_dentry - > d_inode ;
if ( inode - > i_nlink > = LOGFS_LINK_MAX )
return - EMLINK ;
inode - > i_ctime = dir - > i_ctime = dir - > i_mtime = CURRENT_TIME ;
atomic_inc ( & inode - > i_count ) ;
inode - > i_nlink + + ;
mark_inode_dirty_sync ( inode ) ;
return __logfs_create ( dir , dentry , inode , NULL , 0 ) ;
}
static int logfs_get_dd ( struct inode * dir , struct dentry * dentry ,
struct logfs_disk_dentry * dd , loff_t * pos )
{
struct page * page ;
void * map ;
page = logfs_get_dd_page ( dir , dentry ) ;
if ( IS_ERR ( page ) )
return PTR_ERR ( page ) ;
* pos = page - > index ;
map = kmap_atomic ( page , KM_USER0 ) ;
memcpy ( dd , map , sizeof ( * dd ) ) ;
kunmap_atomic ( map , KM_USER0 ) ;
page_cache_release ( page ) ;
return 0 ;
}
static int logfs_delete_dd ( struct inode * dir , loff_t pos )
{
/*
* Getting called with pos somewhere beyond eof is either a goofup
* within this file or means someone maliciously edited the
* ( crc - protected ) journal .
*/
BUG_ON ( beyond_eof ( dir , pos ) ) ;
dir - > i_ctime = dir - > i_mtime = CURRENT_TIME ;
log_dir ( " Delete dentry (%lx, %llx) \n " , dir - > i_ino , pos ) ;
return logfs_delete ( dir , pos , NULL ) ;
}
/*
* Cross - directory rename , target does not exist . Just a little nasty .
* Create a new dentry in the target dir , then remove the old dentry ,
* all the while taking care to remember our operation in the journal .
*/
static int logfs_rename_cross ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct logfs_super * super = logfs_super ( old_dir - > i_sb ) ;
struct logfs_disk_dentry dd ;
struct logfs_transaction * ta ;
loff_t pos ;
int err ;
/* 1. locate source dd */
err = logfs_get_dd ( old_dir , old_dentry , & dd , & pos ) ;
if ( err )
return err ;
ta = kzalloc ( sizeof ( * ta ) , GFP_KERNEL ) ;
if ( ! ta )
return - ENOMEM ;
ta - > state = CROSS_RENAME_1 ;
ta - > dir = old_dir - > i_ino ;
ta - > pos = pos ;
/* 2. write target dd */
mutex_lock ( & super - > s_dirop_mutex ) ;
logfs_add_transaction ( new_dir , ta ) ;
err = logfs_write_dir ( new_dir , new_dentry , old_dentry - > d_inode ) ;
if ( ! err )
err = write_inode ( new_dir ) ;
if ( err ) {
super - > s_rename_dir = 0 ;
super - > s_rename_pos = 0 ;
abort_transaction ( new_dir , ta ) ;
goto out ;
}
/* 3. remove source dd */
ta - > state = CROSS_RENAME_2 ;
logfs_add_transaction ( old_dir , ta ) ;
err = logfs_delete_dd ( old_dir , pos ) ;
if ( ! err )
err = write_inode ( old_dir ) ;
LOGFS_BUG_ON ( err , old_dir - > i_sb ) ;
out :
mutex_unlock ( & super - > s_dirop_mutex ) ;
return err ;
}
static int logfs_replace_inode ( struct inode * dir , struct dentry * dentry ,
struct logfs_disk_dentry * dd , struct inode * inode )
{
loff_t pos ;
int err ;
err = logfs_get_dd ( dir , dentry , dd , & pos ) ;
if ( err )
return err ;
dd - > ino = cpu_to_be64 ( inode - > i_ino ) ;
dd - > type = logfs_type ( inode ) ;
err = write_dir ( dir , dd , pos ) ;
if ( err )
return err ;
log_dir ( " Replace dentry (%lx, %llx) %s -> %llx \n " , dir - > i_ino , pos ,
dd - > name , be64_to_cpu ( dd - > ino ) ) ;
return write_inode ( dir ) ;
}
/* Target dentry exists - the worst case. We need to attach the source
* inode to the target dentry , then remove the orphaned target inode and
* source dentry .
*/
static int logfs_rename_target ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct logfs_super * super = logfs_super ( old_dir - > i_sb ) ;
struct inode * old_inode = old_dentry - > d_inode ;
struct inode * new_inode = new_dentry - > d_inode ;
int isdir = S_ISDIR ( old_inode - > i_mode ) ;
struct logfs_disk_dentry dd ;
struct logfs_transaction * ta ;
loff_t pos ;
int err ;
BUG_ON ( isdir ! = S_ISDIR ( new_inode - > i_mode ) ) ;
if ( isdir ) {
if ( ! logfs_empty_dir ( new_inode ) )
return - ENOTEMPTY ;
}
/* 1. locate source dd */
err = logfs_get_dd ( old_dir , old_dentry , & dd , & pos ) ;
if ( err )
return err ;
ta = kzalloc ( sizeof ( * ta ) , GFP_KERNEL ) ;
if ( ! ta )
return - ENOMEM ;
ta - > state = TARGET_RENAME_1 ;
ta - > dir = old_dir - > i_ino ;
ta - > pos = pos ;
ta - > ino = new_inode - > i_ino ;
/* 2. attach source inode to target dd */
mutex_lock ( & super - > s_dirop_mutex ) ;
logfs_add_transaction ( new_dir , ta ) ;
err = logfs_replace_inode ( new_dir , new_dentry , & dd , old_inode ) ;
if ( err ) {
super - > s_rename_dir = 0 ;
super - > s_rename_pos = 0 ;
super - > s_victim_ino = 0 ;
abort_transaction ( new_dir , ta ) ;
goto out ;
}
/* 3. remove source dd */
ta - > state = TARGET_RENAME_2 ;
logfs_add_transaction ( old_dir , ta ) ;
err = logfs_delete_dd ( old_dir , pos ) ;
if ( ! err )
err = write_inode ( old_dir ) ;
LOGFS_BUG_ON ( err , old_dir - > i_sb ) ;
/* 4. remove target inode */
ta - > state = TARGET_RENAME_3 ;
logfs_add_transaction ( new_inode , ta ) ;
err = logfs_remove_inode ( new_inode ) ;
out :
mutex_unlock ( & super - > s_dirop_mutex ) ;
return err ;
}
static int logfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
if ( new_dentry - > d_inode )
return logfs_rename_target ( old_dir , old_dentry ,
new_dir , new_dentry ) ;
return logfs_rename_cross ( old_dir , old_dentry , new_dir , new_dentry ) ;
}
/* No locking done here, as this is called before .get_sb() returns. */
int logfs_replay_journal ( struct super_block * sb )
{
struct logfs_super * super = logfs_super ( sb ) ;
struct inode * inode ;
u64 ino , pos ;
int err ;
if ( super - > s_victim_ino ) {
/* delete victim inode */
ino = super - > s_victim_ino ;
printk ( KERN_INFO " LogFS: delete unmapped inode #%llx \n " , ino ) ;
inode = logfs_iget ( sb , ino ) ;
if ( IS_ERR ( inode ) )
goto fail ;
LOGFS_BUG_ON ( i_size_read ( inode ) > 0 , sb ) ;
super - > s_victim_ino = 0 ;
err = logfs_remove_inode ( inode ) ;
iput ( inode ) ;
if ( err ) {
super - > s_victim_ino = ino ;
goto fail ;
}
}
if ( super - > s_rename_dir ) {
/* delete old dd from rename */
ino = super - > s_rename_dir ;
pos = super - > s_rename_pos ;
printk ( KERN_INFO " LogFS: delete unbacked dentry (%llx, %llx) \n " ,
ino , pos ) ;
inode = logfs_iget ( sb , ino ) ;
if ( IS_ERR ( inode ) )
goto fail ;
super - > s_rename_dir = 0 ;
super - > s_rename_pos = 0 ;
err = logfs_delete_dd ( inode , pos ) ;
iput ( inode ) ;
if ( err ) {
super - > s_rename_dir = ino ;
super - > s_rename_pos = pos ;
goto fail ;
}
}
return 0 ;
fail :
LOGFS_BUG ( sb ) ;
return - EIO ;
}
const struct inode_operations logfs_symlink_iops = {
. readlink = generic_readlink ,
. follow_link = page_follow_link_light ,
} ;
const struct inode_operations logfs_dir_iops = {
. create = logfs_create ,
. link = logfs_link ,
. lookup = logfs_lookup ,
. mkdir = logfs_mkdir ,
. mknod = logfs_mknod ,
. rename = logfs_rename ,
. rmdir = logfs_rmdir ,
. permission = logfs_permission ,
. symlink = logfs_symlink ,
. unlink = logfs_unlink ,
} ;
const struct file_operations logfs_dir_fops = {
. fsync = logfs_fsync ,
. ioctl = logfs_ioctl ,
. readdir = logfs_readdir ,
. read = generic_read_dir ,
} ;