2005-04-17 02:20:36 +04:00
/*
* fs / bfs / dir . c
* BFS directory operations .
* Copyright ( C ) 1999 , 2000 Tigran Aivazian < tigran @ veritas . com >
2005-09-10 00:02:04 +04:00
* Made endianness - clean by Andrew Stribblehill < ads @ wompom . org > 2005
2005-04-17 02:20:36 +04:00
*/
# include <linux/time.h>
# include <linux/string.h>
# include <linux/fs.h>
# include <linux/buffer_head.h>
# include <linux/sched.h>
# include "bfs.h"
# undef DEBUG
# ifdef DEBUG
# define dprintf(x...) printf(x)
# else
# define dprintf(x...)
# endif
2007-11-15 03:59:47 +03:00
static int bfs_add_entry ( struct inode * dir , const unsigned char * name ,
int namelen , int ino ) ;
static struct buffer_head * bfs_find_entry ( struct inode * dir ,
const unsigned char * name , int namelen ,
struct bfs_dirent * * res_dir ) ;
2005-04-17 02:20:36 +04:00
2007-11-15 03:59:47 +03:00
static int bfs_readdir ( struct file * f , void * dirent , filldir_t filldir )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
struct inode * dir = f - > f_path . dentry - > d_inode ;
struct buffer_head * bh ;
struct bfs_dirent * de ;
2008-07-26 06:44:54 +04:00
struct bfs_sb_info * info = BFS_SB ( dir - > i_sb ) ;
2005-04-17 02:20:36 +04:00
unsigned int offset ;
int block ;
2008-07-26 06:44:54 +04:00
mutex_lock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
2007-11-15 03:59:47 +03:00
if ( f - > f_pos & ( BFS_DIRENT_SIZE - 1 ) ) {
printf ( " Bad f_pos=%08lx for %s:%08lx \n " ,
( unsigned long ) f - > f_pos ,
dir - > i_sb - > s_id , dir - > i_ino ) ;
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return - EBADF ;
}
while ( f - > f_pos < dir - > i_size ) {
2007-11-15 03:59:47 +03:00
offset = f - > f_pos & ( BFS_BSIZE - 1 ) ;
2005-04-17 02:20:36 +04:00
block = BFS_I ( dir ) - > i_sblock + ( f - > f_pos > > BFS_BSIZE_BITS ) ;
bh = sb_bread ( dir - > i_sb , block ) ;
if ( ! bh ) {
f - > f_pos + = BFS_BSIZE - offset ;
continue ;
}
do {
de = ( struct bfs_dirent * ) ( bh - > b_data + offset ) ;
if ( de - > ino ) {
int size = strnlen ( de - > name , BFS_NAMELEN ) ;
2007-11-15 03:59:47 +03:00
if ( filldir ( dirent , de - > name , size , f - > f_pos ,
le16_to_cpu ( de - > ino ) ,
DT_UNKNOWN ) < 0 ) {
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
}
offset + = BFS_DIRENT_SIZE ;
f - > f_pos + = BFS_DIRENT_SIZE ;
2007-11-15 03:59:47 +03:00
} while ( ( offset < BFS_BSIZE ) & & ( f - > f_pos < dir - > i_size ) ) ;
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
}
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-03-28 13:56:42 +04:00
const struct file_operations bfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
. readdir = bfs_readdir ,
2010-05-26 19:53:41 +04:00
. fsync = generic_file_fsync ,
2008-09-03 23:53:01 +04:00
. llseek = generic_file_llseek ,
2005-04-17 02:20:36 +04:00
} ;
extern void dump_imap ( const char * , struct super_block * ) ;
2007-11-15 03:59:47 +03:00
static int bfs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
2005-04-17 02:20:36 +04:00
{
int err ;
2007-11-15 03:59:47 +03:00
struct inode * inode ;
struct super_block * s = dir - > i_sb ;
struct bfs_sb_info * info = BFS_SB ( s ) ;
2005-04-17 02:20:36 +04:00
unsigned long ino ;
inode = new_inode ( s ) ;
if ( ! inode )
return - ENOSPC ;
2008-07-26 06:44:54 +04:00
mutex_lock ( & info - > bfs_lock ) ;
2011-03-21 15:32:53 +03:00
ino = find_first_zero_bit ( info - > si_imap , info - > si_lasti + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ino > info - > si_lasti ) {
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
return - ENOSPC ;
}
2007-11-15 03:59:47 +03:00
set_bit ( ino , info - > si_imap ) ;
2005-04-17 02:20:36 +04:00
info - > si_freei - - ;
2010-03-04 17:31:46 +03:00
inode_init_owner ( inode , dir , mode ) ;
2005-04-17 02:20:36 +04:00
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME_SEC ;
2006-09-27 12:50:49 +04:00
inode - > i_blocks = 0 ;
2005-04-17 02:20:36 +04:00
inode - > i_op = & bfs_file_inops ;
inode - > i_fop = & bfs_file_operations ;
inode - > i_mapping - > a_ops = & bfs_aops ;
inode - > i_ino = ino ;
2005-10-04 20:43:06 +04:00
BFS_I ( inode ) - > i_dsk_ino = ino ;
2005-04-17 02:20:36 +04:00
BFS_I ( inode ) - > i_sblock = 0 ;
BFS_I ( inode ) - > i_eblock = 0 ;
insert_inode_hash ( inode ) ;
mark_inode_dirty ( inode ) ;
2007-11-15 03:59:47 +03:00
dump_imap ( " create " , s ) ;
2005-04-17 02:20:36 +04:00
2007-11-15 03:59:47 +03:00
err = bfs_add_entry ( dir , dentry - > d_name . name , dentry - > d_name . len ,
inode - > i_ino ) ;
2005-04-17 02:20:36 +04:00
if ( err ) {
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( inode ) ;
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2008-09-13 13:33:12 +04:00
iput ( inode ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
d_instantiate ( dentry , inode ) ;
return 0 ;
}
2007-11-15 03:59:47 +03:00
static struct dentry * bfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
struct inode * inode = NULL ;
struct buffer_head * bh ;
struct bfs_dirent * de ;
2008-07-26 06:44:54 +04:00
struct bfs_sb_info * info = BFS_SB ( dir - > i_sb ) ;
2005-04-17 02:20:36 +04:00
if ( dentry - > d_name . len > BFS_NAMELEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
2008-07-26 06:44:54 +04:00
mutex_lock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
bh = bfs_find_entry ( dir , dentry - > d_name . name , dentry - > d_name . len , & de ) ;
if ( bh ) {
2005-09-10 00:02:04 +04:00
unsigned long ino = ( unsigned long ) le16_to_cpu ( de - > ino ) ;
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
2008-02-07 11:15:32 +03:00
inode = bfs_iget ( dir - > i_sb , ino ) ;
if ( IS_ERR ( inode ) ) {
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2008-02-07 11:15:32 +03:00
return ERR_CAST ( inode ) ;
2005-04-17 02:20:36 +04:00
}
}
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
d_add ( dentry , inode ) ;
return NULL ;
}
2007-11-15 03:59:47 +03:00
static int bfs_link ( struct dentry * old , struct inode * dir ,
struct dentry * new )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
struct inode * inode = old - > d_inode ;
2008-07-26 06:44:54 +04:00
struct bfs_sb_info * info = BFS_SB ( inode - > i_sb ) ;
2005-04-17 02:20:36 +04:00
int err ;
2008-07-26 06:44:54 +04:00
mutex_lock ( & info - > bfs_lock ) ;
2007-11-15 03:59:47 +03:00
err = bfs_add_entry ( dir , new - > d_name . name , new - > d_name . len ,
inode - > i_ino ) ;
2005-04-17 02:20:36 +04:00
if ( err ) {
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-10-01 10:29:04 +04:00
inc_nlink ( inode ) ;
2005-04-17 02:20:36 +04:00
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
2010-10-23 19:11:40 +04:00
ihold ( inode ) ;
2005-04-17 02:20:36 +04:00
d_instantiate ( new , inode ) ;
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-11-15 03:59:47 +03:00
static int bfs_unlink ( struct inode * dir , struct dentry * dentry )
2005-04-17 02:20:36 +04:00
{
int error = - ENOENT ;
2008-07-26 06:44:54 +04:00
struct inode * inode = dentry - > d_inode ;
2007-11-15 03:59:47 +03:00
struct buffer_head * bh ;
struct bfs_dirent * de ;
2008-07-26 06:44:54 +04:00
struct bfs_sb_info * info = BFS_SB ( inode - > i_sb ) ;
2005-04-17 02:20:36 +04:00
2008-07-26 06:44:54 +04:00
mutex_lock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
bh = bfs_find_entry ( dir , dentry - > d_name . name , dentry - > d_name . len , & de ) ;
2007-11-15 03:59:47 +03:00
if ( ! bh | | ( le16_to_cpu ( de - > ino ) ! = inode - > i_ino ) )
2005-04-17 02:20:36 +04:00
goto out_brelse ;
if ( ! inode - > i_nlink ) {
2007-11-15 03:59:47 +03:00
printf ( " unlinking non-existent file %s:%lu (nlink=%d) \n " ,
inode - > i_sb - > s_id , inode - > i_ino ,
inode - > i_nlink ) ;
2005-04-17 02:20:36 +04:00
inode - > i_nlink = 1 ;
}
de - > ino = 0 ;
2009-06-08 09:15:58 +04:00
mark_buffer_dirty_inode ( bh , dir ) ;
2005-04-17 02:20:36 +04:00
dir - > i_ctime = dir - > i_mtime = CURRENT_TIME_SEC ;
mark_inode_dirty ( dir ) ;
inode - > i_ctime = dir - > i_ctime ;
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
error = 0 ;
out_brelse :
brelse ( bh ) ;
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2007-11-15 03:59:47 +03:00
static int bfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
struct inode * old_inode , * new_inode ;
struct buffer_head * old_bh , * new_bh ;
struct bfs_dirent * old_de , * new_de ;
2008-07-26 06:44:54 +04:00
struct bfs_sb_info * info ;
2005-04-17 02:20:36 +04:00
int error = - ENOENT ;
old_bh = new_bh = NULL ;
old_inode = old_dentry - > d_inode ;
if ( S_ISDIR ( old_inode - > i_mode ) )
return - EINVAL ;
2008-07-26 06:44:54 +04:00
info = BFS_SB ( old_inode - > i_sb ) ;
mutex_lock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
old_bh = bfs_find_entry ( old_dir ,
old_dentry - > d_name . name ,
old_dentry - > d_name . len , & old_de ) ;
2007-11-15 03:59:47 +03:00
if ( ! old_bh | | ( le16_to_cpu ( old_de - > ino ) ! = old_inode - > i_ino ) )
2005-04-17 02:20:36 +04:00
goto end_rename ;
error = - EPERM ;
new_inode = new_dentry - > d_inode ;
new_bh = bfs_find_entry ( new_dir ,
new_dentry - > d_name . name ,
new_dentry - > d_name . len , & new_de ) ;
if ( new_bh & & ! new_inode ) {
brelse ( new_bh ) ;
new_bh = NULL ;
}
if ( ! new_bh ) {
error = bfs_add_entry ( new_dir ,
new_dentry - > d_name . name ,
2007-11-15 03:59:47 +03:00
new_dentry - > d_name . len ,
old_inode - > i_ino ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto end_rename ;
}
old_de - > ino = 0 ;
old_dir - > i_ctime = old_dir - > i_mtime = CURRENT_TIME_SEC ;
mark_inode_dirty ( old_dir ) ;
if ( new_inode ) {
new_inode - > i_ctime = CURRENT_TIME_SEC ;
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( new_inode ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-08 09:15:58 +04:00
mark_buffer_dirty_inode ( old_bh , old_dir ) ;
2005-04-17 02:20:36 +04:00
error = 0 ;
end_rename :
2008-07-26 06:44:54 +04:00
mutex_unlock ( & info - > bfs_lock ) ;
2005-04-17 02:20:36 +04:00
brelse ( old_bh ) ;
brelse ( new_bh ) ;
return error ;
}
2007-02-12 11:55:38 +03:00
const struct inode_operations bfs_dir_inops = {
2005-04-17 02:20:36 +04:00
. create = bfs_create ,
. lookup = bfs_lookup ,
. link = bfs_link ,
. unlink = bfs_unlink ,
. rename = bfs_rename ,
} ;
2007-11-15 03:59:47 +03:00
static int bfs_add_entry ( struct inode * dir , const unsigned char * name ,
int namelen , int ino )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
struct buffer_head * bh ;
struct bfs_dirent * de ;
int block , sblock , eblock , off , pos ;
2005-04-17 02:20:36 +04:00
int i ;
dprintf ( " name=%s, namelen=%d \n " , name , namelen ) ;
if ( ! namelen )
return - ENOENT ;
if ( namelen > BFS_NAMELEN )
return - ENAMETOOLONG ;
sblock = BFS_I ( dir ) - > i_sblock ;
eblock = BFS_I ( dir ) - > i_eblock ;
2007-11-15 03:59:47 +03:00
for ( block = sblock ; block < = eblock ; block + + ) {
2005-04-17 02:20:36 +04:00
bh = sb_bread ( dir - > i_sb , block ) ;
2007-11-15 03:59:47 +03:00
if ( ! bh )
2005-04-17 02:20:36 +04:00
return - ENOSPC ;
2007-11-15 03:59:47 +03:00
for ( off = 0 ; off < BFS_BSIZE ; off + = BFS_DIRENT_SIZE ) {
2005-04-17 02:20:36 +04:00
de = ( struct bfs_dirent * ) ( bh - > b_data + off ) ;
if ( ! de - > ino ) {
2007-11-15 03:59:47 +03:00
pos = ( block - sblock ) * BFS_BSIZE + off ;
if ( pos > = dir - > i_size ) {
2005-04-17 02:20:36 +04:00
dir - > i_size + = BFS_DIRENT_SIZE ;
dir - > i_ctime = CURRENT_TIME_SEC ;
}
dir - > i_mtime = CURRENT_TIME_SEC ;
mark_inode_dirty ( dir ) ;
2005-09-10 00:02:04 +04:00
de - > ino = cpu_to_le16 ( ( u16 ) ino ) ;
2007-11-15 03:59:47 +03:00
for ( i = 0 ; i < BFS_NAMELEN ; i + + )
de - > name [ i ] =
( i < namelen ) ? name [ i ] : 0 ;
2009-06-08 09:15:58 +04:00
mark_buffer_dirty_inode ( bh , dir ) ;
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
return 0 ;
}
}
brelse ( bh ) ;
}
return - ENOSPC ;
}
2007-11-15 03:59:47 +03:00
static inline int bfs_namecmp ( int len , const unsigned char * name ,
const char * buffer )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
if ( ( len < BFS_NAMELEN ) & & buffer [ len ] )
2005-04-17 02:20:36 +04:00
return 0 ;
return ! memcmp ( name , buffer , len ) ;
}
2007-11-15 03:59:47 +03:00
static struct buffer_head * bfs_find_entry ( struct inode * dir ,
const unsigned char * name , int namelen ,
struct bfs_dirent * * res_dir )
2005-04-17 02:20:36 +04:00
{
2007-11-15 03:59:47 +03:00
unsigned long block = 0 , offset = 0 ;
struct buffer_head * bh = NULL ;
struct bfs_dirent * de ;
2005-04-17 02:20:36 +04:00
* res_dir = NULL ;
if ( namelen > BFS_NAMELEN )
return NULL ;
2007-11-15 03:59:47 +03:00
2005-04-17 02:20:36 +04:00
while ( block * BFS_BSIZE + offset < dir - > i_size ) {
if ( ! bh ) {
bh = sb_bread ( dir - > i_sb , BFS_I ( dir ) - > i_sblock + block ) ;
if ( ! bh ) {
block + + ;
continue ;
}
}
de = ( struct bfs_dirent * ) ( bh - > b_data + offset ) ;
offset + = BFS_DIRENT_SIZE ;
2007-11-15 03:59:47 +03:00
if ( le16_to_cpu ( de - > ino ) & &
bfs_namecmp ( namelen , name , de - > name ) ) {
2005-04-17 02:20:36 +04:00
* res_dir = de ;
return bh ;
}
if ( offset < bh - > b_size )
continue ;
brelse ( bh ) ;
bh = NULL ;
offset = 0 ;
block + + ;
}
brelse ( bh ) ;
return NULL ;
}