2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 17:16:47 +04:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2005-04-17 02:20:36 +04:00
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/crc32.h>
# include <linux/jffs2.h>
2006-05-03 16:07:27 +04:00
# include "jffs2_fs_i.h"
# include "jffs2_fs_sb.h"
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
# include "nodelist.h"
static int jffs2_readdir ( struct file * , void * , filldir_t ) ;
static int jffs2_create ( struct inode * , struct dentry * , int ,
struct nameidata * ) ;
static struct dentry * jffs2_lookup ( struct inode * , struct dentry * ,
struct nameidata * ) ;
static int jffs2_link ( struct dentry * , struct inode * , struct dentry * ) ;
static int jffs2_unlink ( struct inode * , struct dentry * ) ;
static int jffs2_symlink ( struct inode * , struct dentry * , const char * ) ;
static int jffs2_mkdir ( struct inode * , struct dentry * , int ) ;
static int jffs2_rmdir ( struct inode * , struct dentry * ) ;
2005-07-06 16:13:13 +04:00
static int jffs2_mknod ( struct inode * , struct dentry * , int , dev_t ) ;
2005-04-17 02:20:36 +04:00
static int jffs2_rename ( struct inode * , struct dentry * ,
2007-07-10 13:01:22 +04:00
struct inode * , struct dentry * ) ;
2005-04-17 02:20:36 +04:00
2006-03-28 13:56:42 +04:00
const struct file_operations jffs2_dir_operations =
2005-04-17 02:20:36 +04:00
{
. read = generic_read_dir ,
. readdir = jffs2_readdir ,
2008-07-07 16:45:59 +04:00
. unlocked_ioctl = jffs2_ioctl ,
2005-04-17 02:20:36 +04:00
. fsync = jffs2_fsync
} ;
2007-02-12 11:55:39 +03:00
const struct inode_operations jffs2_dir_inode_operations =
2005-04-17 02:20:36 +04:00
{
2005-07-06 16:13:13 +04:00
. create = jffs2_create ,
. lookup = jffs2_lookup ,
2005-04-17 02:20:36 +04:00
. link = jffs2_link ,
. unlink = jffs2_unlink ,
. symlink = jffs2_symlink ,
. mkdir = jffs2_mkdir ,
. rmdir = jffs2_rmdir ,
. mknod = jffs2_mknod ,
. rename = jffs2_rename ,
2006-05-13 10:09:47 +04:00
. permission = jffs2_permission ,
2005-04-17 02:20:36 +04:00
. setattr = jffs2_setattr ,
2006-05-13 10:09:47 +04:00
. setxattr = jffs2_setxattr ,
. getxattr = jffs2_getxattr ,
. listxattr = jffs2_listxattr ,
. removexattr = jffs2_removexattr
2005-04-17 02:20:36 +04:00
} ;
/***********************************************************************/
/* We keep the dirent list sorted in increasing order of name hash,
2005-11-07 14:16:07 +03:00
and we use the same hash function as the dentries . Makes this
2005-04-17 02:20:36 +04:00
nice and simple
*/
static struct dentry * jffs2_lookup ( struct inode * dir_i , struct dentry * target ,
struct nameidata * nd )
{
struct jffs2_inode_info * dir_f ;
struct jffs2_sb_info * c ;
struct jffs2_full_dirent * fd = NULL , * fd_list ;
uint32_t ino = 0 ;
struct inode * inode = NULL ;
D1 ( printk ( KERN_DEBUG " jffs2_lookup() \n " ) ) ;
2006-04-18 05:05:46 +04:00
if ( target - > d_name . len > JFFS2_MAX_NAME_LEN )
return ERR_PTR ( - ENAMETOOLONG ) ;
2005-04-17 02:20:36 +04:00
dir_f = JFFS2_INODE_INFO ( dir_i ) ;
c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
for ( fd_list = dir_f - > dents ; fd_list & & fd_list - > nhash < = target - > d_name . hash ; fd_list = fd_list - > next ) {
2005-11-07 14:16:07 +03:00
if ( fd_list - > nhash = = target - > d_name . hash & &
2005-04-17 02:20:36 +04:00
( ! fd | | fd_list - > version > fd - > version ) & &
strlen ( fd_list - > name ) = = target - > d_name . len & &
! strncmp ( fd_list - > name , target - > d_name . name , target - > d_name . len ) ) {
fd = fd_list ;
}
}
if ( fd )
ino = fd - > ino ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
if ( ino ) {
2008-02-07 11:15:42 +03:00
inode = jffs2_iget ( dir_i - > i_sb , ino ) ;
if ( IS_ERR ( inode ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " iget() failed for ino #%u \n " , ino ) ;
2008-02-07 11:15:42 +03:00
return ERR_CAST ( inode ) ;
2005-04-17 02:20:36 +04:00
}
}
d_add ( target , inode ) ;
return NULL ;
}
/***********************************************************************/
static int jffs2_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
struct jffs2_inode_info * f ;
struct jffs2_sb_info * c ;
2006-12-08 13:37:16 +03:00
struct inode * inode = filp - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
struct jffs2_full_dirent * fd ;
unsigned long offset , curofs ;
2006-12-08 13:37:16 +03:00
D1 ( printk ( KERN_DEBUG " jffs2_readdir() for dir_i #%lu \n " , filp - > f_path . dentry - > d_inode - > i_ino ) ) ;
2005-04-17 02:20:36 +04:00
f = JFFS2_INODE_INFO ( inode ) ;
c = JFFS2_SB_INFO ( inode - > i_sb ) ;
offset = filp - > f_pos ;
if ( offset = = 0 ) {
D1 ( printk ( KERN_DEBUG " Dirent 0: \" . \" , ino #%lu \n " , inode - > i_ino ) ) ;
if ( filldir ( dirent , " . " , 1 , 0 , inode - > i_ino , DT_DIR ) < 0 )
goto out ;
offset + + ;
}
if ( offset = = 1 ) {
2006-12-08 13:37:16 +03:00
unsigned long pino = parent_ino ( filp - > f_path . dentry ) ;
2005-04-17 02:20:36 +04:00
D1 ( printk ( KERN_DEBUG " Dirent 1: \" .. \" , ino #%lu \n " , pino ) ) ;
if ( filldir ( dirent , " .. " , 2 , 1 , pino , DT_DIR ) < 0 )
goto out ;
offset + + ;
}
curofs = 1 ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
for ( fd = f - > dents ; fd ; fd = fd - > next ) {
curofs + + ;
/* First loop: curofs = 2; offset = 2 */
if ( curofs < offset ) {
2005-11-07 14:16:07 +03:00
D2 ( printk ( KERN_DEBUG " Skipping dirent: \" %s \" , ino #%u, type %d, because curofs %ld < offset %ld \n " ,
2005-04-17 02:20:36 +04:00
fd - > name , fd - > ino , fd - > type , curofs , offset ) ) ;
continue ;
}
if ( ! fd - > ino ) {
D2 ( printk ( KERN_DEBUG " Skipping deletion dirent \" %s \" \n " , fd - > name ) ) ;
offset + + ;
continue ;
}
D2 ( printk ( KERN_DEBUG " Dirent %ld: \" %s \" , ino #%u, type %d \n " , offset , fd - > name , fd - > ino , fd - > type ) ) ;
if ( filldir ( dirent , fd - > name , strlen ( fd - > name ) , offset , fd - > ino , fd - > type ) < 0 )
break ;
offset + + ;
}
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
out :
filp - > f_pos = offset ;
return 0 ;
}
/***********************************************************************/
static int jffs2_create ( struct inode * dir_i , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
struct jffs2_raw_inode * ri ;
struct jffs2_inode_info * f , * dir_f ;
struct jffs2_sb_info * c ;
struct inode * inode ;
int ret ;
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri )
return - ENOMEM ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
D1 ( printk ( KERN_DEBUG " jffs2_create() \n " ) ) ;
2007-09-14 10:16:35 +04:00
inode = jffs2_new_inode ( dir_i , mode , ri ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( inode ) ) {
D1 ( printk ( KERN_DEBUG " jffs2_new_inode() failed \n " ) ) ;
jffs2_free_raw_inode ( ri ) ;
return PTR_ERR ( inode ) ;
}
inode - > i_op = & jffs2_file_inode_operations ;
inode - > i_fop = & jffs2_file_operations ;
inode - > i_mapping - > a_ops = & jffs2_file_address_operations ;
inode - > i_mapping - > nrpages = 0 ;
f = JFFS2_INODE_INFO ( inode ) ;
dir_f = JFFS2_INODE_INFO ( dir_i ) ;
2008-05-01 18:53:28 +04:00
/* jffs2_do_create() will want to lock it, _after_ reserving
space and taking c - alloc_sem . If we keep it locked here ,
lockdep gets unhappy ( although it ' s a false positive ;
nothing else will be looking at this inode yet so there ' s
no chance of AB - BA deadlock involving its f - > sem ) . */
mutex_unlock ( & f - > sem ) ;
2005-11-07 14:16:07 +03:00
ret = jffs2_do_create ( c , dir_f , f , ri ,
2005-04-17 02:20:36 +04:00
dentry - > d_name . name , dentry - > d_name . len ) ;
2006-05-13 10:09:47 +04:00
if ( ret )
goto fail ;
2005-04-17 02:20:36 +04:00
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( je32_to_cpu ( ri - > ctime ) ) ;
jffs2_free_raw_inode ( ri ) ;
d_instantiate ( dentry , inode ) ;
D1 ( printk ( KERN_DEBUG " jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld \n " ,
2008-05-01 21:47:17 +04:00
inode - > i_ino , inode - > i_mode , inode - > i_nlink ,
f - > inocache - > pino_nlink , inode - > i_mapping - > nrpages ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-05-13 10:09:47 +04:00
fail :
make_bad_inode ( inode ) ;
iput ( inode ) ;
jffs2_free_raw_inode ( ri ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
/***********************************************************************/
static int jffs2_unlink ( struct inode * dir_i , struct dentry * dentry )
{
struct jffs2_sb_info * c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
struct jffs2_inode_info * dir_f = JFFS2_INODE_INFO ( dir_i ) ;
struct jffs2_inode_info * dead_f = JFFS2_INODE_INFO ( dentry - > d_inode ) ;
int ret ;
2005-08-17 17:46:26 +04:00
uint32_t now = get_seconds ( ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:16:07 +03:00
ret = jffs2_do_unlink ( c , dir_f , dentry - > d_name . name ,
2005-08-17 17:46:26 +04:00
dentry - > d_name . len , dead_f , now ) ;
2005-04-17 02:20:36 +04:00
if ( dead_f - > inocache )
2008-05-01 21:47:17 +04:00
dentry - > d_inode - > i_nlink = dead_f - > inocache - > pino_nlink ;
2005-08-17 17:46:26 +04:00
if ( ! ret )
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( now ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/***********************************************************************/
static int jffs2_link ( struct dentry * old_dentry , struct inode * dir_i , struct dentry * dentry )
{
struct jffs2_sb_info * c = JFFS2_SB_INFO ( old_dentry - > d_inode - > i_sb ) ;
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( old_dentry - > d_inode ) ;
struct jffs2_inode_info * dir_f = JFFS2_INODE_INFO ( dir_i ) ;
int ret ;
uint8_t type ;
2005-08-17 17:46:26 +04:00
uint32_t now ;
2005-04-17 02:20:36 +04:00
/* Don't let people make hard links to bad inodes. */
if ( ! f - > inocache )
return - EIO ;
if ( S_ISDIR ( old_dentry - > d_inode - > i_mode ) )
return - EPERM ;
/* XXX: This is ugly */
type = ( old_dentry - > d_inode - > i_mode & S_IFMT ) > > 12 ;
if ( ! type ) type = DT_REG ;
2005-08-17 17:46:26 +04:00
now = get_seconds ( ) ;
ret = jffs2_do_link ( c , dir_f , f - > inocache - > ino , type , dentry - > d_name . name , dentry - > d_name . len , now ) ;
2005-04-17 02:20:36 +04:00
if ( ! ret ) {
2008-04-22 18:13:40 +04:00
mutex_lock ( & f - > sem ) ;
2008-05-01 21:47:17 +04:00
old_dentry - > d_inode - > i_nlink = + + f - > inocache - > pino_nlink ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
d_instantiate ( dentry , old_dentry - > d_inode ) ;
2005-08-17 17:46:26 +04:00
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( now ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & old_dentry - > d_inode - > i_count ) ;
}
return ret ;
}
/***********************************************************************/
static int jffs2_symlink ( struct inode * dir_i , struct dentry * dentry , const char * target )
{
struct jffs2_inode_info * f , * dir_f ;
struct jffs2_sb_info * c ;
struct inode * inode ;
struct jffs2_raw_inode * ri ;
struct jffs2_raw_dirent * rd ;
struct jffs2_full_dnode * fn ;
struct jffs2_full_dirent * fd ;
int namelen ;
2006-05-23 03:38:06 +04:00
uint32_t alloclen ;
2005-03-01 13:50:52 +03:00
int ret , targetlen = strlen ( target ) ;
2005-04-17 02:20:36 +04:00
/* FIXME: If you care. We'd need to use frags for the target
if it grows much more than this */
2005-03-01 13:50:52 +03:00
if ( targetlen > 254 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri )
return - ENOMEM ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
2005-11-07 14:16:07 +03:00
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now , though
2005-04-17 02:20:36 +04:00
*/
namelen = dentry - > d_name . len ;
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * ri ) + targetlen , & alloclen ,
ALLOC_NORMAL , JFFS2_SUMMARY_INODE_SIZE ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
jffs2_free_raw_inode ( ri ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
inode = jffs2_new_inode ( dir_i , S_IFLNK | S_IRWXUGO , ri ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( inode ) ) {
jffs2_free_raw_inode ( ri ) ;
jffs2_complete_reservation ( c ) ;
return PTR_ERR ( inode ) ;
}
inode - > i_op = & jffs2_symlink_inode_operations ;
f = JFFS2_INODE_INFO ( inode ) ;
2005-03-01 13:50:52 +03:00
inode - > i_size = targetlen ;
2005-04-17 02:20:36 +04:00
ri - > isize = ri - > dsize = ri - > csize = cpu_to_je32 ( inode - > i_size ) ;
ri - > totlen = cpu_to_je32 ( sizeof ( * ri ) + inode - > i_size ) ;
ri - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , ri , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
ri - > compr = JFFS2_COMPR_NONE ;
2005-03-01 13:50:52 +03:00
ri - > data_crc = cpu_to_je32 ( crc32 ( 0 , target , targetlen ) ) ;
2005-04-17 02:20:36 +04:00
ri - > node_crc = cpu_to_je32 ( crc32 ( 0 , ri , sizeof ( * ri ) - 8 ) ) ;
2005-11-07 14:16:07 +03:00
2006-05-23 03:38:06 +04:00
fn = jffs2_write_dnode ( c , f , ri , target , targetlen , ALLOC_NORMAL ) ;
2005-04-17 02:20:36 +04:00
jffs2_free_raw_inode ( ri ) ;
if ( IS_ERR ( fn ) ) {
/* Eeek. Wave bye bye */
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fn ) ;
}
2005-03-01 13:50:52 +03:00
2005-07-17 15:13:51 +04:00
/* We use f->target field to store the target path. */
f - > target = kmalloc ( targetlen + 1 , GFP_KERNEL ) ;
if ( ! f - > target ) {
2005-03-01 13:50:52 +03:00
printk ( KERN_WARNING " Can't allocate %d bytes of memory \n " , targetlen + 1 ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-03-01 13:50:52 +03:00
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return - ENOMEM ;
}
2005-07-17 15:13:51 +04:00
memcpy ( f - > target , target , targetlen + 1 ) ;
D1 ( printk ( KERN_DEBUG " jffs2_symlink: symlink's target '%s' cached \n " , ( char * ) f - > target ) ) ;
2005-03-01 13:50:52 +03:00
2005-11-07 14:16:07 +03:00
/* No data here. Only a metadata node, which will be
2005-04-17 02:20:36 +04:00
obsoleted by the first data write
*/
f - > metadata = fn ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
2006-05-13 10:09:47 +04:00
ret = jffs2_init_security ( inode , dir_i ) ;
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
ret = jffs2_init_acl_post ( inode ) ;
2006-05-13 10:09:47 +04:00
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * rd ) + namelen , & alloclen ,
ALLOC_NORMAL , JFFS2_SUMMARY_DIRENT_SIZE ( namelen ) ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
/* Eep. */
jffs2_clear_inode ( inode ) ;
return ret ;
}
rd = jffs2_alloc_raw_dirent ( ) ;
if ( ! rd ) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return - ENOMEM ;
}
dir_f = JFFS2_INODE_INFO ( dir_i ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
rd - > magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
rd - > nodetype = cpu_to_je16 ( JFFS2_NODETYPE_DIRENT ) ;
rd - > totlen = cpu_to_je32 ( sizeof ( * rd ) + namelen ) ;
rd - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
rd - > pino = cpu_to_je32 ( dir_i - > i_ino ) ;
rd - > version = cpu_to_je32 ( + + dir_f - > highest_version ) ;
rd - > ino = cpu_to_je32 ( inode - > i_ino ) ;
rd - > mctime = cpu_to_je32 ( get_seconds ( ) ) ;
rd - > nsize = namelen ;
rd - > type = DT_LNK ;
rd - > node_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( * rd ) - 8 ) ) ;
rd - > name_crc = cpu_to_je32 ( crc32 ( 0 , dentry - > d_name . name , namelen ) ) ;
2006-05-23 03:38:06 +04:00
fd = jffs2_write_dirent ( c , dir_f , rd , dentry - > d_name . name , namelen , ALLOC_NORMAL ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( fd ) ) {
2005-11-07 14:16:07 +03:00
/* dirent failed to write. Delete the inode normally
2005-04-17 02:20:36 +04:00
as if it were the final unlink ( ) */
jffs2_complete_reservation ( c ) ;
jffs2_free_raw_dirent ( rd ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fd ) ;
}
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( je32_to_cpu ( rd - > mctime ) ) ;
jffs2_free_raw_dirent ( rd ) ;
/* Link the fd into the inode's list, obsoleting an old
one if necessary . */
jffs2_add_fd_to_list ( c , fd , & dir_f - > dents ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static int jffs2_mkdir ( struct inode * dir_i , struct dentry * dentry , int mode )
{
struct jffs2_inode_info * f , * dir_f ;
struct jffs2_sb_info * c ;
struct inode * inode ;
struct jffs2_raw_inode * ri ;
struct jffs2_raw_dirent * rd ;
struct jffs2_full_dnode * fn ;
struct jffs2_full_dirent * fd ;
int namelen ;
2006-05-23 03:38:06 +04:00
uint32_t alloclen ;
2005-04-17 02:20:36 +04:00
int ret ;
mode | = S_IFDIR ;
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri )
return - ENOMEM ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
2005-11-07 14:16:07 +03:00
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now , though
2005-04-17 02:20:36 +04:00
*/
namelen = dentry - > d_name . len ;
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * ri ) , & alloclen , ALLOC_NORMAL ,
JFFS2_SUMMARY_INODE_SIZE ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
jffs2_free_raw_inode ( ri ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
inode = jffs2_new_inode ( dir_i , mode , ri ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( inode ) ) {
jffs2_free_raw_inode ( ri ) ;
jffs2_complete_reservation ( c ) ;
return PTR_ERR ( inode ) ;
}
inode - > i_op = & jffs2_dir_inode_operations ;
inode - > i_fop = & jffs2_dir_operations ;
f = JFFS2_INODE_INFO ( inode ) ;
2008-05-01 21:47:17 +04:00
/* Directories get nlink 2 at start */
inode - > i_nlink = 2 ;
/* but ic->pino_nlink is the parent ino# */
f - > inocache - > pino_nlink = dir_i - > i_ino ;
2005-04-17 02:20:36 +04:00
ri - > data_crc = cpu_to_je32 ( 0 ) ;
ri - > node_crc = cpu_to_je32 ( crc32 ( 0 , ri , sizeof ( * ri ) - 8 ) ) ;
2005-11-07 14:16:07 +03:00
2006-05-23 03:38:06 +04:00
fn = jffs2_write_dnode ( c , f , ri , NULL , 0 , ALLOC_NORMAL ) ;
2005-04-17 02:20:36 +04:00
jffs2_free_raw_inode ( ri ) ;
if ( IS_ERR ( fn ) ) {
/* Eeek. Wave bye bye */
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fn ) ;
}
2005-11-07 14:16:07 +03:00
/* No data here. Only a metadata node, which will be
2005-04-17 02:20:36 +04:00
obsoleted by the first data write
*/
f - > metadata = fn ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
2006-05-13 10:09:47 +04:00
ret = jffs2_init_security ( inode , dir_i ) ;
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
ret = jffs2_init_acl_post ( inode ) ;
2006-05-13 10:09:47 +04:00
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * rd ) + namelen , & alloclen ,
ALLOC_NORMAL , JFFS2_SUMMARY_DIRENT_SIZE ( namelen ) ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
/* Eep. */
jffs2_clear_inode ( inode ) ;
return ret ;
}
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
rd = jffs2_alloc_raw_dirent ( ) ;
if ( ! rd ) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return - ENOMEM ;
}
dir_f = JFFS2_INODE_INFO ( dir_i ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
rd - > magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
rd - > nodetype = cpu_to_je16 ( JFFS2_NODETYPE_DIRENT ) ;
rd - > totlen = cpu_to_je32 ( sizeof ( * rd ) + namelen ) ;
rd - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
rd - > pino = cpu_to_je32 ( dir_i - > i_ino ) ;
rd - > version = cpu_to_je32 ( + + dir_f - > highest_version ) ;
rd - > ino = cpu_to_je32 ( inode - > i_ino ) ;
rd - > mctime = cpu_to_je32 ( get_seconds ( ) ) ;
rd - > nsize = namelen ;
rd - > type = DT_DIR ;
rd - > node_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( * rd ) - 8 ) ) ;
rd - > name_crc = cpu_to_je32 ( crc32 ( 0 , dentry - > d_name . name , namelen ) ) ;
2006-05-23 03:38:06 +04:00
fd = jffs2_write_dirent ( c , dir_f , rd , dentry - > d_name . name , namelen , ALLOC_NORMAL ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( fd ) ) {
2005-11-07 14:16:07 +03:00
/* dirent failed to write. Delete the inode normally
2005-04-17 02:20:36 +04:00
as if it were the final unlink ( ) */
jffs2_complete_reservation ( c ) ;
jffs2_free_raw_dirent ( rd ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fd ) ;
}
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( je32_to_cpu ( rd - > mctime ) ) ;
2006-10-01 10:29:04 +04:00
inc_nlink ( dir_i ) ;
2005-04-17 02:20:36 +04:00
jffs2_free_raw_dirent ( rd ) ;
/* Link the fd into the inode's list, obsoleting an old
one if necessary . */
jffs2_add_fd_to_list ( c , fd , & dir_f - > dents ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static int jffs2_rmdir ( struct inode * dir_i , struct dentry * dentry )
{
2008-05-01 21:47:17 +04:00
struct jffs2_sb_info * c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
struct jffs2_inode_info * dir_f = JFFS2_INODE_INFO ( dir_i ) ;
2005-04-17 02:20:36 +04:00
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( dentry - > d_inode ) ;
struct jffs2_full_dirent * fd ;
int ret ;
2008-05-01 21:47:17 +04:00
uint32_t now = get_seconds ( ) ;
2005-04-17 02:20:36 +04:00
for ( fd = f - > dents ; fd ; fd = fd - > next ) {
if ( fd - > ino )
return - ENOTEMPTY ;
}
2008-05-01 21:47:17 +04:00
ret = jffs2_do_unlink ( c , dir_f , dentry - > d_name . name ,
dentry - > d_name . len , f , now ) ;
if ( ! ret ) {
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( now ) ;
clear_nlink ( dentry - > d_inode ) ;
2006-10-01 10:29:03 +04:00
drop_nlink ( dir_i ) ;
2008-05-01 21:47:17 +04:00
}
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-07-06 16:13:13 +04:00
static int jffs2_mknod ( struct inode * dir_i , struct dentry * dentry , int mode , dev_t rdev )
2005-04-17 02:20:36 +04:00
{
struct jffs2_inode_info * f , * dir_f ;
struct jffs2_sb_info * c ;
struct inode * inode ;
struct jffs2_raw_inode * ri ;
struct jffs2_raw_dirent * rd ;
struct jffs2_full_dnode * fn ;
struct jffs2_full_dirent * fd ;
int namelen ;
2006-05-19 03:28:49 +04:00
union jffs2_device_node dev ;
2005-04-17 02:20:36 +04:00
int devlen = 0 ;
2006-05-23 03:38:06 +04:00
uint32_t alloclen ;
2005-04-17 02:20:36 +04:00
int ret ;
2006-05-19 03:28:49 +04:00
if ( ! new_valid_dev ( rdev ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri )
return - ENOMEM ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
c = JFFS2_SB_INFO ( dir_i - > i_sb ) ;
2005-11-07 14:16:07 +03:00
2006-05-19 03:28:49 +04:00
if ( S_ISBLK ( mode ) | | S_ISCHR ( mode ) )
devlen = jffs2_encode_dev ( & dev , rdev ) ;
2005-11-07 14:16:07 +03:00
/* Try to reserve enough space for both node and dirent.
* Just the node will do for now , though
2005-04-17 02:20:36 +04:00
*/
namelen = dentry - > d_name . len ;
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * ri ) + devlen , & alloclen ,
2006-05-19 03:28:49 +04:00
ALLOC_NORMAL , JFFS2_SUMMARY_INODE_SIZE ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
jffs2_free_raw_inode ( ri ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
inode = jffs2_new_inode ( dir_i , mode , ri ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( inode ) ) {
jffs2_free_raw_inode ( ri ) ;
jffs2_complete_reservation ( c ) ;
return PTR_ERR ( inode ) ;
}
inode - > i_op = & jffs2_file_inode_operations ;
init_special_inode ( inode , inode - > i_mode , rdev ) ;
f = JFFS2_INODE_INFO ( inode ) ;
ri - > dsize = ri - > csize = cpu_to_je32 ( devlen ) ;
ri - > totlen = cpu_to_je32 ( sizeof ( * ri ) + devlen ) ;
ri - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , ri , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
ri - > compr = JFFS2_COMPR_NONE ;
ri - > data_crc = cpu_to_je32 ( crc32 ( 0 , & dev , devlen ) ) ;
ri - > node_crc = cpu_to_je32 ( crc32 ( 0 , ri , sizeof ( * ri ) - 8 ) ) ;
2005-11-07 14:16:07 +03:00
2006-05-23 03:38:06 +04:00
fn = jffs2_write_dnode ( c , f , ri , ( char * ) & dev , devlen , ALLOC_NORMAL ) ;
2005-04-17 02:20:36 +04:00
jffs2_free_raw_inode ( ri ) ;
if ( IS_ERR ( fn ) ) {
/* Eeek. Wave bye bye */
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fn ) ;
}
2005-11-07 14:16:07 +03:00
/* No data here. Only a metadata node, which will be
2005-04-17 02:20:36 +04:00
obsoleted by the first data write
*/
f - > metadata = fn ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
2006-05-13 10:09:47 +04:00
ret = jffs2_init_security ( inode , dir_i ) ;
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2007-09-14 10:16:35 +04:00
ret = jffs2_init_acl_post ( inode ) ;
2006-05-13 10:09:47 +04:00
if ( ret ) {
jffs2_clear_inode ( inode ) ;
return ret ;
}
2006-05-23 03:38:06 +04:00
ret = jffs2_reserve_space ( c , sizeof ( * rd ) + namelen , & alloclen ,
ALLOC_NORMAL , JFFS2_SUMMARY_DIRENT_SIZE ( namelen ) ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
/* Eep. */
jffs2_clear_inode ( inode ) ;
return ret ;
}
rd = jffs2_alloc_raw_dirent ( ) ;
if ( ! rd ) {
/* Argh. Now we treat it like a normal delete */
jffs2_complete_reservation ( c ) ;
jffs2_clear_inode ( inode ) ;
return - ENOMEM ;
}
dir_f = JFFS2_INODE_INFO ( dir_i ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
rd - > magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
rd - > nodetype = cpu_to_je16 ( JFFS2_NODETYPE_DIRENT ) ;
rd - > totlen = cpu_to_je32 ( sizeof ( * rd ) + namelen ) ;
rd - > hdr_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
rd - > pino = cpu_to_je32 ( dir_i - > i_ino ) ;
rd - > version = cpu_to_je32 ( + + dir_f - > highest_version ) ;
rd - > ino = cpu_to_je32 ( inode - > i_ino ) ;
rd - > mctime = cpu_to_je32 ( get_seconds ( ) ) ;
rd - > nsize = namelen ;
/* XXX: This is ugly. */
rd - > type = ( mode & S_IFMT ) > > 12 ;
rd - > node_crc = cpu_to_je32 ( crc32 ( 0 , rd , sizeof ( * rd ) - 8 ) ) ;
rd - > name_crc = cpu_to_je32 ( crc32 ( 0 , dentry - > d_name . name , namelen ) ) ;
2006-05-23 03:38:06 +04:00
fd = jffs2_write_dirent ( c , dir_f , rd , dentry - > d_name . name , namelen , ALLOC_NORMAL ) ;
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( fd ) ) {
2005-11-07 14:16:07 +03:00
/* dirent failed to write. Delete the inode normally
2005-04-17 02:20:36 +04:00
as if it were the final unlink ( ) */
jffs2_complete_reservation ( c ) ;
jffs2_free_raw_dirent ( rd ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_clear_inode ( inode ) ;
return PTR_ERR ( fd ) ;
}
dir_i - > i_mtime = dir_i - > i_ctime = ITIME ( je32_to_cpu ( rd - > mctime ) ) ;
jffs2_free_raw_dirent ( rd ) ;
/* Link the fd into the inode's list, obsoleting an old
one if necessary . */
jffs2_add_fd_to_list ( c , fd , & dir_f - > dents ) ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & dir_f - > sem ) ;
2005-04-17 02:20:36 +04:00
jffs2_complete_reservation ( c ) ;
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static int jffs2_rename ( struct inode * old_dir_i , struct dentry * old_dentry ,
2007-07-10 13:01:22 +04:00
struct inode * new_dir_i , struct dentry * new_dentry )
2005-04-17 02:20:36 +04:00
{
int ret ;
struct jffs2_sb_info * c = JFFS2_SB_INFO ( old_dir_i - > i_sb ) ;
struct jffs2_inode_info * victim_f = NULL ;
uint8_t type ;
2005-08-17 17:46:26 +04:00
uint32_t now ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:16:07 +03:00
/* The VFS will check for us and prevent trying to rename a
2005-04-17 02:20:36 +04:00
* file over a directory and vice versa , but if it ' s a directory ,
* the VFS can ' t check whether the victim is empty . The filesystem
* needs to do that for itself .
*/
if ( new_dentry - > d_inode ) {
victim_f = JFFS2_INODE_INFO ( new_dentry - > d_inode ) ;
if ( S_ISDIR ( new_dentry - > d_inode - > i_mode ) ) {
struct jffs2_full_dirent * fd ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & victim_f - > sem ) ;
2005-04-17 02:20:36 +04:00
for ( fd = victim_f - > dents ; fd ; fd = fd - > next ) {
if ( fd - > ino ) {
2008-04-22 18:13:40 +04:00
mutex_unlock ( & victim_f - > sem ) ;
2005-04-17 02:20:36 +04:00
return - ENOTEMPTY ;
}
}
2008-04-22 18:13:40 +04:00
mutex_unlock ( & victim_f - > sem ) ;
2005-04-17 02:20:36 +04:00
}
}
/* XXX: We probably ought to alloc enough space for
2005-11-07 14:16:07 +03:00
both nodes at the same time . Writing the new link ,
2005-04-17 02:20:36 +04:00
then getting - ENOSPC , is quite bad : )
*/
/* Make a hard link */
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
/* XXX: This is ugly */
type = ( old_dentry - > d_inode - > i_mode & S_IFMT ) > > 12 ;
if ( ! type ) type = DT_REG ;
2005-08-17 17:46:26 +04:00
now = get_seconds ( ) ;
2005-11-07 14:16:07 +03:00
ret = jffs2_do_link ( c , JFFS2_INODE_INFO ( new_dir_i ) ,
2005-04-17 02:20:36 +04:00
old_dentry - > d_inode - > i_ino , type ,
2005-08-17 17:46:26 +04:00
new_dentry - > d_name . name , new_dentry - > d_name . len , now ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
return ret ;
if ( victim_f ) {
/* There was a victim. Kill it off nicely */
2006-10-01 10:29:03 +04:00
drop_nlink ( new_dentry - > d_inode ) ;
2005-04-17 02:20:36 +04:00
/* Don't oops if the victim was a dirent pointing to an
inode which didn ' t exist . */
if ( victim_f - > inocache ) {
2008-04-22 18:13:40 +04:00
mutex_lock ( & victim_f - > sem ) ;
2008-05-01 21:47:17 +04:00
if ( S_ISDIR ( new_dentry - > d_inode - > i_mode ) )
victim_f - > inocache - > pino_nlink = 0 ;
else
victim_f - > inocache - > pino_nlink - - ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & victim_f - > sem ) ;
2005-04-17 02:20:36 +04:00
}
}
2005-11-07 14:16:07 +03:00
/* If it was a directory we moved, and there was no victim,
2005-04-17 02:20:36 +04:00
increase i_nlink on its new parent */
if ( S_ISDIR ( old_dentry - > d_inode - > i_mode ) & & ! victim_f )
2006-10-01 10:29:04 +04:00
inc_nlink ( new_dir_i ) ;
2005-04-17 02:20:36 +04:00
/* Unlink the original */
2005-11-07 14:16:07 +03:00
ret = jffs2_do_unlink ( c , JFFS2_INODE_INFO ( old_dir_i ) ,
2005-08-17 17:46:26 +04:00
old_dentry - > d_name . name , old_dentry - > d_name . len , NULL , now ) ;
2005-04-17 02:20:36 +04:00
/* We don't touch inode->i_nlink */
if ( ret ) {
/* Oh shit. We really ought to make a single node which can do both atomically */
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( old_dentry - > d_inode ) ;
2008-04-22 18:13:40 +04:00
mutex_lock ( & f - > sem ) ;
2006-10-01 10:29:04 +04:00
inc_nlink ( old_dentry - > d_inode ) ;
2008-05-01 21:47:17 +04:00
if ( f - > inocache & & ! S_ISDIR ( old_dentry - > d_inode - > i_mode ) )
f - > inocache - > pino_nlink + + ;
2008-04-22 18:13:40 +04:00
mutex_unlock ( & f - > sem ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_NOTICE " jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link \n " , ret ) ;
/* Might as well let the VFS know */
d_instantiate ( new_dentry , old_dentry - > d_inode ) ;
atomic_inc ( & old_dentry - > d_inode - > i_count ) ;
2005-08-17 17:46:26 +04:00
new_dir_i - > i_mtime = new_dir_i - > i_ctime = ITIME ( now ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
if ( S_ISDIR ( old_dentry - > d_inode - > i_mode ) )
2006-10-01 10:29:03 +04:00
drop_nlink ( old_dir_i ) ;
2005-04-17 02:20:36 +04:00
2005-08-17 17:46:26 +04:00
new_dir_i - > i_mtime = new_dir_i - > i_ctime = old_dir_i - > i_mtime = old_dir_i - > i_ctime = ITIME ( now ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}