Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/ext2fs/ext2_vnops.c
Context not available. | |||||
static vop_strategy_t ext2_strategy; | static vop_strategy_t ext2_strategy; | ||||
static vop_symlink_t ext2_symlink; | static vop_symlink_t ext2_symlink; | ||||
static vop_write_t ext2_write; | static vop_write_t ext2_write; | ||||
static vop_getextattr_t ext2_getextattr; | static vop_closeextattr_t ext2_closeextattr; | ||||
static vop_deleteextattr_t ext2_deleteextattr; | |||||
static vop_getextattr_t ext2_getextattr; | |||||
static vop_listextattr_t ext2_listextattr; | static vop_listextattr_t ext2_listextattr; | ||||
static vop_openextattr_t ext2_openextattr; | |||||
static vop_setextattr_t ext2_setextattr; | |||||
static vop_vptofh_t ext2_vptofh; | static vop_vptofh_t ext2_vptofh; | ||||
static vop_close_t ext2fifo_close; | static vop_close_t ext2fifo_close; | ||||
static vop_kqfilter_t ext2fifo_kqfilter; | static vop_kqfilter_t ext2fifo_kqfilter; | ||||
Context not available. | |||||
.vop_strategy = ext2_strategy, | .vop_strategy = ext2_strategy, | ||||
.vop_symlink = ext2_symlink, | .vop_symlink = ext2_symlink, | ||||
.vop_write = ext2_write, | .vop_write = ext2_write, | ||||
.vop_closeextattr = ext2_closeextattr, | |||||
.vop_deleteextattr = ext2_deleteextattr, | |||||
.vop_getextattr = ext2_getextattr, | .vop_getextattr = ext2_getextattr, | ||||
.vop_listextattr = ext2_listextattr, | .vop_listextattr = ext2_listextattr, | ||||
.vop_openextattr = ext2_openextattr, | |||||
.vop_setextattr = ext2_setextattr, | |||||
.vop_vptofh = ext2_vptofh, | .vop_vptofh = ext2_vptofh, | ||||
}; | }; | ||||
Context not available. | |||||
} | } | ||||
/* | /* | ||||
* Vnode operation to retrieve a named extended attribute. | * Extended attribute area reading. | ||||
*/ | */ | ||||
static int | static int | ||||
ext2_getextattr(struct vop_getextattr_args *ap) | ext2_extread(struct vnode *vp, struct uio *uio, size_t* size, int ioflag) | ||||
{ | |||||
struct inode *ip; | |||||
struct m_ext2fs *fs; | |||||
struct buf *bp; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_dinode *ei; | |||||
int isize; | |||||
uint64_t facl; | |||||
int error; | |||||
ip = VTOI(vp); | |||||
fs = ip->i_e2fs; | |||||
if (size) | |||||
*size = 0; | |||||
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); | |||||
} | |||||
ei = (struct ext2fs_dinode *)((char *)bp->b_data + EXT2_INODE_SIZE(fs) * | |||||
ino_to_fsbo(fs, ip->i_number)); | |||||
facl = ei->e2di_facl; | |||||
if (E2DI_HAS_HUGE_FILE(ip)) | |||||
facl |= (uint64_t)ei->e2di_facl_high << 32; | |||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE && | |||||
ei->e2di_extra_isize) { | |||||
isize = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE - | |||||
ei->e2di_extra_isize; | |||||
if (size) | |||||
*size += isize; | |||||
if (uio) { | |||||
error = uiomove((char *)bp->b_data + | |||||
EXT2_INODE_SIZE(fs) * | |||||
ino_to_fsbo(fs, ip->i_number) + | |||||
EXT2_INODE_SIZE(fs) - isize, | |||||
isize, uio); | |||||
} | |||||
} | |||||
vfs_bio_brelse(bp, ioflag); | |||||
if (error) | |||||
return (error); | |||||
if (size) | |||||
*size += EXT2_EXTATTR_BLOCK_SIZE(ip); | |||||
if (uio) { | |||||
if (facl) { | |||||
error = bread(ip->i_devvp, fsbtodb(fs, facl), | |||||
fs->e2fs_bsize, NOCRED, &bp); | |||||
if (error) { | |||||
brelse(bp); | |||||
return (error); | |||||
} | |||||
error = uiomove(bp->b_data, fs->e2fs_bsize, uio); | |||||
vfs_bio_brelse(bp, ioflag); | |||||
} else { | |||||
hdr = (struct ext2fs_extattr_header *) | |||||
uio->uio_iov->iov_base; | |||||
hdr->h_magic = EXTATTR_MAGIC; | |||||
hdr->h_refcount = 1; | |||||
hdr->h_blocks = 1; | |||||
hdr->h_hash = 0; | |||||
memset(hdr->h_reserved, 0, sizeof(hdr->h_reserved)); | |||||
memset(EXT2_IFIRST(hdr), 0, sizeof(uint32_t)); | |||||
} | |||||
} | |||||
return (error); | |||||
} | |||||
static int | |||||
ext2_extattr_block_clone(struct inode *ip, uint64_t facl, uint64_t *cfacl) | |||||
{ | |||||
struct m_ext2fs *fs; | |||||
struct buf *sbp; | |||||
struct ext2fs_extattr_header *hdr; | |||||
int error; | |||||
fs = ip->i_e2fs; | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, ip->i_ea_area); | |||||
if (hdr->h_magic != EXTATTR_MAGIC || hdr->h_blocks != 1) | |||||
return (EINVAL); | |||||
if (hdr->h_refcount == 1) | |||||
return (0); | |||||
/* Increment refcount on original block and sync it */ | |||||
error = bread(ip->i_devvp, fsbtodb(fs, facl), | |||||
fs->e2fs_bsize, NOCRED, &sbp); | |||||
if (error) { | |||||
brelse(sbp); | |||||
return (error); | |||||
} | |||||
hdr = EXT2_HDR(sbp); | |||||
hdr->h_refcount--; | |||||
bwrite(sbp); | |||||
EXT2_LOCK(ip->i_ump); | |||||
*cfacl = ext2_alloccg(ip, ino_to_cg(fs, ip->i_number), 0, | |||||
fs->e2fs_bsize); | |||||
if (0 == *cfacl) { | |||||
EXT2_UNLOCK(ip->i_ump); | |||||
return (ENOSPC); | |||||
} | |||||
/* Restore cloned header */ | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, ip->i_ea_area); | |||||
hdr->h_refcount = 1; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Extended attribute area writing. | |||||
*/ | |||||
static int | |||||
ext2_extwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred) | |||||
{ | { | ||||
struct inode *ip; | struct inode *ip; | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct buf *bp; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_extattr_entry *eap; | |||||
struct ext2fs_dinode *ei; | |||||
uint64_t facl; | |||||
int update_facl; | |||||
int isize; | |||||
ssize_t resid; | |||||
int error; | int error; | ||||
ip = VTOI(vp); | |||||
fs = ip->i_e2fs; | |||||
resid = uio->uio_resid; | |||||
update_facl = 0; | |||||
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); | |||||
} | |||||
ei = (struct ext2fs_dinode *)((char *)bp->b_data + | |||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | |||||
isize = ei->e2di_extra_isize; | |||||
facl = ei->e2di_facl; | |||||
if (E2DI_HAS_HUGE_FILE(ip)) | |||||
facl |= (uint64_t)ei->e2di_facl_high << 32; | |||||
if (EXT2_EXTATTR_INODE(ip)) { | |||||
error = uiomove((char *)bp->b_data + EXT2_INODE_SIZE(fs) * | |||||
ino_to_fsbo(fs, ip->i_number) + | |||||
E2FS_REV0_INODE_SIZE + isize, | |||||
EXT2_EXTATTR_INODE_SIZE(ip), uio); | |||||
if (error) { | |||||
brelse(bp); | |||||
return (error); | |||||
} | |||||
vfs_bio_set_flags(bp, ioflag); | |||||
if (ioflag & IO_SYNC) { | |||||
(void)bwrite(bp); | |||||
} else if (vm_page_count_severe() || | |||||
buf_dirty_count_severe() || | |||||
(ioflag & (IO_ASYNC | IO_DIRECT))) | |||||
bawrite(bp); | |||||
else | |||||
bdwrite(bp); | |||||
} else { | |||||
vfs_bio_brelse(bp, ioflag); | |||||
if (error) | |||||
return (error); | |||||
} | |||||
if (EXT2_EXTATTR_BLOCK(ip)) { | |||||
/* Allocate block if was not allocated yet */ | |||||
if (!facl) { | |||||
EXT2_LOCK(ip->i_ump); | |||||
facl = ext2_alloccg(ip, ino_to_cg(fs, ip->i_number), | |||||
0, fs->e2fs_bsize); | |||||
if (!facl) { | |||||
EXT2_UNLOCK(ip->i_ump); | |||||
return (ENOSPC); | |||||
} | |||||
update_facl = 1; | |||||
ip->i_blocks += btodb(fs->e2fs_bsize); | |||||
if (ioflag & IO_SYNC) | |||||
error = ext2_update(ip->i_vnode, 1); | |||||
} | |||||
/* Check if block cloning required */ | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, ip->i_ea_area); | |||||
if (hdr->h_refcount != 1) { | |||||
error = ext2_extattr_block_clone(ip, facl, &facl); | |||||
if (error) | |||||
return (error); | |||||
update_facl = 1; | |||||
} | |||||
/* Free block if EA is empty */ | |||||
eap = EXT2_IFIRST(hdr); | |||||
if (EXT2_IS_LAST_ENTRY(eap)) { | |||||
ip->i_blocks -= btodb(fs->e2fs_bsize); | |||||
ext2_blkfree(ip, facl, fs->e2fs_bsize); | |||||
if (ioflag & IO_SYNC) | |||||
error = ext2_update(ip->i_vnode, 1); | |||||
facl = 0; | |||||
update_facl = 1; | |||||
goto out; | |||||
} | |||||
bp = getblk(ip->i_devvp, fsbtodb(fs, facl), | |||||
fs->e2fs_bsize, 0, 0, 0); | |||||
if (!bp) | |||||
return (EIO); | |||||
error = uiomove(bp->b_data, fs->e2fs_bsize, uio); | |||||
if (error) { | |||||
brelse(bp); | |||||
return (error); | |||||
} | |||||
vfs_bio_set_flags(bp, ioflag); | |||||
if (ioflag & IO_SYNC) { | |||||
(void)bwrite(bp); | |||||
} else if (vm_page_count_severe() || | |||||
buf_dirty_count_severe() || | |||||
(ioflag & (IO_ASYNC | IO_DIRECT))) | |||||
bawrite(bp); | |||||
else | |||||
bdwrite(bp); | |||||
} | |||||
out: | |||||
if (update_facl && !error) { | |||||
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); | |||||
} | |||||
ei = (struct ext2fs_dinode *)((char *)bp->b_data + | |||||
EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); | |||||
ei->e2di_facl = facl & 0xffffffff; | |||||
ei->e2di_facl_high = facl >> 32 & 0xffff; | |||||
if (ioflag & IO_SYNC) { | |||||
(void)bwrite(bp); | |||||
} else if (vm_page_count_severe() || | |||||
buf_dirty_count_severe() || | |||||
(ioflag & (IO_ASYNC | IO_DIRECT))) | |||||
bawrite(bp); | |||||
else | |||||
bdwrite(bp); | |||||
} | |||||
return (error); | |||||
} | |||||
/* | |||||
* Vnode operating to retrieve a named extended attribute. | |||||
* | |||||
* Locate a particular EA (nspace:name) using first entry pointer, and return | |||||
* the length of the EA, and possibly the pointer to the entry and to the data. | |||||
*/ | |||||
static int | |||||
ext2_findextattr(int nspace, const char *name, char *off, | |||||
struct ext2fs_extattr_entry *eae, struct ext2fs_extattr_entry **eaepp, | |||||
u_char **eac) | |||||
{ | |||||
while (!EXT2_IS_LAST_ENTRY(eae)) { | |||||
if (ext2_extattr_index_to_bsd(eae->e_name_index) != | |||||
nspace) | |||||
continue; | |||||
if (strlen(name) == eae->e_name_len && | |||||
0 == strncmp(eae->e_name, name, eae->e_name_len)) { | |||||
if (eac) | |||||
*eac = off + eae->e_value_offs; | |||||
if (eaepp) | |||||
*eaepp = eae; | |||||
return (eae->e_value_size); | |||||
} | |||||
eae = EXT2_EXTATTR_NEXT(eae); | |||||
} | |||||
return (-1); | |||||
} | |||||
static int | |||||
ext2_rdextattr(u_char **p, struct vnode *vp, struct thread *td, int extra) | |||||
{ | |||||
struct inode *ip; | |||||
struct m_ext2fs *fs; | |||||
struct uio luio; | |||||
struct iovec liovec; | |||||
size_t easize; | |||||
int error; | |||||
u_char *eap; | |||||
ip = VTOI(vp); | |||||
fs = ip->i_e2fs; | |||||
easize = 0; | |||||
/* Get EAs size */ | |||||
error = ext2_extread(vp, NULL, &easize, IO_EXT | IO_SYNC); | |||||
if (error || !easize) | |||||
return (error); | |||||
/* Read EAs */ | |||||
eap = malloc(easize, M_TEMP, M_WAITOK); | |||||
liovec.iov_base = eap; | |||||
liovec.iov_len = easize; | |||||
luio.uio_iov = &liovec; | |||||
luio.uio_iovcnt = 1; | |||||
luio.uio_offset = 0; | |||||
luio.uio_resid = easize; | |||||
luio.uio_segflg = UIO_SYSSPACE; | |||||
luio.uio_rw = UIO_READ; | |||||
luio.uio_td = td; | |||||
error = ext2_extread(vp, &luio, &easize, IO_EXT | IO_SYNC); | |||||
if (error) { | |||||
free(eap, M_TEMP); | |||||
return(error); | |||||
} | |||||
ip->i_ea_len = easize; | |||||
*p = eap; | |||||
return (0); | |||||
} | |||||
static void | |||||
ext2_lock_ea(struct vnode *vp) | |||||
{ | |||||
struct inode *ip; | |||||
ip = VTOI(vp); | |||||
VI_LOCK(vp); | |||||
while (ip->i_flag & IN_EA_LOCKED) { | |||||
ip->i_flag |= IN_EA_LOCKWAIT; | |||||
msleep(&ip->i_ea_refs, &vp->v_interlock, PINOD + 2, "ext2_ea", | |||||
0); | |||||
} | |||||
ip->i_flag |= IN_EA_LOCKED; | |||||
VI_UNLOCK(vp); | |||||
} | |||||
static void | |||||
ext2_unlock_ea(struct vnode *vp) | |||||
{ | |||||
struct inode *ip; | |||||
ip = VTOI(vp); | |||||
VI_LOCK(vp); | |||||
if (ip->i_flag & IN_EA_LOCKWAIT) | |||||
wakeup(&ip->i_ea_refs); | |||||
ip->i_flag &= ~(IN_EA_LOCKED | IN_EA_LOCKWAIT); | |||||
VI_UNLOCK(vp); | |||||
} | |||||
static int | |||||
ext2_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td) | |||||
{ | |||||
struct inode *ip; | |||||
int error; | |||||
ip = VTOI(vp); | |||||
ext2_lock_ea(vp); | |||||
if (ip->i_ea_area != NULL) { | |||||
ip->i_ea_refs++; | |||||
ext2_unlock_ea(vp); | |||||
return (0); | |||||
} | |||||
error = ext2_rdextattr(&ip->i_ea_area, vp, td, 0); | |||||
if (error) { | |||||
ext2_unlock_ea(vp); | |||||
return (error); | |||||
} | |||||
ip->i_ea_error = 0; | |||||
ip->i_ea_refs++; | |||||
ext2_unlock_ea(vp); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Vnode extattr transaction commit/abort | |||||
*/ | |||||
static int | |||||
ext2_close_ea(struct vnode *vp, int commit, struct ucred *cred, | |||||
struct thread *td) | |||||
{ | |||||
struct inode *ip; | |||||
struct uio luio; | |||||
struct iovec liovec; | |||||
int error; | |||||
ip = VTOI(vp); | |||||
ext2_lock_ea(vp); | |||||
if (ip->i_ea_area == NULL) { | |||||
ext2_unlock_ea(vp); | |||||
return (EINVAL); | |||||
} | |||||
error = ip->i_ea_error; | |||||
if (commit && error == 0) { | |||||
ASSERT_VOP_ELOCKED(vp, "ext2_close_ea commit"); | |||||
if (cred == NOCRED) | |||||
cred = vp->v_mount->mnt_cred; | |||||
liovec.iov_base = ip->i_ea_area; | |||||
liovec.iov_len = ip->i_ea_len; | |||||
luio.uio_iov = &liovec; | |||||
luio.uio_iovcnt = 1; | |||||
luio.uio_offset = 0; | |||||
luio.uio_resid = ip->i_ea_len; | |||||
luio.uio_segflg = UIO_SYSSPACE; | |||||
luio.uio_rw = UIO_WRITE; | |||||
luio.uio_td = td; | |||||
error = ext2_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred); | |||||
} | |||||
if (--ip->i_ea_refs == 0) { | |||||
free(ip->i_ea_area, M_TEMP); | |||||
ip->i_ea_area = NULL; | |||||
ip->i_ea_len = 0; | |||||
ip->i_ea_error = 0; | |||||
} | |||||
ext2_unlock_ea(vp); | |||||
return (error); | |||||
} | |||||
static int | |||||
ext2_openextattr(struct vop_openextattr_args *ap) | |||||
{ | |||||
struct inode *ip; | |||||
ip = VTOI(ap->a_vp); | |||||
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); | |||||
return (ext2_open_ea(ap->a_vp, ap->a_cred, ap->a_td)); | |||||
} | |||||
/* | |||||
* Vnode extattr transaction commit/abort | |||||
*/ | |||||
static int | |||||
ext2_closeextattr(struct vop_closeextattr_args *ap) | |||||
{ | |||||
struct inode *ip; | |||||
ip = VTOI(ap->a_vp); | |||||
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); | |||||
if (ap->a_commit && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) | |||||
return (EROFS); | |||||
return (ext2_close_ea(ap->a_vp, ap->a_commit, ap->a_cred, ap->a_td)); | |||||
} | |||||
/* | |||||
* Vnode operation to remove a named attribute. | |||||
*/ | |||||
static int | |||||
ext2_deleteextattr(struct vop_deleteextattr_args *ap) | |||||
{ | |||||
struct inode *ip; | |||||
struct m_ext2fs *fs; | |||||
struct ext2fs_extattr_dinode_header *ihdr; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_extattr_entry *eae; | |||||
int olen, error, easize; | |||||
u_char *eap; | |||||
char *eaend; | |||||
void *tmp; | |||||
ip = VTOI(ap->a_vp); | ip = VTOI(ap->a_vp); | ||||
fs = ip->i_e2fs; | fs = ip->i_e2fs; | ||||
Context not available. | |||||
if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) | if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
if (strlen(ap->a_name) == 0) | |||||
return (EINVAL); | |||||
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) | |||||
return (EROFS); | |||||
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, | |||||
ap->a_cred, ap->a_td, VWRITE); | |||||
if (error) { | |||||
if (ip->i_ea_area != NULL && ip->i_ea_error == 0) | |||||
ip->i_ea_error = error; | |||||
return (error); | |||||
} | |||||
error = ext2_open_ea(ap->a_vp, ap->a_cred, ap->a_td); | |||||
if (error) | |||||
return (error); | |||||
eap = malloc(ip->i_ea_len, M_TEMP, M_WAITOK); | |||||
bcopy(ip->i_ea_area, eap, ip->i_ea_len); | |||||
if (EXT2_EXTATTR_INODE(ip)) { | |||||
ihdr = EXT2_EXTATTR_INODE_HDR(ip, eap); | |||||
easize = EXT2_EXTATTR_INODE_SIZE(ip); | |||||
eae = EXT2_IFIRST(ihdr); | |||||
eaend = (char *)ihdr + easize; | |||||
if (ihdr->h_magic != EXTATTR_MAGIC) { | |||||
error = ENOATTR; | |||||
goto block; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(ihdr), eaend); | |||||
if (error) | |||||
goto out; | |||||
olen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)EXT2_IFIRST(ihdr), eae, &eae, NULL); | |||||
if (olen >= 0) { | |||||
ext2_extattr_delete_entry((char *)EXT2_IFIRST(ihdr), | |||||
EXT2_IFIRST(ihdr), eae, eaend); | |||||
goto out; | |||||
} | |||||
error = ENOATTR; | |||||
} | |||||
block: | |||||
if (EXT2_EXTATTR_BLOCK(ip)) { | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, eap); | |||||
easize = EXT2_EXTATTR_BLOCK_SIZE(ip); | |||||
eae = EXT2_IFIRST(hdr); | |||||
eaend = (char *)hdr + easize; | |||||
if (hdr->h_magic != EXTATTR_MAGIC) { | |||||
error = EIO; | |||||
goto out; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(hdr), eaend); | |||||
if (error) | |||||
goto out; | |||||
olen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)hdr, eae, &eae, NULL); | |||||
if (olen >= 0) { | |||||
ext2_extattr_delete_entry((char *)hdr, | |||||
EXT2_IFIRST(hdr), eae, eaend); | |||||
goto out; | |||||
} | |||||
error = ENOATTR; | |||||
} | |||||
out: | |||||
if (error) { | |||||
free(eap, M_TEMP); | |||||
ext2_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); | |||||
if (ip->i_ea_area != NULL && ip->i_ea_error == 0) | |||||
ip->i_ea_error = error; | |||||
return (error); | |||||
} | |||||
tmp = ip->i_ea_area; | |||||
ip->i_ea_area = eap; | |||||
free(tmp, M_TEMP); | |||||
error = ext2_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td); | |||||
return (error); | |||||
} | |||||
/* | |||||
* Vnode operation to retrieve a named extended attribute. | |||||
*/ | |||||
static int | |||||
ext2_getextattr(struct vop_getextattr_args *ap) | |||||
{ | |||||
struct inode *ip; | |||||
struct m_ext2fs *fs; | |||||
struct ext2fs_extattr_dinode_header *ihdr; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_extattr_entry *eae; | |||||
u_char *eap, *p; | |||||
unsigned easize; | |||||
int error, ealen; | |||||
char *eaend; | |||||
ip = VTOI(ap->a_vp); | |||||
fs = ip->i_e2fs; | |||||
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, | error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, | ||||
ap->a_cred, ap->a_td, VREAD); | ap->a_cred, ap->a_td, VREAD); | ||||
if (error) | if (error) | ||||
Context not available. | |||||
if (ap->a_size != NULL) | if (ap->a_size != NULL) | ||||
*ap->a_size = 0; | *ap->a_size = 0; | ||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) { | error = ext2_open_ea(ap->a_vp, ap->a_cred, ap->a_td); | ||||
error = ext2_extattr_inode_get(ip, ap->a_attrnamespace, | if (error) | ||||
ap->a_name, ap->a_uio, ap->a_size); | return (error); | ||||
if (EXT2_EXTATTR_INODE(ip)) { | |||||
ihdr = EXT2_EXTATTR_INODE_HDR(ip, ip->i_ea_area); | |||||
eap = (char *)ihdr; | |||||
easize = EXT2_EXTATTR_INODE_SIZE(ip); | |||||
eae = EXT2_IFIRST(ihdr); | |||||
eaend = (char *)ihdr + easize; | |||||
if (ihdr->h_magic != EXTATTR_MAGIC) { | |||||
error = ENOATTR; | |||||
goto block; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(ihdr), eaend); | |||||
if (error) | if (error) | ||||
return (error); | goto out; | ||||
ealen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)eae, eae, NULL, &p); | |||||
if (ealen >= 0) { | |||||
if (ap->a_size != NULL) | |||||
*ap->a_size += ealen; | |||||
else if (ap->a_uio != NULL) | |||||
error = uiomove(p, ealen, ap->a_uio); | |||||
goto out; | |||||
} | |||||
error = ENOATTR; | |||||
} | } | ||||
if (ip->i_facl) | block: | ||||
error = ext2_extattr_block_get(ip, ap->a_attrnamespace, | if (EXT2_EXTATTR_BLOCK(ip)) { | ||||
ap->a_name, ap->a_uio, ap->a_size); | hdr = EXT2_EXTATTR_BLOCK_HDR(ip, ip->i_ea_area); | ||||
eap = (char *)hdr; | |||||
easize = EXT2_EXTATTR_BLOCK_SIZE(ip); | |||||
eae = EXT2_IFIRST(hdr); | |||||
eaend = (char *)hdr + easize; | |||||
if (hdr->h_magic != EXTATTR_MAGIC) { | |||||
error = EIO; | |||||
goto out; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(hdr), eaend); | |||||
if (error) | |||||
goto out; | |||||
ealen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)hdr, eae, NULL, &p); | |||||
if (ealen >= 0) { | |||||
if (ap->a_size != NULL) | |||||
*ap->a_size += ealen; | |||||
else if (ap->a_uio != NULL) { | |||||
error = uiomove(p, ealen, ap->a_uio); | |||||
if (error) | |||||
goto out; | |||||
} | |||||
} else | |||||
error = ENOATTR; | |||||
} | |||||
out: | |||||
ext2_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); | |||||
return (error); | return (error); | ||||
} | } | ||||
Context not available. | |||||
{ | { | ||||
struct inode *ip; | struct inode *ip; | ||||
struct m_ext2fs *fs; | struct m_ext2fs *fs; | ||||
struct ext2fs_extattr_dinode_header *ihdr; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_extattr_entry *eae; | |||||
int error; | int error; | ||||
ip = VTOI(ap->a_vp); | ip = VTOI(ap->a_vp); | ||||
Context not available. | |||||
if (ap->a_size != NULL) | if (ap->a_size != NULL) | ||||
*ap->a_size = 0; | *ap->a_size = 0; | ||||
if (EXT2_INODE_SIZE(fs) != E2FS_REV0_INODE_SIZE) { | error = ext2_open_ea(ap->a_vp, ap->a_cred, ap->a_td); | ||||
error = ext2_extattr_inode_list(ip, ap->a_attrnamespace, | if (error) | ||||
ap->a_uio, ap->a_size); | return (error); | ||||
if(error) | |||||
return (error); | if (EXT2_EXTATTR_INODE(ip)) { | ||||
ihdr = EXT2_EXTATTR_INODE_HDR(ip, ip->i_ea_area); | |||||
if (ihdr->h_magic != EXTATTR_MAGIC) { | |||||
error = ENOATTR; | |||||
goto block; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(ihdr), | |||||
(char *)ihdr + EXT2_EXTATTR_INODE_SIZE(ip)); | |||||
if (error) | |||||
goto out; | |||||
eae = EXT2_IFIRST(ihdr); | |||||
while (!EXT2_IS_LAST_ENTRY(eae)) { | |||||
if (ext2_extattr_index_to_bsd(eae->e_name_index) != | |||||
ap->a_attrnamespace) | |||||
continue; | |||||
if (ap->a_uio == NULL) | |||||
*ap->a_size += eae->e_name_len + 1; | |||||
else { | |||||
char *ea_name = malloc(eae->e_name_len + | |||||
1, M_TEMP, M_WAITOK); | |||||
ea_name[0] = eae->e_name_len; | |||||
memcpy(&ea_name[1], eae->e_name, | |||||
eae->e_name_len); | |||||
error = uiomove(ea_name, eae->e_name_len + 1, | |||||
ap->a_uio); | |||||
free(ea_name, M_TEMP); | |||||
if (error) | |||||
goto out; | |||||
} | |||||
eae = EXT2_EXTATTR_NEXT(eae); | |||||
} | |||||
} | |||||
block: | |||||
if (EXT2_EXTATTR_BLOCK(ip)) { | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, ip->i_ea_area); | |||||
if (hdr->h_magic != EXTATTR_MAGIC) { | |||||
error = EIO; | |||||
goto out; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(hdr), | |||||
(char *)hdr + EXT2_EXTATTR_BLOCK_SIZE(ip)); | |||||
if (error) | |||||
goto out; | |||||
eae = EXT2_IFIRST(hdr); | |||||
while (!EXT2_IS_LAST_ENTRY(eae)) { | |||||
if (ext2_extattr_index_to_bsd(eae->e_name_index) != | |||||
ap->a_attrnamespace) | |||||
continue; | |||||
if (ap->a_uio == NULL) | |||||
*ap->a_size += eae->e_name_len + 1; | |||||
else { | |||||
char *ea_name = malloc(eae->e_name_len + | |||||
1, M_TEMP, M_WAITOK); | |||||
ea_name[0] = eae->e_name_len; | |||||
memcpy(&ea_name[1], eae->e_name, | |||||
eae->e_name_len); | |||||
error = uiomove(ea_name, eae->e_name_len + 1, | |||||
ap->a_uio); | |||||
free(ea_name, M_TEMP); | |||||
if (error) | |||||
goto out; | |||||
} | |||||
eae = EXT2_EXTATTR_NEXT(eae); | |||||
} | |||||
} | |||||
out: | |||||
ext2_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); | |||||
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; | |||||
struct ext2fs_extattr_dinode_header *ihdr; | |||||
struct ext2fs_extattr_header *hdr; | |||||
struct ext2fs_extattr_entry *eae; | |||||
struct ext2fs_extattr_entry *ieae; | |||||
int olen, ealen, error, size, easize; | |||||
u_char *eap; | |||||
char *eaend; | |||||
void *tmp; | |||||
ip = VTOI(ap->a_vp); | |||||
fs = ip->i_e2fs; | |||||
ieae = NULL; | |||||
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); | |||||
if (strlen(ap->a_name) == 0) | |||||
return (EINVAL); | |||||
/* XXX Now unsupported API to delete EAs using NULL uio. */ | |||||
if (ap->a_uio == NULL) | |||||
return (EOPNOTSUPP); | |||||
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) | |||||
return (EROFS); | |||||
ealen = ap->a_uio->uio_resid; | |||||
if (ealen < 0 || ealen > fs->e2fs_bsize) | |||||
return (EINVAL); | |||||
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, | |||||
ap->a_cred, ap->a_td, VWRITE); | |||||
if (error) { | |||||
goto out; | |||||
} | |||||
error = ext2_extattr_valid_attrname(ap->a_attrnamespace, ap->a_name); | |||||
if (error) | |||||
return (error); | |||||
error = ext2_open_ea(ap->a_vp, ap->a_cred, ap->a_td); | |||||
if (error) | |||||
return (error); | |||||
eap = malloc(ip->i_ea_len, M_TEMP, M_WAITOK); | |||||
bcopy(ip->i_ea_area, eap, ip->i_ea_len); | |||||
if (EXT2_EXTATTR_INODE(ip)) { | |||||
ihdr = EXT2_EXTATTR_INODE_HDR(ip, eap); | |||||
easize = EXT2_EXTATTR_INODE_SIZE(ip); | |||||
eae = EXT2_IFIRST(ihdr); | |||||
eaend = (char *)ihdr + easize; | |||||
if (ihdr->h_magic != EXTATTR_MAGIC) { | |||||
error = ENOATTR; | |||||
goto block; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(ihdr), eaend); | |||||
if (error) | |||||
goto out; | |||||
olen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)EXT2_IFIRST(ihdr), eae, &eae, NULL); | |||||
if (olen == -1) { | |||||
size = ext2_extattr_get_size(EXT2_IFIRST(ihdr), | |||||
NULL, sizeof(struct ext2fs_extattr_dinode_header), | |||||
strlen(ap->a_name), ap->a_uio->uio_resid); | |||||
if (size > EXT2_EXTATTR_INODE_SIZE(ip)) { | |||||
error = ENOSPC; | |||||
goto block; | |||||
} | |||||
eae = ext2_extattr_set_new_entry((char *) | |||||
EXT2_IFIRST(ihdr), | |||||
EXT2_IFIRST(ihdr), ap->a_name, | |||||
ap->a_attrnamespace, eaend, ap->a_uio); | |||||
} else { | |||||
size = ext2_extattr_get_size(EXT2_IFIRST(ihdr), | |||||
eae, sizeof(struct ext2fs_extattr_dinode_header), | |||||
eae->e_name_len, ap->a_uio->uio_resid); | |||||
if (size > EXT2_EXTATTR_INODE_SIZE(ip)) { | |||||
ieae= eae; | |||||
error = ENOSPC; | |||||
goto block; | |||||
} | |||||
ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(ihdr), | |||||
EXT2_IFIRST(ihdr), eae, eaend, ap->a_uio); | |||||
} | |||||
error = uiomove((char *)EXT2_IFIRST(ihdr) + eae->e_value_offs, | |||||
eae->e_value_size, ap->a_uio); | |||||
goto out; | |||||
} | |||||
block: | |||||
if (EXT2_EXTATTR_BLOCK(ip)) { | |||||
hdr = EXT2_EXTATTR_BLOCK_HDR(ip, eap); | |||||
easize = EXT2_EXTATTR_BLOCK_SIZE(ip); | |||||
eae = EXT2_IFIRST(hdr); | |||||
eaend = (char *)hdr + easize; | |||||
if (hdr->h_magic != EXTATTR_MAGIC) { | |||||
error = EIO; | |||||
goto out; | |||||
} | |||||
error = ext2_extattr_check(EXT2_IFIRST(hdr), eaend); | |||||
if (error) | |||||
goto out; | |||||
olen = ext2_findextattr(ap->a_attrnamespace, ap->a_name, | |||||
(char *)hdr, eae, &eae, NULL); | |||||
if (olen == -1) { | |||||
size = ext2_extattr_get_size(EXT2_IFIRST(hdr), | |||||
NULL, sizeof(struct ext2fs_extattr_header), | |||||
strlen(ap->a_name), ap->a_uio->uio_resid); | |||||
if (size > EXT2_EXTATTR_BLOCK_SIZE(ip)) { | |||||
error = ENOSPC; | |||||
goto out; | |||||
} | |||||
if (ieae) { | |||||
ext2_extattr_delete_entry((char *) | |||||
EXT2_IFIRST(ihdr), | |||||
EXT2_IFIRST(ihdr), ieae, | |||||
(char *)EXT2_IFIRST(ihdr) + | |||||
EXT2_EXTATTR_INODE_SIZE(ip)); | |||||
} | |||||
eae = ext2_extattr_set_new_entry((char *)hdr, | |||||
EXT2_IFIRST(hdr), ap->a_name, | |||||
ap->a_attrnamespace, eaend, ap->a_uio); | |||||
} else { | |||||
size = ext2_extattr_get_size(EXT2_IFIRST(hdr), | |||||
eae, sizeof(struct ext2fs_extattr_header), | |||||
eae->e_name_len, ap->a_uio->uio_resid); | |||||
if (size > EXT2_EXTATTR_BLOCK_SIZE(ip)) { | |||||
error = ENOSPC; | |||||
goto out; | |||||
} | |||||
ext2_extattr_set_exist_entry((char *)hdr, | |||||
EXT2_IFIRST(hdr), eae, eaend, ap->a_uio); | |||||
} | |||||
error = uiomove((char *)hdr + eae->e_value_offs, | |||||
eae->e_value_size, ap->a_uio); | |||||
ext2_extattr_rehash(hdr, eae); | |||||
} | |||||
out: | |||||
if (error) { | |||||
free(eap, M_TEMP); | |||||
ext2_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); | |||||
if (ip->i_ea_area != NULL && ip->i_ea_error == 0) | |||||
ip->i_ea_error = error; | |||||
return (error); | |||||
} | } | ||||
if(ip->i_facl) | tmp = ip->i_ea_area; | ||||
error = ext2_extattr_block_list(ip, ap->a_attrnamespace, | ip->i_ea_area = eap; | ||||
ap->a_uio, ap->a_size); | free(tmp, M_TEMP); | ||||
error = ext2_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td); | |||||
return (error); | return (error); | ||||
} | } | ||||
Context not available. |