2005-04-17 02:20:36 +04:00
/*
* linux / fs / hfsplus / ioctl . c
*
* Copyright ( C ) 2003
* Ethan Benson < erbenson @ alaska . net >
* partially derived from linux / fs / ext2 / ioctl . c
* Copyright ( C ) 1993 , 1994 , 1995
* Remy Card ( card @ masi . ibp . fr )
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie ( Paris VI )
*
* hfsplus ioctls
*/
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/xattr.h>
2010-04-27 18:24:20 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include "hfsplus_fs.h"
2010-04-27 18:24:20 +04:00
long hfsplus_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2010-04-27 18:24:20 +04:00
struct inode * inode = filp - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
unsigned int flags ;
switch ( cmd ) {
case HFSPLUS_IOC_EXT2_GETFLAGS :
flags = 0 ;
if ( HFSPLUS_I ( inode ) . rootflags & HFSPLUS_FLG_IMMUTABLE )
2006-08-29 22:06:16 +04:00
flags | = FS_IMMUTABLE_FL ; /* EXT2_IMMUTABLE_FL */
2005-04-17 02:20:36 +04:00
if ( HFSPLUS_I ( inode ) . rootflags & HFSPLUS_FLG_APPEND )
2006-08-29 22:06:16 +04:00
flags | = FS_APPEND_FL ; /* EXT2_APPEND_FL */
2005-04-17 02:20:36 +04:00
if ( HFSPLUS_I ( inode ) . userflags & HFSPLUS_FLG_NODUMP )
2006-08-29 22:06:16 +04:00
flags | = FS_NODUMP_FL ; /* EXT2_NODUMP_FL */
2005-04-17 02:20:36 +04:00
return put_user ( flags , ( int __user * ) arg ) ;
case HFSPLUS_IOC_EXT2_SETFLAGS : {
2008-02-16 01:37:46 +03:00
int err = 0 ;
2010-10-01 07:41:27 +04:00
lock_kernel ( ) ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
2010-04-27 18:24:20 +04:00
if ( err ) {
unlock_kernel ( ) ;
2008-02-16 01:37:46 +03:00
return err ;
2010-04-27 18:24:20 +04:00
}
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( ! is_owner_or_cap ( inode ) ) {
err = - EACCES ;
goto setflags_out ;
}
if ( get_user ( flags , ( int __user * ) arg ) ) {
err = - EFAULT ;
goto setflags_out ;
}
2006-08-29 22:06:16 +04:00
if ( flags & ( FS_IMMUTABLE_FL | FS_APPEND_FL ) | |
2005-04-17 02:20:36 +04:00
HFSPLUS_I ( inode ) . rootflags & ( HFSPLUS_FLG_IMMUTABLE | HFSPLUS_FLG_APPEND ) ) {
2008-02-16 01:37:46 +03:00
if ( ! capable ( CAP_LINUX_IMMUTABLE ) ) {
err = - EPERM ;
goto setflags_out ;
}
2005-04-17 02:20:36 +04:00
}
/* don't silently ignore unsupported ext2 flags */
2008-02-16 01:37:46 +03:00
if ( flags & ~ ( FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL ) ) {
err = - EOPNOTSUPP ;
goto setflags_out ;
}
2006-08-29 22:06:16 +04:00
if ( flags & FS_IMMUTABLE_FL ) { /* EXT2_IMMUTABLE_FL */
2005-04-17 02:20:36 +04:00
inode - > i_flags | = S_IMMUTABLE ;
HFSPLUS_I ( inode ) . rootflags | = HFSPLUS_FLG_IMMUTABLE ;
} else {
inode - > i_flags & = ~ S_IMMUTABLE ;
HFSPLUS_I ( inode ) . rootflags & = ~ HFSPLUS_FLG_IMMUTABLE ;
}
2006-08-29 22:06:16 +04:00
if ( flags & FS_APPEND_FL ) { /* EXT2_APPEND_FL */
2005-04-17 02:20:36 +04:00
inode - > i_flags | = S_APPEND ;
HFSPLUS_I ( inode ) . rootflags | = HFSPLUS_FLG_APPEND ;
} else {
inode - > i_flags & = ~ S_APPEND ;
HFSPLUS_I ( inode ) . rootflags & = ~ HFSPLUS_FLG_APPEND ;
}
2006-08-29 22:06:16 +04:00
if ( flags & FS_NODUMP_FL ) /* EXT2_NODUMP_FL */
2005-04-17 02:20:36 +04:00
HFSPLUS_I ( inode ) . userflags | = HFSPLUS_FLG_NODUMP ;
else
HFSPLUS_I ( inode ) . userflags & = ~ HFSPLUS_FLG_NODUMP ;
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
2008-02-16 01:37:46 +03:00
setflags_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2010-04-27 18:24:20 +04:00
unlock_kernel ( ) ;
2008-02-16 01:37:46 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
default :
return - ENOTTY ;
}
}
int hfsplus_setxattr ( struct dentry * dentry , const char * name ,
const void * value , size_t size , int flags )
{
struct inode * inode = dentry - > d_inode ;
struct hfs_find_data fd ;
hfsplus_cat_entry entry ;
struct hfsplus_cat_file * file ;
int res ;
if ( ! S_ISREG ( inode - > i_mode ) | | HFSPLUS_IS_RSRC ( inode ) )
return - EOPNOTSUPP ;
res = hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) . cat_tree , & fd ) ;
if ( res )
return res ;
res = hfsplus_find_cat ( inode - > i_sb , inode - > i_ino , & fd ) ;
if ( res )
goto out ;
hfs_bnode_read ( fd . bnode , & entry , fd . entryoffset ,
sizeof ( struct hfsplus_cat_file ) ) ;
file = & entry . file ;
if ( ! strcmp ( name , " hfs.type " ) ) {
if ( size = = 4 )
memcpy ( & file - > user_info . fdType , value , 4 ) ;
else
res = - ERANGE ;
} else if ( ! strcmp ( name , " hfs.creator " ) ) {
if ( size = = 4 )
memcpy ( & file - > user_info . fdCreator , value , 4 ) ;
else
res = - ERANGE ;
} else
res = - EOPNOTSUPP ;
if ( ! res )
hfs_bnode_write ( fd . bnode , & entry , fd . entryoffset ,
sizeof ( struct hfsplus_cat_file ) ) ;
out :
hfs_find_exit ( & fd ) ;
return res ;
}
ssize_t hfsplus_getxattr ( struct dentry * dentry , const char * name ,
void * value , size_t size )
{
struct inode * inode = dentry - > d_inode ;
struct hfs_find_data fd ;
hfsplus_cat_entry entry ;
struct hfsplus_cat_file * file ;
ssize_t res = 0 ;
if ( ! S_ISREG ( inode - > i_mode ) | | HFSPLUS_IS_RSRC ( inode ) )
return - EOPNOTSUPP ;
if ( size ) {
res = hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) . cat_tree , & fd ) ;
if ( res )
return res ;
res = hfsplus_find_cat ( inode - > i_sb , inode - > i_ino , & fd ) ;
if ( res )
goto out ;
hfs_bnode_read ( fd . bnode , & entry , fd . entryoffset ,
sizeof ( struct hfsplus_cat_file ) ) ;
}
file = & entry . file ;
if ( ! strcmp ( name , " hfs.type " ) ) {
if ( size > = 4 ) {
memcpy ( value , & file - > user_info . fdType , 4 ) ;
res = 4 ;
} else
res = size ? - ERANGE : 4 ;
} else if ( ! strcmp ( name , " hfs.creator " ) ) {
if ( size > = 4 ) {
memcpy ( value , & file - > user_info . fdCreator , 4 ) ;
res = 4 ;
} else
res = size ? - ERANGE : 4 ;
} else
res = - ENODATA ;
out :
if ( size )
hfs_find_exit ( & fd ) ;
return res ;
}
# define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
ssize_t hfsplus_listxattr ( struct dentry * dentry , char * buffer , size_t size )
{
struct inode * inode = dentry - > d_inode ;
if ( ! S_ISREG ( inode - > i_mode ) | | HFSPLUS_IS_RSRC ( inode ) )
return - EOPNOTSUPP ;
if ( ! buffer | | ! size )
return HFSPLUS_ATTRLIST_SIZE ;
if ( size < HFSPLUS_ATTRLIST_SIZE )
return - ERANGE ;
strcpy ( buffer , " hfs.type " ) ;
strcpy ( buffer + sizeof ( " hfs.type " ) , " hfs.creator " ) ;
return HFSPLUS_ATTRLIST_SIZE ;
}