2011-01-10 22:51:47 +03:00
/*
* linux / fs / 9 p / vfs_inode_dotl . c
*
* This file contains vfs inode ops for the 9 P2000 . L protocol .
*
* Copyright ( C ) 2004 by Eric Van Hensbergen < ericvh @ gmail . com >
* Copyright ( C ) 2002 by Ron Minnich < rminnich @ lanl . gov >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* 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
* along with this program ; if not , write to :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/pagemap.h>
# include <linux/stat.h>
# include <linux/string.h>
# include <linux/inet.h>
# include <linux/namei.h>
# include <linux/idr.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/xattr.h>
# include <linux/posix_acl.h>
# include <net/9p/9p.h>
# include <net/9p/client.h>
# include "v9fs.h"
# include "v9fs_vfs.h"
# include "fid.h"
# include "cache.h"
# include "xattr.h"
# include "acl.h"
static int
2011-07-26 09:52:52 +04:00
v9fs_vfs_mknod_dotl ( struct inode * dir , struct dentry * dentry , umode_t omode ,
2011-01-10 22:51:47 +03:00
dev_t rdev ) ;
/**
* v9fs_get_fsgid_for_create - Helper function to get the gid for creating a
* new file system object . This checks the S_ISGID to determine the owning
* group of the new file system object .
*/
static gid_t v9fs_get_fsgid_for_create ( struct inode * dir_inode )
{
BUG_ON ( dir_inode = = NULL ) ;
if ( dir_inode - > i_mode & S_ISGID ) {
/* set_gid bit is set.*/
return dir_inode - > i_gid ;
}
return current_fsgid ( ) ;
}
2011-07-11 20:40:59 +04:00
static int v9fs_test_inode_dotl ( struct inode * inode , void * data )
{
struct v9fs_inode * v9inode = V9FS_I ( inode ) ;
struct p9_stat_dotl * st = ( struct p9_stat_dotl * ) data ;
/* don't match inode of different type */
if ( ( inode - > i_mode & S_IFMT ) ! = ( st - > st_mode & S_IFMT ) )
return 0 ;
if ( inode - > i_generation ! = st - > st_gen )
return 0 ;
/* compare qid details */
if ( memcmp ( & v9inode - > qid . version ,
& st - > qid . version , sizeof ( v9inode - > qid . version ) ) )
return 0 ;
if ( v9inode - > qid . type ! = st - > qid . type )
return 0 ;
return 1 ;
}
2011-07-06 15:02:31 +04:00
/* Always get a new inode */
static int v9fs_test_new_inode_dotl ( struct inode * inode , void * data )
{
return 0 ;
}
2011-07-11 20:40:59 +04:00
static int v9fs_set_inode_dotl ( struct inode * inode , void * data )
{
struct v9fs_inode * v9inode = V9FS_I ( inode ) ;
struct p9_stat_dotl * st = ( struct p9_stat_dotl * ) data ;
memcpy ( & v9inode - > qid , & st - > qid , sizeof ( st - > qid ) ) ;
inode - > i_generation = st - > st_gen ;
return 0 ;
}
2011-02-28 14:34:01 +03:00
static struct inode * v9fs_qid_iget_dotl ( struct super_block * sb ,
struct p9_qid * qid ,
struct p9_fid * fid ,
2011-07-06 15:02:31 +04:00
struct p9_stat_dotl * st ,
int new )
2011-02-28 14:34:01 +03:00
{
int retval ;
unsigned long i_ino ;
struct inode * inode ;
struct v9fs_session_info * v9ses = sb - > s_fs_info ;
2011-07-06 15:02:31 +04:00
int ( * test ) ( struct inode * , void * ) ;
if ( new )
test = v9fs_test_new_inode_dotl ;
else
test = v9fs_test_inode_dotl ;
2011-02-28 14:34:01 +03:00
i_ino = v9fs_qid2ino ( qid ) ;
2011-07-06 15:02:31 +04:00
inode = iget5_locked ( sb , i_ino , test , v9fs_set_inode_dotl , st ) ;
2011-02-28 14:34:01 +03:00
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
if ( ! ( inode - > i_state & I_NEW ) )
return inode ;
/*
* initialize the inode with the stat info
* FIXME ! ! we may need support for stale inodes
* later .
*/
2011-07-11 20:40:59 +04:00
inode - > i_ino = i_ino ;
2011-07-25 22:06:33 +04:00
retval = v9fs_init_inode ( v9ses , inode ,
st - > st_mode , new_decode_dev ( st - > st_rdev ) ) ;
2011-02-28 14:34:01 +03:00
if ( retval )
goto error ;
v9fs_stat2inode_dotl ( st , inode ) ;
# ifdef CONFIG_9P_FSCACHE
v9fs_cache_inode_get_cookie ( inode ) ;
# endif
retval = v9fs_get_acl ( inode , fid ) ;
if ( retval )
goto error ;
unlock_new_inode ( inode ) ;
return inode ;
error :
unlock_new_inode ( inode ) ;
iput ( inode ) ;
return ERR_PTR ( retval ) ;
}
2011-01-10 22:51:47 +03:00
struct inode *
2011-02-28 14:34:02 +03:00
v9fs_inode_from_fid_dotl ( struct v9fs_session_info * v9ses , struct p9_fid * fid ,
2011-07-06 15:02:31 +04:00
struct super_block * sb , int new )
2011-01-10 22:51:47 +03:00
{
struct p9_stat_dotl * st ;
2011-02-28 14:34:01 +03:00
struct inode * inode = NULL ;
2011-01-10 22:51:47 +03:00
2011-07-11 20:40:59 +04:00
st = p9_client_getattr_dotl ( fid , P9_STATS_BASIC | P9_STATS_GEN ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( st ) )
return ERR_CAST ( st ) ;
2011-07-06 15:02:31 +04:00
inode = v9fs_qid_iget_dotl ( sb , & st - > qid , fid , st , new ) ;
2011-01-10 22:51:47 +03:00
kfree ( st ) ;
2011-02-28 14:34:01 +03:00
return inode ;
2011-01-10 22:51:47 +03:00
}
2011-08-03 18:25:32 +04:00
struct dotl_openflag_map {
int open_flag ;
int dotl_flag ;
} ;
static int v9fs_mapped_dotl_flags ( int flags )
{
int i ;
int rflags = 0 ;
struct dotl_openflag_map dotl_oflag_map [ ] = {
{ O_CREAT , P9_DOTL_CREATE } ,
{ O_EXCL , P9_DOTL_EXCL } ,
{ O_NOCTTY , P9_DOTL_NOCTTY } ,
{ O_TRUNC , P9_DOTL_TRUNC } ,
{ O_APPEND , P9_DOTL_APPEND } ,
{ O_NONBLOCK , P9_DOTL_NONBLOCK } ,
{ O_DSYNC , P9_DOTL_DSYNC } ,
{ FASYNC , P9_DOTL_FASYNC } ,
{ O_DIRECT , P9_DOTL_DIRECT } ,
{ O_LARGEFILE , P9_DOTL_LARGEFILE } ,
{ O_DIRECTORY , P9_DOTL_DIRECTORY } ,
{ O_NOFOLLOW , P9_DOTL_NOFOLLOW } ,
{ O_NOATIME , P9_DOTL_NOATIME } ,
{ O_CLOEXEC , P9_DOTL_CLOEXEC } ,
{ O_SYNC , P9_DOTL_SYNC } ,
} ;
for ( i = 0 ; i < ARRAY_SIZE ( dotl_oflag_map ) ; i + + ) {
if ( flags & dotl_oflag_map [ i ] . open_flag )
rflags | = dotl_oflag_map [ i ] . dotl_flag ;
}
return rflags ;
}
/**
* v9fs_open_to_dotl_flags - convert Linux specific open flags to
* plan 9 open flag .
* @ flags : flags to convert
*/
int v9fs_open_to_dotl_flags ( int flags )
{
int rflags = 0 ;
/*
* We have same bits for P9_DOTL_READONLY , P9_DOTL_WRONLY
* and P9_DOTL_NOACCESS
*/
rflags | = flags & O_ACCMODE ;
rflags | = v9fs_mapped_dotl_flags ( flags ) ;
return rflags ;
}
2011-01-10 22:51:47 +03:00
/**
* v9fs_vfs_create_dotl - VFS hook to create files for 9 P2000 . L protocol .
* @ dir : directory inode that is being created
* @ dentry : dentry that is being deleted
* @ mode : create permissions
* @ nd : path information
*
*/
static int
2011-07-26 09:42:34 +04:00
v9fs_vfs_create_dotl ( struct inode * dir , struct dentry * dentry , umode_t omode ,
2011-01-10 22:51:47 +03:00
struct nameidata * nd )
{
int err = 0 ;
gid_t gid ;
int flags ;
2011-07-24 02:37:50 +04:00
umode_t mode ;
2011-02-28 14:34:03 +03:00
char * name = NULL ;
2011-01-10 22:51:47 +03:00
struct file * filp ;
struct p9_qid qid ;
struct inode * inode ;
2011-02-28 14:34:03 +03:00
struct p9_fid * fid = NULL ;
struct v9fs_inode * v9inode ;
struct p9_fid * dfid , * ofid , * inode_fid ;
struct v9fs_session_info * v9ses ;
2011-01-10 22:51:47 +03:00
struct posix_acl * pacl = NULL , * dacl = NULL ;
v9ses = v9fs_inode2v9ses ( dir ) ;
2011-06-26 05:17:17 +04:00
if ( nd )
2011-06-26 03:15:54 +04:00
flags = nd - > intent . open . flags ;
2011-01-10 22:51:47 +03:00
else {
/*
* create call without LOOKUP_OPEN is due
* to mknod of regular files . So use mknod
* operation .
*/
return v9fs_vfs_mknod_dotl ( dir , dentry , omode , 0 ) ;
}
name = ( char * ) dentry - > d_name . name ;
2012-01-11 03:09:01 +04:00
p9_debug ( P9_DEBUG_VFS , " name:%s flags:0x%x mode:0x%hx \n " ,
2011-11-28 22:40:46 +04:00
name , flags , omode ) ;
2011-01-10 22:51:47 +03:00
dfid = v9fs_fid_lookup ( dentry - > d_parent ) ;
if ( IS_ERR ( dfid ) ) {
err = PTR_ERR ( dfid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " fid lookup failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
/* clone a fid to use for creation */
ofid = p9_client_walk ( dfid , 0 , NULL , 1 ) ;
if ( IS_ERR ( ofid ) ) {
err = PTR_ERR ( ofid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_walk failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
gid = v9fs_get_fsgid_for_create ( dir ) ;
mode = omode ;
/* Update mode based on ACL value */
err = v9fs_acl_mode ( dir , & mode , & dacl , & pacl ) ;
if ( err ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " Failed to get acl values in creat %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
2011-08-03 18:25:32 +04:00
err = p9_client_create_dotl ( ofid , name , v9fs_open_to_dotl_flags ( flags ) ,
mode , gid , & qid ) ;
2011-01-10 22:51:47 +03:00
if ( err < 0 ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_open_dotl failed in creat %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
2011-02-28 14:34:08 +03:00
v9fs_invalidate_inode_attr ( dir ) ;
2011-01-10 22:51:47 +03:00
2011-01-10 23:22:21 +03:00
/* instantiate inode and assign the unopened fid to the dentry */
fid = p9_client_walk ( dfid , 1 , & name , 1 ) ;
if ( IS_ERR ( fid ) ) {
err = PTR_ERR ( fid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_walk failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
2011-01-10 23:22:21 +03:00
goto error ;
2011-01-10 22:51:47 +03:00
}
2011-07-06 15:02:31 +04:00
inode = v9fs_get_new_inode_from_fid ( v9ses , fid , dir - > i_sb ) ;
2011-01-10 23:22:21 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " inode creation failed %d \n " , err ) ;
2011-01-10 23:22:21 +03:00
goto error ;
}
err = v9fs_fid_add ( dentry , fid ) ;
if ( err < 0 )
goto error ;
2011-07-25 22:06:32 +04:00
d_instantiate ( dentry , inode ) ;
2011-01-10 23:22:21 +03:00
2011-01-10 22:51:47 +03:00
/* Now set the ACL based on the default value */
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( dentry , & dacl , & pacl ) ;
2011-02-28 14:34:03 +03:00
v9inode = V9FS_I ( inode ) ;
2011-03-08 14:09:46 +03:00
mutex_lock ( & v9inode - > v_mutex ) ;
2011-03-08 14:09:49 +03:00
if ( v9ses - > cache & & ! v9inode - > writeback_fid & &
( ( flags & O_ACCMODE ) ! = O_RDONLY ) ) {
2011-02-28 14:33:57 +03:00
/*
2011-02-28 14:34:03 +03:00
* clone a fid and add it to writeback_fid
2011-02-28 14:33:57 +03:00
* we do it during open time instead of
* page dirty time via write_begin / page_mkwrite
* because we want write after unlink usecase
* to work .
*/
inode_fid = v9fs_writeback_fid ( dentry ) ;
if ( IS_ERR ( inode_fid ) ) {
err = PTR_ERR ( inode_fid ) ;
2011-03-08 14:09:46 +03:00
mutex_unlock ( & v9inode - > v_mutex ) ;
2011-05-20 22:55:51 +04:00
goto err_clunk_old_fid ;
2011-02-28 14:33:57 +03:00
}
2011-02-28 14:34:03 +03:00
v9inode - > writeback_fid = ( void * ) inode_fid ;
2011-02-28 14:33:57 +03:00
}
2011-03-08 14:09:46 +03:00
mutex_unlock ( & v9inode - > v_mutex ) ;
2011-01-10 23:22:21 +03:00
/* Since we are opening a file, assign the open fid to the file */
filp = lookup_instantiate_filp ( nd , dentry , generic_file_open ) ;
if ( IS_ERR ( filp ) ) {
2011-05-20 22:55:51 +04:00
err = PTR_ERR ( filp ) ;
goto err_clunk_old_fid ;
2011-01-10 23:22:21 +03:00
}
filp - > private_data = ofid ;
2011-02-28 14:33:55 +03:00
# ifdef CONFIG_9P_FSCACHE
if ( v9ses - > cache )
v9fs_cache_inode_set_cookie ( inode , filp ) ;
# endif
2011-01-10 22:51:47 +03:00
return 0 ;
error :
if ( fid )
p9_client_clunk ( fid ) ;
2011-05-20 22:55:51 +04:00
err_clunk_old_fid :
if ( ofid )
p9_client_clunk ( ofid ) ;
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( NULL , & dacl , & pacl ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
/**
* v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
* @ dir : inode that is being unlinked
* @ dentry : dentry that is being unlinked
* @ mode : mode for new directory
*
*/
static int v9fs_vfs_mkdir_dotl ( struct inode * dir ,
2011-07-26 09:41:39 +04:00
struct dentry * dentry , umode_t omode )
2011-01-10 22:51:47 +03:00
{
int err ;
struct v9fs_session_info * v9ses ;
struct p9_fid * fid = NULL , * dfid = NULL ;
gid_t gid ;
char * name ;
2011-07-24 02:37:50 +04:00
umode_t mode ;
2011-01-10 22:51:47 +03:00
struct inode * inode ;
struct p9_qid qid ;
struct dentry * dir_dentry ;
struct posix_acl * dacl = NULL , * pacl = NULL ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " name %s \n " , dentry - > d_name . name ) ;
2011-01-10 22:51:47 +03:00
err = 0 ;
v9ses = v9fs_inode2v9ses ( dir ) ;
omode | = S_IFDIR ;
if ( dir - > i_mode & S_ISGID )
omode | = S_ISGID ;
2012-04-03 04:02:53 +04:00
dir_dentry = dentry - > d_parent ;
2011-01-10 22:51:47 +03:00
dfid = v9fs_fid_lookup ( dir_dentry ) ;
if ( IS_ERR ( dfid ) ) {
err = PTR_ERR ( dfid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " fid lookup failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
dfid = NULL ;
goto error ;
}
gid = v9fs_get_fsgid_for_create ( dir ) ;
mode = omode ;
/* Update mode based on ACL value */
err = v9fs_acl_mode ( dir , & mode , & dacl , & pacl ) ;
if ( err ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " Failed to get acl values in mkdir %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
name = ( char * ) dentry - > d_name . name ;
err = p9_client_mkdir_dotl ( dfid , name , mode , gid , & qid ) ;
if ( err < 0 )
goto error ;
/* instantiate inode and assign the unopened fid to the dentry */
if ( v9ses - > cache = = CACHE_LOOSE | | v9ses - > cache = = CACHE_FSCACHE ) {
fid = p9_client_walk ( dfid , 1 , & name , 1 ) ;
if ( IS_ERR ( fid ) ) {
err = PTR_ERR ( fid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_walk failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
goto error ;
}
2011-07-06 15:02:31 +04:00
inode = v9fs_get_new_inode_from_fid ( v9ses , fid , dir - > i_sb ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " inode creation failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
err = v9fs_fid_add ( dentry , fid ) ;
if ( err < 0 )
goto error ;
2011-07-25 22:06:32 +04:00
d_instantiate ( dentry , inode ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
} else {
/*
* Not in cached mode . No need to populate
* inode with stat . We need to get an inode
* so that we can set the acl with dentry
*/
2011-07-25 22:06:33 +04:00
inode = v9fs_get_inode ( dir - > i_sb , mode , 0 ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
goto error ;
}
d_instantiate ( dentry , inode ) ;
}
/* Now set the ACL based on the default value */
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( dentry , & dacl , & pacl ) ;
2011-02-28 14:34:05 +03:00
inc_nlink ( dir ) ;
2011-02-28 14:34:08 +03:00
v9fs_invalidate_inode_attr ( dir ) ;
2011-01-10 22:51:47 +03:00
error :
if ( fid )
p9_client_clunk ( fid ) ;
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( NULL , & dacl , & pacl ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
static int
v9fs_vfs_getattr_dotl ( struct vfsmount * mnt , struct dentry * dentry ,
struct kstat * stat )
{
int err ;
struct v9fs_session_info * v9ses ;
struct p9_fid * fid ;
struct p9_stat_dotl * st ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " dentry: %p \n " , dentry ) ;
2011-01-10 22:51:47 +03:00
err = - EPERM ;
2011-03-08 14:09:50 +03:00
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2011-02-28 14:34:01 +03:00
if ( v9ses - > cache = = CACHE_LOOSE | | v9ses - > cache = = CACHE_FSCACHE ) {
generic_fillattr ( dentry - > d_inode , stat ) ;
return 0 ;
}
2011-01-10 22:51:47 +03:00
fid = v9fs_fid_lookup ( dentry ) ;
if ( IS_ERR ( fid ) )
return PTR_ERR ( fid ) ;
/* Ask for all the fields in stat structure. Server will return
* whatever it supports
*/
st = p9_client_getattr_dotl ( fid , P9_STATS_ALL ) ;
if ( IS_ERR ( st ) )
return PTR_ERR ( st ) ;
v9fs_stat2inode_dotl ( st , dentry - > d_inode ) ;
generic_fillattr ( dentry - > d_inode , stat ) ;
/* Change block size to what the server returned */
stat - > blksize = st - > st_blksize ;
kfree ( st ) ;
return 0 ;
}
2011-12-18 21:33:13 +04:00
/*
* Attribute flags .
*/
# define P9_ATTR_MODE (1 << 0)
# define P9_ATTR_UID (1 << 1)
# define P9_ATTR_GID (1 << 2)
# define P9_ATTR_SIZE (1 << 3)
# define P9_ATTR_ATIME (1 << 4)
# define P9_ATTR_MTIME (1 << 5)
# define P9_ATTR_CTIME (1 << 6)
# define P9_ATTR_ATIME_SET (1 << 7)
# define P9_ATTR_MTIME_SET (1 << 8)
struct dotl_iattr_map {
int iattr_valid ;
int p9_iattr_valid ;
} ;
static int v9fs_mapped_iattr_valid ( int iattr_valid )
{
int i ;
int p9_iattr_valid = 0 ;
struct dotl_iattr_map dotl_iattr_map [ ] = {
{ ATTR_MODE , P9_ATTR_MODE } ,
{ ATTR_UID , P9_ATTR_UID } ,
{ ATTR_GID , P9_ATTR_GID } ,
{ ATTR_SIZE , P9_ATTR_SIZE } ,
{ ATTR_ATIME , P9_ATTR_ATIME } ,
{ ATTR_MTIME , P9_ATTR_MTIME } ,
{ ATTR_CTIME , P9_ATTR_CTIME } ,
{ ATTR_ATIME_SET , P9_ATTR_ATIME_SET } ,
{ ATTR_MTIME_SET , P9_ATTR_MTIME_SET } ,
} ;
for ( i = 0 ; i < ARRAY_SIZE ( dotl_iattr_map ) ; i + + ) {
if ( iattr_valid & dotl_iattr_map [ i ] . iattr_valid )
p9_iattr_valid | = dotl_iattr_map [ i ] . p9_iattr_valid ;
}
return p9_iattr_valid ;
}
2011-01-10 22:51:47 +03:00
/**
* v9fs_vfs_setattr_dotl - set file metadata
* @ dentry : file whose metadata to set
* @ iattr : metadata assignment structure
*
*/
int v9fs_vfs_setattr_dotl ( struct dentry * dentry , struct iattr * iattr )
{
int retval ;
struct v9fs_session_info * v9ses ;
struct p9_fid * fid ;
struct p9_iattr_dotl p9attr ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " \n " ) ;
2011-01-10 22:51:47 +03:00
retval = inode_change_ok ( dentry - > d_inode , iattr ) ;
if ( retval )
return retval ;
2011-12-18 21:33:13 +04:00
p9attr . valid = v9fs_mapped_iattr_valid ( iattr - > ia_valid ) ;
2011-01-10 22:51:47 +03:00
p9attr . mode = iattr - > ia_mode ;
p9attr . uid = iattr - > ia_uid ;
p9attr . gid = iattr - > ia_gid ;
p9attr . size = iattr - > ia_size ;
p9attr . atime_sec = iattr - > ia_atime . tv_sec ;
p9attr . atime_nsec = iattr - > ia_atime . tv_nsec ;
p9attr . mtime_sec = iattr - > ia_mtime . tv_sec ;
p9attr . mtime_nsec = iattr - > ia_mtime . tv_nsec ;
retval = - EPERM ;
2011-03-08 14:09:50 +03:00
v9ses = v9fs_dentry2v9ses ( dentry ) ;
2011-01-10 22:51:47 +03:00
fid = v9fs_fid_lookup ( dentry ) ;
if ( IS_ERR ( fid ) )
return PTR_ERR ( fid ) ;
2011-02-28 14:34:11 +03:00
/* Write all dirty data */
if ( S_ISREG ( dentry - > d_inode - > i_mode ) )
filemap_write_and_wait ( dentry - > d_inode - > i_mapping ) ;
2011-02-28 14:34:10 +03:00
retval = p9_client_setattr ( fid , & p9attr ) ;
if ( retval < 0 )
return retval ;
2011-01-10 22:51:47 +03:00
2011-03-08 14:09:48 +03:00
if ( ( iattr - > ia_valid & ATTR_SIZE ) & &
iattr - > ia_size ! = i_size_read ( dentry - > d_inode ) )
truncate_setsize ( dentry - > d_inode , iattr - > ia_size ) ;
v9fs_invalidate_inode_attr ( dentry - > d_inode ) ;
2011-01-10 22:51:47 +03:00
setattr_copy ( dentry - > d_inode , iattr ) ;
mark_inode_dirty ( dentry - > d_inode ) ;
if ( iattr - > ia_valid & ATTR_MODE ) {
/* We also want to update ACL when we update mode bits */
retval = v9fs_acl_chmod ( dentry ) ;
if ( retval < 0 )
return retval ;
}
return 0 ;
}
/**
* v9fs_stat2inode_dotl - populate an inode structure with stat info
* @ stat : stat structure
* @ inode : inode to populate
* @ sb : superblock of filesystem
*
*/
void
v9fs_stat2inode_dotl ( struct p9_stat_dotl * stat , struct inode * inode )
{
2011-07-26 10:53:22 +04:00
umode_t mode ;
2011-02-28 14:34:06 +03:00
struct v9fs_inode * v9inode = V9FS_I ( inode ) ;
2011-01-10 22:51:47 +03:00
if ( ( stat - > st_result_mask & P9_STATS_BASIC ) = = P9_STATS_BASIC ) {
inode - > i_atime . tv_sec = stat - > st_atime_sec ;
inode - > i_atime . tv_nsec = stat - > st_atime_nsec ;
inode - > i_mtime . tv_sec = stat - > st_mtime_sec ;
inode - > i_mtime . tv_nsec = stat - > st_mtime_nsec ;
inode - > i_ctime . tv_sec = stat - > st_ctime_sec ;
inode - > i_ctime . tv_nsec = stat - > st_ctime_nsec ;
inode - > i_uid = stat - > st_uid ;
inode - > i_gid = stat - > st_gid ;
2011-10-28 16:13:29 +04:00
set_nlink ( inode , stat - > st_nlink ) ;
2011-01-10 22:51:47 +03:00
2011-07-25 22:06:33 +04:00
mode = stat - > st_mode & S_IALLUGO ;
mode | = inode - > i_mode & ~ S_IALLUGO ;
inode - > i_mode = mode ;
2011-01-10 22:51:47 +03:00
i_size_write ( inode , stat - > st_size ) ;
inode - > i_blocks = stat - > st_blocks ;
} else {
if ( stat - > st_result_mask & P9_STATS_ATIME ) {
inode - > i_atime . tv_sec = stat - > st_atime_sec ;
inode - > i_atime . tv_nsec = stat - > st_atime_nsec ;
}
if ( stat - > st_result_mask & P9_STATS_MTIME ) {
inode - > i_mtime . tv_sec = stat - > st_mtime_sec ;
inode - > i_mtime . tv_nsec = stat - > st_mtime_nsec ;
}
if ( stat - > st_result_mask & P9_STATS_CTIME ) {
inode - > i_ctime . tv_sec = stat - > st_ctime_sec ;
inode - > i_ctime . tv_nsec = stat - > st_ctime_nsec ;
}
if ( stat - > st_result_mask & P9_STATS_UID )
inode - > i_uid = stat - > st_uid ;
if ( stat - > st_result_mask & P9_STATS_GID )
inode - > i_gid = stat - > st_gid ;
if ( stat - > st_result_mask & P9_STATS_NLINK )
2011-10-28 16:13:29 +04:00
set_nlink ( inode , stat - > st_nlink ) ;
2011-01-10 22:51:47 +03:00
if ( stat - > st_result_mask & P9_STATS_MODE ) {
inode - > i_mode = stat - > st_mode ;
if ( ( S_ISBLK ( inode - > i_mode ) ) | |
( S_ISCHR ( inode - > i_mode ) ) )
init_special_inode ( inode , inode - > i_mode ,
inode - > i_rdev ) ;
}
if ( stat - > st_result_mask & P9_STATS_RDEV )
inode - > i_rdev = new_decode_dev ( stat - > st_rdev ) ;
if ( stat - > st_result_mask & P9_STATS_SIZE )
i_size_write ( inode , stat - > st_size ) ;
if ( stat - > st_result_mask & P9_STATS_BLOCKS )
inode - > i_blocks = stat - > st_blocks ;
}
if ( stat - > st_result_mask & P9_STATS_GEN )
2011-07-11 20:40:59 +04:00
inode - > i_generation = stat - > st_gen ;
2011-01-10 22:51:47 +03:00
/* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION
* because the inode structure does not have fields for them .
*/
2011-02-28 14:34:06 +03:00
v9inode - > cache_validity & = ~ V9FS_INO_INVALID_ATTR ;
2011-01-10 22:51:47 +03:00
}
static int
v9fs_vfs_symlink_dotl ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
int err ;
gid_t gid ;
2011-02-28 14:34:08 +03:00
char * name ;
struct p9_qid qid ;
struct inode * inode ;
struct p9_fid * dfid ;
struct p9_fid * fid = NULL ;
struct v9fs_session_info * v9ses ;
2011-01-10 22:51:47 +03:00
name = ( char * ) dentry - > d_name . name ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " %lu,%s,%s \n " , dir - > i_ino , name , symname ) ;
2011-01-10 22:51:47 +03:00
v9ses = v9fs_inode2v9ses ( dir ) ;
dfid = v9fs_fid_lookup ( dentry - > d_parent ) ;
if ( IS_ERR ( dfid ) ) {
err = PTR_ERR ( dfid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " fid lookup failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
gid = v9fs_get_fsgid_for_create ( dir ) ;
/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
err = p9_client_symlink ( dfid , name , ( char * ) symname , gid , & qid ) ;
if ( err < 0 ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_symlink failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
2011-02-28 14:34:08 +03:00
v9fs_invalidate_inode_attr ( dir ) ;
2011-01-10 22:51:47 +03:00
if ( v9ses - > cache ) {
/* Now walk from the parent so we can get an unopened fid. */
fid = p9_client_walk ( dfid , 1 , & name , 1 ) ;
if ( IS_ERR ( fid ) ) {
err = PTR_ERR ( fid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_walk failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
goto error ;
}
/* instantiate inode and assign the unopened fid to dentry */
2011-07-06 15:02:31 +04:00
inode = v9fs_get_new_inode_from_fid ( v9ses , fid , dir - > i_sb ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " inode creation failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
err = v9fs_fid_add ( dentry , fid ) ;
if ( err < 0 )
goto error ;
2011-07-25 22:06:32 +04:00
d_instantiate ( dentry , inode ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
} else {
/* Not in cached mode. No need to populate inode with stat */
2011-07-25 22:06:33 +04:00
inode = v9fs_get_inode ( dir - > i_sb , S_IFLNK , 0 ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
goto error ;
}
d_instantiate ( dentry , inode ) ;
}
error :
if ( fid )
p9_client_clunk ( fid ) ;
return err ;
}
/**
* v9fs_vfs_link_dotl - create a hardlink for dotl
* @ old_dentry : dentry for file to link to
* @ dir : inode destination for new link
* @ dentry : dentry for link
*
*/
static int
v9fs_vfs_link_dotl ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
int err ;
char * name ;
struct dentry * dir_dentry ;
2011-02-28 14:34:08 +03:00
struct p9_fid * dfid , * oldfid ;
struct v9fs_session_info * v9ses ;
2011-01-10 22:51:47 +03:00
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " dir ino: %lu, old_name: %s, new_name: %s \n " ,
dir - > i_ino , old_dentry - > d_name . name , dentry - > d_name . name ) ;
2011-01-10 22:51:47 +03:00
v9ses = v9fs_inode2v9ses ( dir ) ;
2012-04-03 04:02:53 +04:00
dir_dentry = dentry - > d_parent ;
2011-01-10 22:51:47 +03:00
dfid = v9fs_fid_lookup ( dir_dentry ) ;
if ( IS_ERR ( dfid ) )
return PTR_ERR ( dfid ) ;
oldfid = v9fs_fid_lookup ( old_dentry ) ;
if ( IS_ERR ( oldfid ) )
return PTR_ERR ( oldfid ) ;
name = ( char * ) dentry - > d_name . name ;
err = p9_client_link ( dfid , oldfid , ( char * ) dentry - > d_name . name ) ;
if ( err < 0 ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_link failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
2011-02-28 14:34:08 +03:00
v9fs_invalidate_inode_attr ( dir ) ;
2011-01-10 22:51:47 +03:00
if ( v9ses - > cache = = CACHE_LOOSE | | v9ses - > cache = = CACHE_FSCACHE ) {
/* Get the latest stat info from server. */
struct p9_fid * fid ;
fid = v9fs_fid_lookup ( old_dentry ) ;
if ( IS_ERR ( fid ) )
return PTR_ERR ( fid ) ;
2011-02-28 14:34:09 +03:00
v9fs_refresh_inode_dotl ( fid , old_dentry - > d_inode ) ;
2011-01-10 22:51:47 +03:00
}
2011-02-28 14:33:55 +03:00
ihold ( old_dentry - > d_inode ) ;
2011-01-10 22:51:47 +03:00
d_instantiate ( dentry , old_dentry - > d_inode ) ;
return err ;
}
/**
* v9fs_vfs_mknod_dotl - create a special file
* @ dir : inode destination for new link
* @ dentry : dentry for file
* @ mode : mode for creation
* @ rdev : device associated with special file
*
*/
static int
2011-07-26 09:52:52 +04:00
v9fs_vfs_mknod_dotl ( struct inode * dir , struct dentry * dentry , umode_t omode ,
2011-01-10 22:51:47 +03:00
dev_t rdev )
{
int err ;
2011-02-28 14:34:08 +03:00
gid_t gid ;
2011-01-10 22:51:47 +03:00
char * name ;
2011-07-24 02:37:50 +04:00
umode_t mode ;
2011-01-10 22:51:47 +03:00
struct v9fs_session_info * v9ses ;
struct p9_fid * fid = NULL , * dfid = NULL ;
struct inode * inode ;
struct p9_qid qid ;
struct dentry * dir_dentry ;
struct posix_acl * dacl = NULL , * pacl = NULL ;
2012-01-11 03:09:01 +04:00
p9_debug ( P9_DEBUG_VFS , " %lu,%s mode: %hx MAJOR: %u MINOR: %u \n " ,
2011-11-28 22:40:46 +04:00
dir - > i_ino , dentry - > d_name . name , omode ,
MAJOR ( rdev ) , MINOR ( rdev ) ) ;
2011-01-10 22:51:47 +03:00
if ( ! new_valid_dev ( rdev ) )
return - EINVAL ;
v9ses = v9fs_inode2v9ses ( dir ) ;
2012-04-03 04:02:53 +04:00
dir_dentry = dentry - > d_parent ;
2011-01-10 22:51:47 +03:00
dfid = v9fs_fid_lookup ( dir_dentry ) ;
if ( IS_ERR ( dfid ) ) {
err = PTR_ERR ( dfid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " fid lookup failed %d \n " , err ) ;
2011-01-10 22:51:47 +03:00
dfid = NULL ;
goto error ;
}
gid = v9fs_get_fsgid_for_create ( dir ) ;
mode = omode ;
/* Update mode based on ACL value */
err = v9fs_acl_mode ( dir , & mode , & dacl , & pacl ) ;
if ( err ) {
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " Failed to get acl values in mknod %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
name = ( char * ) dentry - > d_name . name ;
err = p9_client_mknod_dotl ( dfid , name , mode , rdev , gid , & qid ) ;
if ( err < 0 )
goto error ;
2011-02-28 14:34:08 +03:00
v9fs_invalidate_inode_attr ( dir ) ;
2011-01-10 22:51:47 +03:00
/* instantiate inode and assign the unopened fid to the dentry */
if ( v9ses - > cache = = CACHE_LOOSE | | v9ses - > cache = = CACHE_FSCACHE ) {
fid = p9_client_walk ( dfid , 1 , & name , 1 ) ;
if ( IS_ERR ( fid ) ) {
err = PTR_ERR ( fid ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " p9_client_walk failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
goto error ;
}
2011-07-06 15:02:31 +04:00
inode = v9fs_get_new_inode_from_fid ( v9ses , fid , dir - > i_sb ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " inode creation failed %d \n " ,
err ) ;
2011-01-10 22:51:47 +03:00
goto error ;
}
err = v9fs_fid_add ( dentry , fid ) ;
if ( err < 0 )
goto error ;
2011-07-25 22:06:32 +04:00
d_instantiate ( dentry , inode ) ;
2011-01-10 22:51:47 +03:00
fid = NULL ;
} else {
/*
* Not in cached mode . No need to populate inode with stat .
* socket syscall returns a fd , so we need instantiate
*/
2011-07-25 22:06:33 +04:00
inode = v9fs_get_inode ( dir - > i_sb , mode , rdev ) ;
2011-01-10 22:51:47 +03:00
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
goto error ;
}
d_instantiate ( dentry , inode ) ;
}
/* Now set the ACL based on the default value */
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( dentry , & dacl , & pacl ) ;
2011-01-10 22:51:47 +03:00
error :
if ( fid )
p9_client_clunk ( fid ) ;
2011-07-23 10:28:13 +04:00
v9fs_set_create_acl ( NULL , & dacl , & pacl ) ;
2011-01-10 22:51:47 +03:00
return err ;
}
/**
* v9fs_vfs_follow_link_dotl - follow a symlink path
* @ dentry : dentry for symlink
* @ nd : nameidata
*
*/
static void *
v9fs_vfs_follow_link_dotl ( struct dentry * dentry , struct nameidata * nd )
{
2011-01-08 04:58:46 +03:00
int retval ;
struct p9_fid * fid ;
2011-01-10 22:51:47 +03:00
char * link = __getname ( ) ;
2011-01-08 04:58:46 +03:00
char * target ;
2011-01-10 22:51:47 +03:00
2011-11-28 22:40:46 +04:00
p9_debug ( P9_DEBUG_VFS , " %s \n " , dentry - > d_name . name ) ;
2011-01-10 22:51:47 +03:00
2011-01-08 04:58:46 +03:00
if ( ! link ) {
2011-01-10 22:51:47 +03:00
link = ERR_PTR ( - ENOMEM ) ;
2011-01-08 04:58:46 +03:00
goto ndset ;
2011-01-10 22:51:47 +03:00
}
2011-01-08 04:58:46 +03:00
fid = v9fs_fid_lookup ( dentry ) ;
if ( IS_ERR ( fid ) ) {
__putname ( link ) ;
2011-03-24 20:34:41 +03:00
link = ERR_CAST ( fid ) ;
2011-01-08 04:58:46 +03:00
goto ndset ;
}
retval = p9_client_readlink ( fid , & target ) ;
if ( ! retval ) {
strcpy ( link , target ) ;
kfree ( target ) ;
goto ndset ;
}
__putname ( link ) ;
link = ERR_PTR ( retval ) ;
ndset :
2011-01-10 22:51:47 +03:00
nd_set_link ( nd , link ) ;
return NULL ;
}
2011-02-28 14:34:06 +03:00
int v9fs_refresh_inode_dotl ( struct p9_fid * fid , struct inode * inode )
{
loff_t i_size ;
struct p9_stat_dotl * st ;
struct v9fs_session_info * v9ses ;
v9ses = v9fs_inode2v9ses ( inode ) ;
st = p9_client_getattr_dotl ( fid , P9_STATS_ALL ) ;
if ( IS_ERR ( st ) )
return PTR_ERR ( st ) ;
2011-07-25 22:06:33 +04:00
/*
* Don ' t update inode if the file type is different
*/
if ( ( inode - > i_mode & S_IFMT ) ! = ( st - > st_mode & S_IFMT ) )
goto out ;
2011-02-28 14:34:06 +03:00
spin_lock ( & inode - > i_lock ) ;
/*
* We don ' t want to refresh inode - > i_size ,
* because we may have cached data
*/
i_size = inode - > i_size ;
v9fs_stat2inode_dotl ( st , inode ) ;
if ( v9ses - > cache )
inode - > i_size = i_size ;
spin_unlock ( & inode - > i_lock ) ;
2011-07-25 22:06:33 +04:00
out :
2011-02-28 14:34:06 +03:00
kfree ( st ) ;
return 0 ;
}
2011-01-10 22:51:47 +03:00
const struct inode_operations v9fs_dir_inode_operations_dotl = {
. create = v9fs_vfs_create_dotl ,
. lookup = v9fs_vfs_lookup ,
. link = v9fs_vfs_link_dotl ,
. symlink = v9fs_vfs_symlink_dotl ,
. unlink = v9fs_vfs_unlink ,
. mkdir = v9fs_vfs_mkdir_dotl ,
. rmdir = v9fs_vfs_rmdir ,
. mknod = v9fs_vfs_mknod_dotl ,
. rename = v9fs_vfs_rename ,
. getattr = v9fs_vfs_getattr_dotl ,
. setattr = v9fs_vfs_setattr_dotl ,
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. removexattr = generic_removexattr ,
. listxattr = v9fs_listxattr ,
2011-07-23 19:37:31 +04:00
. get_acl = v9fs_iop_get_acl ,
2011-01-10 22:51:47 +03:00
} ;
const struct inode_operations v9fs_file_inode_operations_dotl = {
. getattr = v9fs_vfs_getattr_dotl ,
. setattr = v9fs_vfs_setattr_dotl ,
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. removexattr = generic_removexattr ,
. listxattr = v9fs_listxattr ,
2011-07-23 19:37:31 +04:00
. get_acl = v9fs_iop_get_acl ,
2011-01-10 22:51:47 +03:00
} ;
const struct inode_operations v9fs_symlink_inode_operations_dotl = {
2011-01-08 04:58:46 +03:00
. readlink = generic_readlink ,
2011-01-10 22:51:47 +03:00
. follow_link = v9fs_vfs_follow_link_dotl ,
. put_link = v9fs_vfs_put_link ,
. getattr = v9fs_vfs_getattr_dotl ,
. setattr = v9fs_vfs_setattr_dotl ,
. setxattr = generic_setxattr ,
. getxattr = generic_getxattr ,
. removexattr = generic_removexattr ,
. listxattr = v9fs_listxattr ,
} ;