ext2fs: add read-write support for Extended Attributes.
Extended attributes and their particular implementation in linux are different from FreeBSD so in this case we have started diverging from the UFS EA implementation, which would be the natural reference. Depending on future progress implementing ACLs this approach may change but for now bring to the tree an implementation that is consistent and can be tested. Submitted by: Fedor Uporov Differential Revision: https://reviews.freebsd.org/D10460
This commit is contained in:
parent
bd29cb78ca
commit
a35f88bfe6
@ -131,6 +131,25 @@ ext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size,
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate EA's block for inode.
|
||||
*/
|
||||
daddr_t
|
||||
ext2_allocfacl(struct inode *ip)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
daddr_t facl;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
EXT2_LOCK(ip->i_ump);
|
||||
facl = ext2_alloccg(ip, ino_to_cg(fs, ip->i_number), 0, fs->e2fs_bsize);
|
||||
if (0 == facl)
|
||||
EXT2_UNLOCK(ip->i_ump);
|
||||
|
||||
return (facl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reallocate a sequence of blocks into a contiguous sequence of blocks.
|
||||
*
|
||||
|
@ -44,21 +44,66 @@
|
||||
#include <fs/ext2fs/ext2_dinode.h>
|
||||
#include <fs/ext2fs/ext2_mount.h>
|
||||
#include <fs/ext2fs/ext2_extattr.h>
|
||||
#include <fs/ext2fs/ext2_extern.h>
|
||||
|
||||
|
||||
static int
|
||||
ext2_extattr_index_to_bsd(int index)
|
||||
{
|
||||
switch (index) {
|
||||
case EXT4_XATTR_INDEX_USER:
|
||||
return EXTATTR_NAMESPACE_USER;
|
||||
|
||||
case EXT4_XATTR_INDEX_SYSTEM:
|
||||
return EXTATTR_NAMESPACE_SYSTEM;
|
||||
return (EXTATTR_NAMESPACE_SYSTEM);
|
||||
|
||||
default:
|
||||
return EXTATTR_NAMESPACE_EMPTY;
|
||||
case EXT4_XATTR_INDEX_USER:
|
||||
return (EXTATTR_NAMESPACE_USER);
|
||||
}
|
||||
|
||||
return (EXTATTR_NAMESPACE_EMPTY);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_extattr_index_to_linux(int index)
|
||||
{
|
||||
switch (index) {
|
||||
case EXTATTR_NAMESPACE_SYSTEM:
|
||||
return (EXT4_XATTR_INDEX_SYSTEM);
|
||||
|
||||
case EXTATTR_NAMESPACE_USER:
|
||||
return (EXT4_XATTR_INDEX_USER);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
|
||||
{
|
||||
if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
|
||||
return (EINVAL);
|
||||
|
||||
if (strlen(attrname) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
|
||||
return (ENAMETOOLONG);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
|
||||
{
|
||||
struct ext2fs_extattr_entry *next;
|
||||
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
next = EXT2_EXTATTR_NEXT(entry);
|
||||
if ((char *)next >= end)
|
||||
return (EIO);
|
||||
|
||||
entry = next;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
@ -69,8 +114,6 @@ ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_dinode_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
char *end;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
@ -95,21 +138,15 @@ ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Check attributes integrity */
|
||||
entry = EXT2_IFIRST(header);
|
||||
end = (char *)dinode + EXT2_INODE_SIZE(fs);
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
next = EXT2_EXTATTR_NEXT(entry);
|
||||
if ((char *)next >= end) {
|
||||
brelse(bp);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
entry = next;
|
||||
error = ext2_extattr_check(EXT2_IFIRST(header),
|
||||
(char *)dinode + EXT2_INODE_SIZE(fs));
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
|
||||
continue;
|
||||
|
||||
@ -121,12 +158,14 @@ ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
|
||||
memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
|
||||
error = uiomove(attr_name, entry->e_name_len + 1, uio);
|
||||
free(attr_name, M_TEMP);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
@ -137,8 +176,6 @@ ext2_extattr_block_list(struct inode *ip, int attrnamespace,
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
char *end;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
@ -157,17 +194,10 @@ ext2_extattr_block_list(struct inode *ip, int attrnamespace,
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Check attributes integrity */
|
||||
end = bp->b_data + bp->b_bufsize;
|
||||
entry = EXT2_FIRST_ENTRY(bp);
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
next = EXT2_EXTATTR_NEXT(entry);
|
||||
if ((char *)next >= end) {
|
||||
brelse(bp);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
entry = next;
|
||||
error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
|
||||
@ -183,12 +213,14 @@ ext2_extattr_block_list(struct inode *ip, int attrnamespace,
|
||||
memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
|
||||
error = uiomove(attr_name, entry->e_name_len + 1, uio);
|
||||
free(attr_name, M_TEMP);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
@ -199,8 +231,6 @@ ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_dinode_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
char *end;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
@ -222,20 +252,14 @@ ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
|
||||
|
||||
if (header->h_magic != EXTATTR_MAGIC) {
|
||||
brelse(bp);
|
||||
return (0);
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
/* Check attributes integrity */
|
||||
entry = EXT2_IFIRST(header);
|
||||
end = (char *)dinode + EXT2_INODE_SIZE(fs);
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
next = EXT2_EXTATTR_NEXT(entry);
|
||||
if ((char *)next >= end) {
|
||||
brelse(bp);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
entry = next;
|
||||
error = ext2_extattr_check(EXT2_IFIRST(header),
|
||||
(char *)dinode + EXT2_INODE_SIZE(fs));
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
|
||||
@ -248,19 +272,18 @@ ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
|
||||
if (uio == NULL)
|
||||
*size += entry->e_value_size;
|
||||
else {
|
||||
error = uiomove(((char *)EXT2_IFIRST(header)) + entry->e_value_offs,
|
||||
entry->e_value_size, uio);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
error = uiomove(((char *)EXT2_IFIRST(header)) +
|
||||
entry->e_value_offs, entry->e_value_size, uio);
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (0);
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
int
|
||||
@ -271,8 +294,6 @@ ext2_extattr_block_get(struct inode *ip, int attrnamespace,
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
char *end;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
@ -291,17 +312,10 @@ ext2_extattr_block_get(struct inode *ip, int attrnamespace,
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Check attributes integrity */
|
||||
end = bp->b_data + bp->b_bufsize;
|
||||
entry = EXT2_FIRST_ENTRY(bp);
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
next = EXT2_EXTATTR_NEXT(entry);
|
||||
if ((char *)next >= end) {
|
||||
brelse(bp);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
entry = next;
|
||||
error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
|
||||
@ -316,15 +330,725 @@ ext2_extattr_block_get(struct inode *ip, int attrnamespace,
|
||||
else {
|
||||
error = uiomove(bp->b_data + entry->e_value_offs,
|
||||
entry->e_value_size, uio);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
ext2_extattr_delete_value(char *off,
|
||||
struct ext2fs_extattr_entry *first_entry,
|
||||
struct ext2fs_extattr_entry *entry, char *end)
|
||||
{
|
||||
uint16_t min_offs;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
|
||||
min_offs = end - off;
|
||||
next = first_entry;
|
||||
while (!EXT2_IS_LAST_ENTRY(next)) {
|
||||
if (min_offs > next->e_value_offs && next->e_value_offs > 0)
|
||||
min_offs = next->e_value_offs;
|
||||
|
||||
next = EXT2_EXTATTR_NEXT(next);
|
||||
}
|
||||
|
||||
if (entry->e_value_size == 0)
|
||||
return (min_offs);
|
||||
|
||||
memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
|
||||
off + min_offs, entry->e_value_offs - min_offs);
|
||||
|
||||
/* Adjust all value offsets */
|
||||
next = first_entry;
|
||||
while (!EXT2_IS_LAST_ENTRY(next))
|
||||
{
|
||||
if (next->e_value_offs > 0 &&
|
||||
next->e_value_offs < entry->e_value_offs)
|
||||
next->e_value_offs +=
|
||||
EXT2_EXTATTR_SIZE(entry->e_value_size);
|
||||
|
||||
next = EXT2_EXTATTR_NEXT(next);
|
||||
}
|
||||
|
||||
min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
|
||||
|
||||
return (min_offs);
|
||||
}
|
||||
|
||||
static void
|
||||
ext2_extattr_delete_entry(char *off,
|
||||
struct ext2fs_extattr_entry *first_entry,
|
||||
struct ext2fs_extattr_entry *entry, char *end)
|
||||
{
|
||||
char *pad;
|
||||
struct ext2fs_extattr_entry *next;
|
||||
|
||||
/* Clean entry value */
|
||||
ext2_extattr_delete_value(off, first_entry, entry, end);
|
||||
|
||||
/* Clean the entry */
|
||||
next = first_entry;
|
||||
while (!EXT2_IS_LAST_ENTRY(next))
|
||||
next = EXT2_EXTATTR_NEXT(next);
|
||||
|
||||
pad = (char*)next + sizeof(uint32_t);
|
||||
|
||||
memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
|
||||
pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
|
||||
}
|
||||
|
||||
int
|
||||
ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_dinode_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if ((error = bread(ip->i_devvp,
|
||||
fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
|
||||
(int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
|
||||
((char *)bp->b_data +
|
||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
|
||||
|
||||
/* Check attributes magic value */
|
||||
header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
|
||||
E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
|
||||
|
||||
if (header->h_magic != EXTATTR_MAGIC) {
|
||||
brelse(bp);
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
error = ext2_extattr_check(EXT2_IFIRST(header),
|
||||
(char *)dinode + EXT2_INODE_SIZE(fs));
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* If I am last entry, just make magic zero */
|
||||
entry = EXT2_IFIRST(header);
|
||||
if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) {
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) {
|
||||
memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
|
||||
continue;
|
||||
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) {
|
||||
ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
|
||||
EXT2_IFIRST(header), entry,
|
||||
(char *)dinode + EXT2_INODE_SIZE(fs));
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *sbp;
|
||||
struct buf *cbp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
uint64_t facl;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
sbp = *bpp;
|
||||
|
||||
header = EXT2_HDR(sbp);
|
||||
if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
|
||||
return (EINVAL);
|
||||
|
||||
facl = ext2_allocfacl(ip);
|
||||
if (!facl)
|
||||
return (ENOSPC);
|
||||
|
||||
cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
|
||||
if (!cbp) {
|
||||
ext2_blkfree(ip, facl, fs->e2fs_bsize);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
|
||||
header->h_refcount--;
|
||||
bwrite(sbp);
|
||||
|
||||
ip->i_facl = facl;
|
||||
ext2_update(ip->i_vnode, 1);
|
||||
|
||||
header = EXT2_HDR(cbp);
|
||||
header->h_refcount = 1;
|
||||
|
||||
*bpp = cbp;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
|
||||
fs->e2fs_bsize, NOCRED, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Check attributes magic value */
|
||||
header = EXT2_HDR(bp);
|
||||
if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
|
||||
brelse(bp);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (header->h_refcount > 1) {
|
||||
error = ext2_extattr_block_clone(ip, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/* If I am last entry, clean me and free the block */
|
||||
entry = EXT2_FIRST_ENTRY(bp);
|
||||
if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) {
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) {
|
||||
ip->i_blocks -= btodb(fs->e2fs_bsize);
|
||||
ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
|
||||
ip->i_facl = 0;
|
||||
error = ext2_update(ip->i_vnode, 1);
|
||||
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
|
||||
continue;
|
||||
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) {
|
||||
ext2_extattr_delete_entry(bp->b_data,
|
||||
EXT2_FIRST_ENTRY(bp), entry,
|
||||
bp->b_data + bp->b_bufsize);
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
}
|
||||
|
||||
brelse(bp);
|
||||
|
||||
return (ENOATTR);
|
||||
}
|
||||
|
||||
static struct ext2fs_extattr_entry *
|
||||
allocate_entry(const char *name, int attrnamespace, uint16_t offs,
|
||||
uint32_t size, uint32_t hash)
|
||||
{
|
||||
size_t name_len;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
|
||||
name_len = strlen(name);
|
||||
entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
|
||||
M_TEMP, M_WAITOK);
|
||||
|
||||
entry->e_name_len = name_len;
|
||||
entry->e_name_index = ext2_extattr_index_to_linux(attrnamespace);
|
||||
entry->e_value_offs = offs;
|
||||
entry->e_value_block = 0;
|
||||
entry->e_value_size = size;
|
||||
entry->e_hash = hash;
|
||||
memcpy(entry->e_name, name, name_len);
|
||||
|
||||
return (entry);
|
||||
}
|
||||
|
||||
static void
|
||||
free_entry(struct ext2fs_extattr_entry *entry)
|
||||
{
|
||||
|
||||
free(entry, M_TEMP);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
|
||||
struct ext2fs_extattr_entry *exist_entry, int header_size,
|
||||
int name_len, int new_size)
|
||||
{
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
int size;
|
||||
|
||||
size = header_size;
|
||||
size += sizeof(uint32_t);
|
||||
|
||||
if (NULL == exist_entry) {
|
||||
size += EXT2_EXTATTR_LEN(name_len);
|
||||
size += EXT2_EXTATTR_SIZE(new_size);
|
||||
}
|
||||
|
||||
if (first_entry)
|
||||
for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (entry != exist_entry)
|
||||
size += EXT2_EXTATTR_LEN(entry->e_name_len) +
|
||||
EXT2_EXTATTR_SIZE(entry->e_value_size);
|
||||
else
|
||||
size += EXT2_EXTATTR_LEN(entry->e_name_len) +
|
||||
EXT2_EXTATTR_SIZE(new_size);
|
||||
}
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
static void
|
||||
ext2_extattr_set_exist_entry(char *off,
|
||||
struct ext2fs_extattr_entry *first_entry,
|
||||
struct ext2fs_extattr_entry *entry,
|
||||
char *end, struct uio *uio)
|
||||
{
|
||||
uint16_t min_offs;
|
||||
|
||||
min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
|
||||
|
||||
entry->e_value_size = uio->uio_resid;
|
||||
if (entry->e_value_size)
|
||||
entry->e_value_offs = min_offs -
|
||||
EXT2_EXTATTR_SIZE(uio->uio_resid);
|
||||
else
|
||||
entry->e_value_offs = 0;
|
||||
|
||||
uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
|
||||
}
|
||||
|
||||
static struct ext2fs_extattr_entry *
|
||||
ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
|
||||
const char *name, int attrnamespace, char *end, struct uio *uio)
|
||||
{
|
||||
int name_len;
|
||||
char *pad;
|
||||
uint16_t min_offs;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
struct ext2fs_extattr_entry *new_entry;
|
||||
|
||||
/* Find pad's */
|
||||
min_offs = end - off;
|
||||
entry = first_entry;
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
|
||||
min_offs = entry->e_value_offs;
|
||||
|
||||
entry = EXT2_EXTATTR_NEXT(entry);
|
||||
}
|
||||
|
||||
pad = (char*)entry + sizeof(uint32_t);
|
||||
|
||||
/* Find entry insert position */
|
||||
name_len = strlen(name);
|
||||
entry = first_entry;
|
||||
while (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
if (!(attrnamespace - entry->e_name_index) &&
|
||||
!(name_len - entry->e_name_len))
|
||||
if (memcmp(name, entry->e_name, name_len) <= 0)
|
||||
break;
|
||||
|
||||
entry = EXT2_EXTATTR_NEXT(entry);
|
||||
}
|
||||
|
||||
/* Create new entry and insert it */
|
||||
new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
|
||||
memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
|
||||
pad - (char*)entry);
|
||||
|
||||
memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
|
||||
free_entry(new_entry);
|
||||
|
||||
new_entry = entry;
|
||||
if (new_entry->e_value_size > 0)
|
||||
new_entry->e_value_offs = min_offs -
|
||||
EXT2_EXTATTR_SIZE(new_entry->e_value_size);
|
||||
|
||||
uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
|
||||
|
||||
return (new_entry);
|
||||
}
|
||||
|
||||
int
|
||||
ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
|
||||
const char *name, struct uio *uio)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_dinode_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
size_t size = 0, max_size;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if ((error = bread(ip->i_devvp,
|
||||
fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
|
||||
(int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
|
||||
((char *)bp->b_data +
|
||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
|
||||
|
||||
/* Check attributes magic value */
|
||||
header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
|
||||
E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
|
||||
|
||||
if (header->h_magic != EXTATTR_MAGIC) {
|
||||
brelse(bp);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
|
||||
EXT2_INODE_SIZE(fs));
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Find if entry exist */
|
||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
|
||||
continue;
|
||||
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len))
|
||||
break;
|
||||
}
|
||||
|
||||
max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
|
||||
dinode->e2di_extra_isize;
|
||||
|
||||
if (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
|
||||
sizeof(struct ext2fs_extattr_dinode_header),
|
||||
entry->e_name_len, uio->uio_resid);
|
||||
if (size > max_size) {
|
||||
brelse(bp);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
|
||||
EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
|
||||
} else {
|
||||
/* Ensure that the same entry does not exist in the block */
|
||||
if (ip->i_facl) {
|
||||
error = ext2_extattr_block_get(ip, attrnamespace, name,
|
||||
NULL, &size);
|
||||
if (error != ENOATTR || size > 0) {
|
||||
brelse(bp);
|
||||
if (size > 0)
|
||||
error = ENOSPC;
|
||||
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
|
||||
sizeof(struct ext2fs_extattr_dinode_header),
|
||||
entry->e_name_len, uio->uio_resid);
|
||||
if (size > max_size) {
|
||||
brelse(bp);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
|
||||
EXT2_IFIRST(header), name, attrnamespace,
|
||||
(char *)header + max_size, uio);
|
||||
}
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
|
||||
static void
|
||||
ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
|
||||
struct ext2fs_extattr_entry *entry)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
char *name = entry->e_name;
|
||||
int n;
|
||||
|
||||
for (n=0; n < entry->e_name_len; n++) {
|
||||
hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
|
||||
(hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
|
||||
(*name++);
|
||||
}
|
||||
|
||||
if (entry->e_value_block == 0 && entry->e_value_size != 0) {
|
||||
uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
|
||||
for (n = (entry->e_value_size +
|
||||
EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
|
||||
hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
|
||||
(hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
|
||||
(*value++);
|
||||
}
|
||||
}
|
||||
|
||||
entry->e_hash = hash;
|
||||
}
|
||||
|
||||
static void
|
||||
ext2_extattr_rehash(struct ext2fs_extattr_header *header,
|
||||
struct ext2fs_extattr_entry *entry)
|
||||
{
|
||||
struct ext2fs_extattr_entry *here;
|
||||
uint32_t hash = 0;
|
||||
|
||||
ext2_extattr_hash_entry(header, entry);
|
||||
|
||||
here = EXT2_ENTRY(header+1);
|
||||
while (!EXT2_IS_LAST_ENTRY(here)) {
|
||||
if (!here->e_hash) {
|
||||
/* Block is not shared if an entry's hash value == 0 */
|
||||
hash = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
|
||||
(hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
|
||||
here->e_hash;
|
||||
|
||||
here = EXT2_EXTATTR_NEXT(here);
|
||||
}
|
||||
|
||||
header->h_hash = hash;
|
||||
}
|
||||
|
||||
int
|
||||
ext2_extattr_block_set(struct inode *ip, int attrnamespace,
|
||||
const char *name, struct uio *uio)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
struct ext2fs_extattr_entry *entry;
|
||||
size_t size;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if (ip->i_facl) {
|
||||
error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
|
||||
fs->e2fs_bsize, NOCRED, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Check attributes magic value */
|
||||
header = EXT2_HDR(bp);
|
||||
if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
|
||||
brelse(bp);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
|
||||
bp->b_data + bp->b_bufsize);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (header->h_refcount > 1) {
|
||||
error = ext2_extattr_block_clone(ip, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
header = EXT2_HDR(bp);
|
||||
}
|
||||
|
||||
/* Find if entry exist */
|
||||
for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
|
||||
entry = EXT2_EXTATTR_NEXT(entry)) {
|
||||
if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
|
||||
continue;
|
||||
|
||||
if (strlen(name) == entry->e_name_len &&
|
||||
0 == strncmp(entry->e_name, name, entry->e_name_len))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!EXT2_IS_LAST_ENTRY(entry)) {
|
||||
size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
|
||||
sizeof(struct ext2fs_extattr_header),
|
||||
entry->e_name_len, uio->uio_resid);
|
||||
if (size > bp->b_bufsize) {
|
||||
brelse(bp);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
|
||||
entry, bp->b_data + bp->b_bufsize, uio);
|
||||
} else {
|
||||
size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
|
||||
sizeof(struct ext2fs_extattr_header),
|
||||
strlen(name), uio->uio_resid);
|
||||
if (size > bp->b_bufsize) {
|
||||
brelse(bp);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
|
||||
name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
|
||||
|
||||
/* Clean the same entry in the inode */
|
||||
error = ext2_extattr_inode_delete(ip, attrnamespace, name);
|
||||
if (error && error != ENOATTR) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
ext2_extattr_rehash(header, entry);
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
|
||||
size = ext2_extattr_get_size(NULL, NULL,
|
||||
sizeof(struct ext2fs_extattr_header), strlen(name), uio->uio_resid);
|
||||
if (size > fs->e2fs_bsize)
|
||||
return (ENOSPC);
|
||||
|
||||
/* Allocate block, fill EA header and insert entry */
|
||||
ip->i_facl = ext2_allocfacl(ip);
|
||||
if (0 == ip->i_facl)
|
||||
return (ENOSPC);
|
||||
|
||||
ip->i_blocks += btodb(fs->e2fs_bsize);
|
||||
ext2_update(ip->i_vnode, 1);
|
||||
|
||||
bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
|
||||
if (!bp) {
|
||||
ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
|
||||
ip->i_blocks -= btodb(fs->e2fs_bsize);
|
||||
ip->i_facl = 0;
|
||||
ext2_update(ip->i_vnode, 1);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
header = EXT2_HDR(bp);
|
||||
header->h_magic = EXTATTR_MAGIC;
|
||||
header->h_refcount = 1;
|
||||
header->h_blocks = 1;
|
||||
header->h_hash = 0;
|
||||
memset(header->h_reserved, 0, sizeof(header->h_reserved));
|
||||
memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
|
||||
memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
|
||||
|
||||
entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
|
||||
name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
|
||||
|
||||
/* Clean the same entry in the inode */
|
||||
error = ext2_extattr_inode_delete(ip, attrnamespace, name);
|
||||
if (error && error != ENOATTR) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
ext2_extattr_rehash(header, entry);
|
||||
|
||||
return (bwrite(bp));
|
||||
}
|
||||
|
||||
int ext2_extattr_free(struct inode *ip)
|
||||
{
|
||||
struct m_ext2fs *fs;
|
||||
struct buf *bp;
|
||||
struct ext2fs_extattr_header *header;
|
||||
int error;
|
||||
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if (!ip->i_facl)
|
||||
return (0);
|
||||
|
||||
error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
|
||||
fs->e2fs_bsize, NOCRED, &bp);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Check attributes magic value */
|
||||
header = EXT2_HDR(bp);
|
||||
if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
|
||||
brelse(bp);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
|
||||
if (error) {
|
||||
brelse(bp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (header->h_refcount > 1) {
|
||||
header->h_refcount--;
|
||||
bwrite(bp);
|
||||
} else {
|
||||
ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
|
||||
brelse(bp);
|
||||
}
|
||||
|
||||
ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
|
||||
ip->i_facl = 0;
|
||||
ext2_update(ip->i_vnode, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -30,25 +30,34 @@
|
||||
#define _FS_EXT2FS_EXT2_EXTARTTR_H_
|
||||
|
||||
/* Linux xattr name indexes */
|
||||
#define EXT4_XATTR_INDEX_USER 1
|
||||
#define EXT4_XATTR_INDEX_USER 1
|
||||
#define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS 2
|
||||
#define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT 3
|
||||
#define EXT4_XATTR_INDEX_TRUSTED 4
|
||||
#define EXT4_XATTR_INDEX_LUSTRE 5
|
||||
#define EXT4_XATTR_INDEX_SECURITY 6
|
||||
#define EXT4_XATTR_INDEX_SYSTEM 7
|
||||
#define EXT4_XATTR_INDEX_RICHACL 8
|
||||
#define EXT4_XATTR_INDEX_ENCRYPTION 9
|
||||
#define EXT4_XATTR_INDEX_TRUSTED 4
|
||||
#define EXT4_XATTR_INDEX_LUSTRE 5
|
||||
#define EXT4_XATTR_INDEX_SECURITY 6
|
||||
#define EXT4_XATTR_INDEX_SYSTEM 7
|
||||
#define EXT4_XATTR_INDEX_RICHACL 8
|
||||
#define EXT4_XATTR_INDEX_ENCRYPTION 9
|
||||
|
||||
/* Magic value in attribute blocks */
|
||||
#define EXTATTR_MAGIC 0xEA020000
|
||||
|
||||
/* Max EA name length */
|
||||
#define EXT2_EXTATTR_NAMELEN_MAX 255
|
||||
|
||||
/* EA hash constants */
|
||||
#define EXT2_EXTATTR_NAME_HASH_SHIFT 5
|
||||
#define EXT2_EXTATTR_VALUE_HASH_SHIFT 16
|
||||
#define EXT2_EXTATTR_BLOCK_HASH_SHIFT 16
|
||||
|
||||
|
||||
struct ext2fs_extattr_header {
|
||||
int32_t h_magic; /* magic number for identification */
|
||||
int32_t h_refcount; /* reference count */
|
||||
int32_t h_blocks; /* number of disk blocks used */
|
||||
int32_t h_hash; /* hash value of all attributes */
|
||||
uint32_t h_reserved[4]; /* zero right now */
|
||||
uint32_t h_reserved[4]; /* zero right now */
|
||||
};
|
||||
|
||||
struct ext2fs_extattr_dinode_header {
|
||||
@ -56,21 +65,15 @@ struct ext2fs_extattr_dinode_header {
|
||||
};
|
||||
|
||||
struct ext2fs_extattr_entry {
|
||||
uint8_t e_name_len; /* length of name */
|
||||
uint8_t e_name_len; /* length of name */
|
||||
uint8_t e_name_index; /* attribute name index */
|
||||
uint16_t e_value_offs; /* offset in disk block of value */
|
||||
uint32_t e_value_block; /* disk block attribute is stored on (n/i) */
|
||||
uint32_t e_value_size; /* size of attribute value */
|
||||
uint32_t e_hash; /* hash value of name and value */
|
||||
char e_name[0]; /* attribute name */
|
||||
uint32_t e_hash; /* hash value of name and value */
|
||||
char e_name[0]; /* attribute name */
|
||||
};
|
||||
|
||||
#define EXT2_IHDR(inode, raw_inode) \
|
||||
((struct ext4_xattr_ibody_header *) \
|
||||
((void *)raw_inode + \
|
||||
EXT4_GOOD_OLD_INODE_SIZE + \
|
||||
EXT4_I(inode)->i_extra_isize))
|
||||
|
||||
#define EXT2_IFIRST(hdr) ((struct ext2fs_extattr_entry *)((hdr)+1))
|
||||
|
||||
#define EXT2_HDR(bh) ((struct ext2fs_extattr_header *)((bh)->b_data))
|
||||
@ -85,10 +88,20 @@ struct ext2fs_extattr_entry {
|
||||
(((name_len) + EXT2_EXTATTR_ROUND + \
|
||||
sizeof(struct ext2fs_extattr_entry)) & ~EXT2_EXTATTR_ROUND)
|
||||
|
||||
#define EXT2_EXTATTR_SIZE(size) \
|
||||
(((size) + EXT2_EXTATTR_ROUND) & ~EXT2_EXTATTR_ROUND)
|
||||
|
||||
#define EXT2_EXTATTR_NEXT(entry) \
|
||||
( (struct ext2fs_extattr_entry *)( \
|
||||
(char *)(entry) + EXT2_EXTATTR_LEN((entry)->e_name_len)) )
|
||||
|
||||
int ext2_extattr_inode_delete(struct inode *ip, int attrnamespace,
|
||||
const char *name);
|
||||
|
||||
int ext2_extattr_block_delete(struct inode *ip, int attrnamespace,
|
||||
const char *name);
|
||||
|
||||
int ext2_extattr_free(struct inode *ip);
|
||||
int ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
|
||||
struct uio *uio, size_t *size);
|
||||
|
||||
@ -101,4 +114,12 @@ int ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
|
||||
int ext2_extattr_block_get(struct inode *ip, int attrnamespace,
|
||||
const char *name, struct uio *uio, size_t *size);
|
||||
|
||||
int ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
|
||||
const char *name, struct uio *uio);
|
||||
|
||||
int ext2_extattr_block_set(struct inode *ip, int attrnamespace,
|
||||
const char *name, struct uio *uio);
|
||||
|
||||
int ext2_extattr_valid_attrname(int attrnamespace, const char *attrname);
|
||||
|
||||
#endif /* !_FS_EXT2FS_EXT2_EXTARTTR_H_ */
|
||||
|
@ -51,6 +51,7 @@ struct vnode;
|
||||
int ext2_add_entry(struct vnode *, struct ext2fs_direct_2 *);
|
||||
int ext2_alloc(struct inode *, daddr_t, e4fs_daddr_t, int,
|
||||
struct ucred *, e4fs_daddr_t *);
|
||||
daddr_t ext2_allocfacl(struct inode *ip);
|
||||
int ext2_balloc(struct inode *,
|
||||
e2fs_lbn_t, int, struct ucred *, struct buf **, int);
|
||||
int ext2_blkatoff(struct vnode *, off_t, char **, struct buf **);
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include <fs/ext2fs/ext2fs.h>
|
||||
#include <fs/ext2fs/fs.h>
|
||||
#include <fs/ext2fs/ext2_extern.h>
|
||||
#include <fs/ext2fs/ext2_extattr.h>
|
||||
|
||||
static int ext2_indirtrunc(struct inode *, daddr_t, daddr_t,
|
||||
daddr_t, int, e4fs_daddr_t *);
|
||||
@ -488,6 +489,7 @@ ext2_inactive(struct vop_inactive_args *ap)
|
||||
if (ip->i_mode == 0)
|
||||
goto out;
|
||||
if (ip->i_nlink <= 0) {
|
||||
ext2_extattr_free(ip);
|
||||
error = ext2_truncate(vp, (off_t)0, 0, NOCRED, td);
|
||||
ip->i_rdev = 0;
|
||||
mode = ip->i_mode;
|
||||
|
@ -51,8 +51,8 @@ ext2_print_inode(struct inode *in)
|
||||
|
||||
printf("Inode: %5ju", (uintmax_t)in->i_number);
|
||||
printf( /* "Inode: %5d" */
|
||||
" Type: %10s Mode: 0x%o Flags: 0x%x Version: %d\n",
|
||||
"n/a", in->i_mode, in->i_flags, in->i_gen);
|
||||
" Type: %10s Mode: 0x%o Flags: 0x%x Version: %d acl: 0x%llx\n",
|
||||
"n/a", in->i_mode, in->i_flags, in->i_gen, in->i_facl);
|
||||
printf("User: %5u Group: %5u Size: %ju\n",
|
||||
in->i_uid, in->i_gid, (uintmax_t)in->i_size);
|
||||
printf("Links: %3d Blockcount: %ju\n",
|
||||
@ -167,6 +167,8 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
|
||||
ei->e2di_flags |= (ip->i_flag & IN_E4EXTENTS) ? EXT4_EXTENTS : 0;
|
||||
ei->e2di_nblock = ip->i_blocks & 0xffffffff;
|
||||
ei->e2di_nblock_high = ip->i_blocks >> 32 & 0xffff;
|
||||
ei->e2di_facl = ip->i_facl & 0xffffffff;
|
||||
ei->e2di_facl_high = ip->i_facl >> 32 & 0xffff;
|
||||
ei->e2di_gen = ip->i_gen;
|
||||
ei->e2di_uid = ip->i_uid;
|
||||
ei->e2di_gid = ip->i_gid;
|
||||
|
@ -117,8 +117,10 @@ static vop_setattr_t ext2_setattr;
|
||||
static vop_strategy_t ext2_strategy;
|
||||
static vop_symlink_t ext2_symlink;
|
||||
static vop_write_t ext2_write;
|
||||
static vop_deleteextattr_t ext2_deleteextattr;
|
||||
static vop_getextattr_t ext2_getextattr;
|
||||
static vop_listextattr_t ext2_listextattr;
|
||||
static vop_setextattr_t ext2_setextattr;
|
||||
static vop_vptofh_t ext2_vptofh;
|
||||
static vop_close_t ext2fifo_close;
|
||||
static vop_kqfilter_t ext2fifo_kqfilter;
|
||||
@ -157,8 +159,10 @@ struct vop_vector ext2_vnodeops = {
|
||||
.vop_strategy = ext2_strategy,
|
||||
.vop_symlink = ext2_symlink,
|
||||
.vop_write = ext2_write,
|
||||
.vop_deleteextattr = ext2_deleteextattr,
|
||||
.vop_getextattr = ext2_getextattr,
|
||||
.vop_listextattr = ext2_listextattr,
|
||||
.vop_setextattr = ext2_setextattr,
|
||||
.vop_vptofh = ext2_vptofh,
|
||||
};
|
||||
|
||||
@ -1485,6 +1489,42 @@ ext2_pathconf(struct vop_pathconf_args *ap)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnode operation to remove a named attribute.
|
||||
*/
|
||||
static int
|
||||
ext2_deleteextattr(struct vop_deleteextattr_args *ap)
|
||||
{
|
||||
struct inode *ip;
|
||||
struct m_ext2fs *fs;
|
||||
int error;
|
||||
|
||||
ip = VTOI(ap->a_vp);
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if (!EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_EXT_ATTR))
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
|
||||
ap->a_cred, ap->a_td, VWRITE);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) {
|
||||
error = ext2_extattr_inode_delete(ip, ap->a_attrnamespace, ap->a_name);
|
||||
if (error != ENOATTR)
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (ip->i_facl)
|
||||
error = ext2_extattr_block_delete(ip, ap->a_attrnamespace, ap->a_name);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnode operation to retrieve a named extended attribute.
|
||||
*/
|
||||
@ -1515,7 +1555,7 @@ ext2_getextattr(struct vop_getextattr_args *ap)
|
||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) {
|
||||
error = ext2_extattr_inode_get(ip, ap->a_attrnamespace,
|
||||
ap->a_name, ap->a_uio, ap->a_size);
|
||||
if (error)
|
||||
if (error != ENOATTR)
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -1556,17 +1596,58 @@ ext2_listextattr(struct vop_listextattr_args *ap)
|
||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) {
|
||||
error = ext2_extattr_inode_list(ip, ap->a_attrnamespace,
|
||||
ap->a_uio, ap->a_size);
|
||||
if(error)
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
if(ip->i_facl)
|
||||
if (ip->i_facl)
|
||||
error = ext2_extattr_block_list(ip, ap->a_attrnamespace,
|
||||
ap->a_uio, ap->a_size);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnode operation to set a named attribute.
|
||||
*/
|
||||
static int
|
||||
ext2_setextattr(struct vop_setextattr_args *ap)
|
||||
{
|
||||
struct inode *ip;
|
||||
struct m_ext2fs *fs;
|
||||
int error;
|
||||
|
||||
ip = VTOI(ap->a_vp);
|
||||
fs = ip->i_e2fs;
|
||||
|
||||
if (!EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_EXT_ATTR))
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
|
||||
ap->a_cred, ap->a_td, VWRITE);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = ext2_extattr_valid_attrname(ap->a_attrnamespace, ap->a_name);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) {
|
||||
error = ext2_extattr_inode_set(ip, ap->a_attrnamespace,
|
||||
ap->a_name, ap->a_uio);
|
||||
if (error != ENOSPC)
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = ext2_extattr_block_set(ip, ap->a_attrnamespace,
|
||||
ap->a_name, ap->a_uio);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vnode pointer to File handle
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user