2001-09-18 19:38:54 +04:00
/*
* dmfs - lv . c
*
* Copyright ( C ) 2001 Sistina Software
*
* This software is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 , or ( at
* your option ) any later version .
*
* This software 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 GNU CC ; see the file COPYING . If not , write to
* the Free Software Foundation , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
/* Heavily based upon ramfs */
2001-09-19 14:32:51 +04:00
# include <linux/config.h>
2001-09-26 12:06:46 +04:00
# include <linux/ctype.h>
2001-09-19 14:32:51 +04:00
# include <linux/fs.h>
2001-10-11 01:49:21 +04:00
# include "dm.h"
2001-10-12 14:06:40 +04:00
extern struct address_space_operations dmfs_address_space_operations ;
extern struct inode * dmfs_create_tdir ( struct inode * dir , int mode ) ;
2001-09-26 15:47:02 +04:00
2001-10-12 14:06:40 +04:00
struct dentry * dmfs_verify_name ( struct inode * dir , const char * name )
2001-09-26 15:47:02 +04:00
{
struct nameidata nd ;
int err = - ENOENT ;
2001-10-15 15:31:00 +04:00
struct file file ;
struct dentry * dentry ;
memset ( & file , 0 , sizeof ( struct file ) ) ;
2001-09-26 15:47:02 +04:00
if ( path_init ( name , LOOKUP_FOLLOW , & nd ) )
2001-09-27 00:24:39 +04:00
return ERR_PTR ( - EINVAL ) ;
2001-09-26 15:47:02 +04:00
2001-10-12 14:06:40 +04:00
err = path_walk ( name , & nd ) ;
2001-09-26 15:47:02 +04:00
if ( err )
2001-09-27 00:24:39 +04:00
goto err_out ;
2001-09-26 15:47:02 +04:00
2001-09-27 00:24:39 +04:00
err = - EINVAL ;
2001-10-12 14:06:40 +04:00
if ( nd . mnt - > mnt_sb ! = dir - > i_sb )
2001-09-27 00:24:39 +04:00
goto err_out ;
2001-09-26 15:47:02 +04:00
2001-10-12 14:06:40 +04:00
if ( nd . dentry - > d_parent - > d_inode ! = dir )
2001-09-27 00:24:39 +04:00
goto err_out ;
2001-09-26 15:47:02 +04:00
2001-10-15 15:31:00 +04:00
if ( DMFS_I ( nd . dentry - > d_inode ) = = NULL | |
DMFS_I ( nd . dentry - > d_inode ) - > table = = NULL )
goto err_out ;
dentry = nd . dentry ;
file . f_dentry = nd . dentry - > d_parent ;
err = deny_write_access ( & file ) ;
if ( err )
goto err_out ;
dget ( dentry ) ;
2001-10-12 14:06:40 +04:00
path_release ( & nd ) ;
2001-10-15 15:31:00 +04:00
return dentry ;
2001-09-27 00:24:39 +04:00
err_out :
2001-10-12 14:06:40 +04:00
path_release ( & nd ) ;
2001-09-27 00:24:39 +04:00
return ERR_PTR ( err ) ;
2001-09-26 15:47:02 +04:00
}
struct inode * dmfs_create_symlink ( struct inode * dir , int mode )
{
2001-10-15 15:31:00 +04:00
struct inode * inode = dmfs_new_inode ( dir - > i_sb , mode | S_IFLNK ) ;
2001-09-26 15:47:02 +04:00
if ( inode ) {
2001-10-12 14:06:40 +04:00
inode - > i_mapping - > a_ops = & dmfs_address_space_operations ;
2001-09-26 15:47:02 +04:00
inode - > i_op = & page_symlink_inode_operations ;
}
return inode ;
}
2001-10-11 01:49:21 +04:00
static int dmfs_lv_unlink ( struct inode * dir , struct dentry * dentry )
2001-10-05 14:00:13 +04:00
{
2001-10-11 01:49:21 +04:00
struct inode * inode = dentry - > d_inode ;
2001-10-15 15:31:00 +04:00
struct file file = { f_dentry : dentry - > d_parent } ;
2001-10-11 01:49:21 +04:00
2001-10-05 14:00:13 +04:00
if ( ! ( inode - > i_mode & S_IFLNK ) )
return - EINVAL ;
2001-10-15 15:31:00 +04:00
dm_suspend ( DMFS_I ( dir ) - > md ) ;
allow_write_access ( & file ) ;
2001-10-12 14:06:40 +04:00
inode - > i_nlink - - ;
2001-10-05 14:00:13 +04:00
dput ( dentry ) ;
return 0 ;
}
2001-09-26 15:47:02 +04:00
static int dmfs_lv_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
struct inode * inode ;
struct dentry * de ;
int rv ;
int l ;
2001-10-15 15:31:00 +04:00
if ( dentry - > d_name . len ! = 6 | |
memcmp ( dentry - > d_name . name , " ACTIVE " , 6 ) ! = 0 )
2001-10-12 00:29:00 +04:00
return - EINVAL ;
2001-09-26 15:47:02 +04:00
de = dmfs_verify_name ( dir , symname ) ;
if ( IS_ERR ( de ) )
return PTR_ERR ( de ) ;
inode = dmfs_create_symlink ( dir , S_IRWXUGO ) ;
if ( IS_ERR ( inode ) ) {
2001-10-15 15:31:00 +04:00
rv = PTR_ERR ( inode ) ;
goto out_allow_write ;
2001-09-26 15:47:02 +04:00
}
2001-10-15 15:31:00 +04:00
DMFS_I ( inode ) - > dentry = de ;
2001-09-26 15:47:02 +04:00
d_instantiate ( dentry , inode ) ;
dget ( dentry ) ;
2001-09-27 00:24:39 +04:00
l = strlen ( symname ) + 1 ;
rv = block_symlink ( inode , symname , l ) ;
2001-10-15 15:31:00 +04:00
if ( rv )
goto out_dput ;
rv = dm_activate ( DMFS_I ( dir ) - > md , DMFS_I ( de - > d_inode ) - > table ) ;
if ( rv )
goto out_dput ;
return rv ;
2001-09-26 15:47:02 +04:00
2001-10-15 15:31:00 +04:00
out_dput :
dput ( DMFS_I ( inode ) - > dentry ) ;
DMFS_I ( inode ) - > dentry = NULL ;
out_allow_write :
{
struct file file = { f_dentry : de - > d_parent } ;
allow_write_access ( & file ) ;
dput ( de ) ;
}
2001-09-26 15:47:02 +04:00
return rv ;
}
2001-09-26 12:06:46 +04:00
static int is_identifier ( const char * str , int len )
2001-09-18 19:38:54 +04:00
{
2001-09-26 12:06:46 +04:00
while ( len - - ) {
if ( ! isalnum ( * str ) & & * str ! = ' _ ' )
return 0 ;
str + + ;
}
return 1 ;
2001-09-18 19:38:54 +04:00
}
2001-09-26 12:06:46 +04:00
static int dmfs_lv_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
struct inode * inode ;
if ( dentry - > d_name . len > = DM_NAME_LEN )
return - EINVAL ;
2001-10-12 14:06:40 +04:00
if ( ! is_identifier ( dentry - > d_name . name , dentry - > d_name . len ) )
2001-09-26 12:06:46 +04:00
return - EPERM ;
2001-10-15 15:31:00 +04:00
if ( dentry - > d_name . len = = 6 & &
memcmp ( dentry - > d_name . name , " ACTIVE " , 6 ) = = 0 )
2001-09-26 12:06:46 +04:00
return - EINVAL ;
2001-10-12 14:06:40 +04:00
if ( dentry - > d_name . name [ 0 ] = = ' . ' )
2001-09-26 12:06:46 +04:00
return - EINVAL ;
2001-10-12 00:29:00 +04:00
inode = dmfs_create_tdir ( dir , mode ) ;
2001-09-26 12:06:46 +04:00
if ( ! IS_ERR ( inode ) ) {
2001-10-12 00:29:00 +04:00
d_instantiate ( dentry , inode ) ;
dget ( dentry ) ;
return 0 ;
2001-09-26 12:06:46 +04:00
}
return PTR_ERR ( inode ) ;
}
/*
* if u . generic_ip is not NULL , then it indicates an inode which
* represents a table . If it is NULL then the inode is a virtual
* file and should be deleted along with the directory .
*/
2001-10-12 14:06:40 +04:00
static inline int positive ( struct dentry * dentry )
2001-09-26 12:06:46 +04:00
{
2001-09-26 15:47:02 +04:00
return dentry - > d_inode & & ! d_unhashed ( dentry ) ;
2001-09-26 12:06:46 +04:00
}
static int empty ( struct dentry * dentry )
2001-09-18 19:38:54 +04:00
{
2001-09-26 12:06:46 +04:00
struct list_head * list ;
2001-09-24 19:10:33 +04:00
2001-09-26 12:06:46 +04:00
spin_lock ( & dcache_lock ) ;
list = dentry - > d_subdirs . next ;
while ( list ! = & dentry - > d_subdirs ) {
struct dentry * de = list_entry ( list , struct dentry , d_child ) ;
if ( positive ( de ) ) {
spin_unlock ( & dcache_lock ) ;
return 0 ;
}
list = list - > next ;
}
spin_unlock ( & dcache_lock ) ;
return 1 ;
}
static int dmfs_lv_rmdir ( struct inode * dir , struct dentry * dentry )
2001-09-20 01:27:46 +04:00
{
2001-09-26 12:06:46 +04:00
int ret = - ENOTEMPTY ;
if ( empty ( dentry ) ) {
struct inode * inode = dentry - > d_inode ;
2001-10-15 15:31:00 +04:00
inode - > i_nlink - - ;
dput ( dentry ) ;
2001-09-20 01:27:46 +04:00
}
2001-09-26 12:06:46 +04:00
return ret ;
2001-09-18 19:38:54 +04:00
}
2001-09-26 12:06:46 +04:00
static struct dentry * dmfs_lv_lookup ( struct inode * dir , struct dentry * dentry )
{
d_add ( dentry , NULL ) ;
return NULL ;
}
2001-09-18 19:38:54 +04:00
static int dmfs_lv_sync ( struct file * file , struct dentry * dentry , int datasync )
{
return 0 ;
}
2001-10-12 14:06:40 +04:00
static struct file_operations dmfs_lv_file_operations = {
2001-09-18 19:38:54 +04:00
read : generic_read_dir ,
2001-09-26 12:06:46 +04:00
readdir : dcache_readdir ,
2001-09-18 19:38:54 +04:00
fsync : dmfs_lv_sync ,
} ;
2001-10-12 14:06:40 +04:00
static struct inode_operations dmfs_lv_inode_operations = {
2001-09-18 19:38:54 +04:00
lookup : dmfs_lv_lookup ,
2001-09-26 15:47:02 +04:00
unlink : dmfs_lv_unlink ,
symlink : dmfs_lv_symlink ,
2001-09-26 12:06:46 +04:00
mkdir : dmfs_lv_mkdir ,
rmdir : dmfs_lv_rmdir ,
2001-09-18 19:38:54 +04:00
} ;
2001-10-12 00:29:00 +04:00
struct inode * dmfs_create_lv ( struct super_block * sb , int mode , struct dentry * dentry )
2001-09-18 19:38:54 +04:00
{
2001-10-11 01:49:21 +04:00
struct inode * inode = dmfs_new_inode ( sb , mode | S_IFDIR ) ;
2001-10-12 00:29:00 +04:00
struct mapped_device * md ;
2001-10-12 14:06:40 +04:00
const char * name = dentry - > d_name . name ;
2001-09-18 19:38:54 +04:00
if ( inode ) {
inode - > i_fop = & dmfs_lv_file_operations ;
2001-10-12 14:06:40 +04:00
inode - > i_op = & dmfs_lv_inode_operations ;
2001-10-12 00:29:00 +04:00
md = dm_create ( name , - 1 ) ;
if ( md = = NULL ) {
iput ( inode ) ;
return NULL ;
}
DMFS_I ( inode ) - > md = md ;
2001-09-18 19:38:54 +04:00
}
return inode ;
}