Changeset View
Changeset View
Standalone View
Standalone View
head/sys/fs/ext2fs/ext2_extattr.c
Show All 38 Lines | |||||
#include <sys/extattr.h> | #include <sys/extattr.h> | ||||
#include <fs/ext2fs/fs.h> | #include <fs/ext2fs/fs.h> | ||||
#include <fs/ext2fs/ext2fs.h> | #include <fs/ext2fs/ext2fs.h> | ||||
#include <fs/ext2fs/inode.h> | #include <fs/ext2fs/inode.h> | ||||
#include <fs/ext2fs/ext2_dinode.h> | #include <fs/ext2fs/ext2_dinode.h> | ||||
#include <fs/ext2fs/ext2_mount.h> | #include <fs/ext2fs/ext2_mount.h> | ||||
#include <fs/ext2fs/ext2_extattr.h> | #include <fs/ext2fs/ext2_extattr.h> | ||||
#include <fs/ext2fs/ext2_extern.h> | |||||
static int | static int | ||||
ext2_extattr_index_to_bsd(int index) | ext2_extattr_index_to_bsd(int index) | ||||
{ | { | ||||
switch (index) { | switch (index) { | ||||
case EXT4_XATTR_INDEX_SYSTEM: | |||||
return (EXTATTR_NAMESPACE_SYSTEM); | |||||
case EXT4_XATTR_INDEX_USER: | case EXT4_XATTR_INDEX_USER: | ||||
return EXTATTR_NAMESPACE_USER; | return (EXTATTR_NAMESPACE_USER); | ||||
} | |||||
case EXT4_XATTR_INDEX_SYSTEM: | return (EXTATTR_NAMESPACE_EMPTY); | ||||
return EXTATTR_NAMESPACE_SYSTEM; | } | ||||
default: | static int | ||||
return EXTATTR_NAMESPACE_EMPTY; | 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 | 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 | |||||
ext2_extattr_inode_list(struct inode *ip, int attrnamespace, | ext2_extattr_inode_list(struct inode *ip, int attrnamespace, | ||||
struct uio *uio, size_t *size) | struct uio *uio, size_t *size) | ||||
{ | { | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct buf *bp; | struct buf *bp; | ||||
struct ext2fs_extattr_dinode_header *header; | struct ext2fs_extattr_dinode_header *header; | ||||
struct ext2fs_extattr_entry *entry; | struct ext2fs_extattr_entry *entry; | ||||
struct ext2fs_extattr_entry *next; | |||||
char *end; | |||||
int error; | int error; | ||||
fs = ip->i_e2fs; | fs = ip->i_e2fs; | ||||
if ((error = bread(ip->i_devvp, | if ((error = bread(ip->i_devvp, | ||||
fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), | fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), | ||||
(int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { | (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | return (error); | ||||
} | } | ||||
struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) | struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) | ||||
((char *)bp->b_data + | ((char *)bp->b_data + | ||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | ||||
/* Check attributes magic value */ | /* Check attributes magic value */ | ||||
header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + | header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + | ||||
E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); | E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); | ||||
if (header->h_magic != EXTATTR_MAGIC) { | if (header->h_magic != EXTATTR_MAGIC) { | ||||
brelse(bp); | brelse(bp); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Check attributes integrity */ | error = ext2_extattr_check(EXT2_IFIRST(header), | ||||
entry = EXT2_IFIRST(header); | (char *)dinode + EXT2_INODE_SIZE(fs)); | ||||
end = (char *)dinode + EXT2_INODE_SIZE(fs); | if (error) { | ||||
while (!EXT2_IS_LAST_ENTRY(entry)) { | |||||
next = EXT2_EXTATTR_NEXT(entry); | |||||
if ((char *)next >= end) { | |||||
brelse(bp); | brelse(bp); | ||||
return (EIO); | return (error); | ||||
} | } | ||||
entry = next; | |||||
} | |||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); | 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) | if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) | ||||
continue; | continue; | ||||
if (uio == NULL) | if (uio == NULL) | ||||
*size += entry->e_name_len + 1; | *size += entry->e_name_len + 1; | ||||
else { | else { | ||||
char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); | char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); | ||||
attr_name[0] = entry->e_name_len; | attr_name[0] = entry->e_name_len; | ||||
memcpy(&attr_name[1], entry->e_name, entry->e_name_len); | memcpy(&attr_name[1], entry->e_name, entry->e_name_len); | ||||
error = uiomove(attr_name, entry->e_name_len + 1, uio); | error = uiomove(attr_name, entry->e_name_len + 1, uio); | ||||
free(attr_name, M_TEMP); | free(attr_name, M_TEMP); | ||||
if (error) | |||||
break; | |||||
} | } | ||||
} | } | ||||
brelse(bp); | brelse(bp); | ||||
return (0); | return (error); | ||||
} | } | ||||
int | int | ||||
ext2_extattr_block_list(struct inode *ip, int attrnamespace, | ext2_extattr_block_list(struct inode *ip, int attrnamespace, | ||||
struct uio *uio, size_t *size) | struct uio *uio, size_t *size) | ||||
{ | { | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct buf *bp; | struct buf *bp; | ||||
struct ext2fs_extattr_header *header; | struct ext2fs_extattr_header *header; | ||||
struct ext2fs_extattr_entry *entry; | struct ext2fs_extattr_entry *entry; | ||||
struct ext2fs_extattr_entry *next; | |||||
char *end; | |||||
int error; | int error; | ||||
fs = ip->i_e2fs; | fs = ip->i_e2fs; | ||||
error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), | error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), | ||||
fs->e2fs_bsize, NOCRED, &bp); | fs->e2fs_bsize, NOCRED, &bp); | ||||
if (error) { | if (error) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* Check attributes magic value */ | /* Check attributes magic value */ | ||||
header = EXT2_HDR(bp); | header = EXT2_HDR(bp); | ||||
if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { | if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { | ||||
brelse(bp); | brelse(bp); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* Check attributes integrity */ | error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); | ||||
end = bp->b_data + bp->b_bufsize; | if (error) { | ||||
entry = EXT2_FIRST_ENTRY(bp); | |||||
while (!EXT2_IS_LAST_ENTRY(entry)) { | |||||
next = EXT2_EXTATTR_NEXT(entry); | |||||
if ((char *)next >= end) { | |||||
brelse(bp); | brelse(bp); | ||||
return (EIO); | return (error); | ||||
} | } | ||||
entry = next; | |||||
} | |||||
for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); | for (entry = EXT2_FIRST_ENTRY(bp); !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) | if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) | ||||
continue; | continue; | ||||
if (uio == NULL) | if (uio == NULL) | ||||
*size += entry->e_name_len + 1; | *size += entry->e_name_len + 1; | ||||
else { | else { | ||||
char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); | char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); | ||||
attr_name[0] = entry->e_name_len; | attr_name[0] = entry->e_name_len; | ||||
memcpy(&attr_name[1], entry->e_name, entry->e_name_len); | memcpy(&attr_name[1], entry->e_name, entry->e_name_len); | ||||
error = uiomove(attr_name, entry->e_name_len + 1, uio); | error = uiomove(attr_name, entry->e_name_len + 1, uio); | ||||
free(attr_name, M_TEMP); | free(attr_name, M_TEMP); | ||||
if (error) | |||||
break; | |||||
} | } | ||||
} | } | ||||
brelse(bp); | brelse(bp); | ||||
return (0); | return (error); | ||||
} | } | ||||
int | int | ||||
ext2_extattr_inode_get(struct inode *ip, int attrnamespace, | ext2_extattr_inode_get(struct inode *ip, int attrnamespace, | ||||
const char *name, struct uio *uio, size_t *size) | const char *name, struct uio *uio, size_t *size) | ||||
{ | { | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct buf *bp; | struct buf *bp; | ||||
struct ext2fs_extattr_dinode_header *header; | struct ext2fs_extattr_dinode_header *header; | ||||
struct ext2fs_extattr_entry *entry; | struct ext2fs_extattr_entry *entry; | ||||
struct ext2fs_extattr_entry *next; | |||||
char *end; | |||||
int error; | int error; | ||||
fs = ip->i_e2fs; | fs = ip->i_e2fs; | ||||
if ((error = bread(ip->i_devvp, | if ((error = bread(ip->i_devvp, | ||||
fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), | fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), | ||||
(int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { | (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | return (error); | ||||
} | } | ||||
struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) | struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) | ||||
((char *)bp->b_data + | ((char *)bp->b_data + | ||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | ||||
/* Check attributes magic value */ | /* Check attributes magic value */ | ||||
header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + | header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + | ||||
E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); | E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); | ||||
if (header->h_magic != EXTATTR_MAGIC) { | if (header->h_magic != EXTATTR_MAGIC) { | ||||
brelse(bp); | brelse(bp); | ||||
return (0); | return (ENOATTR); | ||||
} | } | ||||
/* Check attributes integrity */ | error = ext2_extattr_check(EXT2_IFIRST(header), | ||||
entry = EXT2_IFIRST(header); | (char *)dinode + EXT2_INODE_SIZE(fs)); | ||||
end = (char *)dinode + EXT2_INODE_SIZE(fs); | if (error) { | ||||
while (!EXT2_IS_LAST_ENTRY(entry)) { | |||||
next = EXT2_EXTATTR_NEXT(entry); | |||||
if ((char *)next >= end) { | |||||
brelse(bp); | brelse(bp); | ||||
return (EIO); | return (error); | ||||
} | } | ||||
entry = next; | 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)) { | |||||
if (uio == NULL) | |||||
*size += entry->e_value_size; | |||||
else { | |||||
error = uiomove(((char *)EXT2_IFIRST(header)) + | |||||
entry->e_value_offs, entry->e_value_size, uio); | |||||
} | } | ||||
for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); | brelse(bp); | ||||
return (error); | |||||
} | |||||
} | |||||
brelse(bp); | |||||
return (ENOATTR); | |||||
} | |||||
int | |||||
ext2_extattr_block_get(struct inode *ip, int attrnamespace, | |||||
const char *name, struct uio *uio, size_t *size) | |||||
{ | |||||
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); | |||||
} | |||||
for (entry = EXT2_FIRST_ENTRY(bp); !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) | if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) | ||||
continue; | continue; | ||||
if (strlen(name) == entry->e_name_len && | if (strlen(name) == entry->e_name_len && | ||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) { | 0 == strncmp(entry->e_name, name, entry->e_name_len)) { | ||||
if (uio == NULL) | if (uio == NULL) | ||||
*size += entry->e_value_size; | *size += entry->e_value_size; | ||||
else { | else { | ||||
error = uiomove(((char *)EXT2_IFIRST(header)) + entry->e_value_offs, | error = uiomove(bp->b_data + entry->e_value_offs, | ||||
entry->e_value_size, uio); | entry->e_value_size, uio); | ||||
} | |||||
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) { | if (error) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | 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); | 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); | return (0); | ||||
} | } | ||||
int | int | ||||
ext2_extattr_block_get(struct inode *ip, int attrnamespace, | ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name) | ||||
const char *name, struct uio *uio, size_t *size) | |||||
{ | { | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct buf *bp; | struct buf *bp; | ||||
struct ext2fs_extattr_header *header; | struct ext2fs_extattr_header *header; | ||||
struct ext2fs_extattr_entry *entry; | struct ext2fs_extattr_entry *entry; | ||||
struct ext2fs_extattr_entry *next; | |||||
char *end; | |||||
int error; | int error; | ||||
fs = ip->i_e2fs; | fs = ip->i_e2fs; | ||||
error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), | error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), | ||||
fs->e2fs_bsize, NOCRED, &bp); | fs->e2fs_bsize, NOCRED, &bp); | ||||
if (error) { | if (error) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* Check attributes magic value */ | /* Check attributes magic value */ | ||||
header = EXT2_HDR(bp); | header = EXT2_HDR(bp); | ||||
if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { | if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { | ||||
brelse(bp); | brelse(bp); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* Check attributes integrity */ | error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); | ||||
end = bp->b_data + bp->b_bufsize; | if (error) { | ||||
entry = EXT2_FIRST_ENTRY(bp); | |||||
while (!EXT2_IS_LAST_ENTRY(entry)) { | |||||
next = EXT2_EXTATTR_NEXT(entry); | |||||
if ((char *)next >= end) { | |||||
brelse(bp); | brelse(bp); | ||||
return (EIO); | return (error); | ||||
} | } | ||||
entry = next; | 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); | for (entry = EXT2_FIRST_ENTRY(bp); !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) | if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) | ||||
continue; | continue; | ||||
if (strlen(name) == entry->e_name_len && | if (strlen(name) == entry->e_name_len && | ||||
0 == strncmp(entry->e_name, name, entry->e_name_len)) { | 0 == strncmp(entry->e_name, name, entry->e_name_len)) { | ||||
if (uio == NULL) | ext2_extattr_delete_entry(bp->b_data, | ||||
*size += entry->e_value_size; | EXT2_FIRST_ENTRY(bp), entry, | ||||
else { | bp->b_data + bp->b_bufsize); | ||||
error = uiomove(bp->b_data + entry->e_value_offs, | |||||
entry->e_value_size, uio); | 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) { | if (error) { | ||||
brelse(bp); | brelse(bp); | ||||
return (error); | 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); | 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); | return (0); | ||||
} | } |