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/smp_lock.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
2005-09-10 00:02:04 +04: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
static struct buffer_head * bfs_find_entry ( struct inode * dir ,
2005-09-10 00:02:04 +04:00
const unsigned char * name , int namelen , struct bfs_dirent * * res_dir ) ;
2005-04-17 02:20:36 +04:00
static int bfs_readdir ( struct file * f , void * dirent , filldir_t filldir )
{
2006-12-08 13:36:53 +03:00
struct inode * dir = f - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
struct buffer_head * bh ;
struct bfs_dirent * de ;
unsigned int offset ;
int block ;
lock_kernel ( ) ;
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 ) ;
unlock_kernel ( ) ;
return - EBADF ;
}
while ( f - > f_pos < dir - > i_size ) {
offset = f - > f_pos & ( BFS_BSIZE - 1 ) ;
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 ) ;
2005-09-10 00:02:04 +04: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 ) ;
unlock_kernel ( ) ;
return 0 ;
}
}
offset + = BFS_DIRENT_SIZE ;
f - > f_pos + = BFS_DIRENT_SIZE ;
} while ( offset < BFS_BSIZE & & f - > f_pos < dir - > i_size ) ;
brelse ( bh ) ;
}
unlock_kernel ( ) ;
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 ,
. fsync = file_fsync ,
} ;
extern void dump_imap ( const char * , struct super_block * ) ;
static int bfs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
int err ;
struct inode * inode ;
struct super_block * s = dir - > i_sb ;
struct bfs_sb_info * info = BFS_SB ( s ) ;
unsigned long ino ;
inode = new_inode ( s ) ;
if ( ! inode )
return - ENOSPC ;
lock_kernel ( ) ;
ino = find_first_zero_bit ( info - > si_imap , info - > si_lasti ) ;
if ( ino > info - > si_lasti ) {
unlock_kernel ( ) ;
iput ( inode ) ;
return - ENOSPC ;
}
set_bit ( ino , info - > si_imap ) ;
info - > si_freei - - ;
inode - > i_uid = current - > fsuid ;
inode - > i_gid = ( dir - > i_mode & S_ISGID ) ? dir - > i_gid : current - > fsgid ;
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_mode = mode ;
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 ) ;
dump_imap ( " create " , s ) ;
err = bfs_add_entry ( dir , dentry - > d_name . name , dentry - > d_name . len , inode - > i_ino ) ;
if ( err ) {
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
unlock_kernel ( ) ;
return err ;
}
unlock_kernel ( ) ;
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static struct dentry * bfs_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
struct inode * inode = NULL ;
struct buffer_head * bh ;
struct bfs_dirent * de ;
if ( dentry - > d_name . len > BFS_NAMELEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
lock_kernel ( ) ;
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 ) ;
inode = iget ( dir - > i_sb , ino ) ;
if ( ! inode ) {
unlock_kernel ( ) ;
return ERR_PTR ( - EACCES ) ;
}
}
unlock_kernel ( ) ;
d_add ( dentry , inode ) ;
return NULL ;
}
static int bfs_link ( struct dentry * old , struct inode * dir , struct dentry * new )
{
struct inode * inode = old - > d_inode ;
int err ;
lock_kernel ( ) ;
err = bfs_add_entry ( dir , new - > d_name . name , new - > d_name . len , inode - > i_ino ) ;
if ( err ) {
unlock_kernel ( ) ;
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 ) ;
atomic_inc ( & inode - > i_count ) ;
d_instantiate ( new , inode ) ;
unlock_kernel ( ) ;
return 0 ;
}
static int bfs_unlink ( struct inode * dir , struct dentry * dentry )
{
int error = - ENOENT ;
struct inode * inode ;
struct buffer_head * bh ;
struct bfs_dirent * de ;
inode = dentry - > d_inode ;
lock_kernel ( ) ;
bh = bfs_find_entry ( dir , dentry - > d_name . name , dentry - > d_name . len , & de ) ;
2005-09-10 00:02:04 +04: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 ) {
printf ( " unlinking non-existent file %s:%lu (nlink=%d) \n " , inode - > i_sb - > s_id ,
inode - > i_ino , inode - > i_nlink ) ;
inode - > i_nlink = 1 ;
}
de - > ino = 0 ;
mark_buffer_dirty ( bh ) ;
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 ) ;
unlock_kernel ( ) ;
return error ;
}
static int bfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct inode * old_inode , * new_inode ;
struct buffer_head * old_bh , * new_bh ;
struct bfs_dirent * old_de , * new_de ;
int error = - ENOENT ;
old_bh = new_bh = NULL ;
old_inode = old_dentry - > d_inode ;
if ( S_ISDIR ( old_inode - > i_mode ) )
return - EINVAL ;
lock_kernel ( ) ;
old_bh = bfs_find_entry ( old_dir ,
old_dentry - > d_name . name ,
old_dentry - > d_name . len , & old_de ) ;
2005-09-10 00:02:04 +04: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 ,
new_dentry - > d_name . len , old_inode - > i_ino ) ;
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
}
mark_buffer_dirty ( old_bh ) ;
error = 0 ;
end_rename :
unlock_kernel ( ) ;
brelse ( old_bh ) ;
brelse ( new_bh ) ;
return error ;
}
struct inode_operations bfs_dir_inops = {
. create = bfs_create ,
. lookup = bfs_lookup ,
. link = bfs_link ,
. unlink = bfs_unlink ,
. rename = bfs_rename ,
} ;
2005-09-10 00:02:04 +04: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
{
struct buffer_head * bh ;
struct bfs_dirent * de ;
int block , sblock , eblock , off , eoff ;
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 ;
eoff = dir - > i_size % BFS_BSIZE ;
for ( block = sblock ; block < = eblock ; block + + ) {
bh = sb_bread ( dir - > i_sb , block ) ;
if ( ! bh )
return - ENOSPC ;
for ( off = 0 ; off < BFS_BSIZE ; off + = BFS_DIRENT_SIZE ) {
de = ( struct bfs_dirent * ) ( bh - > b_data + off ) ;
if ( block = = eblock & & off > = eoff ) {
/* Do not read/interpret the garbage in the end of eblock. */
de - > ino = 0 ;
}
if ( ! de - > ino ) {
if ( ( block - sblock ) * BFS_BSIZE + off > = dir - > i_size ) {
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 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < BFS_NAMELEN ; i + + )
de - > name [ i ] = ( i < namelen ) ? name [ i ] : 0 ;
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
return 0 ;
}
}
brelse ( bh ) ;
}
return - ENOSPC ;
}
2005-09-10 00:02:04 +04:00
static inline int bfs_namecmp ( int len , const unsigned char * name , const char * buffer )
2005-04-17 02:20:36 +04:00
{
if ( len < BFS_NAMELEN & & buffer [ len ] )
return 0 ;
return ! memcmp ( name , buffer , len ) ;
}
static struct buffer_head * bfs_find_entry ( struct inode * dir ,
2005-09-10 00:02:04 +04:00
const unsigned char * name , int namelen , struct bfs_dirent * * res_dir )
2005-04-17 02:20:36 +04:00
{
unsigned long block , offset ;
struct buffer_head * bh ;
struct bfs_dirent * de ;
* res_dir = NULL ;
if ( namelen > BFS_NAMELEN )
return NULL ;
bh = NULL ;
block = offset = 0 ;
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 ;
2005-09-10 00:02:04 +04: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 ;
}