2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) International Business Machines Corp . , 2000 - 2004
* Portions Copyright ( C ) Christoph Hellwig , 2001 - 2002
*
* 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
2006-10-02 18:55:27 +04:00
* the Free Software Foundation ; either version 2 of the License , or
2005-04-17 02:20:36 +04:00
* ( at your option ) any later version .
2006-10-02 18:55:27 +04:00
*
2005-04-17 02:20:36 +04:00
* 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
2006-10-02 18:55:27 +04:00
* along with this program ; if not , write to the Free Software
2005-04-17 02:20:36 +04:00
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/fs.h>
2011-01-07 09:49:25 +03:00
# include <linux/namei.h>
2005-04-17 02:20:36 +04:00
# include <linux/ctype.h>
# include <linux/quotaops.h>
2007-10-22 03:42:09 +04:00
# include <linux/exportfs.h>
2005-04-17 02:20:36 +04:00
# include "jfs_incore.h"
# include "jfs_superblock.h"
# include "jfs_inode.h"
# include "jfs_dinode.h"
# include "jfs_dmap.h"
# include "jfs_unicode.h"
# include "jfs_metapage.h"
# include "jfs_xattr.h"
# include "jfs_acl.h"
# include "jfs_debug.h"
/*
* forward references
*/
2009-02-20 09:00:49 +03:00
const struct dentry_operations jfs_ci_dentry_operations ;
2005-04-17 02:20:36 +04:00
static s64 commitZeroLink ( tid_t , struct inode * ) ;
2005-09-01 18:02:43 +04:00
/*
* NAME : free_ea_wmap ( inode )
*
2006-10-02 18:55:27 +04:00
* FUNCTION : free uncommitted extended attributes from working map
2005-09-01 18:02:43 +04:00
*
*/
static inline void free_ea_wmap ( struct inode * inode )
{
dxd_t * ea = & JFS_IP ( inode ) - > ea ;
if ( ea - > flag & DXD_EXTENT ) {
/* free EA pages from cache */
invalidate_dxd_metapages ( inode , * ea ) ;
dbFree ( inode , addressDXD ( ea ) , lengthDXD ( ea ) ) ;
}
ea - > flag = 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* NAME : jfs_create ( dip , dentry , mode )
*
* FUNCTION : create a regular file in the parent directory < dip >
* with name = < from dentry > and mode = < mode >
*
2006-10-02 18:55:27 +04:00
* PARAMETER : dip - parent directory vnode
2005-04-17 02:20:36 +04:00
* dentry - dentry of new file
* mode - create mode ( rwxrwxrwx ) .
* nd - nd struct
*
* RETURN : Errors from subroutines
*
*/
2011-07-26 09:42:34 +04:00
static int jfs_create ( struct inode * dip , struct dentry * dentry , umode_t mode ,
2012-06-11 02:05:36 +04:00
bool excl )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
tid_t tid ; /* transaction id */
struct inode * ip = NULL ; /* child directory inode */
ino_t ino ;
struct component_name dname ; /* child directory name */
struct btstack btstack ;
struct inode * iplist [ 2 ] ;
struct tblock * tblk ;
jfs_info ( " jfs_create: dip:0x%p name:%s " , dip , dentry - > d_name . name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( dip ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
/*
* search parent directory for entry / freespace
* ( dtSearch ( ) returns parent directory page pinned )
*/
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out1 ;
/*
* Either iAlloc ( ) or txBegin ( ) may block . Deadlock can occur if we
* block there while holding dtree page , so we allocate the inode &
* begin the transaction before we search the directory .
*/
ip = ialloc ( dip , mode ) ;
2006-09-14 18:22:38 +04:00
if ( IS_ERR ( ip ) ) {
rc = PTR_ERR ( ip ) ;
2005-04-17 02:20:36 +04:00
goto out2 ;
}
tid = txBegin ( dip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dip ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
2005-09-01 18:02:43 +04:00
rc = jfs_init_acl ( tid , ip , dip ) ;
if ( rc )
goto out3 ;
2011-02-01 19:05:39 +03:00
rc = jfs_init_security ( tid , ip , dip , & dentry - > d_name ) ;
2005-09-01 18:05:39 +04:00
if ( rc ) {
txAbort ( tid , 0 ) ;
goto out3 ;
}
2005-04-17 02:20:36 +04:00
if ( ( rc = dtSearch ( dip , & dname , & ino , & btstack , JFS_CREATE ) ) ) {
jfs_err ( " jfs_create: dtSearch returned %d " , rc ) ;
2005-09-01 18:02:43 +04:00
txAbort ( tid , 0 ) ;
2005-04-17 02:20:36 +04:00
goto out3 ;
}
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_CREATE ;
tblk - > ino = ip - > i_ino ;
tblk - > u . ixpxd = JFS_IP ( ip ) - > ixpxd ;
iplist [ 0 ] = dip ;
iplist [ 1 ] = ip ;
/*
* initialize the child XAD tree root in - line in inode
*/
xtInitRoot ( tid , ip ) ;
/*
* create entry in parent directory for child directory
* ( dtInsert ( ) releases parent directory page )
*/
ino = ip - > i_ino ;
if ( ( rc = dtInsert ( tid , dip , & dname , & ino , & btstack ) ) ) {
if ( rc = = - EIO ) {
jfs_err ( " jfs_create: dtInsert returned -EIO " ) ;
txAbort ( tid , 1 ) ; /* Marks Filesystem dirty */
} else
txAbort ( tid , 0 ) ; /* Filesystem full */
goto out3 ;
}
ip - > i_op = & jfs_file_inode_operations ;
ip - > i_fop = & jfs_file_operations ;
ip - > i_mapping - > a_ops = & jfs_aops ;
mark_inode_dirty ( ip ) ;
dip - > i_ctime = dip - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( dip ) ;
rc = txCommit ( tid , 2 , & iplist [ 0 ] , 0 ) ;
out3 :
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2005-09-01 18:02:43 +04:00
free_ea_wmap ( ip ) ;
2011-10-28 16:13:28 +04:00
clear_nlink ( ip ) ;
2008-12-31 07:08:37 +03:00
unlock_new_inode ( ip ) ;
2005-04-17 02:20:36 +04:00
iput ( ip ) ;
2008-12-31 07:08:37 +03:00
} else {
unlock_new_inode ( ip ) ;
2012-07-19 09:18:15 +04:00
d_instantiate ( dentry , ip ) ;
2008-12-31 07:08:37 +03:00
}
2005-04-17 02:20:36 +04:00
out2 :
free_UCSname ( & dname ) ;
out1 :
jfs_info ( " jfs_create: rc:%d " , rc ) ;
return rc ;
}
/*
* NAME : jfs_mkdir ( dip , dentry , mode )
*
* FUNCTION : create a child directory in the parent directory < dip >
* with name = < from dentry > and mode = < mode >
*
2006-10-02 18:55:27 +04:00
* PARAMETER : dip - parent directory vnode
2005-04-17 02:20:36 +04:00
* dentry - dentry of child directory
* mode - create mode ( rwxrwxrwx ) .
*
* RETURN : Errors from subroutines
*
* note :
* EACCESS : user needs search + write permission on the parent directory
*/
2011-07-26 09:41:39 +04:00
static int jfs_mkdir ( struct inode * dip , struct dentry * dentry , umode_t mode )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
tid_t tid ; /* transaction id */
struct inode * ip = NULL ; /* child directory inode */
ino_t ino ;
struct component_name dname ; /* child directory name */
struct btstack btstack ;
struct inode * iplist [ 2 ] ;
struct tblock * tblk ;
jfs_info ( " jfs_mkdir: dip:0x%p name:%s " , dip , dentry - > d_name . name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( dip ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
/*
* search parent directory for entry / freespace
* ( dtSearch ( ) returns parent directory page pinned )
*/
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out1 ;
/*
* Either iAlloc ( ) or txBegin ( ) may block . Deadlock can occur if we
* block there while holding dtree page , so we allocate the inode &
* begin the transaction before we search the directory .
*/
ip = ialloc ( dip , S_IFDIR | mode ) ;
2006-09-14 18:22:38 +04:00
if ( IS_ERR ( ip ) ) {
rc = PTR_ERR ( ip ) ;
2005-04-17 02:20:36 +04:00
goto out2 ;
}
tid = txBegin ( dip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dip ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
2005-09-01 18:02:43 +04:00
rc = jfs_init_acl ( tid , ip , dip ) ;
if ( rc )
goto out3 ;
2011-02-01 19:05:39 +03:00
rc = jfs_init_security ( tid , ip , dip , & dentry - > d_name ) ;
2005-09-01 18:05:39 +04:00
if ( rc ) {
txAbort ( tid , 0 ) ;
goto out3 ;
}
2005-04-17 02:20:36 +04:00
if ( ( rc = dtSearch ( dip , & dname , & ino , & btstack , JFS_CREATE ) ) ) {
jfs_err ( " jfs_mkdir: dtSearch returned %d " , rc ) ;
2005-09-01 18:02:43 +04:00
txAbort ( tid , 0 ) ;
2005-04-17 02:20:36 +04:00
goto out3 ;
}
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_CREATE ;
tblk - > ino = ip - > i_ino ;
tblk - > u . ixpxd = JFS_IP ( ip ) - > ixpxd ;
iplist [ 0 ] = dip ;
iplist [ 1 ] = ip ;
/*
* initialize the child directory in - line in inode
*/
dtInitRoot ( tid , ip , dip - > i_ino ) ;
/*
* create entry in parent directory for child directory
* ( dtInsert ( ) releases parent directory page )
*/
ino = ip - > i_ino ;
if ( ( rc = dtInsert ( tid , dip , & dname , & ino , & btstack ) ) ) {
if ( rc = = - EIO ) {
jfs_err ( " jfs_mkdir: dtInsert returned -EIO " ) ;
txAbort ( tid , 1 ) ; /* Marks Filesystem dirty */
} else
txAbort ( tid , 0 ) ; /* Filesystem full */
goto out3 ;
}
2011-10-28 16:13:29 +04:00
set_nlink ( ip , 2 ) ; /* for '.' */
2005-04-17 02:20:36 +04:00
ip - > i_op = & jfs_dir_inode_operations ;
ip - > i_fop = & jfs_dir_operations ;
mark_inode_dirty ( ip ) ;
/* update parent directory inode */
2006-10-01 10:29:04 +04:00
inc_nlink ( dip ) ; /* for '..' from child directory */
2005-04-17 02:20:36 +04:00
dip - > i_ctime = dip - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( dip ) ;
rc = txCommit ( tid , 2 , & iplist [ 0 ] , 0 ) ;
out3 :
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2005-09-01 18:02:43 +04:00
free_ea_wmap ( ip ) ;
2011-10-28 16:13:28 +04:00
clear_nlink ( ip ) ;
2008-12-31 07:08:37 +03:00
unlock_new_inode ( ip ) ;
2005-04-17 02:20:36 +04:00
iput ( ip ) ;
2008-12-31 07:08:37 +03:00
} else {
unlock_new_inode ( ip ) ;
2012-07-19 09:18:15 +04:00
d_instantiate ( dentry , ip ) ;
2008-12-31 07:08:37 +03:00
}
2005-04-17 02:20:36 +04:00
out2 :
free_UCSname ( & dname ) ;
out1 :
jfs_info ( " jfs_mkdir: rc:%d " , rc ) ;
return rc ;
}
/*
* NAME : jfs_rmdir ( dip , dentry )
*
* FUNCTION : remove a link to child directory
*
2006-10-02 18:55:27 +04:00
* PARAMETER : dip - parent inode
2005-04-17 02:20:36 +04:00
* dentry - child directory dentry
*
* RETURN : - EINVAL - if name is . or . .
2007-06-07 00:28:35 +04:00
* - EINVAL - if . or . . exist but are invalid .
2005-04-17 02:20:36 +04:00
* errors from subroutines
*
* note :
2006-10-02 18:55:27 +04:00
* if other threads have the directory open when the last link
* is removed , the " . " and " .. " entries , if present , are removed before
* rmdir ( ) returns and no new entries may be created in the directory ,
* but the directory is not removed until the last reference to
2005-04-17 02:20:36 +04:00
* the directory is released ( cf . unlink ( ) of regular file ) .
*/
static int jfs_rmdir ( struct inode * dip , struct dentry * dentry )
{
int rc ;
tid_t tid ; /* transaction id */
struct inode * ip = dentry - > d_inode ;
ino_t ino ;
struct component_name dname ;
struct inode * iplist [ 2 ] ;
struct tblock * tblk ;
jfs_info ( " jfs_rmdir: dip:0x%p name:%s " , dip , dentry - > d_name . name ) ;
/* Init inode for quota operations. */
2010-03-03 17:05:07 +03:00
dquot_initialize ( dip ) ;
dquot_initialize ( ip ) ;
2005-04-17 02:20:36 +04:00
/* directory must be empty to be removed */
if ( ! dtEmpty ( ip ) ) {
rc = - ENOTEMPTY ;
goto out ;
}
if ( ( rc = get_UCSname ( & dname , dentry ) ) ) {
goto out ;
}
tid = txBegin ( dip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dip ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
iplist [ 0 ] = dip ;
iplist [ 1 ] = ip ;
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_DELETE ;
tblk - > u . ip = ip ;
/*
* delete the entry of target directory from parent directory
*/
ino = ip - > i_ino ;
if ( ( rc = dtDelete ( tid , dip , & dname , & ino , JFS_REMOVE ) ) ) {
jfs_err ( " jfs_rmdir: dtDelete returned %d " , rc ) ;
if ( rc = = - EIO )
txAbort ( tid , 1 ) ;
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
goto out2 ;
}
/* update parent directory's link count corresponding
* to " .. " entry of the target directory deleted
*/
dip - > i_ctime = dip - > i_mtime = CURRENT_TIME ;
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( dip ) ;
2005-04-17 02:20:36 +04:00
/*
* OS / 2 could have created EA and / or ACL
*/
/* free EA from both persistent and working map */
if ( JFS_IP ( ip ) - > ea . flag & DXD_EXTENT ) {
/* free EA pages */
txEA ( tid , ip , & JFS_IP ( ip ) - > ea , NULL ) ;
}
JFS_IP ( ip ) - > ea . flag = 0 ;
/* free ACL from both persistent and working map */
if ( JFS_IP ( ip ) - > acl . flag & DXD_EXTENT ) {
/* free ACL pages */
txEA ( tid , ip , & JFS_IP ( ip ) - > acl , NULL ) ;
}
JFS_IP ( ip ) - > acl . flag = 0 ;
/* mark the target directory as deleted */
2006-10-01 10:29:06 +04:00
clear_nlink ( ip ) ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( ip ) ;
rc = txCommit ( tid , 2 , & iplist [ 0 ] , 0 ) ;
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
/*
* Truncating the directory index table is not guaranteed . It
* may need to be done iteratively
*/
if ( test_cflag ( COMMIT_Stale , dip ) ) {
if ( dip - > i_size > 1 )
jfs_truncate_nolock ( dip , 0 ) ;
clear_cflag ( COMMIT_Stale , dip ) ;
}
out2 :
free_UCSname ( & dname ) ;
out :
jfs_info ( " jfs_rmdir: rc:%d " , rc ) ;
return rc ;
}
/*
* NAME : jfs_unlink ( dip , dentry )
*
2006-10-02 18:55:27 +04:00
* FUNCTION : remove a link to object < vp > named by < name >
2005-04-17 02:20:36 +04:00
* from parent directory < dvp >
*
2006-10-02 18:55:27 +04:00
* PARAMETER : dip - inode of parent directory
* dentry - dentry of object to be removed
2005-04-17 02:20:36 +04:00
*
* RETURN : errors from subroutines
*
* note :
* temporary file : if one or more processes have the file open
* when the last link is removed , the link will be removed before
* unlink ( ) returns , but the removal of the file contents will be
* postponed until all references to the files are closed .
*
* JFS does NOT support unlink ( ) on directories .
*
*/
static int jfs_unlink ( struct inode * dip , struct dentry * dentry )
{
int rc ;
tid_t tid ; /* transaction id */
struct inode * ip = dentry - > d_inode ;
ino_t ino ;
struct component_name dname ; /* object name */
struct inode * iplist [ 2 ] ;
struct tblock * tblk ;
s64 new_size = 0 ;
int commit_flag ;
jfs_info ( " jfs_unlink: dip:0x%p name:%s " , dip , dentry - > d_name . name ) ;
/* Init inode for quota operations. */
2010-03-03 17:05:07 +03:00
dquot_initialize ( dip ) ;
dquot_initialize ( ip ) ;
2005-04-17 02:20:36 +04:00
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out ;
2007-01-09 23:14:48 +03:00
IWRITE_LOCK ( ip , RDWRLOCK_NORMAL ) ;
2005-04-17 02:20:36 +04:00
tid = txBegin ( dip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dip ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
iplist [ 0 ] = dip ;
iplist [ 1 ] = ip ;
/*
* delete the entry of target file from parent directory
*/
ino = ip - > i_ino ;
if ( ( rc = dtDelete ( tid , dip , & dname , & ino , JFS_REMOVE ) ) ) {
jfs_err ( " jfs_unlink: dtDelete returned %d " , rc ) ;
if ( rc = = - EIO )
txAbort ( tid , 1 ) ; /* Marks FS Dirty */
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
IWRITE_UNLOCK ( ip ) ;
goto out1 ;
}
ASSERT ( ip - > i_nlink ) ;
ip - > i_ctime = dip - > i_ctime = dip - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( dip ) ;
/* update target's inode */
2006-10-01 10:29:03 +04:00
inode_dec_link_count ( ip ) ;
2005-04-17 02:20:36 +04:00
/*
2007-06-07 00:28:35 +04:00
* commit zero link count object
2005-04-17 02:20:36 +04:00
*/
if ( ip - > i_nlink = = 0 ) {
assert ( ! test_cflag ( COMMIT_Nolink , ip ) ) ;
/* free block resources */
if ( ( new_size = commitZeroLink ( tid , ip ) ) < 0 ) {
txAbort ( tid , 1 ) ; /* Marks FS Dirty */
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
IWRITE_UNLOCK ( ip ) ;
rc = new_size ;
goto out1 ;
}
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_DELETE ;
tblk - > u . ip = ip ;
}
/*
* Incomplete truncate of file data can
* result in timing problems unless we synchronously commit the
* transaction .
*/
if ( new_size )
commit_flag = COMMIT_SYNC ;
else
commit_flag = 0 ;
/*
* If xtTruncate was incomplete , commit synchronously to avoid
* timing complications
*/
rc = txCommit ( tid , 2 , & iplist [ 0 ] , commit_flag ) ;
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
while ( new_size & & ( rc = = 0 ) ) {
tid = txBegin ( dip - > i_sb , 0 ) ;
2006-01-25 00:22:50 +03:00
mutex_lock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
new_size = xtTruncate_pmap ( tid , ip , new_size ) ;
if ( new_size < 0 ) {
txAbort ( tid , 1 ) ; /* Marks FS Dirty */
rc = new_size ;
} else
rc = txCommit ( tid , 2 , & iplist [ 0 ] , COMMIT_SYNC ) ;
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( ip - > i_nlink = = 0 )
set_cflag ( COMMIT_Nolink , ip ) ;
IWRITE_UNLOCK ( ip ) ;
/*
* Truncating the directory index table is not guaranteed . It
* may need to be done iteratively
*/
if ( test_cflag ( COMMIT_Stale , dip ) ) {
if ( dip - > i_size > 1 )
jfs_truncate_nolock ( dip , 0 ) ;
clear_cflag ( COMMIT_Stale , dip ) ;
}
out1 :
free_UCSname ( & dname ) ;
out :
jfs_info ( " jfs_unlink: rc:%d " , rc ) ;
return rc ;
}
/*
* NAME : commitZeroLink ( )
*
2007-06-07 00:28:35 +04:00
* FUNCTION : for non - directory , called by jfs_remove ( ) ,
2005-04-17 02:20:36 +04:00
* truncate a regular file , directory or symbolic
2006-10-02 18:55:27 +04:00
* link to zero length . return 0 if type is not
2005-04-17 02:20:36 +04:00
* one of these .
*
* if the file is currently associated with a VM segment
* only permanent disk and inode map resources are freed ,
* and neither the inode nor indirect blocks are modified
* so that the resources can be later freed in the work
* map by ctrunc1 .
* if there is no VM segment on entry , the resources are
* freed in both work and permanent map .
2006-10-02 18:55:27 +04:00
* ( ? for temporary file - memory object is cached even
2005-04-17 02:20:36 +04:00
* after no reference :
* reference count > 0 - )
*
* PARAMETERS : cd - pointer to commit data structure .
* current inode is the one to truncate .
*
* RETURN : Errors from subroutines
*/
static s64 commitZeroLink ( tid_t tid , struct inode * ip )
{
int filetype ;
struct tblock * tblk ;
jfs_info ( " commitZeroLink: tid = %d, ip = 0x%p " , tid , ip ) ;
filetype = ip - > i_mode & S_IFMT ;
switch ( filetype ) {
case S_IFREG :
break ;
case S_IFLNK :
/* fast symbolic link */
if ( ip - > i_size < IDATASIZE ) {
ip - > i_size = 0 ;
return 0 ;
}
break ;
default :
assert ( filetype ! = S_IFDIR ) ;
return 0 ;
}
set_cflag ( COMMIT_Freewmap , ip ) ;
/* mark transaction of block map update type */
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_PMAP ;
/*
* free EA
*/
if ( JFS_IP ( ip ) - > ea . flag & DXD_EXTENT )
/* acquire maplock on EA to be freed from block map */
txEA ( tid , ip , & JFS_IP ( ip ) - > ea , NULL ) ;
/*
* free ACL
*/
if ( JFS_IP ( ip ) - > acl . flag & DXD_EXTENT )
/* acquire maplock on EA to be freed from block map */
txEA ( tid , ip , & JFS_IP ( ip ) - > acl , NULL ) ;
/*
* free xtree / data ( truncate to zero length ) :
2006-10-02 18:55:27 +04:00
* free xtree / data pages from cache if COMMIT_PWMAP ,
2005-04-17 02:20:36 +04:00
* free xtree / data blocks from persistent block map , and
* free xtree / data blocks from working block map if COMMIT_PWMAP ;
*/
if ( ip - > i_size )
return xtTruncate_pmap ( tid , ip , 0 ) ;
return 0 ;
}
/*
2005-05-05 00:29:35 +04:00
* NAME : jfs_free_zero_link ( )
2005-04-17 02:20:36 +04:00
*
2007-06-07 00:28:35 +04:00
* FUNCTION : for non - directory , called by iClose ( ) ,
2006-10-02 18:55:27 +04:00
* free resources of a file from cache and WORKING map
2005-04-17 02:20:36 +04:00
* for a file previously committed with zero link count
* while associated with a pager object ,
*
* PARAMETER : ip - pointer to inode of file .
*/
2005-05-05 00:29:35 +04:00
void jfs_free_zero_link ( struct inode * ip )
2005-04-17 02:20:36 +04:00
{
int type ;
2005-05-05 00:29:35 +04:00
jfs_info ( " jfs_free_zero_link: ip = 0x%p " , ip ) ;
2005-04-17 02:20:36 +04:00
/* return if not reg or symbolic link or if size is
* already ok .
*/
type = ip - > i_mode & S_IFMT ;
switch ( type ) {
case S_IFREG :
break ;
case S_IFLNK :
/* if its contained in inode nothing to do */
if ( ip - > i_size < IDATASIZE )
2005-05-05 00:29:35 +04:00
return ;
2005-04-17 02:20:36 +04:00
break ;
default :
2005-05-05 00:29:35 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
/*
* free EA
*/
if ( JFS_IP ( ip ) - > ea . flag & DXD_EXTENT ) {
s64 xaddr = addressDXD ( & JFS_IP ( ip ) - > ea ) ;
int xlen = lengthDXD ( & JFS_IP ( ip ) - > ea ) ;
struct maplock maplock ; /* maplock for COMMIT_WMAP */
struct pxd_lock * pxdlock ; /* maplock for COMMIT_WMAP */
/* free EA pages from cache */
invalidate_dxd_metapages ( ip , JFS_IP ( ip ) - > ea ) ;
/* free EA extent from working block map */
maplock . index = 1 ;
pxdlock = ( struct pxd_lock * ) & maplock ;
pxdlock - > flag = mlckFREEPXD ;
PXDaddress ( & pxdlock - > pxd , xaddr ) ;
PXDlength ( & pxdlock - > pxd , xlen ) ;
txFreeMap ( ip , pxdlock , NULL , COMMIT_WMAP ) ;
}
/*
* free ACL
*/
if ( JFS_IP ( ip ) - > acl . flag & DXD_EXTENT ) {
s64 xaddr = addressDXD ( & JFS_IP ( ip ) - > acl ) ;
int xlen = lengthDXD ( & JFS_IP ( ip ) - > acl ) ;
struct maplock maplock ; /* maplock for COMMIT_WMAP */
struct pxd_lock * pxdlock ; /* maplock for COMMIT_WMAP */
invalidate_dxd_metapages ( ip , JFS_IP ( ip ) - > acl ) ;
/* free ACL extent from working block map */
maplock . index = 1 ;
pxdlock = ( struct pxd_lock * ) & maplock ;
pxdlock - > flag = mlckFREEPXD ;
PXDaddress ( & pxdlock - > pxd , xaddr ) ;
PXDlength ( & pxdlock - > pxd , xlen ) ;
txFreeMap ( ip , pxdlock , NULL , COMMIT_WMAP ) ;
}
/*
* free xtree / data ( truncate to zero length ) :
* free xtree / data pages from cache , and
* free xtree / data blocks from working block map ;
*/
if ( ip - > i_size )
2005-05-05 00:29:35 +04:00
xtTruncate ( 0 , ip , 0 , COMMIT_WMAP ) ;
2005-04-17 02:20:36 +04:00
}
/*
* NAME : jfs_link ( vp , dvp , name , crp )
*
* FUNCTION : create a link to < vp > by the name = < name >
* in the parent directory < dvp >
*
2006-10-02 18:55:27 +04:00
* PARAMETER : vp - target object
2005-04-17 02:20:36 +04:00
* dvp - parent directory of new link
* name - name of new link to target object
* crp - credential
*
* RETURN : Errors from subroutines
*
* note :
* JFS does NOT support link ( ) on directories ( to prevent circular
* path in the directory hierarchy ) ;
* EPERM : the target object is a directory , and either the caller
* does not have appropriate privileges or the implementation prohibits
* using link ( ) on directories [ XPG4 .2 ] .
*
* JFS does NOT support links between file systems :
* EXDEV : target object and new link are on different file systems and
* implementation does not support links between file systems [ XPG4 .2 ] .
*/
static int jfs_link ( struct dentry * old_dentry ,
struct inode * dir , struct dentry * dentry )
{
int rc ;
tid_t tid ;
struct inode * ip = old_dentry - > d_inode ;
ino_t ino ;
struct component_name dname ;
struct btstack btstack ;
struct inode * iplist [ 2 ] ;
jfs_info ( " jfs_link: %s %s " , old_dentry - > d_name . name ,
dentry - > d_name . name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( dir ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
tid = txBegin ( ip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dir ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
/*
* scan parent directory for entry / freespace
*/
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out ;
if ( ( rc = dtSearch ( dir , & dname , & ino , & btstack , JFS_CREATE ) ) )
goto free_dname ;
/*
* create entry for new link in parent directory
*/
ino = ip - > i_ino ;
if ( ( rc = dtInsert ( tid , dir , & dname , & ino , & btstack ) ) )
goto free_dname ;
/* update object inode */
2006-10-01 10:29:04 +04:00
inc_nlink ( ip ) ; /* for new link */
2005-04-17 02:20:36 +04:00
ip - > i_ctime = CURRENT_TIME ;
2005-11-01 01:53:04 +03:00
dir - > i_ctime = dir - > i_mtime = CURRENT_TIME ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( dir ) ;
2010-10-23 19:11:40 +04:00
ihold ( ip ) ;
2005-04-17 02:20:36 +04:00
iplist [ 0 ] = ip ;
iplist [ 1 ] = dir ;
rc = txCommit ( tid , 2 , & iplist [ 0 ] , 0 ) ;
if ( rc ) {
2011-10-28 16:13:28 +04:00
drop_nlink ( ip ) ; /* never instantiated */
2005-04-17 02:20:36 +04:00
iput ( ip ) ;
} else
d_instantiate ( dentry , ip ) ;
free_dname :
free_UCSname ( & dname ) ;
out :
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dir ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
jfs_info ( " jfs_link: rc:%d " , rc ) ;
return rc ;
}
/*
* NAME : jfs_symlink ( dip , dentry , name )
*
* FUNCTION : creates a symbolic link to < symlink > by name < name >
2007-06-07 00:28:35 +04:00
* in directory < dip >
2005-04-17 02:20:36 +04:00
*
2007-06-07 00:28:35 +04:00
* PARAMETER : dip - parent directory vnode
* dentry - dentry of symbolic link
* name - the path name of the existing object
* that will be the source of the link
2005-04-17 02:20:36 +04:00
*
* RETURN : errors from subroutines
*
* note :
* ENAMETOOLONG : pathname resolution of a symbolic link produced
* an intermediate result whose length exceeds PATH_MAX [ XPG4 .2 ]
*/
static int jfs_symlink ( struct inode * dip , struct dentry * dentry ,
const char * name )
{
int rc ;
tid_t tid ;
ino_t ino = 0 ;
struct component_name dname ;
int ssize ; /* source pathname size */
struct btstack btstack ;
struct inode * ip = dentry - > d_inode ;
unchar * i_fastsymlink ;
s64 xlen = 0 ;
int bmask = 0 , xsize ;
2011-06-20 22:00:27 +04:00
s64 xaddr ;
2005-04-17 02:20:36 +04:00
struct metapage * mp ;
struct super_block * sb ;
struct tblock * tblk ;
struct inode * iplist [ 2 ] ;
jfs_info ( " jfs_symlink: dip:0x%p name:%s " , dip , name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( dip ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
ssize = strlen ( name ) + 1 ;
/*
* search parent directory for entry / freespace
* ( dtSearch ( ) returns parent directory page pinned )
*/
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out1 ;
/*
* allocate on - disk / in - memory inode for symbolic link :
* ( iAlloc ( ) returns new , locked inode )
*/
ip = ialloc ( dip , S_IFLNK | 0777 ) ;
2006-09-14 18:22:38 +04:00
if ( IS_ERR ( ip ) ) {
rc = PTR_ERR ( ip ) ;
2005-04-17 02:20:36 +04:00
goto out2 ;
}
tid = txBegin ( dip - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dip ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
2011-02-01 19:05:39 +03:00
rc = jfs_init_security ( tid , ip , dip , & dentry - > d_name ) ;
2005-09-01 18:05:39 +04:00
if ( rc )
goto out3 ;
2005-04-17 02:20:36 +04:00
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_CREATE ;
tblk - > ino = ip - > i_ino ;
tblk - > u . ixpxd = JFS_IP ( ip ) - > ixpxd ;
/* fix symlink access permission
2006-10-02 18:55:27 +04:00
* ( dir_create ( ) ANDs in the u . u_cmask ,
2005-04-17 02:20:36 +04:00
* but symlinks really need to be 777 access )
*/
ip - > i_mode | = 0777 ;
/*
* write symbolic link target path name
*/
xtInitRoot ( tid , ip ) ;
/*
* write source path name inline in on - disk inode ( fast symbolic link )
*/
if ( ssize < = IDATASIZE ) {
2010-04-16 17:05:50 +04:00
ip - > i_op = & jfs_fast_symlink_inode_operations ;
2005-04-17 02:20:36 +04:00
i_fastsymlink = JFS_IP ( ip ) - > i_inline ;
memcpy ( i_fastsymlink , name , ssize ) ;
ip - > i_size = ssize - 1 ;
/*
* if symlink is > 128 bytes , we don ' t have the space to
* store inline extended attributes
*/
if ( ssize > sizeof ( JFS_IP ( ip ) - > i_inline ) )
JFS_IP ( ip ) - > mode2 & = ~ INLINEEA ;
jfs_info ( " jfs_symlink: fast symlink added ssize:%d name:%s " ,
ssize , name ) ;
}
/*
* write source path name in a single extent
*/
else {
jfs_info ( " jfs_symlink: allocate extent ip:0x%p " , ip ) ;
2010-04-16 17:05:50 +04:00
ip - > i_op = & jfs_symlink_inode_operations ;
2005-04-17 02:20:36 +04:00
ip - > i_mapping - > a_ops = & jfs_aops ;
/*
2006-10-02 18:55:27 +04:00
* even though the data of symlink object ( source
2005-04-17 02:20:36 +04:00
* path name ) is treated as non - journaled user data ,
* it is read / written thru buffer cache for performance .
*/
sb = ip - > i_sb ;
bmask = JFS_SBI ( sb ) - > bsize - 1 ;
xsize = ( ssize + bmask ) & ~ bmask ;
xaddr = 0 ;
xlen = xsize > > JFS_SBI ( sb ) - > l2bsize ;
if ( ( rc = xtInsert ( tid , ip , 0 , 0 , xlen , & xaddr , 0 ) ) ) {
txAbort ( tid , 0 ) ;
goto out3 ;
}
ip - > i_size = ssize - 1 ;
while ( ssize ) {
/* This is kind of silly since PATH_MAX == 4K */
int copy_size = min ( ssize , PSIZE ) ;
mp = get_metapage ( ip , xaddr , PSIZE , 1 ) ;
if ( mp = = NULL ) {
xtTruncate ( tid , ip , 0 , COMMIT_PWMAP ) ;
rc = - EIO ;
txAbort ( tid , 0 ) ;
goto out3 ;
}
memcpy ( mp - > data , name , copy_size ) ;
flush_metapage ( mp ) ;
ssize - = copy_size ;
name + = copy_size ;
xaddr + = JFS_SBI ( sb ) - > nbperpage ;
}
}
/*
* create entry for symbolic link in parent directory
*/
rc = dtSearch ( dip , & dname , & ino , & btstack , JFS_CREATE ) ;
if ( rc = = 0 ) {
ino = ip - > i_ino ;
rc = dtInsert ( tid , dip , & dname , & ino , & btstack ) ;
}
if ( rc ) {
if ( xlen )
xtTruncate ( tid , ip , 0 , COMMIT_PWMAP ) ;
txAbort ( tid , 0 ) ;
/* discard new inode */
goto out3 ;
}
mark_inode_dirty ( ip ) ;
2005-11-01 01:53:04 +03:00
dip - > i_ctime = dip - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( dip ) ;
2005-04-17 02:20:36 +04:00
/*
* commit update of parent directory and link object
*/
iplist [ 0 ] = dip ;
iplist [ 1 ] = ip ;
rc = txCommit ( tid , 2 , & iplist [ 0 ] , 0 ) ;
out3 :
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( dip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2005-09-01 18:02:43 +04:00
free_ea_wmap ( ip ) ;
2011-10-28 16:13:28 +04:00
clear_nlink ( ip ) ;
2008-12-31 07:08:37 +03:00
unlock_new_inode ( ip ) ;
2005-04-17 02:20:36 +04:00
iput ( ip ) ;
2008-12-31 07:08:37 +03:00
} else {
unlock_new_inode ( ip ) ;
2012-07-19 09:18:15 +04:00
d_instantiate ( dentry , ip ) ;
2008-12-31 07:08:37 +03:00
}
2005-04-17 02:20:36 +04:00
out2 :
free_UCSname ( & dname ) ;
out1 :
jfs_info ( " jfs_symlink: rc:%d " , rc ) ;
return rc ;
}
/*
2007-06-07 00:28:35 +04:00
* NAME : jfs_rename
2005-04-17 02:20:36 +04:00
*
2007-06-07 00:28:35 +04:00
* FUNCTION : rename a file or directory
2005-04-17 02:20:36 +04:00
*/
static int jfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct btstack btstack ;
ino_t ino ;
struct component_name new_dname ;
struct inode * new_ip ;
struct component_name old_dname ;
struct inode * old_ip ;
int rc ;
tid_t tid ;
struct tlock * tlck ;
struct dt_lock * dtlck ;
struct lv * lv ;
int ipcount ;
struct inode * iplist [ 4 ] ;
struct tblock * tblk ;
s64 new_size = 0 ;
int commit_flag ;
jfs_info ( " jfs_rename: %s %s " , old_dentry - > d_name . name ,
new_dentry - > d_name . name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( old_dir ) ;
dquot_initialize ( new_dir ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
old_ip = old_dentry - > d_inode ;
new_ip = new_dentry - > d_inode ;
if ( ( rc = get_UCSname ( & old_dname , old_dentry ) ) )
goto out1 ;
if ( ( rc = get_UCSname ( & new_dname , new_dentry ) ) )
goto out2 ;
/*
* Make sure source inode number is what we think it is
*/
rc = dtSearch ( old_dir , & old_dname , & ino , & btstack , JFS_LOOKUP ) ;
if ( rc | | ( ino ! = old_ip - > i_ino ) ) {
rc = - ENOENT ;
goto out3 ;
}
/*
* Make sure dest inode number ( if any ) is what we think it is
*/
rc = dtSearch ( new_dir , & new_dname , & ino , & btstack , JFS_LOOKUP ) ;
2007-11-14 07:16:08 +03:00
if ( ! rc ) {
2007-11-14 07:25:41 +03:00
if ( ( ! new_ip ) | | ( ino ! = new_ip - > i_ino ) ) {
2005-04-17 02:20:36 +04:00
rc = - ESTALE ;
goto out3 ;
}
} else if ( rc ! = - ENOENT )
goto out3 ;
else if ( new_ip ) {
/* no entry exists, but one was expected */
rc = - ESTALE ;
goto out3 ;
}
if ( S_ISDIR ( old_ip - > i_mode ) ) {
if ( new_ip ) {
if ( ! dtEmpty ( new_ip ) ) {
rc = - ENOTEMPTY ;
goto out3 ;
}
}
} else if ( new_ip ) {
2007-01-09 23:14:48 +03:00
IWRITE_LOCK ( new_ip , RDWRLOCK_NORMAL ) ;
2005-04-17 02:20:36 +04:00
/* Init inode for quota operations. */
2010-03-03 17:05:07 +03:00
dquot_initialize ( new_ip ) ;
2005-04-17 02:20:36 +04:00
}
/*
* The real work starts here
*/
tid = txBegin ( new_dir - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
/*
* How do we know the locking is safe from deadlocks ?
* The vfs does the hard part for us . Any time we are taking nested
* commit_mutexes , the vfs already has i_mutex held on the parent .
* Here , the vfs has already taken i_mutex on both old_dir and new_dir .
*/
mutex_lock_nested ( & JFS_IP ( new_dir ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( old_ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
if ( old_dir ! = new_dir )
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( old_dir ) - > commit_mutex ,
COMMIT_MUTEX_SECOND_PARENT ) ;
2005-04-17 02:20:36 +04:00
if ( new_ip ) {
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( new_ip ) - > commit_mutex ,
COMMIT_MUTEX_VICTIM ) ;
2005-04-17 02:20:36 +04:00
/*
* Change existing directory entry to new inode number
*/
ino = new_ip - > i_ino ;
rc = dtModify ( tid , new_dir , & new_dname , & ino ,
old_ip - > i_ino , JFS_RENAME ) ;
if ( rc )
goto out4 ;
2006-10-01 10:29:03 +04:00
drop_nlink ( new_ip ) ;
2005-04-17 02:20:36 +04:00
if ( S_ISDIR ( new_ip - > i_mode ) ) {
2006-10-01 10:29:03 +04:00
drop_nlink ( new_ip ) ;
2005-04-17 02:20:36 +04:00
if ( new_ip - > i_nlink ) {
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( new_ip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( old_dir ! = new_dir )
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( old_dir ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
mutex_unlock ( & JFS_IP ( old_ip ) - > commit_mutex ) ;
mutex_unlock ( & JFS_IP ( new_dir ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ! S_ISDIR ( old_ip - > i_mode ) & & new_ip )
IWRITE_UNLOCK ( new_ip ) ;
jfs_error ( new_ip - > i_sb ,
2013-06-05 03:39:15 +04:00
" new_ip->i_nlink != 0 \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_DELETE ;
tblk - > u . ip = new_ip ;
} else if ( new_ip - > i_nlink = = 0 ) {
assert ( ! test_cflag ( COMMIT_Nolink , new_ip ) ) ;
/* free block resources */
if ( ( new_size = commitZeroLink ( tid , new_ip ) ) < 0 ) {
txAbort ( tid , 1 ) ; /* Marks FS Dirty */
2006-10-02 18:55:27 +04:00
rc = new_size ;
2005-04-17 02:20:36 +04:00
goto out4 ;
}
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_DELETE ;
tblk - > u . ip = new_ip ;
} else {
new_ip - > i_ctime = CURRENT_TIME ;
mark_inode_dirty ( new_ip ) ;
}
} else {
/*
* Add new directory entry
*/
rc = dtSearch ( new_dir , & new_dname , & ino , & btstack ,
JFS_CREATE ) ;
if ( rc ) {
jfs_err ( " jfs_rename didn't expect dtSearch to fail "
" w/rc = %d " , rc ) ;
goto out4 ;
}
ino = old_ip - > i_ino ;
rc = dtInsert ( tid , new_dir , & new_dname , & ino , & btstack ) ;
if ( rc ) {
if ( rc = = - EIO )
jfs_err ( " jfs_rename: dtInsert returned -EIO " ) ;
goto out4 ;
}
if ( S_ISDIR ( old_ip - > i_mode ) )
2006-10-01 10:29:04 +04:00
inc_nlink ( new_dir ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Remove old directory entry
*/
ino = old_ip - > i_ino ;
rc = dtDelete ( tid , old_dir , & old_dname , & ino , JFS_REMOVE ) ;
if ( rc ) {
jfs_err ( " jfs_rename did not expect dtDelete to return rc = %d " ,
rc ) ;
txAbort ( tid , 1 ) ; /* Marks Filesystem dirty */
goto out4 ;
}
if ( S_ISDIR ( old_ip - > i_mode ) ) {
2006-10-01 10:29:03 +04:00
drop_nlink ( old_dir ) ;
2005-04-17 02:20:36 +04:00
if ( old_dir ! = new_dir ) {
/*
* Change inode number of parent for moved directory
*/
JFS_IP ( old_ip ) - > i_dtroot . header . idotdot =
cpu_to_le32 ( new_dir - > i_ino ) ;
/* Linelock header of dtree */
tlck = txLock ( tid , old_ip ,
( struct metapage * ) & JFS_IP ( old_ip ) - > bxflag ,
tlckDTREE | tlckBTROOT | tlckRELINK ) ;
dtlck = ( struct dt_lock * ) & tlck - > lock ;
ASSERT ( dtlck - > index = = 0 ) ;
lv = & dtlck - > lv [ 0 ] ;
lv - > offset = 0 ;
lv - > length = 1 ;
dtlck - > index + + ;
}
}
/*
* Update ctime on changed / moved inodes & mark dirty
*/
old_ip - > i_ctime = CURRENT_TIME ;
mark_inode_dirty ( old_ip ) ;
new_dir - > i_ctime = new_dir - > i_mtime = current_fs_time ( new_dir - > i_sb ) ;
mark_inode_dirty ( new_dir ) ;
/* Build list of inodes modified by this transaction */
ipcount = 0 ;
iplist [ ipcount + + ] = old_ip ;
if ( new_ip )
iplist [ ipcount + + ] = new_ip ;
iplist [ ipcount + + ] = old_dir ;
if ( old_dir ! = new_dir ) {
iplist [ ipcount + + ] = new_dir ;
old_dir - > i_ctime = old_dir - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( old_dir ) ;
}
/*
* Incomplete truncate of file data can
* result in timing problems unless we synchronously commit the
* transaction .
*/
if ( new_size )
commit_flag = COMMIT_SYNC ;
else
commit_flag = 0 ;
rc = txCommit ( tid , ipcount , iplist , commit_flag ) ;
out4 :
txEnd ( tid ) ;
if ( new_ip )
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( new_ip ) - > commit_mutex ) ;
2006-06-05 17:21:03 +04:00
if ( old_dir ! = new_dir )
mutex_unlock ( & JFS_IP ( old_dir ) - > commit_mutex ) ;
mutex_unlock ( & JFS_IP ( old_ip ) - > commit_mutex ) ;
mutex_unlock ( & JFS_IP ( new_dir ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
while ( new_size & & ( rc = = 0 ) ) {
tid = txBegin ( new_ip - > i_sb , 0 ) ;
2006-01-25 00:22:50 +03:00
mutex_lock ( & JFS_IP ( new_ip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
new_size = xtTruncate_pmap ( tid , new_ip , new_size ) ;
if ( new_size < 0 ) {
txAbort ( tid , 1 ) ;
2006-10-02 18:55:27 +04:00
rc = new_size ;
2005-04-17 02:20:36 +04:00
} else
rc = txCommit ( tid , 1 , & new_ip , COMMIT_SYNC ) ;
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( new_ip ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( new_ip & & ( new_ip - > i_nlink = = 0 ) )
set_cflag ( COMMIT_Nolink , new_ip ) ;
out3 :
free_UCSname ( & new_dname ) ;
out2 :
free_UCSname ( & old_dname ) ;
out1 :
if ( new_ip & & ! S_ISDIR ( new_ip - > i_mode ) )
IWRITE_UNLOCK ( new_ip ) ;
/*
* Truncating the directory index table is not guaranteed . It
* may need to be done iteratively
*/
if ( test_cflag ( COMMIT_Stale , old_dir ) ) {
if ( old_dir - > i_size > 1 )
jfs_truncate_nolock ( old_dir , 0 ) ;
clear_cflag ( COMMIT_Stale , old_dir ) ;
}
jfs_info ( " jfs_rename: returning %d " , rc ) ;
return rc ;
}
/*
2007-06-07 00:28:35 +04:00
* NAME : jfs_mknod
2005-04-17 02:20:36 +04:00
*
2007-06-07 00:28:35 +04:00
* FUNCTION : Create a special file ( device )
2005-04-17 02:20:36 +04:00
*/
static int jfs_mknod ( struct inode * dir , struct dentry * dentry ,
2011-07-26 09:52:52 +04:00
umode_t mode , dev_t rdev )
2005-04-17 02:20:36 +04:00
{
struct jfs_inode_info * jfs_ip ;
struct btstack btstack ;
struct component_name dname ;
ino_t ino ;
struct inode * ip ;
struct inode * iplist [ 2 ] ;
int rc ;
tid_t tid ;
struct tblock * tblk ;
if ( ! new_valid_dev ( rdev ) )
return - EINVAL ;
jfs_info ( " jfs_mknod: %s " , dentry - > d_name . name ) ;
2010-03-03 17:05:07 +03:00
dquot_initialize ( dir ) ;
2010-03-03 17:05:06 +03:00
2005-04-17 02:20:36 +04:00
if ( ( rc = get_UCSname ( & dname , dentry ) ) )
goto out ;
ip = ialloc ( dir , mode ) ;
2006-09-14 18:22:38 +04:00
if ( IS_ERR ( ip ) ) {
rc = PTR_ERR ( ip ) ;
2005-04-17 02:20:36 +04:00
goto out1 ;
}
jfs_ip = JFS_IP ( ip ) ;
tid = txBegin ( dir - > i_sb , 0 ) ;
2007-01-09 23:14:48 +03:00
mutex_lock_nested ( & JFS_IP ( dir ) - > commit_mutex , COMMIT_MUTEX_PARENT ) ;
mutex_lock_nested ( & JFS_IP ( ip ) - > commit_mutex , COMMIT_MUTEX_CHILD ) ;
2005-04-17 02:20:36 +04:00
2005-09-01 18:02:43 +04:00
rc = jfs_init_acl ( tid , ip , dir ) ;
if ( rc )
2005-04-17 02:20:36 +04:00
goto out3 ;
2011-02-01 19:05:39 +03:00
rc = jfs_init_security ( tid , ip , dir , & dentry - > d_name ) ;
2005-09-01 18:05:39 +04:00
if ( rc ) {
txAbort ( tid , 0 ) ;
goto out3 ;
}
2005-09-01 18:02:43 +04:00
if ( ( rc = dtSearch ( dir , & dname , & ino , & btstack , JFS_CREATE ) ) ) {
txAbort ( tid , 0 ) ;
goto out3 ;
}
2005-04-17 02:20:36 +04:00
tblk = tid_to_tblock ( tid ) ;
tblk - > xflag | = COMMIT_CREATE ;
tblk - > ino = ip - > i_ino ;
tblk - > u . ixpxd = JFS_IP ( ip ) - > ixpxd ;
ino = ip - > i_ino ;
2005-09-01 18:02:43 +04:00
if ( ( rc = dtInsert ( tid , dir , & dname , & ino , & btstack ) ) ) {
txAbort ( tid , 0 ) ;
2005-04-17 02:20:36 +04:00
goto out3 ;
2005-09-01 18:02:43 +04:00
}
2005-04-17 02:20:36 +04:00
ip - > i_op = & jfs_file_inode_operations ;
jfs_ip - > dev = new_encode_dev ( rdev ) ;
init_special_inode ( ip , ip - > i_mode , rdev ) ;
mark_inode_dirty ( ip ) ;
dir - > i_ctime = dir - > i_mtime = CURRENT_TIME ;
mark_inode_dirty ( dir ) ;
iplist [ 0 ] = dir ;
iplist [ 1 ] = ip ;
rc = txCommit ( tid , 2 , iplist , 0 ) ;
out3 :
txEnd ( tid ) ;
2006-01-25 00:22:50 +03:00
mutex_unlock ( & JFS_IP ( ip ) - > commit_mutex ) ;
mutex_unlock ( & JFS_IP ( dir ) - > commit_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2005-09-01 18:02:43 +04:00
free_ea_wmap ( ip ) ;
2011-10-28 16:13:28 +04:00
clear_nlink ( ip ) ;
2008-12-31 07:08:37 +03:00
unlock_new_inode ( ip ) ;
2005-04-17 02:20:36 +04:00
iput ( ip ) ;
2008-12-31 07:08:37 +03:00
} else {
unlock_new_inode ( ip ) ;
2012-07-19 09:18:15 +04:00
d_instantiate ( dentry , ip ) ;
2008-12-31 07:08:37 +03:00
}
2005-04-17 02:20:36 +04:00
out1 :
free_UCSname ( & dname ) ;
out :
jfs_info ( " jfs_mknod: returning %d " , rc ) ;
return rc ;
}
2012-06-11 01:13:09 +04:00
static struct dentry * jfs_lookup ( struct inode * dip , struct dentry * dentry , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
struct btstack btstack ;
ino_t inum ;
struct inode * ip ;
struct component_name key ;
int rc ;
2011-07-17 18:17:46 +04:00
jfs_info ( " jfs_lookup: name = %s " , dentry - > d_name . name ) ;
if ( ( rc = get_UCSname ( & key , dentry ) ) )
return ERR_PTR ( rc ) ;
rc = dtSearch ( dip , & key , & inum , & btstack , JFS_LOOKUP ) ;
free_UCSname ( & key ) ;
if ( rc = = - ENOENT ) {
ip = NULL ;
} else if ( rc ) {
jfs_err ( " jfs_lookup: dtSearch returned %d " , rc ) ;
ip = ERR_PTR ( rc ) ;
} else {
ip = jfs_iget ( dip - > i_sb , inum ) ;
if ( IS_ERR ( ip ) )
jfs_err ( " jfs_lookup: iget failed on inum %d " , ( uint ) inum ) ;
2005-04-17 02:20:36 +04:00
}
2010-12-18 18:59:31 +03:00
return d_splice_alias ( ip , dentry ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 03:42:09 +04:00
static struct inode * jfs_nfs_get_inode ( struct super_block * sb ,
u64 ino , u32 generation )
2007-07-17 15:04:29 +04:00
{
struct inode * inode ;
if ( ino = = 0 )
return ERR_PTR ( - ESTALE ) ;
2008-02-07 11:15:43 +03:00
inode = jfs_iget ( sb , ino ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
2007-07-17 15:04:29 +04:00
2008-02-07 11:15:43 +03:00
if ( generation & & inode - > i_generation ! = generation ) {
2007-10-22 03:42:09 +04:00
iput ( inode ) ;
return ERR_PTR ( - ESTALE ) ;
2007-07-17 15:04:29 +04:00
}
2007-10-22 03:42:09 +04:00
return inode ;
}
2007-07-17 15:04:29 +04:00
2007-10-22 03:42:09 +04:00
struct dentry * jfs_fh_to_dentry ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
return generic_fh_to_dentry ( sb , fid , fh_len , fh_type ,
jfs_nfs_get_inode ) ;
}
struct dentry * jfs_fh_to_parent ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
return generic_fh_to_parent ( sb , fid , fh_len , fh_type ,
jfs_nfs_get_inode ) ;
2007-07-17 15:04:29 +04:00
}
2005-04-17 02:20:36 +04:00
struct dentry * jfs_get_parent ( struct dentry * dentry )
{
unsigned long parent_ino ;
parent_ino =
le32_to_cpu ( JFS_IP ( dentry - > d_inode ) - > i_dtroot . header . idotdot ) ;
2008-08-11 17:49:04 +04:00
return d_obtain_alias ( jfs_iget ( dentry - > d_inode - > i_sb , parent_ino ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:39 +03:00
const struct inode_operations jfs_dir_inode_operations = {
2005-04-17 02:20:36 +04:00
. create = jfs_create ,
. lookup = jfs_lookup ,
. link = jfs_link ,
. unlink = jfs_unlink ,
. symlink = jfs_symlink ,
. mkdir = jfs_mkdir ,
. rmdir = jfs_rmdir ,
. mknod = jfs_mknod ,
. rename = jfs_rename ,
. setxattr = jfs_setxattr ,
. getxattr = jfs_getxattr ,
. listxattr = jfs_listxattr ,
. removexattr = jfs_removexattr ,
. setattr = jfs_setattr ,
2010-03-03 17:05:02 +03:00
# ifdef CONFIG_JFS_POSIX_ACL
2011-07-23 19:37:31 +04:00
. get_acl = jfs_get_acl ,
2013-12-20 17:16:51 +04:00
. set_acl = jfs_set_acl ,
2005-04-17 02:20:36 +04:00
# endif
} ;
2006-03-28 13:56:42 +04:00
const struct file_operations jfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
2013-05-18 01:00:34 +04:00
. iterate = jfs_readdir ,
2005-04-17 02:20:36 +04:00
. fsync = jfs_fsync ,
2008-01-28 01:58:51 +03:00
. unlocked_ioctl = jfs_ioctl ,
2008-01-28 02:02:02 +03:00
# ifdef CONFIG_COMPAT
. compat_ioctl = jfs_compat_ioctl ,
# endif
2008-09-03 23:53:01 +04:00
. llseek = generic_file_llseek ,
2005-04-17 02:20:36 +04:00
} ;
2013-05-22 02:22:44 +04:00
static int jfs_ci_hash ( const struct dentry * dir , struct qstr * this )
2005-04-17 02:20:36 +04:00
{
unsigned long hash ;
int i ;
hash = init_name_hash ( ) ;
for ( i = 0 ; i < this - > len ; i + + )
hash = partial_name_hash ( tolower ( this - > name [ i ] ) , hash ) ;
this - > hash = end_name_hash ( hash ) ;
return 0 ;
}
2013-05-22 02:22:44 +04:00
static int jfs_ci_compare ( const struct dentry * parent , const struct dentry * dentry ,
2011-01-07 09:49:27 +03:00
unsigned int len , const char * str , const struct qstr * name )
2005-04-17 02:20:36 +04:00
{
int i , result = 1 ;
2011-01-07 09:49:27 +03:00
if ( len ! = name - > len )
2005-04-17 02:20:36 +04:00
goto out ;
2011-01-07 09:49:27 +03:00
for ( i = 0 ; i < len ; i + + ) {
if ( tolower ( str [ i ] ) ! = tolower ( name - > name [ i ] ) )
2005-04-17 02:20:36 +04:00
goto out ;
}
result = 0 ;
2011-01-07 09:49:25 +03:00
out :
return result ;
}
2005-04-17 02:20:36 +04:00
2012-06-11 00:03:43 +04:00
static int jfs_ci_revalidate ( struct dentry * dentry , unsigned int flags )
2011-01-07 09:49:25 +03:00
{
2005-04-17 02:20:36 +04:00
/*
2011-01-07 09:49:25 +03:00
* This is not negative dentry . Always valid .
*
* Note , rename ( ) to existing directory entry will have - > d_inode ,
* and will use existing name which isn ' t specified name by user .
*
* We may be able to drop this positive dentry here . But dropping
* positive dentry isn ' t good idea . So it ' s unsupported like
* rename ( " filename " , " FILENAME " ) for now .
2005-04-17 02:20:36 +04:00
*/
2011-01-07 09:49:25 +03:00
if ( dentry - > d_inode )
return 1 ;
/*
* This may be nfsd ( or something ) , anyway , we can ' t see the
* intent of this . So , since this can be for creation , drop it .
*/
2012-06-11 00:03:43 +04:00
if ( ! flags )
2011-01-07 09:49:25 +03:00
return 0 ;
/*
* Drop the negative dentry , in order to make sure to use the
* case sensitive name which is specified by user if this is
* for creation .
*/
2012-06-11 00:03:43 +04:00
if ( flags & ( LOOKUP_CREATE | LOOKUP_RENAME_TARGET ) )
2011-06-26 05:37:18 +04:00
return 0 ;
2011-01-07 09:49:25 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2009-02-20 09:00:49 +03:00
const struct dentry_operations jfs_ci_dentry_operations =
2005-04-17 02:20:36 +04:00
{
. d_hash = jfs_ci_hash ,
. d_compare = jfs_ci_compare ,
2011-01-07 09:49:25 +03:00
. d_revalidate = jfs_ci_revalidate ,
2005-04-17 02:20:36 +04:00
} ;