2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
# include <linux/namei.h>
# include <linux/utsname.h>
# include <linux/mm.h>
# include <linux/xattr.h>
# include <linux/posix_acl.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-03-28 23:14:04 +04:00
# include <linux/crc32.h>
2006-01-16 19:50:04 +03:00
# include <asm/uaccess.h>
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "lm_interface.h"
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "acl.h"
# include "bmap.h"
# include "dir.h"
# include "eaops.h"
# include "eattr.h"
# include "glock.h"
# include "inode.h"
# include "meta_io.h"
# include "ops_dentry.h"
# include "ops_inode.h"
# include "quota.h"
# include "rgrp.h"
# include "trans.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
/**
* gfs2_create - Create a file
* @ dir : The directory in which to create the file
* @ dentry : The dentry of the new file
* @ mode : The mode of the new file
*
* Returns : errno
*/
static int gfs2_create ( struct inode * dir , struct dentry * dentry ,
int mode , struct nameidata * nd )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
struct inode * inode ;
gfs2_holder_init ( dip - > i_gl , 0 , 0 , ghs ) ;
for ( ; ; ) {
2006-02-13 15:27:43 +03:00
inode = gfs2_createi ( ghs , & dentry - > d_name , S_IFREG | mode ) ;
if ( ! IS_ERR ( inode ) ) {
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
if ( dip - > i_alloc . al_rgd )
gfs2_inplace_release ( dip ) ;
gfs2_quota_unlock ( dip ) ;
gfs2_alloc_put ( dip ) ;
gfs2_glock_dq_uninit_m ( 2 , ghs ) ;
2006-06-19 17:10:39 +04:00
mark_inode_dirty ( inode ) ;
2006-01-16 19:50:04 +03:00
break ;
2006-02-13 15:27:43 +03:00
} else if ( PTR_ERR ( inode ) ! = - EEXIST | |
2006-01-16 19:50:04 +03:00
( nd - > intent . open . flags & O_EXCL ) ) {
gfs2_holder_uninit ( ghs ) ;
2006-02-13 15:27:43 +03:00
return PTR_ERR ( inode ) ;
2006-01-16 19:50:04 +03:00
}
2006-03-20 20:30:04 +03:00
inode = gfs2_lookupi ( dir , & dentry - > d_name , 0 , nd ) ;
if ( inode ) {
if ( ! IS_ERR ( inode ) ) {
gfs2_holder_uninit ( ghs ) ;
break ;
} else {
gfs2_holder_uninit ( ghs ) ;
return PTR_ERR ( inode ) ;
}
2006-01-16 19:50:04 +03:00
}
}
d_instantiate ( dentry , inode ) ;
return 0 ;
}
/**
* gfs2_lookup - Look up a filename in a directory and return its inode
* @ dir : The directory inode
* @ dentry : The dentry of the new inode
* @ nd : passed from Linux VFS , ignored by us
*
* Called by the VFS layer . Lock dir and call gfs2_lookupi ( )
*
* Returns : errno
*/
static struct dentry * gfs2_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
struct inode * inode = NULL ;
2006-03-20 20:30:04 +03:00
dentry - > d_op = & gfs2_dops ;
2006-01-16 19:50:04 +03:00
2006-03-20 20:30:04 +03:00
inode = gfs2_lookupi ( dir , & dentry - > d_name , 0 , nd ) ;
if ( inode & & IS_ERR ( inode ) )
return ERR_PTR ( PTR_ERR ( inode ) ) ;
2006-01-16 19:50:04 +03:00
if ( inode )
return d_splice_alias ( inode , dentry ) ;
d_add ( dentry , inode ) ;
return NULL ;
}
/**
* gfs2_link - Link to a file
* @ old_dentry : The inode to link
* @ dir : Add link to this directory
* @ dentry : The name of the link
*
* Link the inode in " old_dentry " into the directory " dir " with the
* name in " dentry " .
*
* Returns : errno
*/
static int gfs2_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
2006-01-16 19:50:04 +03:00
struct inode * inode = old_dentry - > d_inode ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
int alloc_required ;
int error ;
if ( S_ISDIR ( ip - > i_di . di_mode ) )
return - EPERM ;
gfs2_holder_init ( dip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs ) ;
gfs2_holder_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + 1 ) ;
error = gfs2_glock_nq_m ( 2 , ghs ) ;
if ( error )
goto out ;
2006-06-22 18:59:10 +04:00
error = permission ( dir , MAY_WRITE | MAY_EXEC , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
2006-03-20 20:30:04 +03:00
error = gfs2_dir_search ( dir , & dentry - > d_name , NULL , NULL ) ;
2006-01-16 19:50:04 +03:00
switch ( error ) {
case - ENOENT :
break ;
case 0 :
error = - EEXIST ;
default :
goto out_gunlock ;
}
error = - EINVAL ;
if ( ! dip - > i_di . di_nlink )
goto out_gunlock ;
error = - EFBIG ;
if ( dip - > i_di . di_entries = = ( uint32_t ) - 1 )
goto out_gunlock ;
error = - EPERM ;
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
goto out_gunlock ;
error = - EINVAL ;
if ( ! ip - > i_di . di_nlink )
goto out_gunlock ;
error = - EMLINK ;
if ( ip - > i_di . di_nlink = = ( uint32_t ) - 1 )
goto out_gunlock ;
2006-03-20 20:30:04 +03:00
alloc_required = error = gfs2_diradd_alloc_required ( dir , & dentry - > d_name ) ;
if ( error < 0 )
2006-01-16 19:50:04 +03:00
goto out_gunlock ;
2006-03-20 20:30:04 +03:00
error = 0 ;
2006-01-16 19:50:04 +03:00
if ( alloc_required ) {
struct gfs2_alloc * al = gfs2_alloc_get ( dip ) ;
error = gfs2_quota_lock ( dip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out_alloc ;
error = gfs2_quota_check ( dip , dip - > i_di . di_uid ,
dip - > i_di . di_gid ) ;
if ( error )
goto out_gunlock_q ;
al - > al_requested = sdp - > sd_max_dirres ;
error = gfs2_inplace_reserve ( dip ) ;
if ( error )
goto out_gunlock_q ;
2006-05-18 22:10:52 +04:00
error = gfs2_trans_begin ( sdp , sdp - > sd_max_dirres +
2006-01-16 19:50:04 +03:00
al - > al_rgd - > rd_ri . ri_length +
2 * RES_DINODE + RES_STATFS +
RES_QUOTA , 0 ) ;
if ( error )
goto out_ipres ;
} else {
error = gfs2_trans_begin ( sdp , 2 * RES_DINODE + RES_LEAF , 0 ) ;
if ( error )
goto out_ipres ;
}
2006-03-20 20:30:04 +03:00
error = gfs2_dir_add ( dir , & dentry - > d_name , & ip - > i_num ,
2006-01-16 19:50:04 +03:00
IF2DT ( ip - > i_di . di_mode ) ) ;
if ( error )
goto out_end_trans ;
error = gfs2_change_nlink ( ip , + 1 ) ;
2006-06-14 23:32:57 +04:00
out_end_trans :
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
2006-06-14 23:32:57 +04:00
out_ipres :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_inplace_release ( dip ) ;
2006-06-14 23:32:57 +04:00
out_gunlock_q :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_quota_unlock ( dip ) ;
2006-06-14 23:32:57 +04:00
out_alloc :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_alloc_put ( dip ) ;
2006-06-14 23:32:57 +04:00
out_gunlock :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_m ( 2 , ghs ) ;
2006-06-14 23:32:57 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_holder_uninit ( ghs ) ;
gfs2_holder_uninit ( ghs + 1 ) ;
if ( ! error ) {
2006-07-07 01:58:03 +04:00
atomic_inc ( & inode - > i_count ) ;
2006-01-16 19:50:04 +03:00
d_instantiate ( dentry , inode ) ;
mark_inode_dirty ( inode ) ;
}
return error ;
}
/**
* gfs2_unlink - Unlink a file
* @ dir : The inode of the directory containing the file to unlink
* @ dentry : The file itself
*
* Unlink a file . Call gfs2_unlinki ( )
*
* Returns : errno
*/
static int gfs2_unlink ( struct inode * dir , struct dentry * dentry )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
struct gfs2_inode * ip = GFS2_I ( dentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
int error ;
gfs2_holder_init ( dip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs ) ;
gfs2_holder_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + 1 ) ;
error = gfs2_glock_nq_m ( 2 , ghs ) ;
if ( error )
goto out ;
error = gfs2_unlink_ok ( dip , & dentry - > d_name , ip ) ;
if ( error )
goto out_gunlock ;
2006-06-14 23:32:57 +04:00
error = gfs2_trans_begin ( sdp , 2 * RES_DINODE + RES_LEAF + RES_RG_BIT , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
2006-06-14 23:32:57 +04:00
error = gfs2_dir_del ( dip , & dentry - > d_name ) ;
if ( error )
goto out_end_trans ;
2006-01-16 19:50:04 +03:00
2006-06-14 23:32:57 +04:00
error = gfs2_change_nlink ( ip , - 1 ) ;
2006-01-16 19:50:04 +03:00
2006-06-14 23:32:57 +04:00
out_end_trans :
gfs2_trans_end ( sdp ) ;
out_gunlock :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_m ( 2 , ghs ) ;
2006-06-14 23:32:57 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_holder_uninit ( ghs ) ;
gfs2_holder_uninit ( ghs + 1 ) ;
return error ;
}
/**
* gfs2_symlink - Create a symlink
* @ dir : The directory to create the symlink in
* @ dentry : The dentry to put the symlink in
* @ symname : The thing which the link points to
*
* Returns : errno
*/
static int gfs2_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) , * ip ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
struct inode * inode ;
struct buffer_head * dibh ;
int size ;
int error ;
/* Must be stuffed with a null terminator for gfs2_follow_link() */
size = strlen ( symname ) ;
if ( size > sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_dinode ) - 1 )
return - ENAMETOOLONG ;
gfs2_holder_init ( dip - > i_gl , 0 , 0 , ghs ) ;
2006-02-13 15:27:43 +03:00
inode = gfs2_createi ( ghs , & dentry - > d_name , S_IFLNK | S_IRWXUGO ) ;
if ( IS_ERR ( inode ) ) {
2006-01-16 19:50:04 +03:00
gfs2_holder_uninit ( ghs ) ;
2006-02-13 15:27:43 +03:00
return PTR_ERR ( inode ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-28 01:23:27 +03:00
ip = ghs [ 1 ] . gh_gl - > gl_object ;
2006-01-16 19:50:04 +03:00
ip - > i_di . di_size = size ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( ! gfs2_assert_withdraw ( sdp , ! error ) ) {
gfs2_dinode_out ( & ip - > i_di , dibh - > b_data ) ;
memcpy ( dibh - > b_data + sizeof ( struct gfs2_dinode ) , symname ,
size ) ;
brelse ( dibh ) ;
}
gfs2_trans_end ( sdp ) ;
if ( dip - > i_alloc . al_rgd )
gfs2_inplace_release ( dip ) ;
gfs2_quota_unlock ( dip ) ;
gfs2_alloc_put ( dip ) ;
gfs2_glock_dq_uninit_m ( 2 , ghs ) ;
d_instantiate ( dentry , inode ) ;
mark_inode_dirty ( inode ) ;
return 0 ;
}
/**
* gfs2_mkdir - Make a directory
* @ dir : The parent directory of the new one
* @ dentry : The dentry of the new directory
* @ mode : The mode of the new directory
*
* Returns : errno
*/
static int gfs2_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) , * ip ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
struct inode * inode ;
struct buffer_head * dibh ;
int error ;
gfs2_holder_init ( dip - > i_gl , 0 , 0 , ghs ) ;
2006-02-13 15:27:43 +03:00
inode = gfs2_createi ( ghs , & dentry - > d_name , S_IFDIR | mode ) ;
if ( IS_ERR ( inode ) ) {
2006-01-16 19:50:04 +03:00
gfs2_holder_uninit ( ghs ) ;
2006-02-13 15:27:43 +03:00
return PTR_ERR ( inode ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-28 01:23:27 +03:00
ip = ghs [ 1 ] . gh_gl - > gl_object ;
2006-01-16 19:50:04 +03:00
ip - > i_di . di_nlink = 2 ;
ip - > i_di . di_size = sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_dinode ) ;
ip - > i_di . di_flags | = GFS2_DIF_JDATA ;
ip - > i_di . di_payload_format = GFS2_FORMAT_DE ;
ip - > i_di . di_entries = 2 ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( ! gfs2_assert_withdraw ( sdp , ! error ) ) {
struct gfs2_dinode * di = ( struct gfs2_dinode * ) dibh - > b_data ;
2006-03-20 20:30:04 +03:00
struct gfs2_dirent * dent = ( struct gfs2_dirent * ) ( di + 1 ) ;
2006-03-28 23:14:04 +04:00
struct qstr str ;
2006-01-16 19:50:04 +03:00
2006-03-28 23:14:04 +04:00
gfs2_str2qstr ( & str , " . " ) ;
2006-03-20 20:30:04 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
gfs2_qstr2dirent ( & str , GFS2_DIRENT_SIZE ( str . len ) , dent ) ;
2006-01-16 19:50:04 +03:00
dent - > de_inum = di - > di_num ; /* already GFS2 endian */
dent - > de_type = DT_DIR ;
di - > di_entries = cpu_to_be32 ( 1 ) ;
2006-03-28 23:14:04 +04:00
gfs2_str2qstr ( & str , " .. " ) ;
2006-03-20 20:30:04 +03:00
dent = ( struct gfs2_dirent * ) ( ( char * ) dent + GFS2_DIRENT_SIZE ( 1 ) ) ;
gfs2_qstr2dirent ( & str , dibh - > b_size - GFS2_DIRENT_SIZE ( 1 ) - sizeof ( struct gfs2_dinode ) , dent ) ;
2006-01-16 19:50:04 +03:00
gfs2_inum_out ( & dip - > i_num , ( char * ) & dent - > de_inum ) ;
dent - > de_type = DT_DIR ;
gfs2_dinode_out ( & ip - > i_di , ( char * ) di ) ;
brelse ( dibh ) ;
}
error = gfs2_change_nlink ( dip , + 1 ) ;
gfs2_assert_withdraw ( sdp , ! error ) ; /* dip already pinned */
gfs2_trans_end ( sdp ) ;
if ( dip - > i_alloc . al_rgd )
gfs2_inplace_release ( dip ) ;
gfs2_quota_unlock ( dip ) ;
gfs2_alloc_put ( dip ) ;
gfs2_glock_dq_uninit_m ( 2 , ghs ) ;
d_instantiate ( dentry , inode ) ;
mark_inode_dirty ( inode ) ;
return 0 ;
}
/**
* gfs2_rmdir - Remove a directory
* @ dir : The parent directory of the directory to be removed
* @ dentry : The dentry of the directory to remove
*
* Remove a directory . Call gfs2_rmdiri ( )
*
* Returns : errno
*/
static int gfs2_rmdir ( struct inode * dir , struct dentry * dentry )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
struct gfs2_inode * ip = GFS2_I ( dentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
int error ;
gfs2_holder_init ( dip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs ) ;
gfs2_holder_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + 1 ) ;
error = gfs2_glock_nq_m ( 2 , ghs ) ;
if ( error )
goto out ;
error = gfs2_unlink_ok ( dip , & dentry - > d_name , ip ) ;
if ( error )
goto out_gunlock ;
if ( ip - > i_di . di_entries < 2 ) {
if ( gfs2_consist_inode ( ip ) )
gfs2_dinode_print ( & ip - > i_di ) ;
error = - EIO ;
goto out_gunlock ;
}
if ( ip - > i_di . di_entries > 2 ) {
error = - ENOTEMPTY ;
goto out_gunlock ;
}
2006-06-14 23:32:57 +04:00
error = gfs2_trans_begin ( sdp , 2 * RES_DINODE + 3 * RES_LEAF + RES_RG_BIT , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
2006-06-14 23:32:57 +04:00
error = gfs2_rmdiri ( dip , & dentry - > d_name , ip ) ;
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
out_gunlock :
gfs2_glock_dq_m ( 2 , ghs ) ;
out :
gfs2_holder_uninit ( ghs ) ;
gfs2_holder_uninit ( ghs + 1 ) ;
return error ;
}
/**
* gfs2_mknod - Make a special file
* @ dir : The directory in which the special file will reside
* @ dentry : The dentry of the special file
* @ mode : The mode of the special file
* @ rdev : The device specification of the special file
*
*/
static int gfs2_mknod ( struct inode * dir , struct dentry * dentry , int mode ,
dev_t dev )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * dip = GFS2_I ( dir ) , * ip ;
struct gfs2_sbd * sdp = GFS2_SB ( dir ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 2 ] ;
struct inode * inode ;
struct buffer_head * dibh ;
uint32_t major = 0 , minor = 0 ;
int error ;
switch ( mode & S_IFMT ) {
case S_IFBLK :
case S_IFCHR :
major = MAJOR ( dev ) ;
minor = MINOR ( dev ) ;
break ;
case S_IFIFO :
case S_IFSOCK :
break ;
default :
return - EOPNOTSUPP ;
} ;
gfs2_holder_init ( dip - > i_gl , 0 , 0 , ghs ) ;
2006-02-13 15:27:43 +03:00
inode = gfs2_createi ( ghs , & dentry - > d_name , mode ) ;
if ( IS_ERR ( inode ) ) {
2006-01-16 19:50:04 +03:00
gfs2_holder_uninit ( ghs ) ;
2006-02-13 15:27:43 +03:00
return PTR_ERR ( inode ) ;
2006-01-16 19:50:04 +03:00
}
2006-02-28 01:23:27 +03:00
ip = ghs [ 1 ] . gh_gl - > gl_object ;
2006-01-16 19:50:04 +03:00
ip - > i_di . di_major = major ;
ip - > i_di . di_minor = minor ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( ! gfs2_assert_withdraw ( sdp , ! error ) ) {
gfs2_dinode_out ( & ip - > i_di , dibh - > b_data ) ;
brelse ( dibh ) ;
}
gfs2_trans_end ( sdp ) ;
if ( dip - > i_alloc . al_rgd )
gfs2_inplace_release ( dip ) ;
gfs2_quota_unlock ( dip ) ;
gfs2_alloc_put ( dip ) ;
gfs2_glock_dq_uninit_m ( 2 , ghs ) ;
d_instantiate ( dentry , inode ) ;
mark_inode_dirty ( inode ) ;
return 0 ;
}
/**
* gfs2_rename - Rename a file
* @ odir : Parent directory of old file name
* @ odentry : The old dentry of the file
* @ ndir : Parent directory of new file name
* @ ndentry : The new dentry of the file
*
* Returns : errno
*/
static int gfs2_rename ( struct inode * odir , struct dentry * odentry ,
struct inode * ndir , struct dentry * ndentry )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * odip = GFS2_I ( odir ) ;
struct gfs2_inode * ndip = GFS2_I ( ndir ) ;
struct gfs2_inode * ip = GFS2_I ( odentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_inode * nip = NULL ;
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( odir ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder ghs [ 4 ] , r_gh ;
unsigned int num_gh ;
int dir_rename = 0 ;
int alloc_required ;
unsigned int x ;
int error ;
if ( ndentry - > d_inode ) {
2006-06-14 23:32:57 +04:00
nip = GFS2_I ( ndentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
if ( ip = = nip )
return 0 ;
}
/* Make sure we aren't trying to move a dirctory into it's subdir */
if ( S_ISDIR ( ip - > i_di . di_mode ) & & odip ! = ndip ) {
dir_rename = 1 ;
error = gfs2_glock_nq_init ( sdp - > sd_rename_gl ,
LM_ST_EXCLUSIVE , 0 ,
& r_gh ) ;
if ( error )
goto out ;
error = gfs2_ok_to_move ( ip , ndip ) ;
if ( error )
goto out_gunlock_r ;
}
2006-06-21 23:38:17 +04:00
num_gh = 1 ;
2006-01-16 19:50:04 +03:00
gfs2_holder_init ( odip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs ) ;
2006-06-21 23:38:17 +04:00
if ( odip ! = ndip ) {
gfs2_holder_init ( ndip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + num_gh ) ;
num_gh + + ;
}
gfs2_holder_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + num_gh ) ;
num_gh + + ;
2006-01-16 19:50:04 +03:00
2006-06-21 23:38:17 +04:00
if ( nip ) {
gfs2_holder_init ( nip - > i_gl , LM_ST_EXCLUSIVE , 0 , ghs + num_gh ) ;
num_gh + + ;
}
2006-01-16 19:50:04 +03:00
error = gfs2_glock_nq_m ( num_gh , ghs ) ;
if ( error )
goto out_uninit ;
/* Check out the old directory */
error = gfs2_unlink_ok ( odip , & odentry - > d_name , ip ) ;
if ( error )
goto out_gunlock ;
/* Check out the new directory */
if ( nip ) {
error = gfs2_unlink_ok ( ndip , & ndentry - > d_name , nip ) ;
if ( error )
goto out_gunlock ;
if ( S_ISDIR ( nip - > i_di . di_mode ) ) {
if ( nip - > i_di . di_entries < 2 ) {
if ( gfs2_consist_inode ( nip ) )
gfs2_dinode_print ( & nip - > i_di ) ;
error = - EIO ;
goto out_gunlock ;
}
if ( nip - > i_di . di_entries > 2 ) {
error = - ENOTEMPTY ;
goto out_gunlock ;
}
}
} else {
2006-06-22 18:59:10 +04:00
error = permission ( ndir , MAY_WRITE | MAY_EXEC , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
2006-03-20 20:30:04 +03:00
error = gfs2_dir_search ( ndir , & ndentry - > d_name , NULL , NULL ) ;
2006-01-16 19:50:04 +03:00
switch ( error ) {
case - ENOENT :
error = 0 ;
break ;
case 0 :
error = - EEXIST ;
default :
goto out_gunlock ;
} ;
if ( odip ! = ndip ) {
if ( ! ndip - > i_di . di_nlink ) {
error = - EINVAL ;
goto out_gunlock ;
}
if ( ndip - > i_di . di_entries = = ( uint32_t ) - 1 ) {
error = - EFBIG ;
goto out_gunlock ;
}
if ( S_ISDIR ( ip - > i_di . di_mode ) & &
ndip - > i_di . di_nlink = = ( uint32_t ) - 1 ) {
error = - EMLINK ;
goto out_gunlock ;
}
}
}
/* Check out the dir to be renamed */
if ( dir_rename ) {
2006-06-22 18:59:10 +04:00
error = permission ( odentry - > d_inode , MAY_WRITE , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
}
2006-03-20 20:30:04 +03:00
alloc_required = error = gfs2_diradd_alloc_required ( ndir , & ndentry - > d_name ) ;
if ( error < 0 )
2006-01-16 19:50:04 +03:00
goto out_gunlock ;
2006-03-20 20:30:04 +03:00
error = 0 ;
2006-01-16 19:50:04 +03:00
if ( alloc_required ) {
struct gfs2_alloc * al = gfs2_alloc_get ( ndip ) ;
error = gfs2_quota_lock ( ndip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out_alloc ;
error = gfs2_quota_check ( ndip , ndip - > i_di . di_uid ,
ndip - > i_di . di_gid ) ;
if ( error )
goto out_gunlock_q ;
al - > al_requested = sdp - > sd_max_dirres ;
error = gfs2_inplace_reserve ( ndip ) ;
if ( error )
goto out_gunlock_q ;
2006-04-18 18:09:15 +04:00
error = gfs2_trans_begin ( sdp , sdp - > sd_max_dirres +
2006-01-16 19:50:04 +03:00
al - > al_rgd - > rd_ri . ri_length +
4 * RES_DINODE + 4 * RES_LEAF +
2006-06-14 23:32:57 +04:00
RES_STATFS + RES_QUOTA , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_ipreserv ;
} else {
error = gfs2_trans_begin ( sdp , 4 * RES_DINODE +
2006-06-14 23:32:57 +04:00
5 * RES_LEAF , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock ;
}
/* Remove the target file, if it exists */
if ( nip ) {
if ( S_ISDIR ( nip - > i_di . di_mode ) )
2006-06-14 23:32:57 +04:00
error = gfs2_rmdiri ( ndip , & ndentry - > d_name , nip ) ;
else {
error = gfs2_dir_del ( ndip , & ndentry - > d_name ) ;
if ( error )
goto out_end_trans ;
error = gfs2_change_nlink ( nip , - 1 ) ;
}
2006-01-16 19:50:04 +03:00
if ( error )
goto out_end_trans ;
}
if ( dir_rename ) {
struct qstr name ;
2006-03-28 23:14:04 +04:00
gfs2_str2qstr ( & name , " .. " ) ;
2006-01-16 19:50:04 +03:00
error = gfs2_change_nlink ( ndip , + 1 ) ;
if ( error )
goto out_end_trans ;
error = gfs2_change_nlink ( odip , - 1 ) ;
if ( error )
goto out_end_trans ;
error = gfs2_dir_mvino ( ip , & name , & ndip - > i_num , DT_DIR ) ;
if ( error )
goto out_end_trans ;
} else {
struct buffer_head * dibh ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out_end_trans ;
ip - > i_di . di_ctime = get_seconds ( ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-01-16 19:50:04 +03:00
gfs2_dinode_out ( & ip - > i_di , dibh - > b_data ) ;
brelse ( dibh ) ;
}
error = gfs2_dir_del ( odip , & odentry - > d_name ) ;
if ( error )
goto out_end_trans ;
2006-03-20 20:30:04 +03:00
error = gfs2_dir_add ( ndir , & ndentry - > d_name , & ip - > i_num ,
2006-01-16 19:50:04 +03:00
IF2DT ( ip - > i_di . di_mode ) ) ;
if ( error )
goto out_end_trans ;
2006-06-14 23:32:57 +04:00
out_end_trans :
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
2006-06-14 23:32:57 +04:00
out_ipreserv :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_inplace_release ( ndip ) ;
2006-06-14 23:32:57 +04:00
out_gunlock_q :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_quota_unlock ( ndip ) ;
2006-06-14 23:32:57 +04:00
out_alloc :
2006-01-16 19:50:04 +03:00
if ( alloc_required )
gfs2_alloc_put ( ndip ) ;
2006-06-14 23:32:57 +04:00
out_gunlock :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_m ( num_gh , ghs ) ;
2006-06-14 23:32:57 +04:00
out_uninit :
2006-01-16 19:50:04 +03:00
for ( x = 0 ; x < num_gh ; x + + )
gfs2_holder_uninit ( ghs + x ) ;
2006-06-14 23:32:57 +04:00
out_gunlock_r :
2006-01-16 19:50:04 +03:00
if ( dir_rename )
gfs2_glock_dq_uninit ( & r_gh ) ;
2006-06-14 23:32:57 +04:00
out :
2006-01-16 19:50:04 +03:00
return error ;
}
/**
* gfs2_readlink - Read the value of a symlink
* @ dentry : the symlink
* @ buf : the buffer to read the symlink data into
* @ size : the size of the buffer
*
* Returns : errno
*/
static int gfs2_readlink ( struct dentry * dentry , char __user * user_buf ,
int user_size )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( dentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
char array [ GFS2_FAST_NAME_SIZE ] , * buf = array ;
unsigned int len = GFS2_FAST_NAME_SIZE ;
int error ;
error = gfs2_readlinki ( ip , & buf , & len ) ;
if ( error )
return error ;
if ( user_size > len - 1 )
user_size = len - 1 ;
if ( copy_to_user ( user_buf , buf , user_size ) )
error = - EFAULT ;
else
error = user_size ;
if ( buf ! = array )
kfree ( buf ) ;
return error ;
}
/**
* gfs2_follow_link - Follow a symbolic link
* @ dentry : The dentry of the link
* @ nd : Data that we pass to vfs_follow_link ( )
*
* This can handle symlinks of any size . It is optimised for symlinks
* under GFS2_FAST_NAME_SIZE .
*
* Returns : 0 on success or error code
*/
static void * gfs2_follow_link ( struct dentry * dentry , struct nameidata * nd )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( dentry - > d_inode ) ;
2006-01-16 19:50:04 +03:00
char array [ GFS2_FAST_NAME_SIZE ] , * buf = array ;
unsigned int len = GFS2_FAST_NAME_SIZE ;
int error ;
error = gfs2_readlinki ( ip , & buf , & len ) ;
if ( ! error ) {
error = vfs_follow_link ( nd , buf ) ;
if ( buf ! = array )
kfree ( buf ) ;
}
return ERR_PTR ( error ) ;
}
/**
* gfs2_permission -
* @ inode :
* @ mask :
* @ nd : passed from Linux VFS , ignored by us
*
* Returns : errno
*/
static int gfs2_permission ( struct inode * inode , int mask , struct nameidata * nd )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder i_gh ;
int error ;
if ( ip - > i_vn = = ip - > i_gl - > gl_vn )
return generic_permission ( inode , mask , gfs2_check_acl ) ;
2006-06-22 18:59:10 +04:00
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_SHARED , LM_FLAG_ANY , & i_gh ) ;
2006-01-16 19:50:04 +03:00
if ( ! error ) {
error = generic_permission ( inode , mask , gfs2_check_acl_locked ) ;
gfs2_glock_dq_uninit ( & i_gh ) ;
}
return error ;
}
static int setattr_size ( struct inode * inode , struct iattr * attr )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
int error ;
if ( attr - > ia_size ! = ip - > i_di . di_size ) {
error = vmtruncate ( inode , attr - > ia_size ) ;
if ( error )
return error ;
}
2006-01-24 13:37:06 +03:00
error = gfs2_truncatei ( ip , attr - > ia_size ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
return error ;
}
static int setattr_chown ( struct inode * inode , struct iattr * attr )
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * dibh ;
uint32_t ouid , ogid , nuid , ngid ;
int error ;
ouid = ip - > i_di . di_uid ;
ogid = ip - > i_di . di_gid ;
nuid = attr - > ia_uid ;
ngid = attr - > ia_gid ;
if ( ! ( attr - > ia_valid & ATTR_UID ) | | ouid = = nuid )
ouid = nuid = NO_QUOTA_CHANGE ;
if ( ! ( attr - > ia_valid & ATTR_GID ) | | ogid = = ngid )
ogid = ngid = NO_QUOTA_CHANGE ;
gfs2_alloc_get ( ip ) ;
error = gfs2_quota_lock ( ip , nuid , ngid ) ;
if ( error )
goto out_alloc ;
if ( ouid ! = NO_QUOTA_CHANGE | | ogid ! = NO_QUOTA_CHANGE ) {
error = gfs2_quota_check ( ip , nuid , ngid ) ;
if ( error )
goto out_gunlock_q ;
}
error = gfs2_trans_begin ( sdp , RES_DINODE + 2 * RES_QUOTA , 0 ) ;
if ( error )
goto out_gunlock_q ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out_end_trans ;
error = inode_setattr ( inode , attr ) ;
gfs2_assert_warn ( sdp , ! error ) ;
gfs2_inode_attr_out ( ip ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-01-16 19:50:04 +03:00
gfs2_dinode_out ( & ip - > i_di , dibh - > b_data ) ;
brelse ( dibh ) ;
if ( ouid ! = NO_QUOTA_CHANGE | | ogid ! = NO_QUOTA_CHANGE ) {
2006-06-24 23:42:21 +04:00
gfs2_quota_change ( ip , - ip - > i_di . di_blocks , ouid , ogid ) ;
gfs2_quota_change ( ip , ip - > i_di . di_blocks , nuid , ngid ) ;
2006-01-16 19:50:04 +03:00
}
out_end_trans :
gfs2_trans_end ( sdp ) ;
out_gunlock_q :
gfs2_quota_unlock ( ip ) ;
out_alloc :
gfs2_alloc_put ( ip ) ;
return error ;
}
/**
* gfs2_setattr - Change attributes on an inode
* @ dentry : The dentry which is changing
* @ attr : The structure describing the change
*
* The VFS layer wants to change one or more of an inodes attributes . Write
* that change out to disk .
*
* Returns : errno
*/
static int gfs2_setattr ( struct dentry * dentry , struct iattr * attr )
{
struct inode * inode = dentry - > d_inode ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder i_gh ;
int error ;
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , & i_gh ) ;
if ( error )
return error ;
error = - EPERM ;
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
goto out ;
error = inode_change_ok ( inode , attr ) ;
if ( error )
goto out ;
if ( attr - > ia_valid & ATTR_SIZE )
error = setattr_size ( inode , attr ) ;
else if ( attr - > ia_valid & ( ATTR_UID | ATTR_GID ) )
error = setattr_chown ( inode , attr ) ;
else if ( ( attr - > ia_valid & ATTR_MODE ) & & IS_POSIXACL ( inode ) )
error = gfs2_acl_chmod ( ip , attr ) ;
else
error = gfs2_setattr_simple ( ip , attr ) ;
out :
gfs2_glock_dq_uninit ( & i_gh ) ;
if ( ! error )
mark_inode_dirty ( inode ) ;
return error ;
}
/**
* gfs2_getattr - Read out an inode ' s attributes
* @ mnt : ?
* @ dentry : The dentry to stat
* @ stat : The inode ' s stats
*
* Returns : errno
*/
static int gfs2_getattr ( struct vfsmount * mnt , struct dentry * dentry ,
struct kstat * stat )
{
struct inode * inode = dentry - > d_inode ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder gh ;
int error ;
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_SHARED , LM_FLAG_ANY , & gh ) ;
if ( ! error ) {
generic_fillattr ( inode , stat ) ;
gfs2_glock_dq_uninit ( & gh ) ;
}
return error ;
}
static int gfs2_setxattr ( struct dentry * dentry , const char * name ,
const void * data , size_t size , int flags )
{
2006-06-14 23:32:57 +04:00
struct inode * inode = dentry - > d_inode ;
2006-01-16 19:50:04 +03:00
struct gfs2_ea_request er ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
er . er_type = gfs2_ea_name2type ( name , & er . er_name ) ;
if ( er . er_type = = GFS2_EATYPE_UNUSED )
return - EOPNOTSUPP ;
er . er_data = ( char * ) data ;
er . er_name_len = strlen ( er . er_name ) ;
er . er_data_len = size ;
er . er_flags = flags ;
2006-06-14 23:32:57 +04:00
gfs2_assert_warn ( GFS2_SB ( inode ) , ! ( er . er_flags & GFS2_ERF_MODE ) ) ;
2006-01-16 19:50:04 +03:00
2006-06-14 23:32:57 +04:00
return gfs2_ea_set ( GFS2_I ( inode ) , & er ) ;
2006-01-16 19:50:04 +03:00
}
static ssize_t gfs2_getxattr ( struct dentry * dentry , const char * name ,
void * data , size_t size )
{
struct gfs2_ea_request er ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
er . er_type = gfs2_ea_name2type ( name , & er . er_name ) ;
if ( er . er_type = = GFS2_EATYPE_UNUSED )
return - EOPNOTSUPP ;
er . er_data = data ;
er . er_name_len = strlen ( er . er_name ) ;
er . er_data_len = size ;
2006-06-14 23:32:57 +04:00
return gfs2_ea_get ( GFS2_I ( dentry - > d_inode ) , & er ) ;
2006-01-16 19:50:04 +03:00
}
static ssize_t gfs2_listxattr ( struct dentry * dentry , char * buffer , size_t size )
{
struct gfs2_ea_request er ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
er . er_data = ( size ) ? buffer : NULL ;
er . er_data_len = size ;
2006-06-14 23:32:57 +04:00
return gfs2_ea_list ( GFS2_I ( dentry - > d_inode ) , & er ) ;
2006-01-16 19:50:04 +03:00
}
static int gfs2_removexattr ( struct dentry * dentry , const char * name )
{
struct gfs2_ea_request er ;
memset ( & er , 0 , sizeof ( struct gfs2_ea_request ) ) ;
er . er_type = gfs2_ea_name2type ( name , & er . er_name ) ;
if ( er . er_type = = GFS2_EATYPE_UNUSED )
return - EOPNOTSUPP ;
er . er_name_len = strlen ( er . er_name ) ;
2006-06-14 23:32:57 +04:00
return gfs2_ea_remove ( GFS2_I ( dentry - > d_inode ) , & er ) ;
2006-01-16 19:50:04 +03:00
}
struct inode_operations gfs2_file_iops = {
. permission = gfs2_permission ,
. setattr = gfs2_setattr ,
. getattr = gfs2_getattr ,
. setxattr = gfs2_setxattr ,
. getxattr = gfs2_getxattr ,
. listxattr = gfs2_listxattr ,
. removexattr = gfs2_removexattr ,
} ;
struct inode_operations gfs2_dev_iops = {
. permission = gfs2_permission ,
. setattr = gfs2_setattr ,
. getattr = gfs2_getattr ,
. setxattr = gfs2_setxattr ,
. getxattr = gfs2_getxattr ,
. listxattr = gfs2_listxattr ,
. removexattr = gfs2_removexattr ,
} ;
struct inode_operations gfs2_dir_iops = {
. create = gfs2_create ,
. lookup = gfs2_lookup ,
. link = gfs2_link ,
. unlink = gfs2_unlink ,
. symlink = gfs2_symlink ,
. mkdir = gfs2_mkdir ,
. rmdir = gfs2_rmdir ,
. mknod = gfs2_mknod ,
. rename = gfs2_rename ,
. permission = gfs2_permission ,
. setattr = gfs2_setattr ,
. getattr = gfs2_getattr ,
. setxattr = gfs2_setxattr ,
. getxattr = gfs2_getxattr ,
. listxattr = gfs2_listxattr ,
. removexattr = gfs2_removexattr ,
} ;
struct inode_operations gfs2_symlink_iops = {
. readlink = gfs2_readlink ,
. follow_link = gfs2_follow_link ,
. permission = gfs2_permission ,
. setattr = gfs2_setattr ,
. getattr = gfs2_getattr ,
. setxattr = gfs2_setxattr ,
. getxattr = gfs2_getxattr ,
. listxattr = gfs2_listxattr ,
. removexattr = gfs2_removexattr ,
} ;