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/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.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-09-19 09:56:29 +04:00
# include <linux/lm_interface.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "dir.h"
# include "glock.h"
# include "glops.h"
# include "inode.h"
2007-01-11 22:25:00 +03:00
# include "ops_dentry.h"
2007-06-01 17:11:58 +04:00
# include "ops_fstype.h"
2006-01-16 19:50:04 +03:00
# include "rgrp.h"
2006-03-20 20:30:04 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2007-06-01 17:11:58 +04:00
# define GFS2_SMALL_FH_SIZE 4
# define GFS2_LARGE_FH_SIZE 10
struct gfs2_fh_obj {
struct gfs2_inum_host this ;
u32 imode ;
} ;
2006-01-16 19:50:04 +03:00
static struct dentry * gfs2_decode_fh ( struct super_block * sb ,
2006-10-14 18:46:30 +04:00
__u32 * p ,
2006-01-16 19:50:04 +03:00
int fh_len ,
int fh_type ,
int ( * acceptable ) ( void * context ,
struct dentry * dentry ) ,
void * context )
{
2006-10-14 18:46:30 +04:00
__be32 * fh = ( __force __be32 * ) p ;
2006-07-11 21:28:53 +04:00
struct gfs2_fh_obj fh_obj ;
2006-10-14 06:51:24 +04:00
struct gfs2_inum_host * this , parent ;
2006-01-16 19:50:04 +03:00
2006-07-11 21:28:53 +04:00
this = & fh_obj . this ;
fh_obj . imode = DT_UNKNOWN ;
2006-01-16 19:50:04 +03:00
memset ( & parent , 0 , sizeof ( struct gfs2_inum ) ) ;
2007-02-23 08:21:17 +03:00
switch ( fh_len ) {
2006-09-05 00:16:45 +04:00
case GFS2_LARGE_FH_SIZE :
2006-09-04 20:49:07 +04:00
parent . no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 4 ] ) ) < < 32 ;
2006-01-16 19:50:04 +03:00
parent . no_formal_ino | = be32_to_cpu ( fh [ 5 ] ) ;
2006-09-04 20:49:07 +04:00
parent . no_addr = ( ( u64 ) be32_to_cpu ( fh [ 6 ] ) ) < < 32 ;
2006-01-16 19:50:04 +03:00
parent . no_addr | = be32_to_cpu ( fh [ 7 ] ) ;
2006-07-11 21:28:53 +04:00
fh_obj . imode = be32_to_cpu ( fh [ 8 ] ) ;
2006-09-05 00:16:45 +04:00
case GFS2_SMALL_FH_SIZE :
2006-09-04 20:49:07 +04:00
this - > no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 0 ] ) ) < < 32 ;
2006-07-11 21:28:53 +04:00
this - > no_formal_ino | = be32_to_cpu ( fh [ 1 ] ) ;
2006-09-04 20:49:07 +04:00
this - > no_addr = ( ( u64 ) be32_to_cpu ( fh [ 2 ] ) ) < < 32 ;
2006-07-11 21:28:53 +04:00
this - > no_addr | = be32_to_cpu ( fh [ 3 ] ) ;
2006-01-16 19:50:04 +03:00
break ;
default :
return NULL ;
}
2006-07-11 21:28:53 +04:00
return gfs2_export_ops . find_exported_dentry ( sb , & fh_obj , & parent ,
2006-01-16 19:50:04 +03:00
acceptable , context ) ;
}
2006-10-14 18:46:30 +04:00
static int gfs2_encode_fh ( struct dentry * dentry , __u32 * p , int * len ,
2006-01-16 19:50:04 +03:00
int connectable )
{
2006-10-14 18:46:30 +04:00
__be32 * fh = ( __force __be32 * ) p ;
2006-01-16 19:50:04 +03:00
struct inode * inode = dentry - > d_inode ;
2006-03-01 23:31:02 +03:00
struct super_block * sb = inode - > i_sb ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
2006-09-05 00:16:45 +04:00
if ( * len < GFS2_SMALL_FH_SIZE | |
( connectable & & * len < GFS2_LARGE_FH_SIZE ) )
2006-01-16 19:50:04 +03:00
return 255 ;
2007-05-15 18:37:50 +04:00
fh [ 0 ] = cpu_to_be32 ( ip - > i_no_formal_ino > > 32 ) ;
fh [ 1 ] = cpu_to_be32 ( ip - > i_no_formal_ino & 0xFFFFFFFF ) ;
fh [ 2 ] = cpu_to_be32 ( ip - > i_no_addr > > 32 ) ;
fh [ 3 ] = cpu_to_be32 ( ip - > i_no_addr & 0xFFFFFFFF ) ;
2006-09-05 00:16:45 +04:00
* len = GFS2_SMALL_FH_SIZE ;
2006-01-16 19:50:04 +03:00
2006-03-01 23:31:02 +03:00
if ( ! connectable | | inode = = sb - > s_root - > d_inode )
2006-01-16 19:50:04 +03:00
return * len ;
spin_lock ( & dentry - > d_lock ) ;
inode = dentry - > d_parent - > d_inode ;
2006-06-14 23:32:57 +04:00
ip = GFS2_I ( inode ) ;
igrab ( inode ) ;
2006-01-16 19:50:04 +03:00
spin_unlock ( & dentry - > d_lock ) ;
2007-05-15 18:37:50 +04:00
fh [ 4 ] = cpu_to_be32 ( ip - > i_no_formal_ino > > 32 ) ;
fh [ 5 ] = cpu_to_be32 ( ip - > i_no_formal_ino & 0xFFFFFFFF ) ;
fh [ 6 ] = cpu_to_be32 ( ip - > i_no_addr > > 32 ) ;
fh [ 7 ] = cpu_to_be32 ( ip - > i_no_addr & 0xFFFFFFFF ) ;
2006-07-11 21:28:53 +04:00
fh [ 8 ] = cpu_to_be32 ( inode - > i_mode ) ;
fh [ 9 ] = 0 ; /* pad to double word */
2006-09-05 00:16:45 +04:00
* len = GFS2_LARGE_FH_SIZE ;
2006-01-16 19:50:04 +03:00
2006-06-14 23:32:57 +04:00
iput ( inode ) ;
2006-01-16 19:50:04 +03:00
return * len ;
}
struct get_name_filldir {
2006-10-14 06:51:24 +04:00
struct gfs2_inum_host inum ;
2006-01-16 19:50:04 +03:00
char * name ;
} ;
2007-01-17 18:09:20 +03:00
static int get_name_filldir ( void * opaque , const char * name , int length ,
loff_t offset , u64 inum , unsigned int type )
2006-01-16 19:50:04 +03:00
{
2007-01-17 18:09:20 +03:00
struct get_name_filldir * gnfd = opaque ;
2006-01-16 19:50:04 +03:00
2007-01-17 18:09:20 +03:00
if ( inum ! = gnfd - > inum . no_addr )
2006-01-16 19:50:04 +03:00
return 0 ;
memcpy ( gnfd - > name , name , length ) ;
gnfd - > name [ length ] = 0 ;
return 1 ;
}
static int gfs2_get_name ( struct dentry * parent , char * name ,
struct dentry * child )
{
struct inode * dir = parent - > d_inode ;
struct inode * inode = child - > d_inode ;
struct gfs2_inode * dip , * ip ;
struct get_name_filldir gnfd ;
struct gfs2_holder gh ;
2006-09-04 20:49:07 +04:00
u64 offset = 0 ;
2006-01-16 19:50:04 +03:00
int error ;
if ( ! dir )
return - EINVAL ;
if ( ! S_ISDIR ( dir - > i_mode ) | | ! inode )
return - EINVAL ;
2006-06-14 23:32:57 +04:00
dip = GFS2_I ( dir ) ;
ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
* name = 0 ;
2007-05-15 18:37:50 +04:00
gnfd . inum . no_addr = ip - > i_no_addr ;
gnfd . inum . no_formal_ino = ip - > i_no_formal_ino ;
2006-01-16 19:50:04 +03:00
gnfd . name = name ;
error = gfs2_glock_nq_init ( dip - > i_gl , LM_ST_SHARED , 0 , & gh ) ;
if ( error )
return error ;
2006-03-28 23:14:04 +04:00
error = gfs2_dir_read ( dir , & offset , & gnfd , get_name_filldir ) ;
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & gh ) ;
if ( ! error & & ! * name )
error = - ENOENT ;
return error ;
}
static struct dentry * gfs2_get_parent ( struct dentry * child )
{
2006-03-28 23:14:04 +04:00
struct qstr dotdot ;
2006-01-16 19:50:04 +03:00
struct inode * inode ;
struct dentry * dentry ;
2006-03-28 23:14:04 +04:00
gfs2_str2qstr ( & dotdot , " .. " ) ;
2006-03-20 20:30:04 +03:00
inode = gfs2_lookupi ( child - > d_inode , & dotdot , 1 , NULL ) ;
if ( ! inode )
return ERR_PTR ( - ENOENT ) ;
2006-09-05 23:56:17 +04:00
/*
* In case of an error , @ inode carries the error value , and we
* have to return that as a ( n invalid ) pointer to dentry .
*/
2006-03-20 20:30:04 +03:00
if ( IS_ERR ( inode ) )
return ERR_PTR ( PTR_ERR ( inode ) ) ;
2006-01-16 19:50:04 +03:00
dentry = d_alloc_anon ( inode ) ;
if ( ! dentry ) {
iput ( inode ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2007-01-11 22:25:00 +03:00
dentry - > d_op = & gfs2_dops ;
2006-01-16 19:50:04 +03:00
return dentry ;
}
2006-07-11 21:28:53 +04:00
static struct dentry * gfs2_get_dentry ( struct super_block * sb , void * inum_obj )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_sbd * sdp = sb - > s_fs_info ;
2006-07-11 21:28:53 +04:00
struct gfs2_fh_obj * fh_obj = ( struct gfs2_fh_obj * ) inum_obj ;
2006-10-14 06:51:24 +04:00
struct gfs2_inum_host * inum = & fh_obj - > this ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder i_gh , ri_gh , rgd_gh ;
struct gfs2_rgrpd * rgd ;
struct inode * inode ;
struct dentry * dentry ;
int error ;
/* System files? */
2007-05-15 18:37:50 +04:00
inode = gfs2_ilookup ( sb , inum - > no_addr ) ;
2006-01-16 19:50:04 +03:00
if ( inode ) {
2007-05-15 18:37:50 +04:00
if ( GFS2_I ( inode ) - > i_no_formal_ino ! = inum - > no_formal_ino ) {
2006-01-16 19:50:04 +03:00
iput ( inode ) ;
return ERR_PTR ( - ESTALE ) ;
}
goto out_inode ;
}
2006-06-14 23:32:57 +04:00
error = gfs2_glock_nq_num ( sdp , inum - > no_addr , & gfs2_inode_glops ,
2007-01-22 20:10:39 +03:00
LM_ST_SHARED , LM_FLAG_ANY , & i_gh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return ERR_PTR ( error ) ;
error = gfs2_rindex_hold ( sdp , & ri_gh ) ;
if ( error )
goto fail ;
error = - EINVAL ;
rgd = gfs2_blk2rgrpd ( sdp , inum - > no_addr ) ;
if ( ! rgd )
goto fail_rindex ;
error = gfs2_glock_nq_init ( rgd - > rd_gl , LM_ST_SHARED , 0 , & rgd_gh ) ;
if ( error )
goto fail_rindex ;
error = - ESTALE ;
if ( gfs2_get_block_type ( rgd , inum - > no_addr ) ! = GFS2_BLKST_DINODE )
goto fail_rgd ;
gfs2_glock_dq_uninit ( & rgd_gh ) ;
gfs2_glock_dq_uninit ( & ri_gh ) ;
2007-06-28 01:07:08 +04:00
inode = gfs2_inode_lookup ( sb , fh_obj - > imode ,
inum - > no_addr ,
inum - > no_formal_ino ) ;
2006-06-14 23:32:57 +04:00
if ( ! inode )
goto fail ;
if ( IS_ERR ( inode ) ) {
error = PTR_ERR ( inode ) ;
2006-01-16 19:50:04 +03:00
goto fail ;
2006-06-14 23:32:57 +04:00
}
2006-01-16 19:50:04 +03:00
2006-06-14 23:32:57 +04:00
error = gfs2_inode_refresh ( GFS2_I ( inode ) ) ;
2006-01-16 19:50:04 +03:00
if ( error ) {
2006-06-14 23:32:57 +04:00
iput ( inode ) ;
2006-01-16 19:50:04 +03:00
goto fail ;
}
2007-05-15 18:37:50 +04:00
if ( GFS2_I ( inode ) - > i_no_formal_ino ! = inum - > no_formal_ino ) {
iput ( inode ) ;
goto fail ;
}
2006-01-16 19:50:04 +03:00
error = - EIO ;
2006-06-14 23:32:57 +04:00
if ( GFS2_I ( inode ) - > i_di . di_flags & GFS2_DIF_SYSTEM ) {
iput ( inode ) ;
2006-01-16 19:50:04 +03:00
goto fail ;
}
gfs2_glock_dq_uninit ( & i_gh ) ;
2006-06-14 23:32:57 +04:00
out_inode :
2006-01-16 19:50:04 +03:00
dentry = d_alloc_anon ( inode ) ;
if ( ! dentry ) {
iput ( inode ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2007-01-11 22:25:00 +03:00
dentry - > d_op = & gfs2_dops ;
2006-01-16 19:50:04 +03:00
return dentry ;
2006-06-14 23:32:57 +04:00
fail_rgd :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & rgd_gh ) ;
2006-06-14 23:32:57 +04:00
fail_rindex :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & ri_gh ) ;
2006-06-14 23:32:57 +04:00
fail :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & i_gh ) ;
return ERR_PTR ( error ) ;
}
struct export_operations gfs2_export_ops = {
. decode_fh = gfs2_decode_fh ,
. encode_fh = gfs2_encode_fh ,
. get_name = gfs2_get_name ,
. get_parent = gfs2_get_parent ,
. get_dentry = gfs2_get_dentry ,
} ;