diff options
author | Shengyu Zhang <lastavengers@outlook.com> | 2016-07-06 14:21:23 +0800 |
---|---|---|
committer | Justus Winter <justus@gnupg.org> | 2016-11-10 15:31:02 +0100 |
commit | 7615b51f53b2dd07d877e8ee9b222f723fe1f223 (patch) | |
tree | a34dec3e0171b15c41e4a33545038cd71049b98f /ext2fs/inode.c | |
parent | 1b38918eb7408501b9a370e98cb15613a7f3a2ad (diff) |
ext2fs: Use xattr to store passive translator
* ext2fs/inode.c (diskfs_set_translator): Use xattr to store passive translator
if xattr is supported by filesystem.
(diskfs_get_translator): Read value of xattr key "gnu.translator" if it exists.
(diskfs_user_read_node): Set translator flag on reading.
Diffstat (limited to 'ext2fs/inode.c')
-rw-r--r-- | ext2fs/inode.c | 224 |
1 files changed, 161 insertions, 63 deletions
diff --git a/ext2fs/inode.c b/ext2fs/inode.c index ccc8d699..6b582b80 100644 --- a/ext2fs/inode.c +++ b/ext2fs/inode.c @@ -26,6 +26,7 @@ #include <sys/stat.h> #include <sys/statfs.h> #include <sys/statvfs.h> +#include <sys/xattr.h> /* these flags aren't actually defined by a header file yet, so temporarily disable them if necessary. */ @@ -166,8 +167,16 @@ diskfs_user_read_node (struct node *np, struct lookup_context *ctx) { st->st_mode = di->i_mode | (di->i_mode_high << 16); st->st_mode &= ~S_ITRANS; + if (di->i_translator) st->st_mode |= S_IPTRANS; + else + { + size_t datalen; + err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen); + if (! err && datalen > 0) + st->st_mode |= S_IPTRANS; + } st->st_uid = di->i_uid | (di->i_uid_high << 16); st->st_gid = di->i_gid | (di->i_gid_high << 16); @@ -178,7 +187,11 @@ diskfs_user_read_node (struct node *np, struct lookup_context *ctx) } else { + size_t datalen; st->st_mode = di->i_mode & ~S_ITRANS; + err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen); + if (! err && datalen > 0) + st->st_mode |= S_IPTRANS; st->st_uid = di->i_uid; st->st_gid = di->i_gid; st->st_author = st->st_uid; @@ -540,81 +553,143 @@ error_t diskfs_set_translator (struct node *np, const char *name, unsigned namelen, struct protid *cred) { - daddr_t blkno; error_t err; - char buf[block_size]; - struct ext2_inode *di; assert (!diskfs_readonly); if (sblock->s_creator_os != EXT2_OS_HURD) return EOPNOTSUPP; - if (namelen + 2 > block_size) - return ENAMETOOLONG; - err = diskfs_catch_exception (); if (err) return err; - di = dino_ref (np->cache_id); - blkno = di->i_translator; - - if (namelen && !blkno) + /* If xattr is supported for this filesystem, use xattr to store translator + record, otherwise, use legacy translator record */ + if (EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR)) { - /* Allocate block for translator */ - blkno = - ext2_new_block ((diskfs_node_disknode (np)->info.i_block_group - * EXT2_BLOCKS_PER_GROUP (sblock)) - + sblock->s_first_data_block, - 0, 0, 0); - if (blkno == 0) + daddr_t blkno; + struct ext2_inode *di; + + di = dino_ref (np->cache_id); + blkno = di->i_translator; + + /* If a legacy translator record found, clear it */ + if (blkno) + { + ext2_warning ("Old translator record found, clear it"); + + /* Clear block for translator going away. */ + di->i_translator = 0; + diskfs_node_disknode (np)->info_i_translator = 0; + record_global_poke (di); + ext2_free_blocks (blkno, 1); + + np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; + np->dn_stat.st_mode &= ~S_IPTRANS; + np->dn_set_ctime = 1; + } + else + dino_deref (di); + + /* Use xattr to store translator record, with key "gnu.translator" */ + if (namelen) { - dino_deref (di); - diskfs_end_catch_exception (); - return ENOSPC; + err = ext2_set_xattr (np, "gnu.translator", name, namelen, 0); + + if (!err) + { + np->dn_stat.st_mode |= S_IPTRANS; + np->dn_set_ctime = 1; + } } + else + { + /* Removing the translator. */ + err = ext2_set_xattr (np, "gnu.translator", NULL, 0, 0); - di->i_translator = blkno; - diskfs_node_disknode (np)->info_i_translator = blkno; - record_global_poke (di); + if (err == ENODATA) + /* Happens if the key did not exist in the first place. */ + err = 0; - np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block; - np->dn_set_ctime = 1; - } - else if (!namelen && blkno) - { - /* Clear block for translator going away. */ - di->i_translator = 0; - diskfs_node_disknode (np)->info_i_translator = 0; - record_global_poke (di); - ext2_free_blocks (blkno, 1); - - np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; - np->dn_stat.st_mode &= ~S_IPTRANS; - np->dn_set_ctime = 1; + if (!err) + { + /* Do not use hurd extensions on non-hurd created filesystem */ + np->dn_stat.st_mode &= ~S_IPTRANS; + np->dn_set_ctime = 1; + } + } } else - dino_deref (di); - - if (namelen) { - void *blkptr; + /* Use legacy translator record when xattr is no supported */ + daddr_t blkno; + struct ext2_inode *di; + char buf[block_size]; - buf[0] = namelen & 0xFF; - buf[1] = (namelen >> 8) & 0xFF; - memcpy (buf + 2, name, namelen); + if (namelen + 2 > block_size) + return ENAMETOOLONG; - blkptr = disk_cache_block_ref (blkno); - memcpy (blkptr, buf, block_size); - record_global_poke (blkptr); + di = dino_ref (np->cache_id); + blkno = di->i_translator; - np->dn_stat.st_mode |= S_IPTRANS; - np->dn_set_ctime = 1; + if (namelen && !blkno) + { + /* Allocate block for translator */ + blkno = + ext2_new_block ((diskfs_node_disknode (np)->info.i_block_group + * EXT2_BLOCKS_PER_GROUP (sblock)) + + sblock->s_first_data_block, + 0, 0, 0); + if (blkno == 0) + { + dino_deref (di); + diskfs_end_catch_exception (); + return ENOSPC; + } + + di->i_translator = blkno; + diskfs_node_disknode (np)->info_i_translator = blkno; + record_global_poke (di); + + np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block; + np->dn_set_ctime = 1; + } + else if (!namelen && blkno) + { + /* Clear block for translator going away. */ + di->i_translator = 0; + diskfs_node_disknode (np)->info_i_translator = 0; + record_global_poke (di); + ext2_free_blocks (blkno, 1); + + np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; + np->dn_stat.st_mode &= ~S_IPTRANS; + np->dn_set_ctime = 1; + } + else + dino_deref (di); + + if (namelen) + { + void *blkptr; + + buf[0] = namelen & 0xFF; + buf[1] = (namelen >> 8) & 0xFF; + memcpy (buf + 2, name, namelen); + + blkptr = disk_cache_block_ref (blkno); + memcpy (blkptr, buf, block_size); + record_global_poke (blkptr); + + np->dn_stat.st_mode |= S_IPTRANS; + np->dn_set_ctime = 1; + } } diskfs_end_catch_exception (); return err; + } /* Implement the diskfs_get_translator callback from the diskfs library. @@ -624,11 +699,12 @@ diskfs_get_translator (struct node *np, char **namep, unsigned *namelen) { error_t err = 0; daddr_t blkno; - unsigned datalen; + int datalen; void *transloc; struct ext2_inode *di; - assert (sblock->s_creator_os == EXT2_OS_HURD); + if (sblock->s_creator_os != EXT2_OS_HURD) + return EOPNOTSUPP; err = diskfs_catch_exception (); if (err) @@ -637,23 +713,45 @@ diskfs_get_translator (struct node *np, char **namep, unsigned *namelen) di = dino_ref (np->cache_id); blkno = di->i_translator; dino_deref (di); - assert (blkno); - transloc = disk_cache_block_ref (blkno); - datalen = - ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8); - if (datalen > block_size - 2) - err = EFTYPE; /* ? */ - else + /* If an old translator record found, read it firstly */ + if (blkno) { - *namep = malloc (datalen); - if (!*namep) - err = ENOMEM; + /* If xattr is no supported by this filesystem, don't report a warning */ + if (EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR)) + ext2_warning ("This is a old translotor record, please update it"); + + transloc = disk_cache_block_ref (blkno); + datalen = + ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8); + if (datalen > block_size - 2) + err = EFTYPE; /* ? */ else - memcpy (*namep, transloc + 2, datalen); + { + *namep = malloc (datalen); + if (!*namep) + err = ENOMEM; + else + memcpy (*namep, transloc + 2, datalen); + } + + disk_cache_block_deref (transloc); + diskfs_end_catch_exception (); + + *namelen = datalen; + return err; } - disk_cache_block_deref (transloc); + err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen); + if (err) + return err; + + *namep = malloc (datalen); + if (!*namep) + err = ENOMEM; + else + err = ext2_get_xattr (np, "gnu.translator", *namep, &datalen); + diskfs_end_catch_exception (); *namelen = datalen; |