2005-04-17 02:20:36 +04:00
/*
* linux / fs / ufs / namei . c
*
2006-06-25 16:47:22 +04:00
* Migration to usage of " page cache " on May 2006 by
* Evgeniy Dushistov < dushistov @ mail . ru > based on ext2 code base .
*
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1998
* Daniel Pirkl < daniel . pirkl @ email . cz >
* Charles University , Faculty of Mathematics and Physics
*
* from
*
* 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/time.h>
# include <linux/fs.h>
# include <linux/ufs_fs.h>
# include <linux/smp_lock.h>
# include "swab.h" /* will go away - see comment in mknod() */
# include "util.h"
static inline int ufs_add_nondir ( struct dentry * dentry , struct inode * inode )
{
int err = ufs_add_link ( dentry , inode ) ;
if ( ! err ) {
d_instantiate ( dentry , inode ) ;
return 0 ;
}
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
return err ;
}
static struct dentry * ufs_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
struct inode * inode = NULL ;
ino_t ino ;
if ( dentry - > d_name . len > UFS_MAXNAMLEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
lock_kernel ( ) ;
ino = ufs_inode_by_name ( dir , dentry ) ;
if ( ino ) {
inode = iget ( dir - > i_sb , ino ) ;
if ( ! inode ) {
unlock_kernel ( ) ;
return ERR_PTR ( - EACCES ) ;
}
}
unlock_kernel ( ) ;
d_add ( dentry , inode ) ;
return NULL ;
}
/*
* 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 ( ) .
*/
static int ufs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
2006-06-25 16:47:24 +04:00
struct inode * inode ;
int err ;
UFSD ( " BEGIN \n " ) ;
inode = ufs_new_inode ( dir , mode ) ;
err = PTR_ERR ( inode ) ;
2005-04-17 02:20:36 +04:00
if ( ! IS_ERR ( inode ) ) {
inode - > i_op = & ufs_file_inode_operations ;
inode - > i_fop = & ufs_file_operations ;
inode - > i_mapping - > a_ops = & ufs_aops ;
mark_inode_dirty ( inode ) ;
lock_kernel ( ) ;
err = ufs_add_nondir ( dentry , inode ) ;
unlock_kernel ( ) ;
}
2006-06-25 16:47:24 +04:00
UFSD ( " END: err=%d \n " , err ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int ufs_mknod ( struct inode * dir , struct dentry * dentry , int mode , dev_t rdev )
{
struct inode * inode ;
int err ;
if ( ! old_valid_dev ( rdev ) )
return - EINVAL ;
inode = ufs_new_inode ( dir , mode ) ;
err = PTR_ERR ( inode ) ;
if ( ! IS_ERR ( inode ) ) {
init_special_inode ( inode , mode , rdev ) ;
/* NOTE: that'll go when we get wide dev_t */
ufs_set_inode_dev ( inode - > i_sb , UFS_I ( inode ) , rdev ) ;
mark_inode_dirty ( inode ) ;
lock_kernel ( ) ;
err = ufs_add_nondir ( dentry , inode ) ;
unlock_kernel ( ) ;
}
return err ;
}
static int ufs_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
struct super_block * sb = dir - > i_sb ;
int err = - ENAMETOOLONG ;
unsigned l = strlen ( symname ) + 1 ;
struct inode * inode ;
if ( l > sb - > s_blocksize )
2006-07-30 14:03:59 +04:00
goto out_notlocked ;
2005-04-17 02:20:36 +04:00
lock_kernel ( ) ;
inode = ufs_new_inode ( dir , S_IFLNK | S_IRWXUGO ) ;
err = PTR_ERR ( inode ) ;
if ( IS_ERR ( inode ) )
goto out ;
if ( l > UFS_SB ( sb ) - > s_uspi - > s_maxsymlinklen ) {
/* slow symlink */
inode - > i_op = & page_symlink_inode_operations ;
inode - > i_mapping - > a_ops = & ufs_aops ;
err = page_symlink ( inode , symname , l ) ;
if ( err )
goto out_fail ;
} else {
/* fast symlink */
inode - > i_op = & ufs_fast_symlink_inode_operations ;
memcpy ( ( char * ) & UFS_I ( inode ) - > i_u1 . i_data , symname , l ) ;
inode - > i_size = l - 1 ;
}
mark_inode_dirty ( inode ) ;
err = ufs_add_nondir ( dentry , inode ) ;
out :
unlock_kernel ( ) ;
2006-07-30 14:03:59 +04:00
out_notlocked :
2005-04-17 02:20:36 +04:00
return err ;
out_fail :
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
goto out ;
}
static int ufs_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
struct inode * inode = old_dentry - > d_inode ;
int error ;
lock_kernel ( ) ;
if ( inode - > i_nlink > = UFS_LINK_MAX ) {
unlock_kernel ( ) ;
return - EMLINK ;
}
inode - > i_ctime = CURRENT_TIME_SEC ;
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & inode - > i_count ) ;
error = ufs_add_nondir ( dentry , inode ) ;
unlock_kernel ( ) ;
return error ;
}
static int ufs_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
struct inode * inode ;
int err = - EMLINK ;
if ( dir - > i_nlink > = UFS_LINK_MAX )
goto out ;
lock_kernel ( ) ;
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( dir ) ;
2005-04-17 02:20:36 +04:00
inode = ufs_new_inode ( dir , S_IFDIR | mode ) ;
err = PTR_ERR ( inode ) ;
if ( IS_ERR ( inode ) )
goto out_dir ;
inode - > i_op = & ufs_dir_inode_operations ;
inode - > i_fop = & ufs_dir_operations ;
2006-06-25 16:47:21 +04:00
inode - > i_mapping - > a_ops = & ufs_aops ;
2005-04-17 02:20:36 +04:00
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
err = ufs_make_empty ( inode , dir ) ;
if ( err )
goto out_fail ;
err = ufs_add_link ( dentry , inode ) ;
if ( err )
goto out_fail ;
unlock_kernel ( ) ;
d_instantiate ( dentry , inode ) ;
out :
return err ;
out_fail :
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( inode ) ;
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
out_dir :
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( dir ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
goto out ;
}
2006-06-25 16:47:22 +04:00
static int ufs_unlink ( struct inode * dir , struct dentry * dentry )
2005-04-17 02:20:36 +04:00
{
struct inode * inode = dentry - > d_inode ;
2006-06-25 16:47:22 +04:00
struct ufs_dir_entry * de ;
struct page * page ;
2005-04-17 02:20:36 +04:00
int err = - ENOENT ;
2006-06-25 16:47:22 +04:00
de = ufs_find_entry ( dir , dentry , & page ) ;
2005-04-17 02:20:36 +04:00
if ( ! de )
goto out ;
2006-06-25 16:47:22 +04:00
err = ufs_delete_entry ( dir , de , page ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out ;
inode - > i_ctime = dir - > i_ctime ;
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( inode ) ;
2005-04-17 02:20:36 +04:00
err = 0 ;
out :
return err ;
}
static int ufs_rmdir ( struct inode * dir , struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
int err = - ENOTEMPTY ;
lock_kernel ( ) ;
if ( ufs_empty_dir ( inode ) ) {
err = ufs_unlink ( dir , dentry ) ;
if ( ! err ) {
inode - > i_size = 0 ;
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( inode ) ;
inode_dec_link_count ( dir ) ;
2005-04-17 02:20:36 +04:00
}
}
unlock_kernel ( ) ;
return err ;
}
2006-06-25 16:47:22 +04:00
static int ufs_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
{
struct inode * old_inode = old_dentry - > d_inode ;
struct inode * new_inode = new_dentry - > d_inode ;
2006-06-25 16:47:22 +04:00
struct page * dir_page = NULL ;
struct ufs_dir_entry * dir_de = NULL ;
struct page * old_page ;
2005-04-17 02:20:36 +04:00
struct ufs_dir_entry * old_de ;
int err = - ENOENT ;
2006-06-25 16:47:22 +04:00
old_de = ufs_find_entry ( old_dir , old_dentry , & old_page ) ;
2005-04-17 02:20:36 +04:00
if ( ! old_de )
goto out ;
if ( S_ISDIR ( old_inode - > i_mode ) ) {
err = - EIO ;
2006-06-25 16:47:22 +04:00
dir_de = ufs_dotdot ( old_inode , & dir_page ) ;
2005-04-17 02:20:36 +04:00
if ( ! dir_de )
goto out_old ;
}
if ( new_inode ) {
2006-06-25 16:47:22 +04:00
struct page * new_page ;
2005-04-17 02:20:36 +04:00
struct ufs_dir_entry * new_de ;
err = - ENOTEMPTY ;
2006-06-25 16:47:22 +04:00
if ( dir_de & & ! ufs_empty_dir ( new_inode ) )
2005-04-17 02:20:36 +04:00
goto out_dir ;
2006-06-25 16:47:22 +04:00
2005-04-17 02:20:36 +04:00
err = - ENOENT ;
2006-06-25 16:47:22 +04:00
new_de = ufs_find_entry ( new_dir , new_dentry , & new_page ) ;
2005-04-17 02:20:36 +04:00
if ( ! new_de )
goto out_dir ;
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( old_inode ) ;
2006-06-25 16:47:22 +04:00
ufs_set_link ( new_dir , new_de , new_page , old_inode ) ;
2005-04-17 02:20:36 +04:00
new_inode - > i_ctime = CURRENT_TIME_SEC ;
if ( dir_de )
2006-10-01 10:29:03 +04:00
drop_nlink ( new_inode ) ;
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( new_inode ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( dir_de ) {
err = - EMLINK ;
if ( new_dir - > i_nlink > = UFS_LINK_MAX )
goto out_dir ;
}
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( old_inode ) ;
2005-04-17 02:20:36 +04:00
err = ufs_add_link ( new_dentry , old_inode ) ;
if ( err ) {
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( old_inode ) ;
2005-04-17 02:20:36 +04:00
goto out_dir ;
}
if ( dir_de )
2006-03-23 14:00:53 +03:00
inode_inc_link_count ( new_dir ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-25 16:47:22 +04:00
/*
* Like most other Unix systems , set the ctime for inodes on a
* rename .
* inode_dec_link_count ( ) will mark the inode dirty .
*/
old_inode - > i_ctime = CURRENT_TIME_SEC ;
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:22 +04:00
ufs_delete_entry ( old_dir , old_de , old_page ) ;
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( old_inode ) ;
2005-04-17 02:20:36 +04:00
if ( dir_de ) {
2006-06-25 16:47:22 +04:00
ufs_set_link ( old_inode , dir_de , dir_page , new_dir ) ;
2006-03-23 14:00:53 +03:00
inode_dec_link_count ( old_dir ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
2006-06-25 16:47:22 +04:00
2005-04-17 02:20:36 +04:00
out_dir :
2006-06-25 16:47:22 +04:00
if ( dir_de ) {
kunmap ( dir_page ) ;
page_cache_release ( dir_page ) ;
}
2005-04-17 02:20:36 +04:00
out_old :
2006-06-25 16:47:22 +04:00
kunmap ( old_page ) ;
page_cache_release ( old_page ) ;
2005-04-17 02:20:36 +04:00
out :
return err ;
}
struct inode_operations ufs_dir_inode_operations = {
. create = ufs_create ,
. lookup = ufs_lookup ,
. link = ufs_link ,
. unlink = ufs_unlink ,
. symlink = ufs_symlink ,
. mkdir = ufs_mkdir ,
. rmdir = ufs_rmdir ,
. mknod = ufs_mknod ,
. rename = ufs_rename ,
} ;