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>
2007-07-17 15:04:28 +04:00
# include <linux/exportfs.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 "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"
2008-10-14 19:05:55 +04:00
# include "super.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
2007-06-28 01:07:53 +04:00
# define GFS2_LARGE_FH_SIZE 8
2007-07-10 15:28:27 +04:00
# define GFS2_OLD_FH_SIZE 10
2007-06-01 17:11:58 +04:00
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-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 dentry * dentry ;
2006-09-05 23:56:17 +04:00
/*
2008-08-11 17:49:04 +04:00
* XXX ( hch ) : it would be a good idea to keep this around as a
* static variable .
2006-09-05 23:56:17 +04:00
*/
2008-08-11 17:49:04 +04:00
gfs2_str2qstr ( & dotdot , " .. " ) ;
2006-01-16 19:50:04 +03:00
2008-08-11 17:49:04 +04:00
dentry = d_obtain_alias ( gfs2_lookupi ( child - > d_inode , & dotdot , 1 ) ) ;
if ( ! IS_ERR ( dentry ) )
dentry - > d_op = & gfs2_dops ;
2006-01-16 19:50:04 +03:00
return dentry ;
}
2007-10-22 03:42:14 +04:00
static struct dentry * gfs2_get_dentry ( struct super_block * sb ,
2009-09-08 21:00:30 +04:00
struct gfs2_inum_host * inum )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_sbd * sdp = sb - > s_fs_info ;
2009-09-08 21:00:30 +04:00
struct gfs2_holder i_gh ;
2006-01-16 19:50:04 +03:00
struct inode * inode ;
struct dentry * dentry ;
int error ;
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 ) ;
2009-09-08 21:00:30 +04:00
error = gfs2_check_blk_type ( sdp , inum - > no_addr , GFS2_BLKST_DINODE ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto fail ;
2009-09-08 21:00:30 +04:00
inode = gfs2_inode_lookup ( sb , DT_UNKNOWN , inum - > no_addr , 0 , 0 ) ;
2006-06-14 23:32:57 +04:00
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-06-28 01:07:53 +04:00
/* Pick up the works we bypass in gfs2_inode_lookup */
if ( inode - > i_state & I_NEW )
gfs2_set_iop ( inode ) ;
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 ;
2008-11-04 13:05:22 +03:00
if ( GFS2_I ( inode ) - > i_diskflags & GFS2_DIF_SYSTEM ) {
2006-06-14 23:32:57 +04:00
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 :
2008-08-11 17:49:04 +04:00
dentry = d_obtain_alias ( inode ) ;
if ( ! IS_ERR ( dentry ) )
dentry - > d_op = & gfs2_dops ;
2006-01-16 19:50:04 +03:00
return dentry ;
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 ) ;
}
2007-10-22 03:42:14 +04:00
static struct dentry * gfs2_fh_to_dentry ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
struct gfs2_inum_host this ;
__be32 * fh = ( __force __be32 * ) fid - > raw ;
switch ( fh_type ) {
case GFS2_SMALL_FH_SIZE :
case GFS2_LARGE_FH_SIZE :
case GFS2_OLD_FH_SIZE :
this . no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 0 ] ) ) < < 32 ;
this . no_formal_ino | = be32_to_cpu ( fh [ 1 ] ) ;
this . no_addr = ( ( u64 ) be32_to_cpu ( fh [ 2 ] ) ) < < 32 ;
this . no_addr | = be32_to_cpu ( fh [ 3 ] ) ;
return gfs2_get_dentry ( sb , & this ) ;
default :
return NULL ;
}
}
static struct dentry * gfs2_fh_to_parent ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
struct gfs2_inum_host parent ;
__be32 * fh = ( __force __be32 * ) fid - > raw ;
switch ( fh_type ) {
case GFS2_LARGE_FH_SIZE :
case GFS2_OLD_FH_SIZE :
parent . no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 4 ] ) ) < < 32 ;
parent . no_formal_ino | = be32_to_cpu ( fh [ 5 ] ) ;
parent . no_addr = ( ( u64 ) be32_to_cpu ( fh [ 6 ] ) ) < < 32 ;
parent . no_addr | = be32_to_cpu ( fh [ 7 ] ) ;
return gfs2_get_dentry ( sb , & parent ) ;
default :
return NULL ;
}
}
2007-10-22 03:42:17 +04:00
const struct export_operations gfs2_export_ops = {
2006-01-16 19:50:04 +03:00
. encode_fh = gfs2_encode_fh ,
2007-10-22 03:42:14 +04:00
. fh_to_dentry = gfs2_fh_to_dentry ,
. fh_to_parent = gfs2_fh_to_parent ,
2006-01-16 19:50:04 +03:00
. get_name = gfs2_get_name ,
. get_parent = gfs2_get_parent ,
} ;