blob: 2419414d0dc1ea2d9883d1271964bb19800e9508 [file] [log] [blame]
/*
* Inode attributes
*
* Original copyright (c) 2008 Daniel Phillips <phillips@phunq.net>
* Licensed under the GPL version 2
*
* By contributing changes to this file you grant the original copyright holder
* the right to distribute those changes under any license.
*/
#include "tux3.h"
#include "ileaf.h"
#include "iattr.h"
/*
* Variable size attribute format:
*
* immediate data: kind+version:16, bytes:16, data[bytes]
* immediate xattr: kind+version:16, bytes:16, atom:16, data[bytes - 2]
*/
unsigned atsize[MAX_ATTRS] = {
/* Fixed size attrs */
[RDEV_ATTR] = 8,
[MODE_OWNER_ATTR] = 12,
[CTIME_SIZE_ATTR] = 16,
[DATA_BTREE_ATTR] = 8,
[LINK_COUNT_ATTR] = 4,
[MTIME_ATTR] = 8,
/* Variable size (extended) attrs */
[IDATA_ATTR] = 2,
[XATTR_ATTR] = 4,
};
/*
* Tux3 times are 32.32 fixed point while time attributes are stored in 32.16
* format, trading away some precision to compress time fields by two bytes
* each. It is not clear whether the saved space is worth the lower precision.
*
* On-disk format is changed to use 32.32.
*/
#define TIME_ATTR_SHIFT 0
typedef u64 fixed32; /* Tux3 time values */
static inline u32 high32(fixed32 val)
{
return val >> 32;
}
static inline unsigned billionths(fixed32 val)
{
return (((val & 0xffffffff) * 1000000000) + 0x80000000) >> 32;
}
static inline struct timespec spectime(const fixed32 time)
{
struct timespec ts = {
.tv_sec = high32(time),
.tv_nsec = billionths(time),
};
return ts;
}
static inline fixed32 tuxtime(const struct timespec ts)
{
const u64 mult = ((1ULL << 63) / 1000000000ULL);
return ((u64)ts.tv_sec << 32) + ((ts.tv_nsec * mult + (3 << 29)) >> 31);
}
static unsigned encode_asize(unsigned bits)
{
unsigned need = 0;
for (int kind = 0; kind < VAR_ATTRS; kind++)
if ((bits & (1 << kind)))
need += atsize[kind] + 2;
return need;
}
/* unused */
int attr_check(void *attrs, unsigned size)
{
void *limit = attrs + size;
unsigned head;
while (attrs < limit - 1)
{
attrs = decode16(attrs, &head);
unsigned kind = head >> 12;
if (kind >= MAX_ATTRS)
return 0;
if (attrs + atsize[kind] > limit)
return 0;
attrs += atsize[kind];
}
return 1;
}
void dump_attrs(struct inode *inode)
{
//tux3_dbg("present = %x", inode->present);
struct tux3_inode *tuxnode = tux_inode(inode);
for (int kind = 0; kind < MAX_ATTRS; kind++) {
if (!(tux_inode(inode)->present & (1 << kind)))
continue;
switch (kind) {
case RDEV_ATTR:
__tux3_dbg("rdev %x:%x ", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
break;
case MODE_OWNER_ATTR:
__tux3_dbg("mode %07ho uid %x gid %x ", inode->i_mode, i_uid_read(inode), i_gid_read(inode));
break;
case CTIME_SIZE_ATTR:
__tux3_dbg("ctime %Lx size %Lx ", tuxtime(inode->i_ctime), (s64)inode->i_size);
break;
case LINK_COUNT_ATTR:
__tux3_dbg("links %u ", inode->i_nlink);
break;
case MTIME_ATTR:
__tux3_dbg("mtime %Lx ", tuxtime(inode->i_mtime));
break;
case XATTR_ATTR:
__tux3_dbg("xattr(s) ");
break;
default:
__tux3_dbg("<%i>? ", kind);
break;
}
}
if (has_root(&tuxnode->btree))
__tux3_dbg("root %Lx:%u ", tuxnode->btree.root.block, tuxnode->btree.root.depth);
__tux3_dbg("\n");
}
void *encode_kind(void *attrs, unsigned kind, unsigned version)
{
return encode16(attrs, (kind << 12) | version);
}
static void *encode_attrs(struct btree *btree, void *data, void *attrs,
unsigned size)
{
struct sb *sb = btree->sb;
struct iattr_req_data *iattr_data = data;
struct tux3_iattr_data *idata = iattr_data->idata;
struct btree *attr_btree = iattr_data->btree;
void *limit = attrs + size - 3;
for (int kind = 0; kind < VAR_ATTRS; kind++) {
if (!(idata->present & (1 << kind)))
continue;
if (attrs >= limit)
break;
attrs = encode_kind(attrs, kind, sb->version);
switch (kind) {
case RDEV_ATTR:
attrs = encode64(attrs, huge_encode_dev(idata->i_rdev));
break;
case MODE_OWNER_ATTR:
/* FIXME: i_mode is enough with 16bits */
attrs = encode32(attrs, idata->i_mode);
attrs = encode32(attrs, idata->i_uid);
attrs = encode32(attrs, idata->i_gid);
break;
case CTIME_SIZE_ATTR:
attrs = encode64(attrs, tuxtime(idata->i_ctime) >> TIME_ATTR_SHIFT);
attrs = encode64(attrs, idata->i_size);
break;
case DATA_BTREE_ATTR:
attrs = encode64(attrs, pack_root(&attr_btree->root));
break;
case LINK_COUNT_ATTR:
attrs = encode32(attrs, idata->i_nlink);
break;
case MTIME_ATTR:
attrs = encode64(attrs, tuxtime(idata->i_mtime) >> TIME_ATTR_SHIFT);
break;
}
}
return attrs;
}
void *decode_kind(void *attrs, unsigned *kind, unsigned *version)
{
unsigned head;
attrs = decode16(attrs, &head);
*version = head & 0xfff;
*kind = head >> 12;
return attrs;
}
static void *decode_attrs(struct inode *inode, void *attrs, unsigned size)
{
trace_off("decode %u attr bytes", size);
struct sb *sb = tux_sb(inode->i_sb);
struct tux3_inode *tuxnode = tux_inode(inode);
struct root btree_root = no_root;
void *limit = attrs + size;
u64 v64;
u32 v32;
while (attrs < limit - 1) {
unsigned version, kind;
attrs = decode_kind(attrs, &kind, &version);
if (version != sb->version) {
attrs += atsize[kind];
continue;
}
switch (kind) {
case RDEV_ATTR:
attrs = decode64(attrs, &v64);
/* vfs, trying to be helpful, will rewrite the field */
inode->i_rdev = huge_decode_dev(v64);
break;
case MODE_OWNER_ATTR:
attrs = decode32(attrs, &v32);
inode->i_mode = v32;
attrs = decode32(attrs, &v32);
i_uid_write(inode, v32);
attrs = decode32(attrs, &v32);
i_gid_write(inode, v32);
break;
case CTIME_SIZE_ATTR:
attrs = decode64(attrs, &v64);
inode->i_ctime = spectime(v64 << TIME_ATTR_SHIFT);
attrs = decode64(attrs, &v64);
inode->i_size = v64;
break;
case DATA_BTREE_ATTR:
attrs = decode64(attrs, &v64);
btree_root = unpack_root(v64);
goto skip_present;
break;
case LINK_COUNT_ATTR: {
unsigned nlink;
attrs = decode32(attrs, &nlink);
set_nlink(inode, nlink);
break;
}
case MTIME_ATTR:
attrs = decode64(attrs, &v64);
inode->i_mtime = spectime(v64 << TIME_ATTR_SHIFT);
break;
case XATTR_ATTR:
attrs = decode_xattr(inode, attrs);
break;
default:
return NULL;
}
tuxnode->present |= 1 << kind;
skip_present:
;
}
/* We don't use ->present for btree root */
init_btree(&tuxnode->btree, sb, btree_root, &dtree_ops);
return attrs;
}
static int iattr_encoded_size(struct btree *btree, void *data)
{
struct iattr_req_data *iattr_data = data;
struct inode *inode = iattr_data->inode;
return encode_asize(iattr_data->idata->present) + encode_xsize(inode);
}
static void iattr_encode(struct btree *btree, void *data, void *attrs, int size)
{
struct iattr_req_data *iattr_data = data;
struct inode *inode = iattr_data->inode;
void *attr;
attr = encode_attrs(btree, data, attrs, size);
attr = encode_xattrs(inode, attr, attrs + size - attr);
assert(attr == attrs + size);
}
static int iattr_decode(struct btree *btree, void *data, void *attrs, int size)
{
struct inode *inode = data;
unsigned xsize;
xsize = decode_xsize(inode, attrs, size);
if (xsize) {
int err = new_xcache(inode, xsize);
if (err)
return err;
}
decode_attrs(inode, attrs, size); // error???
if (tux3_trace)
dump_attrs(inode);
if (tux_inode(inode)->xcache)
xcache_dump(inode);
return 0;
}
struct ileaf_attr_ops iattr_ops = {
.magic = cpu_to_be16(TUX3_MAGIC_ILEAF),
.encoded_size = iattr_encoded_size,
.encode = iattr_encode,
.decode = iattr_decode,
};