Page MenuHomeFreeBSD

D10460.id54425.diff
No OneTemporary

D10460.id54425.diff

Index: head/sys/fs/ext2fs/ext2_alloc.c
===================================================================
--- head/sys/fs/ext2fs/ext2_alloc.c
+++ head/sys/fs/ext2fs/ext2_alloc.c
@@ -132,6 +132,25 @@
}
/*
+ * 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.
*
* The vnode and an array of buffer pointers for a range of sequential
Index: head/sys/fs/ext2fs/ext2_extattr.h
===================================================================
--- head/sys/fs/ext2fs/ext2_extattr.h
+++ head/sys/fs/ext2fs/ext2_extattr.h
@@ -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_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 @@
(((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_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_ */
Index: head/sys/fs/ext2fs/ext2_extattr.c
===================================================================
--- head/sys/fs/ext2fs/ext2_extattr.c
+++ head/sys/fs/ext2fs/ext2_extattr.c
@@ -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_SYSTEM:
+ return (EXTATTR_NAMESPACE_SYSTEM);
+
case EXT4_XATTR_INDEX_USER:
- return EXTATTR_NAMESPACE_USER;
+ return (EXTATTR_NAMESPACE_USER);
+ }
- case EXT4_XATTR_INDEX_SYSTEM:
- return EXTATTR_NAMESPACE_SYSTEM;
+ 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);
- default:
- return EXTATTR_NAMESPACE_EMPTY;
+ entry = next;
}
+
+ return (0);
}
int
@@ -69,8 +114,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}
Index: head/sys/fs/ext2fs/ext2_extern.h
===================================================================
--- head/sys/fs/ext2fs/ext2_extern.h
+++ head/sys/fs/ext2fs/ext2_extern.h
@@ -51,6 +51,7 @@
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 **);
Index: head/sys/fs/ext2fs/ext2_inode.c
===================================================================
--- head/sys/fs/ext2fs/ext2_inode.c
+++ head/sys/fs/ext2fs/ext2_inode.c
@@ -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 @@
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;
Index: head/sys/fs/ext2fs/ext2_inode_cnv.c
===================================================================
--- head/sys/fs/ext2fs/ext2_inode_cnv.c
+++ head/sys/fs/ext2fs/ext2_inode_cnv.c
@@ -51,8 +51,8 @@
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 @@
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;
Index: head/sys/fs/ext2fs/ext2_vnops.c
===================================================================
--- head/sys/fs/ext2fs/ext2_vnops.c
+++ head/sys/fs/ext2fs/ext2_vnops.c
@@ -117,8 +117,10 @@
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 @@
.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,
};
@@ -1486,6 +1490,42 @@
}
/*
+ * 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.
*/
static int
@@ -1515,7 +1555,7 @@
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,11 +1596,11 @@
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);
@@ -1568,6 +1608,47 @@
}
/*
+ * 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
*/
/* ARGSUSED */

File Metadata

Mime Type
text/plain
Expires
Fri, Dec 26, 7:29 PM (4 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27288469
Default Alt Text
D10460.id54425.diff (33 KB)

Event Timeline