From: Oleg Drokin This is a forward port of 2.4's inode attributes support for reiserfs. Original implementation for 2.4 was performed by Nikita Danilov. In order to enable this support, one must use "attrs" mount options, eg: mount /dev/hda1 /mount/pont -t reiserfs -o attrs Also either the filesystem must have been created with a recent mkreiserfs or must have been modified by a recent version of reiserfsck with its "--clean-attributes" option. If that is not done, attributes support will not be enabled and a kernel message will be printed. This is necessary because old kernels left random garbage in the place where these attributes now live. These attributes are totally compatible with ext2's ones. You can manipulate them with chattr/lsattr etc. Additionally the chattr 'd' option may be used to disable tail packing on a specific file or a directory tree. (The 'd' option normally means "don't dump". reiserfs has overloaded it). fs/reiserfs/dir.c | 1 fs/reiserfs/inode.c | 73 ++++++++++++++++++++++++++++++++++++----- fs/reiserfs/ioctl.c | 62 ++++++++++++++++++++++++++++++++-- fs/reiserfs/procfs.c | 6 +++ fs/reiserfs/super.c | 22 ++++++++++++ include/linux/reiserfs_fs.h | 43 +++++++++++++++++++++++- include/linux/reiserfs_fs_i.h | 3 + include/linux/reiserfs_fs_sb.h | 5 ++ 8 files changed, 201 insertions(+), 14 deletions(-) diff -puN fs/reiserfs/dir.c~reiserfs-inode-attribute-support fs/reiserfs/dir.c --- 25/fs/reiserfs/dir.c~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/fs/reiserfs/dir.c 2003-05-16 01:03:32.000000000 -0700 @@ -21,6 +21,7 @@ struct file_operations reiserfs_dir_oper .read = generic_read_dir, .readdir = reiserfs_readdir, .fsync = reiserfs_dir_fsync, + .ioctl = reiserfs_ioctl, }; int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync) { diff -puN fs/reiserfs/inode.c~reiserfs-inode-attribute-support fs/reiserfs/inode.c --- 25/fs/reiserfs/inode.c~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/fs/reiserfs/inode.c 2003-05-16 01:03:32.000000000 -0700 @@ -918,8 +918,6 @@ static void init_inode (struct inode * i REISERFS_I(inode)->i_prealloc_count = 0; REISERFS_I(inode)->i_trans_id = 0; REISERFS_I(inode)->i_trans_index = 0; - /* nopack = 0, by default */ - REISERFS_I(inode)->i_flags &= ~i_nopack_mask; if (stat_data_v1 (ih)) { struct stat_data_v1 * sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); @@ -954,6 +952,9 @@ static void init_inode (struct inode * i rdev = sd_v1_rdev(sd); REISERFS_I(inode)->i_first_direct_byte = sd_v1_first_direct_byte(sd); + /* nopack is initially zero for v1 objects. For v2 objects, + nopack is initialised from sd_attrs */ + REISERFS_I(inode)->i_flags &= ~i_nopack_mask; } else { // new stat data found, but object may have old items // (directories and symlinks) @@ -983,6 +984,10 @@ static void init_inode (struct inode * i set_inode_item_key_version (inode, KEY_FORMAT_3_6); REISERFS_I(inode)->i_first_direct_byte = 0; set_inode_sd_version (inode, STAT_DATA_V2); + /* read persistent inode attributes from sd and initalise + generic inode flags from them */ + REISERFS_I(inode)->i_attrs = sd_v2_attrs( sd ); + sd_attrs_to_i_attrs( sd_v2_attrs( sd ), inode ); } pathrelse (path); @@ -1007,6 +1012,7 @@ static void init_inode (struct inode * i static void inode2sd (void * sd, struct inode * inode) { struct stat_data * sd_v2 = (struct stat_data *)sd; + __u16 flags; set_sd_v2_mode(sd_v2, inode->i_mode ); set_sd_v2_nlink(sd_v2, inode->i_nlink ); @@ -1017,13 +1023,13 @@ static void inode2sd (void * sd, struct set_sd_v2_atime(sd_v2, inode->i_atime.tv_sec ); set_sd_v2_ctime(sd_v2, inode->i_ctime.tv_sec ); set_sd_v2_blocks(sd_v2, inode->i_blocks ); - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { - set_sd_v2_rdev(sd_v2, kdev_t_to_nr(inode->i_rdev) ); -} + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + set_sd_v2_rdev(sd_v2, kdev_t_to_nr(inode->i_rdev) ); else - { - set_sd_v2_generation(sd_v2, inode->i_generation); - } + set_sd_v2_generation(sd_v2, inode->i_generation); + flags = REISERFS_I(inode)->i_attrs; + i_attrs_to_sd_attrs( inode, &flags ); + set_sd_v2_attrs( sd_v2, flags ); } @@ -1553,6 +1559,10 @@ int reiserfs_new_inode (struct reiserfs_ /* uid and gid must already be set by the caller for quota init */ + /* symlink cannot be immutable or append only, right? */ + if( S_ISLNK( inode -> i_mode ) ) + inode -> i_flags &= ~ ( S_IMMUTABLE | S_APPEND ); + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_size = i_size; inode->i_blocks = (inode->i_size + 511) >> 9; @@ -1565,6 +1575,9 @@ int reiserfs_new_inode (struct reiserfs_ REISERFS_I(inode)->i_prealloc_count = 0; REISERFS_I(inode)->i_trans_id = 0; REISERFS_I(inode)->i_trans_index = 0; + REISERFS_I(inode)->i_attrs = + REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK; + sd_attrs_to_i_attrs( REISERFS_I(inode) -> i_attrs, inode ); if (old_format_only (sb)) make_le_item_head (&ih, 0, KEY_FORMAT_3_5, SD_OFFSET, TYPE_STAT_DATA, SD_V1_SIZE, MAX_US_INT); @@ -2210,6 +2223,50 @@ static int reiserfs_commit_write(struct return ret ; } +void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode ) +{ + if( reiserfs_attrs( inode -> i_sb ) ) { + if( sd_attrs & REISERFS_SYNC_FL ) + inode -> i_flags |= S_SYNC; + else + inode -> i_flags &= ~S_SYNC; + if( sd_attrs & REISERFS_IMMUTABLE_FL ) + inode -> i_flags |= S_IMMUTABLE; + else + inode -> i_flags &= ~S_IMMUTABLE; + if( sd_attrs & REISERFS_NOATIME_FL ) + inode -> i_flags |= S_NOATIME; + else + inode -> i_flags &= ~S_NOATIME; + if( sd_attrs & REISERFS_NOTAIL_FL ) + REISERFS_I(inode)->i_flags |= i_nopack_mask; + else + REISERFS_I(inode)->i_flags &= ~i_nopack_mask; + } +} + +void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs ) +{ + if( reiserfs_attrs( inode -> i_sb ) ) { + if( inode -> i_flags & S_IMMUTABLE ) + *sd_attrs |= REISERFS_IMMUTABLE_FL; + else + *sd_attrs &= ~REISERFS_IMMUTABLE_FL; + if( inode -> i_flags & S_SYNC ) + *sd_attrs |= REISERFS_SYNC_FL; + else + *sd_attrs &= ~REISERFS_SYNC_FL; + if( inode -> i_flags & S_NOATIME ) + *sd_attrs |= REISERFS_NOATIME_FL; + else + *sd_attrs &= ~REISERFS_NOATIME_FL; + if( REISERFS_I(inode)->i_flags & i_nopack_mask ) + *sd_attrs |= REISERFS_NOTAIL_FL; + else + *sd_attrs &= ~REISERFS_NOTAIL_FL; + } +} + /* * Returns 1 if the page's buffers were dropped. The page is locked. * diff -puN fs/reiserfs/ioctl.c~reiserfs-inode-attribute-support fs/reiserfs/ioctl.c --- 25/fs/reiserfs/ioctl.c~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/fs/reiserfs/ioctl.c 2003-05-16 01:03:32.000000000 -0700 @@ -14,17 +14,70 @@ ** supported commands: ** 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect ** and prevent packing file (argument arg has to be non-zero) -** 2) That's all for a while ... +** 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION +** 3) That's all for a while ... */ int reiserfs_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { + unsigned int flags; + switch (cmd) { case REISERFS_IOC_UNPACK: + if( S_ISREG( inode -> i_mode ) ) { if (arg) return reiserfs_unpack (inode, filp); + else + return 0; + } else + return -ENOTTY; + /* following two cases are taken from fs/ext2/ioctl.c by Remy + Card (card@masi.ibp.fr) */ + case REISERFS_IOC_GETFLAGS: + flags = REISERFS_I(inode) -> i_attrs; + i_attrs_to_sd_attrs( inode, ( __u16 * ) &flags ); + return put_user(flags, (int *) arg); + case REISERFS_IOC_SETFLAGS: { + if (IS_RDONLY(inode)) + return -EROFS; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (get_user(flags, (int *) arg)) + return -EFAULT; + + if ( ( flags & REISERFS_IMMUTABLE_FL ) && + !capable( CAP_LINUX_IMMUTABLE ) ) + return -EPERM; - default: + if( ( flags & REISERFS_NOTAIL_FL ) && + S_ISREG( inode -> i_mode ) ) { + int result; + + result = reiserfs_unpack( inode, filp ); + if( result ) + return result; + } + sd_attrs_to_i_attrs( flags, inode ); + REISERFS_I(inode) -> i_attrs = flags; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; + } + case REISERFS_IOC_GETVERSION: + return put_user(inode->i_generation, (int *) arg); + case REISERFS_IOC_SETVERSION: + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + if (IS_RDONLY(inode)) + return -EROFS; + if (get_user(inode->i_generation, (int *) arg)) + return -EFAULT; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; + default: return -ENOTTY; } } @@ -32,7 +85,7 @@ int reiserfs_ioctl (struct inode * inode /* ** reiserfs_unpack ** Function try to convert tail from direct item into indirect. -** It set up nopack attribute in the inode.u.reiserfs_i.nopack +** It set up nopack attribute in the REISERFS_I(inode)->nopack */ int reiserfs_unpack (struct inode * inode, struct file * filp) { @@ -43,7 +96,8 @@ int reiserfs_unpack (struct inode * inod unsigned long blocksize = inode->i_sb->s_blocksize ; if (inode->i_size == 0) { - return -EINVAL ; + REISERFS_I(inode)->i_flags |= i_nopack_mask; + return 0 ; } /* ioctl already done */ if (REISERFS_I(inode)->i_flags & i_nopack_mask) { diff -puN fs/reiserfs/procfs.c~reiserfs-inode-attribute-support fs/reiserfs/procfs.c --- 25/fs/reiserfs/procfs.c~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/fs/reiserfs/procfs.c 2003-05-16 01:03:32.000000000 -0700 @@ -350,6 +350,7 @@ int reiserfs_on_disk_super_in_proc( char struct reiserfs_sb_info *sb_info; struct reiserfs_super_block *rs; int hash_code; + __u32 flags; int len = 0; sb = procinfo_prologue((int)data); @@ -358,6 +359,7 @@ int reiserfs_on_disk_super_in_proc( char sb_info = REISERFS_SB(sb); rs = sb_info -> s_rs; hash_code = DFL( s_hash_function_code ); + flags = DJF( s_flags ); len += sprintf( &buffer[ len ], "block_count: \t%i\n" @@ -373,6 +375,7 @@ int reiserfs_on_disk_super_in_proc( char "tree_height: \t%i\n" "bmap_nr: \t%i\n" "version: \t%i\n" + "flags: \t%x[%s]\n" "reserved_for_journal: \t%i\n", DFL( s_block_count ), @@ -391,6 +394,9 @@ int reiserfs_on_disk_super_in_proc( char DF( s_tree_height ), DF( s_bmap_nr ), DF( s_version ), + flags, + ( flags & reiserfs_attrs_cleared ) + ? "attrs_cleared" : "", DF (s_reserved_for_journal)); procinfo_epilogue( sb ); diff -puN fs/reiserfs/super.c~reiserfs-inode-attribute-support fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/fs/reiserfs/super.c 2003-05-16 01:03:32.000000000 -0700 @@ -708,6 +708,24 @@ for old setups still work */ return 1; } +static void handle_attrs( struct super_block *s ) +{ + struct reiserfs_super_block * rs; + + if( reiserfs_attrs( s ) ) { + rs = SB_DISK_SUPER_BLOCK (s); + if( old_format_only(s) ) { + reiserfs_warning( "reiserfs: cannot support attributes on 3.5.x disk format\n" ); + REISERFS_SB(s) -> s_mount_opt &= ~ ( 1 << REISERFS_ATTRS ); + return; + } + if( !( le32_to_cpu( rs -> s_flags ) & reiserfs_attrs_cleared ) ) { + reiserfs_warning( "reiserfs: cannot support attributes until flag is set in super-block\n" ); + REISERFS_SB(s) -> s_mount_opt &= ~ ( 1 << REISERFS_ATTRS ); + } + } +} + static int reiserfs_remount (struct super_block * s, int * mount_flags, char * arg) { struct reiserfs_super_block * rs; @@ -720,6 +738,8 @@ static int reiserfs_remount (struct supe if (!reiserfs_parse_options(s, arg, &mount_options, &blocks, NULL)) return -EINVAL; + handle_attrs( s ); + if(blocks) { int rc = reiserfs_resize(s, blocks); if (rc != 0) @@ -1319,6 +1339,8 @@ static int reiserfs_fill_super (struct s // mark hash in super block: it could be unset. overwrite should be ok set_sb_hash_function_code( rs, function2code(sbi->s_hash_function ) ); + handle_attrs( s ); + reiserfs_proc_info_init( s ); reiserfs_proc_register( s, "version", reiserfs_version_in_proc ); reiserfs_proc_register( s, "super", reiserfs_super_in_proc ); diff -puN include/linux/reiserfs_fs.h~reiserfs-inode-attribute-support include/linux/reiserfs_fs.h --- 25/include/linux/reiserfs_fs.h~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/include/linux/reiserfs_fs.h 2003-05-16 01:03:32.000000000 -0700 @@ -872,11 +872,41 @@ struct stat_data_v1 #define set_sd_v1_first_direct_byte(sdp,v) \ ((sdp)->sd_first_direct_byte = cpu_to_le32(v)) +#include + +/* inode flags stored in sd_attrs (nee sd_reserved) */ + +/* we want common flags to have the same values as in ext2, + so chattr(1) will work without problems */ +#define REISERFS_IMMUTABLE_FL EXT2_IMMUTABLE_FL +#define REISERFS_SYNC_FL EXT2_SYNC_FL +#define REISERFS_NOATIME_FL EXT2_NOATIME_FL +#define REISERFS_NODUMP_FL EXT2_NODUMP_FL +#define REISERFS_SECRM_FL EXT2_SECRM_FL +#define REISERFS_UNRM_FL EXT2_UNRM_FL +#define REISERFS_COMPR_FL EXT2_COMPR_FL +/* persistent flag to disable tails on per-file basic. + Note, that is inheritable: mark directory with this and + all new files inside will not have tails. + + Teodore Tso allocated EXT2_NODUMP_FL (0x00008000) for this. Change + numeric constant to ext2 macro when available. */ +#define REISERFS_NOTAIL_FL (0x00008000) /* EXT2_NOTAIL_FL */ + +/* persistent flags that file inherits from the parent directory */ +#define REISERFS_INHERIT_MASK ( REISERFS_IMMUTABLE_FL | \ + REISERFS_SYNC_FL | \ + REISERFS_NOATIME_FL | \ + REISERFS_NODUMP_FL | \ + REISERFS_SECRM_FL | \ + REISERFS_COMPR_FL | \ + REISERFS_NOTAIL_FL ) + /* Stat Data on disk (reiserfs version of UFS disk inode minus the address blocks) */ struct stat_data { __u16 sd_mode; /* file type, permissions */ - __u16 sd_reserved; + __u16 sd_attrs; /* persistent inode flags */ __u32 sd_nlink; /* number of hard links */ __u64 sd_size; /* file size */ __u32 sd_uid; /* owner */ @@ -929,6 +959,8 @@ struct stat_data { #define set_sd_v2_rdev(sdp,v) ((sdp)->u.sd_rdev = cpu_to_le32(v)) #define sd_v2_generation(sdp) (le32_to_cpu((sdp)->u.sd_generation)) #define set_sd_v2_generation(sdp,v) ((sdp)->u.sd_generation = cpu_to_le32(v)) +#define sd_v2_attrs(sdp) (le16_to_cpu((sdp)->sd_attrs)) +#define set_sd_v2_attrs(sdp,v) ((sdp)->sd_attrs = cpu_to_le16(v)) /***************************************************************************/ @@ -1871,6 +1903,9 @@ int reiserfs_new_inode (struct reiserfs_ int reiserfs_sync_inode (struct reiserfs_transaction_handle *th, struct inode * inode); void reiserfs_update_sd (struct reiserfs_transaction_handle *th, struct inode * inode); +void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode ); +void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs ); + /* namei.c */ inline void set_de_name_and_namelen (struct reiserfs_dir_entry * de); int search_by_entry_key (struct super_block * sb, const struct cpu_key * key, @@ -2145,6 +2180,12 @@ int reiserfs_unpack (struct inode * inod /* ioctl's command */ #define REISERFS_IOC_UNPACK _IOW(0xCD,1,long) +/* define following flags to be the same as in ext2, so that chattr(1), + lsattr(1) will work with us. */ +#define REISERFS_IOC_GETFLAGS EXT2_IOC_GETFLAGS +#define REISERFS_IOC_SETFLAGS EXT2_IOC_SETFLAGS +#define REISERFS_IOC_GETVERSION EXT2_IOC_GETVERSION +#define REISERFS_IOC_SETVERSION EXT2_IOC_SETVERSION /* Locking primitives */ /* Right now we are still falling back to (un)lock_kernel, but eventually that diff -puN include/linux/reiserfs_fs_i.h~reiserfs-inode-attribute-support include/linux/reiserfs_fs_i.h --- 25/include/linux/reiserfs_fs_i.h~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/include/linux/reiserfs_fs_i.h 2003-05-16 01:03:32.000000000 -0700 @@ -32,6 +32,9 @@ struct reiserfs_inode_info { __u32 i_first_direct_byte; // offset of first byte stored in direct item. + /* copy of persistent inode flags read from sd_attrs. */ + __u32 i_attrs; + int i_prealloc_block; /* first unused block of a sequence of unused blocks */ int i_prealloc_count; /* length of that sequence */ struct list_head i_prealloc_list; /* per-transaction list of inodes which diff -puN include/linux/reiserfs_fs_sb.h~reiserfs-inode-attribute-support include/linux/reiserfs_fs_sb.h --- 25/include/linux/reiserfs_fs_sb.h~reiserfs-inode-attribute-support 2003-05-16 01:03:32.000000000 -0700 +++ 25-akpm/include/linux/reiserfs_fs_sb.h 2003-05-16 01:03:32.000000000 -0700 @@ -8,6 +8,9 @@ #include #endif +typedef enum { + reiserfs_attrs_cleared = 0x00000001, +} reiserfs_super_block_flags; /* struct reiserfs_super_block accessors/mutators * since this is a disk structure, it will always be in @@ -436,7 +439,6 @@ struct reiserfs_sb_info #define REISERFS_NO_BORDER 11 #define REISERFS_NO_UNHASHED_RELOCATION 12 #define REISERFS_HASHED_RELOCATION 13 -#define REISERFS_TEST4 14 #define REISERFS_ATTRS 15 @@ -458,6 +460,7 @@ struct reiserfs_sb_info #define have_small_tails(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_SMALLTAIL)) #define replay_only(s) (REISERFS_SB(s)->s_mount_opt & (1 << REPLAYONLY)) #define reiserfs_dont_log(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_NOLOG)) +#define reiserfs_attrs(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_ATTRS)) #define old_format_only(s) (REISERFS_SB(s)->s_properties & (1 << REISERFS_3_5)) #define convert_reiserfs(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_CONVERT)) _