2001-08-20 12:03:02 +04:00
/*
2001-09-07 15:34:46 +04:00
* * very * heavily based on ramfs
2001-08-20 12:03:02 +04:00
*
* Copyright ( C ) 2001 Sistina Software
*
2001-09-07 15:34:46 +04:00
* This file is released under the GPL .
2001-08-20 12:03:02 +04:00
*/
2001-09-07 15:34:46 +04:00
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/pagemap.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/locks.h>
# include <linux/file.h>
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
# include <asm/uaccess.h>
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
# include "dm.h"
2001-08-21 19:24:02 +04:00
2001-09-13 23:41:46 +04:00
/* some magic number */
# define DM_MAGIC 0x444D4653
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static struct super_operations dm_ops ;
static struct address_space_operations dm_aops ;
static struct file_operations dm_dir_operations ;
static struct file_operations dm_file_operations ;
static struct inode_operations dm_dir_inode_operations ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
struct vfsmount * _mnt ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
static int _unlink ( struct inode * dir , struct dentry * dentry ) ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
# define NOT_A_TABLE ((struct dm_table *) 1)
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
/*
* context for the line splitter and error function .
*/
struct line_c {
unsigned int line_num ;
loff_t next_read ;
char data [ MAX_TARGET_LINE ] ;
struct file * in ;
struct file * out ;
2001-08-20 17:45:43 +04:00
} ;
2001-08-20 12:03:02 +04:00
2001-09-14 00:10:14 +04:00
static int is_identifier ( const char * str , int len )
2001-09-13 23:41:46 +04:00
{
while ( len - - ) {
2001-09-14 01:50:38 +04:00
if ( ! isalnum ( * str ) & & * str ! = ' _ ' )
2001-09-13 23:41:46 +04:00
return 0 ;
str + + ;
}
return 1 ;
}
2001-09-07 15:34:46 +04:00
/*
* Grabs lines one at a time from the table file .
*/
int extract_line ( struct text_region * line , void * private )
2001-08-20 12:03:02 +04:00
{
2001-09-07 15:34:46 +04:00
struct line_c * lc = ( struct line_c * ) private ;
struct text_region text ;
ssize_t n ;
loff_t off = lc - > next_read ;
const char * read_begin ;
2001-09-14 16:27:57 +04:00
mm_segment_t fs ;
fs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
2001-09-07 15:34:46 +04:00
n = lc - > in - > f_op - > read ( lc - > in , lc - > data , sizeof ( lc - > data ) , & off ) ;
2001-09-14 16:27:57 +04:00
set_fs ( fs ) ;
2001-09-07 15:34:46 +04:00
if ( n < = 0 )
return 0 ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
read_begin = text . b = lc - > data ;
text . e = lc - > data + n ;
if ( ! dm_get_line ( & text , line ) )
2001-08-20 12:03:02 +04:00
return 0 ;
2001-09-07 15:34:46 +04:00
lc - > line_num + + ;
lc - > next_read + = line - > e - read_begin ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
return 1 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static struct file * open_error_file ( struct file * table )
{
char * name ;
struct file * f ;
2001-08-20 12:03:02 +04:00
2001-09-13 20:52:50 +04:00
name = kmalloc ( PATH_MAX + 1 , GFP_KERNEL ) ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
if ( ! name )
return 0 ;
2001-08-20 17:45:43 +04:00
2001-09-13 20:52:50 +04:00
/* FIXME: Assumes path depth; avoid filp_open. */
2001-09-14 16:27:57 +04:00
sprintf ( name , " /%s/%s/%s.err " ,
2001-09-13 20:52:50 +04:00
table - > f_vfsmnt - > mnt_mountpoint - > d_name . name ,
2001-09-07 15:34:46 +04:00
table - > f_dentry - > d_parent - > d_name . name ,
table - > f_dentry - > d_name . name ) ;
2001-09-14 14:06:22 +04:00
f = filp_open ( name , O_WRONLY | O_TRUNC | O_CREAT , S_IRUGO ) ;
2001-09-07 15:34:46 +04:00
kfree ( name ) ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
if ( f )
f - > f_dentry - > d_inode - > u . generic_ip = NOT_A_TABLE ;
return f ;
2001-08-20 12:03:02 +04:00
}
2001-09-07 15:34:46 +04:00
static void close_error_file ( struct file * out )
2001-08-20 12:03:02 +04:00
{
2001-09-07 15:34:46 +04:00
fput ( out ) ;
}
static void parse_error ( const char * message , void * private )
{
char buffer [ 32 ] ;
struct line_c * lc = ( struct line_c * ) private ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
# define emit(b, l) lc->out->f_op->write(lc->out, (b), (l), &lc->out->f_pos)
emit ( lc - > in - > f_dentry - > d_name . name , lc - > in - > f_dentry - > d_name . len ) ;
sprintf ( buffer , " (%d): " , lc - > line_num ) ;
emit ( buffer , strlen ( buffer ) ) ;
emit ( message , strlen ( message ) ) ;
emit ( " \n " , 1 ) ;
# undef emit
2001-08-20 17:45:43 +04:00
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static int _release_file ( struct inode * inode , struct file * f )
2001-08-22 19:01:09 +04:00
{
2001-09-07 15:34:46 +04:00
/* FIXME: we should lock the inode to
prevent someone else opening it while
we are parsing */
struct line_c * lc ;
struct dm_table * table = ( struct dm_table * ) inode - > u . generic_ip ;
/* noop for files without tables (.err files) */
if ( table = = NOT_A_TABLE )
return 0 ;
2001-08-22 19:01:09 +04:00
2001-09-07 15:34:46 +04:00
/* only bother parsing if it was open for a write */
if ( ! ( f - > f_mode & S_IWUGO ) )
return 0 ;
2001-08-22 19:01:09 +04:00
2001-09-07 15:34:46 +04:00
/* free off the old table */
if ( table ) {
dm_table_destroy ( table ) ;
inode - > u . generic_ip = 0 ;
}
2001-08-22 19:01:09 +04:00
2001-09-07 15:34:46 +04:00
if ( ! ( lc = kmalloc ( sizeof ( * lc ) , GFP_KERNEL ) ) )
2001-08-22 19:01:09 +04:00
return - ENOMEM ;
2001-09-07 15:34:46 +04:00
memset ( lc , 0 , sizeof ( * lc ) ) ;
lc - > in = f ;
2001-08-22 19:01:09 +04:00
2001-09-14 15:15:54 +04:00
if ( ! ( lc - > out = open_error_file ( lc - > in ) ) )
return - ENOMEM ;
2001-09-07 15:34:46 +04:00
table = dm_parse ( extract_line , lc , parse_error , lc ) ;
2001-08-22 19:33:08 +04:00
2001-09-14 15:15:54 +04:00
close_error_file ( lc - > out ) ;
2001-09-07 15:34:46 +04:00
kfree ( lc ) ;
inode - > u . generic_ip = table ;
2001-08-22 19:33:08 +04:00
2001-08-22 19:01:09 +04:00
return 0 ;
}
2001-09-07 15:34:46 +04:00
void _release_inode ( struct inode * inode )
2001-08-22 19:01:09 +04:00
{
2001-09-07 15:34:46 +04:00
struct mapped_device * md = ( struct mapped_device * ) inode - > u . generic_ip ;
struct dm_table * table = ( struct dm_table * ) inode - > u . generic_ip ;
2001-08-31 19:13:33 +04:00
2001-09-07 15:34:46 +04:00
if ( inode - > i_mode & S_IFDIR ) {
if ( md )
dm_remove ( md - > name ) ;
} else {
if ( table )
dm_table_destroy ( table ) ;
2001-08-31 19:13:33 +04:00
2001-08-22 19:01:09 +04:00
}
2001-08-22 19:33:08 +04:00
2001-09-07 15:34:46 +04:00
inode - > u . generic_ip = 0 ;
force_delete ( inode ) ;
2001-08-22 19:01:09 +04:00
}
2001-09-07 15:34:46 +04:00
static int _statfs ( struct super_block * sb , struct statfs * buf )
2001-08-20 12:03:02 +04:00
{
2001-09-07 15:34:46 +04:00
buf - > f_type = DM_MAGIC ;
buf - > f_bsize = PAGE_CACHE_SIZE ;
buf - > f_namelen = 255 ;
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
/*
* Lookup the data . This is trivial - if the dentry didn ' t already
* exist , we know it is negative .
*/
static struct dentry * _lookup ( struct inode * dir , struct dentry * dentry )
{
d_add ( dentry , NULL ) ;
return NULL ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
/*
* Read a page . Again trivial . If it didn ' t already exist
* in the page cache , it is zero - filled .
*/
static int _readpage ( struct file * file , struct page * page )
{
if ( ! Page_Uptodate ( page ) ) {
memset ( kmap ( page ) , 0 , PAGE_CACHE_SIZE ) ;
kunmap ( page ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
}
UnlockPage ( page ) ;
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
/*
* Writing : just make sure the page gets marked dirty , so that
* the page stealer won ' t grab it .
*/
static int _writepage ( struct page * page )
{
SetPageDirty ( page ) ;
UnlockPage ( page ) ;
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static int _prepare_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
void * addr = kmap ( page ) ;
if ( ! Page_Uptodate ( page ) ) {
memset ( addr , 0 , PAGE_CACHE_SIZE ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
}
SetPageDirty ( page ) ;
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static int _commit_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
struct inode * inode = page - > mapping - > host ;
loff_t pos = ( ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ) + to ;
2001-08-22 19:01:09 +04:00
2001-09-07 15:34:46 +04:00
kunmap ( page ) ;
if ( pos > inode - > i_size )
inode - > i_size = pos ;
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
struct inode * _get_inode ( struct super_block * sb , int mode , int dev )
{
struct inode * inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_mode = mode ;
inode - > i_uid = current - > fsuid ;
inode - > i_gid = current - > fsgid ;
inode - > i_blksize = PAGE_CACHE_SIZE ;
inode - > i_blocks = 0 ;
inode - > i_rdev = NODEV ;
inode - > i_mapping - > a_ops = & dm_aops ;
inode - > i_atime = inode - > i_mtime =
inode - > i_ctime = CURRENT_TIME ;
switch ( mode & S_IFMT ) {
2001-09-13 23:41:46 +04:00
case S_IFBLK :
case S_IFCHR :
2001-09-07 15:34:46 +04:00
init_special_inode ( inode , mode , dev ) ;
break ;
case S_IFREG :
inode - > i_fop = & dm_file_operations ;
break ;
case S_IFDIR :
inode - > i_op = & dm_dir_inode_operations ;
inode - > i_fop = & dm_dir_operations ;
break ;
case S_IFLNK :
inode - > i_op = & page_symlink_inode_operations ;
break ;
2001-09-13 23:41:46 +04:00
default :
make_bad_inode ( inode ) ;
2001-08-20 17:45:43 +04:00
}
2001-09-07 15:34:46 +04:00
}
return inode ;
}
2001-08-21 19:24:02 +04:00
2001-09-07 15:34:46 +04:00
/*
* File creation . Allocate an inode , and we ' re done . .
*/
static int _mknod ( struct inode * dir , struct dentry * dentry , int mode )
{
int error = - ENOSPC ;
struct inode * inode = _get_inode ( dir - > i_sb , mode , 0 ) ;
if ( inode ) {
d_instantiate ( dentry , inode ) ;
dget ( dentry ) ; /* Extra count - pin the dentry in core */
error = 0 ;
2001-08-20 12:03:02 +04:00
}
2001-09-07 15:34:46 +04:00
return error ;
2001-08-20 12:03:02 +04:00
}
2001-09-07 15:34:46 +04:00
static int _mkdir ( struct inode * dir , struct dentry * dentry , int mode )
2001-08-20 12:03:02 +04:00
{
2001-08-23 16:35:02 +04:00
int r ;
2001-09-07 15:34:46 +04:00
const char * name = ( const char * ) dentry - > d_name . name ;
2001-08-20 12:03:02 +04:00
2001-09-13 23:41:46 +04:00
if ( ! is_identifier ( name , dentry - > d_name . len ) )
return - EPERM ; /* or EINVAL ? */
2001-09-07 15:34:46 +04:00
r = dm_create ( name , - 1 ) ;
if ( r )
return r ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
r = _mknod ( dir , dentry , mode | S_IFDIR ) ;
if ( r ) {
dm_remove ( name ) ;
return r ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
dentry - > d_inode - > u . generic_ip = dm_find_by_name ( name ) ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
return 0 ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
static int _rmdir ( struct inode * dir , struct dentry * dentry )
{
int r = _unlink ( dir , dentry ) ;
if ( r )
return r ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
dm_remove ( dentry - > d_name . name ) ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
return 0 ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
static int _create ( struct inode * dir , struct dentry * dentry , int mode )
{
int r ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
if ( ( r = _mknod ( dir , dentry , mode | S_IFREG ) ) )
return r ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
dentry - > d_inode - > u . generic_ip = 0 ;
return 0 ;
}
2001-08-20 17:45:43 +04:00
2001-09-13 23:41:46 +04:00
static inline int positive ( struct dentry * dentry )
2001-09-07 15:34:46 +04:00
{
return dentry - > d_inode & & ! d_unhashed ( dentry ) ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
/*
* Check that a directory is empty ( this works
* for regular files too , they ' ll just always be
* considered empty . . ) .
*
* Note that an empty directory can still have
* children , they just all have to be negative . .
*/
static int _empty ( struct dentry * dentry )
{
struct list_head * list ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
spin_lock ( & dcache_lock ) ;
list = dentry - > d_subdirs . next ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
while ( list ! = & dentry - > d_subdirs ) {
struct dentry * de = list_entry ( list , struct dentry , d_child ) ;
2001-08-20 17:45:43 +04:00
2001-09-13 23:41:46 +04:00
if ( positive ( de ) ) {
2001-09-07 15:34:46 +04:00
spin_unlock ( & dcache_lock ) ;
return 0 ;
2001-08-31 20:36:56 +04:00
}
2001-09-07 15:34:46 +04:00
list = list - > next ;
2001-08-23 16:35:02 +04:00
}
2001-09-07 15:34:46 +04:00
spin_unlock ( & dcache_lock ) ;
return 1 ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
/*
* This works for both directories and regular files .
* ( non - directories will always have empty subdirs )
*/
static int _unlink ( struct inode * dir , struct dentry * dentry )
{
int retval = - ENOTEMPTY ;
if ( _empty ( dentry ) ) {
struct inode * inode = dentry - > d_inode ;
inode - > i_nlink - - ;
dput ( dentry ) ; /* Undo the count from "create" - this does all the work */
retval = 0 ;
}
return retval ;
2001-08-20 17:45:43 +04:00
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
/*
* The VFS layer already does all the dentry stuff for rename ,
* we just have to decrement the usage count for the target if
* it exists so that the VFS layer correctly free ' s it when it
* gets overwritten .
*/
static int _rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
2001-08-20 17:45:43 +04:00
{
2001-09-07 15:34:46 +04:00
struct inode * inode = new_dentry - > d_inode ;
struct mapped_device * md = old_dir - > u . generic_ip ;
struct dm_table * table = old_dentry - > d_inode - > u . generic_ip ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
if ( ! md | | ! table )
return - EINVAL ;
if ( ! _empty ( new_dentry ) )
return - ENOTEMPTY ;
if ( ! strcmp ( new_dentry - > d_name . name , " ACTIVE " ) ) {
/* activate the table */
dm_activate ( md , table ) ;
} else if ( ! strcmp ( old_dentry - > d_name . name , " ACTIVE " ) ) {
dm_suspend ( md ) ;
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
if ( inode ) {
inode - > i_nlink - - ;
dput ( new_dentry ) ;
2001-08-20 12:03:02 +04:00
}
2001-09-07 15:34:46 +04:00
return 0 ;
2001-08-20 17:45:43 +04:00
}
2001-09-07 15:34:46 +04:00
static int _sync_file ( struct file * file , struct dentry * dentry ,
int datasync )
2001-08-20 17:45:43 +04:00
{
2001-09-07 15:34:46 +04:00
return 0 ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
static struct address_space_operations dm_aops = {
readpage : _readpage ,
writepage : _writepage ,
prepare_write : _prepare_write ,
commit_write : _commit_write
} ;
static struct file_operations dm_file_operations = {
read : generic_file_read ,
write : generic_file_write ,
mmap : generic_file_mmap ,
fsync : _sync_file ,
release : _release_file ,
} ;
static struct file_operations dm_dir_operations = {
read : generic_read_dir ,
readdir : dcache_readdir ,
fsync : _sync_file ,
} ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
static struct inode_operations root_dir_inode_operations = {
lookup : _lookup ,
mkdir : _mkdir ,
rmdir : _rmdir ,
rename : _rename ,
} ;
static struct inode_operations dm_dir_inode_operations = {
create : _create ,
lookup : _lookup ,
unlink : _unlink ,
rename : _rename ,
} ;
static struct super_operations dm_ops = {
statfs : _statfs ,
put_inode : _release_inode ,
} ;
static struct super_block * _read_super ( struct super_block * sb , void * data ,
int silent )
{
struct inode * inode ;
struct dentry * root ;
sb - > s_blocksize = PAGE_CACHE_SIZE ;
sb - > s_blocksize_bits = PAGE_CACHE_SHIFT ;
sb - > s_magic = DM_MAGIC ;
sb - > s_op = & dm_ops ;
inode = _get_inode ( sb , S_IFDIR | 0755 , 0 ) ;
inode - > i_op = & root_dir_inode_operations ;
if ( ! inode )
return NULL ;
root = d_alloc_root ( inode ) ;
if ( ! root ) {
iput ( inode ) ;
return NULL ;
2001-08-20 17:45:43 +04:00
}
2001-09-07 15:34:46 +04:00
sb - > s_root = root ;
return sb ;
}
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
static DECLARE_FSTYPE ( _fs_type , " dm-fs " , _read_super , FS_SINGLE ) ;
2001-08-20 17:45:43 +04:00
2001-09-07 15:34:46 +04:00
int __init dm_fs_init ( void )
{
int r ;
if ( ( r = register_filesystem ( & _fs_type ) ) )
return r ;
_mnt = kern_mount ( & _fs_type ) ;
if ( IS_ERR ( _mnt ) ) {
dm_fs_exit ( ) ;
return PTR_ERR ( _mnt ) ;
}
2001-08-20 12:03:02 +04:00
2001-09-07 15:34:46 +04:00
return 0 ;
2001-08-20 12:03:02 +04:00
}
2001-08-21 19:24:02 +04:00
2001-09-07 15:34:46 +04:00
void __exit dm_fs_exit ( void )
{
unregister_filesystem ( & _fs_type ) ;
}