Compare commits

...

10 Commits

Author SHA1 Message Date
Zach Brown
a2a3042037 Add format-version back/forward compat test
Signed-off-by: Zach Brown <zab@versity.com>
2022-02-03 15:13:23 -08:00
Zach Brown
e740f93da7 t_quiet appends command output
The t_quiet test command execution helper was constantly truncating the
quiet.log with the output of each command.   It was meant to show each
command and its output as they're run.

Signed-off-by: Zach Brown <zab@versity.com>
2022-02-03 15:13:23 -08:00
Zach Brown
8e8817a049 Add fs test functions for mounted paths
We have some fs functions which return info based on the test mount nr
as the test has setup.   This refactofs those a bit to also provide
some of the info when the caller has a path in a given mount.  This will
let tests work with scratch mounts a little more easily.

Signed-off-by: Zach Brown <zab@versity.com>
2022-02-03 15:13:23 -08:00
Zach Brown
07c09272e8 Add T_MODULE for tests
Signed-off-by: Zach Brown <zab@versity.com>
2022-02-03 15:13:23 -08:00
Zach Brown
f3ff89f27d (fix loading v1 inode in v2) 2022-02-03 15:13:12 -08:00
Zach Brown
0bd5fc4942 (fix v1 bytes to fix mount) 2022-02-03 14:45:15 -08:00
Bryant G. Duffy-Ly
9291f1a231 Add unit tests for worm
Signed-off-by: Bryant G. Duffy-Ly <bduffyly@versity.com>
2022-02-03 09:26:21 -08:00
Bryant G. Duffy-Ly
1d55a4c099 Add worm support
We want to add the first version of worm support. The implementation
would prevent users from modifying, touching, or removing of files
with the worm attribute set.

Note in this version; we also prevent the root user from modifying
the expiration time once one is set. The user would need to wait
until expiration prior to being able to modify the attribute or file.
This was done by design/request.

Signed-off-by: Bryant G. Duffy-Ly <bduffyly@versity.com>
2022-02-03 09:26:21 -08:00
Bryant G. Duffy-Ly
f9fbef7aff Increment format version
Increment format version to 2 and also prevent downgrade

Signed-off-by: Bryant G. Duffy-Ly <bduffyly@versity.com>
2022-02-03 09:26:21 -08:00
Bryant G. Duffy-Ly
096bf83b43 Add support for different inode sizes
In order to support different format versions
we need to add the ability to support different
inode sizes.

Signed-off-by: Bryant G. Duffy-Ly <bduffyly@versity.com>
2022-02-03 09:26:21 -08:00
23 changed files with 723 additions and 49 deletions

View File

@@ -529,6 +529,11 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
goto out;
}
if (create && !si->staging && scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto out;
}
/* convert unwritten to written, could be staging */
if (create && ext.map && (ext.flags & SEF_UNWRITTEN)) {
un.start = iblock;
@@ -1189,6 +1194,10 @@ int scoutfs_data_move_blocks(struct inode *from, u64 from_off,
if (ret)
goto out;
if (scoutfs_inode_worm_denied(to)) {
return -EACCES;
}
if ((from_off & SCOUTFS_BLOCK_SM_MASK) ||
(to_off & SCOUTFS_BLOCK_SM_MASK) ||
((byte_len & SCOUTFS_BLOCK_SM_MASK) &&

View File

@@ -1029,6 +1029,11 @@ static int scoutfs_unlink(struct inode *dir, struct dentry *dentry)
goto unlock;
}
if (scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto unlock;
}
if (should_orphan(inode)) {
ret = scoutfs_lock_orphan(sb, SCOUTFS_LOCK_WRITE_ONLY, 0, scoutfs_ino(inode),
&orph_lock);
@@ -1694,6 +1699,12 @@ static int scoutfs_rename_common(struct inode *old_dir,
goto out_unlock;
}
if ((old_inode && scoutfs_inode_worm_denied(old_inode)) ||
(new_inode && scoutfs_inode_worm_denied(new_inode))) {
ret = -EACCES;
goto out_unlock;
}
if (should_orphan(new_inode)) {
ret = scoutfs_lock_orphan(sb, SCOUTFS_LOCK_WRITE_ONLY, 0, scoutfs_ino(new_inode),
&orph_lock);

View File

@@ -107,6 +107,11 @@ retry:
if (ret)
goto out;
if (scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto out;
}
ret = scoutfs_complete_truncate(inode, inode_lock);
if (ret)
goto out;

View File

@@ -8,7 +8,7 @@
*/
#define SCOUTFS_FORMAT_VERSION_MIN 1
#define SCOUTFS_FORMAT_VERSION_MIN_STR __stringify(SCOUTFS_FORMAT_VERSION_MIN)
#define SCOUTFS_FORMAT_VERSION_MAX 1
#define SCOUTFS_FORMAT_VERSION_MAX 2
#define SCOUTFS_FORMAT_VERSION_MAX_STR __stringify(SCOUTFS_FORMAT_VERSION_MAX)
/* statfs(2) f_type */
@@ -856,8 +856,14 @@ struct scoutfs_inode {
struct scoutfs_timespec ctime;
struct scoutfs_timespec mtime;
struct scoutfs_timespec crtime;
__le64 worm_bits;
struct scoutfs_timespec worm_expiration;
};
#define SCOUTFS_WORM_V1_BIT (1 << 0)
#define SCOUTFS_INODE_FMT_V2_BYTES sizeof(struct scoutfs_inode)
#define SCOUTFS_INODE_FMT_V1_BYTES offsetof(struct scoutfs_inode, worm_bits)
#define SCOUTFS_INO_FLAG_TRUNCATE 0x1
#define SCOUTFS_ROOT_INO 1

View File

@@ -88,6 +88,7 @@ static void scoutfs_inode_ctor(void *obj)
{
struct scoutfs_inode_info *si = obj;
seqlock_init(&si->seqlock);
init_rwsem(&si->extent_sem);
mutex_init(&si->item_mutex);
seqcount_init(&si->seqcount);
@@ -217,6 +218,40 @@ static u64 get_item_minor(struct scoutfs_inode_info *si, u8 type)
return si->item_minors[ind];
}
static u64 get_worm_inode_info(struct scoutfs_inode_info *si,
struct scoutfs_timespec **ts_ret)
{
u64 worm_bits = 0;
unsigned int seq;
do {
seq = read_seqbegin(&si->seqlock);
*ts_ret = &si->worm_expiration;
worm_bits = si->worm_bits;
} while (read_seqretry(&si->seqlock, seq));
return worm_bits;
}
void scoutfs_inode_set_worm(struct scoutfs_inode_info *si, __le64 worm_bits,
struct scoutfs_timespec *ts)
{
write_seqlock(&si->seqlock);
if (ts) {
si->worm_bits = le64_to_cpu(worm_bits);
si->worm_expiration.sec = ts->sec;
si->worm_expiration.nsec = ts->nsec;
} else {
si->worm_bits = 0;
si->worm_expiration.sec = 0;
si->worm_expiration.nsec = 0;
}
write_sequnlock(&si->seqlock);
}
/*
* The caller has ensured that the fields in the incoming scoutfs inode
* reflect both the inode item and the inode index items. This happens
@@ -237,7 +272,7 @@ static void set_item_info(struct scoutfs_inode_info *si,
set_item_major(si, SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE, sinode->data_seq);
}
static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
static void load_inode(struct inode *inode, struct scoutfs_inode *cinode, int inode_bytes)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
@@ -266,6 +301,11 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
si->crtime.tv_sec = le64_to_cpu(cinode->crtime.sec);
si->crtime.tv_nsec = le32_to_cpu(cinode->crtime.nsec);
if (inode_bytes == SCOUTFS_INODE_FMT_V2_BYTES)
scoutfs_inode_set_worm(si, cinode->worm_bits, &cinode->worm_expiration);
else
scoutfs_inode_set_worm(si, 0, NULL);
/*
* i_blocks is initialized from online and offline and is then
* maintained as blocks come and go.
@@ -276,6 +316,36 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
set_item_info(si, cinode);
}
/* Returns the max inode size given format version */
static int max_inode_fmt_ver_bytes(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
int ret = 0;
if (sbi->fmt_vers == 1)
ret = SCOUTFS_INODE_FMT_V1_BYTES;
else if (sbi->fmt_vers == 2)
ret = SCOUTFS_INODE_FMT_V2_BYTES;
return ret;
}
/* Returns if inode bytes is valid for our format version */
static bool valid_inode_fmt_ver_bytes(struct super_block *sb, int bytes)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
int ver;
if (bytes == SCOUTFS_INODE_FMT_V1_BYTES)
ver = 1;
else if (bytes == SCOUTFS_INODE_FMT_V2_BYTES)
ver = 2;
else
ver = 0;
return ver > 0 && ver <= sbi->fmt_vers;
}
void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
{
*key = (struct scoutfs_key) {
@@ -285,6 +355,21 @@ void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
};
}
static int lookup_inode_item(struct super_block *sb, struct scoutfs_key *key,
struct scoutfs_lock *lock,
struct scoutfs_inode_payload_wrapper *inode_payload)
{
int ret;
ret = scoutfs_item_lookup(sb, key, &inode_payload->sinode,
sizeof(struct scoutfs_inode_payload_wrapper), lock);
if (ret >= 0 && !valid_inode_fmt_ver_bytes(sb, ret))
return -EIO;
return ret;
}
/*
* Refresh the vfs inode fields if the lock indicates that the current
* contents could be stale.
@@ -299,9 +384,9 @@ void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct scoutfs_inode_payload_wrapper wrap;
struct super_block *sb = inode->i_sb;
struct scoutfs_key key;
struct scoutfs_inode sinode;
const u64 refresh_gen = lock->refresh_gen;
int ret;
@@ -320,13 +405,13 @@ int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock)
mutex_lock(&si->item_mutex);
if (atomic64_read(&si->last_refreshed) < refresh_gen) {
ret = scoutfs_item_lookup_exact(sb, &key, &sinode,
sizeof(sinode), lock);
if (ret == 0) {
load_inode(inode, &sinode);
ret = lookup_inode_item(sb, &key, lock, &wrap);
if (ret > 0) {
load_inode(inode, &wrap.sinode, ret);
atomic64_set(&si->last_refreshed, refresh_gen);
scoutfs_lock_add_coverage(sb, lock, &si->ino_lock_cov);
si->drop_invalidated = false;
ret = 0;
}
} else {
ret = 0;
@@ -459,6 +544,11 @@ retry:
if (ret)
goto out;
if (scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto out;
}
attr_size = (attr->ia_valid & ATTR_SIZE) ? attr->ia_size :
i_size_read(inode);
@@ -749,11 +839,13 @@ out:
return inode;
}
static void store_inode(struct scoutfs_inode *cinode, struct inode *inode)
static void store_inode(struct scoutfs_inode *cinode, struct inode *inode, int inode_bytes)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct scoutfs_timespec *ts = NULL;
u64 online_blocks;
u64 offline_blocks;
u64 worm_bits = 0;
scoutfs_inode_get_onoff(inode, &online_blocks, &offline_blocks);
@@ -785,6 +877,15 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode)
cinode->crtime.sec = cpu_to_le64(si->crtime.tv_sec);
cinode->crtime.nsec = cpu_to_le32(si->crtime.tv_nsec);
memset(cinode->crtime.__pad, 0, sizeof(cinode->crtime.__pad));
worm_bits = get_worm_inode_info(si, &ts);
if (inode_bytes == SCOUTFS_INODE_FMT_V2_BYTES && ts) {
cinode->worm_bits = cpu_to_le64(worm_bits);
cinode->worm_expiration.sec = ts->sec;
cinode->worm_expiration.nsec = ts->nsec;
memset(cinode->worm_expiration.__pad, 0, sizeof(cinode->worm_expiration.__pad));
}
}
/*
@@ -810,13 +911,15 @@ int scoutfs_dirty_inode_item(struct inode *inode, struct scoutfs_lock *lock)
struct super_block *sb = inode->i_sb;
struct scoutfs_inode sinode;
struct scoutfs_key key;
int inode_bytes;
int ret;
store_inode(&sinode, inode);
inode_bytes = max_inode_fmt_ver_bytes(sb);
store_inode(&sinode, inode, inode_bytes);
scoutfs_inode_init_key(&key, scoutfs_ino(inode));
ret = scoutfs_item_update(sb, &key, &sinode, sizeof(sinode), lock);
ret = scoutfs_item_update(sb, &key, &sinode, inode_bytes, lock);
if (!ret)
trace_scoutfs_dirty_inode(inode);
return ret;
@@ -1017,8 +1120,9 @@ void scoutfs_update_inode_item(struct inode *inode, struct scoutfs_lock *lock,
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_key key;
struct scoutfs_inode sinode;
struct scoutfs_key key;
int inode_bytes;
int ret;
int err;
@@ -1027,15 +1131,17 @@ void scoutfs_update_inode_item(struct inode *inode, struct scoutfs_lock *lock,
/* set the meta version once per trans for any inode updates */
scoutfs_inode_set_meta_seq(inode);
inode_bytes = max_inode_fmt_ver_bytes(sb);
/* only race with other inode field stores once */
store_inode(&sinode, inode);
store_inode(&sinode, inode, inode_bytes);
ret = update_indices(sb, si, ino, inode->i_mode, &sinode, lock_list);
BUG_ON(ret);
scoutfs_inode_init_key(&key, ino);
err = scoutfs_item_update(sb, &key, &sinode, sizeof(sinode), lock);
err = scoutfs_item_update(sb, &key, &sinode, inode_bytes, lock);
if (err) {
scoutfs_err(sb, "inode %llu update err %d", ino, err);
BUG_ON(err);
@@ -1399,9 +1505,10 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
struct scoutfs_lock *lock)
{
struct scoutfs_inode_info *si;
struct scoutfs_key key;
struct scoutfs_inode sinode;
struct scoutfs_key key;
struct inode *inode;
int inode_bytes;
int ret;
inode = new_inode(sb);
@@ -1421,6 +1528,8 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
si->drop_invalidated = false;
si->flags = 0;
scoutfs_inode_set_worm(si, 0, NULL);
scoutfs_inode_set_meta_seq(inode);
scoutfs_inode_set_data_seq(inode);
@@ -1431,14 +1540,16 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
inode->i_rdev = rdev;
set_inode_ops(inode);
store_inode(&sinode, inode);
inode_bytes = max_inode_fmt_ver_bytes(sb);
store_inode(&sinode, inode, inode_bytes);
scoutfs_inode_init_key(&key, scoutfs_ino(inode));
ret = scoutfs_omap_inc(sb, ino);
if (ret < 0)
goto out;
ret = scoutfs_item_create(sb, &key, &sinode, sizeof(sinode), lock);
ret = scoutfs_item_create(sb, &key, &sinode, inode_bytes, lock);
if (ret < 0)
scoutfs_omap_dec(sb, ino);
out:
@@ -1543,7 +1654,7 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_lo
{
DECLARE_INODE_SB_INFO(sb, inf);
struct deleting_ino_entry del = {{NULL, }};
struct scoutfs_inode sinode;
struct scoutfs_inode_payload_wrapper wrap;
struct scoutfs_key key;
LIST_HEAD(ind_locks);
bool release = false;
@@ -1559,8 +1670,7 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_lo
scoutfs_inode_init_key(&key, ino);
ret = scoutfs_item_lookup_exact(sb, &key, &sinode, sizeof(sinode),
lock);
ret = lookup_inode_item(sb, &key, lock, &wrap);
if (ret < 0) {
if (ret == -ENOENT)
ret = 0;
@@ -1568,14 +1678,14 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_lo
}
/* XXX corruption, inode probably won't be freed without repair */
if (le32_to_cpu(sinode.nlink)) {
if (le32_to_cpu(wrap.sinode.nlink)) {
scoutfs_warn(sb, "Dangling orphan item for inode %llu.", ino);
ret = -EIO;
goto out;
}
mode = le32_to_cpu(sinode.mode);
size = le64_to_cpu(sinode.size);
mode = le32_to_cpu(wrap.sinode.mode);
size = le64_to_cpu(wrap.sinode.size);
trace_scoutfs_delete_inode(sb, ino, mode, size);
/* remove data items in their own transactions */
@@ -1593,7 +1703,7 @@ static int delete_inode_items(struct super_block *sb, u64 ino, struct scoutfs_lo
/* then delete the small known number of remaining inode items */
retry:
ret = scoutfs_inode_index_start(sb, &ind_seq) ?:
prepare_index_deletion(sb, &ind_locks, ino, mode, &sinode) ?:
prepare_index_deletion(sb, &ind_locks, ino, mode, &wrap.sinode) ?:
scoutfs_inode_index_try_lock_hold(sb, &ind_locks, ind_seq, false);
if (ret > 0)
goto retry;
@@ -1602,7 +1712,7 @@ retry:
release = true;
ret = remove_index_items(sb, ino, &sinode, &ind_locks);
ret = remove_index_items(sb, ino, &wrap.sinode, &ind_locks);
if (ret)
goto out;
@@ -1979,6 +2089,28 @@ out:
return ret;
}
bool scoutfs_inode_worm_denied(struct inode *inode)
{
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct timespec cur_time = CURRENT_TIME;
struct scoutfs_timespec *worm = NULL;
struct timespec local_ts;
u64 worm_bits = 0;
worm_bits = get_worm_inode_info(si, &worm);
if (!worm)
return true;
local_ts.tv_sec = le64_to_cpu(worm->sec);
local_ts.tv_nsec = le32_to_cpu(worm->nsec);
if ((worm_bits & SCOUTFS_WORM_V1_BIT) &&
(timespec64_compare(&cur_time, &local_ts) < 0))
return true;
return false;
}
int scoutfs_inode_setup(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);

View File

@@ -23,6 +23,11 @@ struct scoutfs_inode_info {
u64 offline_blocks;
u32 flags;
struct timespec crtime;
u64 worm_bits;
struct scoutfs_timespec worm_expiration;
/* Prevent readers from racing with xattr_set */
seqlock_t seqlock;
/*
* Protects per-inode extent items, most particularly readers
@@ -64,6 +69,11 @@ struct scoutfs_inode_info {
struct inode inode;
};
struct scoutfs_inode_payload_wrapper {
struct scoutfs_inode sinode;
u64 overflow_detection;
};
static inline struct scoutfs_inode_info *SCOUTFS_I(struct inode *inode)
{
return container_of(inode, struct scoutfs_inode_info, inode);
@@ -139,4 +149,8 @@ void scoutfs_inode_orphan_stop(struct super_block *sb);
void scoutfs_inode_flush_iput(struct super_block *sb);
void scoutfs_inode_destroy(struct super_block *sb);
bool scoutfs_inode_worm_denied(struct inode *inode);
void scoutfs_inode_set_worm(struct scoutfs_inode_info *si, __le64 worm_bits,
struct scoutfs_timespec *ts);
#endif

View File

@@ -659,6 +659,11 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
if (ret)
goto unlock;
if (scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto unlock;
}
/* can only change size/dv on untouched regular files */
if ((sm.i_size != 0 || sm.data_version != 0) &&
((!S_ISREG(inode->i_mode) ||
@@ -823,7 +828,7 @@ static long scoutfs_ioc_search_xattrs(struct file *file, unsigned long arg)
goto out;
}
if (scoutfs_xattr_parse_tags(name, sx.name_bytes, &tgs) < 0 ||
if (scoutfs_xattr_parse_tags(sb, name, sx.name_bytes, &tgs) < 0 ||
!tgs.srch) {
ret = -EINVAL;
goto out;

View File

@@ -98,11 +98,13 @@ static int unknown_prefix(const char *name)
#define HIDE_TAG "hide."
#define SRCH_TAG "srch."
#define TOTL_TAG "totl."
#define WORM_TAG "worm."
#define TAG_LEN (sizeof(HIDE_TAG) - 1)
int scoutfs_xattr_parse_tags(const char *name, unsigned int name_len,
struct scoutfs_xattr_prefix_tags *tgs)
int scoutfs_xattr_parse_tags(struct super_block *sb, const char *name,
unsigned int name_len, struct scoutfs_xattr_prefix_tags *tgs)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
bool found;
memset(tgs, 0, sizeof(struct scoutfs_xattr_prefix_tags));
@@ -123,6 +125,9 @@ int scoutfs_xattr_parse_tags(const char *name, unsigned int name_len,
} else if (!strncmp(name, TOTL_TAG, TAG_LEN)) {
if (++tgs->totl == 0)
return -EINVAL;
} else if (!strncmp(name, WORM_TAG, TAG_LEN)) {
if (++tgs->worm == 0 || sbi->fmt_vers < 2)
return -EINVAL;
} else {
/* only reason to use scoutfs. is tags */
if (!found)
@@ -481,6 +486,23 @@ void scoutfs_xattr_init_totl_key(struct scoutfs_key *key, u64 *name)
key->skxt_c = cpu_to_le64(name[2]);
}
/*
* Parse for v1_expiration within the xattr
* the passed in character array must be NULL
* terminated and is.
*/
static int parse_worm_name(const char *name)
{
static const char worm_name[] = "v1_expiration";
char *last_chr;
last_chr = strrchr(name, '.');
if (!last_chr)
return -EINVAL;
return strcmp(worm_name, last_chr + 1) == 0 ? 0 : -EINVAL;
}
/*
* Parse a u64 in any base after null terminating it while forbidding
* the leading + and trailing \n that kstrotull allows.
@@ -498,6 +520,67 @@ static int parse_totl_u64(const char *s, int len, u64 *res)
return kstrtoull(str, 0, res) != 0 ? -EINVAL : 0;
}
static int parse_worm_u32(const char *s, int len, u32 *res)
{
u64 tmp;
int ret;
ret = parse_totl_u64(s, len, &tmp);
if (ret == 0 && tmp > U32_MAX) {
tmp = 0;
ret = -EINVAL;
}
*res = tmp;
return ret;
}
static int parse_worm_timespec(struct scoutfs_timespec *ts, const char *name, int name_len)
{
const char *start = name;
char *delim;
u64 sec;
u32 nsec;
int sec_len;
int nsec_len;
int ret;
memset(ts, 0, sizeof(struct scoutfs_timespec));
if (name_len < 3)
return -EINVAL;
delim = strnchr(name, name_len, '.');
if (!delim)
return -EINVAL;
if (delim == start || delim == (name + name_len - 1))
return -EINVAL;
sec_len = delim - name;
nsec_len = name_len - (sec_len + 1);
/* Check to make sure only one '.' */
if (strnchr(delim + 1, nsec_len, '.'))
return -EINVAL;
ret = parse_totl_u64(name, sec_len, &sec);
if (ret < 0)
return ret;
ret = parse_worm_u32(delim + 1, nsec_len, &nsec);
if (ret < 0)
return ret;
if (sec > S64_MAX || nsec >= NSEC_PER_SEC)
return -EINVAL;
ts->sec = cpu_to_le64(sec);
ts->nsec = cpu_to_le32(nsec);
return 0;
}
/*
* non-destructive relatively quick parse of the last 3 dotted u64s that
* make up the name of the xattr total. -EINVAL is returned if there
@@ -582,22 +665,24 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name,
{
struct inode *inode = dentry->d_inode;
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
struct super_block *sb = inode->i_sb;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_xattr_totl_val tval = {0,};
struct scoutfs_lock *totl_lock = NULL;
struct super_block *sb = inode->i_sb;
struct scoutfs_xattr_prefix_tags tgs;
const u64 ino = scoutfs_ino(inode);
struct scoutfs_timespec ts = {0,};
struct scoutfs_xattr *xat = NULL;
struct scoutfs_lock *lck = NULL;
struct scoutfs_lock *totl_lock = NULL;
size_t name_len = strlen(name);
struct scoutfs_key totl_key;
struct scoutfs_key key;
bool undo_srch = false;
bool undo_totl = false;
LIST_HEAD(ind_locks);
u8 found_parts;
unsigned int bytes;
unsigned int val_len;
unsigned int bytes;
u64 worm_bits = 0;
u8 found_parts;
u64 ind_seq;
u64 total;
u64 hash = 0;
@@ -620,15 +705,31 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name,
if (unknown_prefix(name))
return -EOPNOTSUPP;
if (scoutfs_xattr_parse_tags(name, name_len, &tgs) != 0)
if (scoutfs_xattr_parse_tags(sb, name, name_len, &tgs) != 0)
return -EINVAL;
if ((tgs.hide | tgs.srch | tgs.totl) && !capable(CAP_SYS_ADMIN))
if ((tgs.hide | tgs.srch | tgs.totl | tgs.worm) && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (tgs.worm && !tgs.hide)
return -EINVAL;
if (tgs.totl && ((ret = parse_totl_key(&totl_key, name, name_len)) != 0))
return ret;
if (tgs.worm) {
ret = parse_worm_name(name);
if (ret != 0) {
return -EINVAL;
}
if (value) {
ret = parse_worm_timespec(&ts, value, size);
if (ret < 0)
return ret;
worm_bits = SCOUTFS_WORM_V1_BIT;
}
}
bytes = sizeof(struct scoutfs_xattr) + name_len + size;
/* alloc enough to read old totl value */
xat = __vmalloc(bytes + SCOUTFS_XATTR_MAX_TOTL_U64, GFP_NOFS, PAGE_KERNEL);
@@ -644,6 +745,11 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name,
down_write(&si->xattr_rwsem);
if (!S_ISREG(inode->i_mode) && tgs.worm) {
ret = -EINVAL;
goto unlock;
}
/* find an existing xattr to delete, including possible totl value */
ret = get_next_xattr(inode, &key, xat,
sizeof(struct scoutfs_xattr) + name_len + SCOUTFS_XATTR_MAX_TOTL_U64,
@@ -666,6 +772,11 @@ static int scoutfs_xattr_set(struct dentry *dentry, const char *name,
goto unlock;
}
if (scoutfs_inode_worm_denied(inode)) {
ret = -EACCES;
goto unlock;
}
/* s64 count delta if we create or delete */
if (tgs.totl)
tval.count = cpu_to_le64((u64)!!(value) - (u64)!!(ret != -ENOENT));
@@ -752,6 +863,9 @@ retry:
if (ret < 0)
goto release;
if (tgs.worm)
scoutfs_inode_set_worm(si, cpu_to_le64(worm_bits), &ts);
/* XXX do these want i_mutex or anything? */
inode_inc_iversion(inode);
inode->i_ctime = CURRENT_TIME;
@@ -842,7 +956,7 @@ ssize_t scoutfs_list_xattrs(struct inode *inode, char *buffer,
break;
}
is_hidden = scoutfs_xattr_parse_tags(xat->name, xat->name_len,
is_hidden = scoutfs_xattr_parse_tags(sb, xat->name, xat->name_len,
&tgs) == 0 && tgs.hide;
if (show_hidden == is_hidden) {
@@ -938,8 +1052,7 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino,
}
if (key.skx_part != 0 ||
scoutfs_xattr_parse_tags(xat->name, xat->name_len,
&tgs) != 0)
scoutfs_xattr_parse_tags(sb, xat->name, xat->name_len, &tgs) != 0)
memset(&tgs, 0, sizeof(tgs));
if (tgs.totl) {

View File

@@ -17,11 +17,12 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino,
struct scoutfs_xattr_prefix_tags {
unsigned long hide:1,
srch:1,
totl:1;
totl:1,
worm:1;
};
int scoutfs_xattr_parse_tags(const char *name, unsigned int name_len,
struct scoutfs_xattr_prefix_tags *tgs);
int scoutfs_xattr_parse_tags(struct super_block *sb, const char *name,
unsigned int name_len, struct scoutfs_xattr_prefix_tags *tgs);
void scoutfs_xattr_init_totl_key(struct scoutfs_key *key, u64 *name);
int scoutfs_xattr_combine_totl(void *dst, int dst_len, void *src, int src_len);

View File

@@ -112,6 +112,7 @@ used during the test.
| T\_EX\_META\_DEV | scratch meta bdev | -f | /dev/vdd |
| T\_EX\_DATA\_DEV | scratch meta bdev | -e | /dev/vdc |
| T\_M[0-9] | mount paths | mounted per run | /mnt/test.[0-9]/ |
| T\_MODULE | built kernel module | created per run | ../kmod/src/..ko |
| T\_NR\_MOUNTS | number of mounts | -n | 3 |
| T\_O[0-9] | mount options | created per run | -o server\_addr= |
| T\_QUORUM | quorum count | -q | 2 |

View File

@@ -35,7 +35,7 @@ t_fail()
t_quiet()
{
echo "# $*" >> "$T_TMPDIR/quiet.log"
"$@" > "$T_TMPDIR/quiet.log" 2>&1 || \
"$@" >> "$T_TMPDIR/quiet.log" 2>&1 || \
t_fail "quiet command failed"
}

View File

@@ -79,5 +79,9 @@ t_filter_dmesg()
re="$re|scoutfs .* error .*reading quorum block.*to update event.*"
re="$re|scoutfs .* error.*server failed to bind to.*"
# format vers back/compat tries bad mounts
re="$re|scoutfs .* error.*outside of supported version.*"
re="$re|scoutfs .* error.*could not get .*super.*"
egrep -v "($re)"
}

View File

@@ -29,13 +29,12 @@ t_mount_rid()
}
#
# Output the "f.$fsid.r.$rid" identifier string for the given mount
# number, 0 is used by default if none is specified.
# Output the "f.$fsid.r.$rid" identifier string for the given path
# in a mounted scoutfs volume.
#
t_ident()
t_ident_from_mnt()
{
local nr="${1:-0}"
local mnt="$(eval echo \$T_M$nr)"
local mnt="$1"
local fsid
local rid
@@ -45,6 +44,38 @@ t_ident()
echo "f.${fsid:0:6}.r.${rid:0:6}"
}
#
# Output the "f.$fsid.r.$rid" identifier string for the given mount
# number, 0 is used by default if none is specified.
#
t_ident()
{
local nr="${1:-0}"
local mnt="$(eval echo \$T_M$nr)"
t_ident_from_mnt "$mnt"
}
#
# Output the sysfs path for a path in a mounted fs.
#
t_sysfs_path_from_ident()
{
local ident="$1"
echo "/sys/fs/scoutfs/$ident"
}
#
# Output the sysfs path for a path in a mounted fs.
#
t_sysfs_path_from_mnt()
{
local mnt="$1"
t_sysfs_path_from_ident $(t_ident_from_mnt $mnt)
}
#
# Output the mount's sysfs path, defaulting to mount 0 if none is
# specified.
@@ -53,7 +84,7 @@ t_sysfs_path()
{
local nr="$1"
echo "/sys/fs/scoutfs/$(t_ident $nr)"
t_sysfs_path_from_ident $(t_ident $nr)
}
#

View File

@@ -0,0 +1,4 @@
== ensuring utils and module for old versions
== unmounting test fs and removing test module
== testing combinations of old and new format versions
== restoring test module and mount

View File

@@ -0,0 +1,41 @@
== test creation of worm xattr without .hide
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation of worm xattr on a directory
setfattr: /mnt/test/test/worm-xattr-unit: Invalid argument
== test creation of worm xattr
== test value is set correctly
== test deletion of xattr 1 sec after setting
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Permission denied
== test setting xattr to a previous time after setting
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Permission denied
== test deletion before expiration
rm: cannot remove /mnt/test/test/worm-xattr-unit/file-1: Permission denied
== Try to move the file prior to expiration
mv: cannot move /mnt/test/test/worm-xattr-unit/file-1 to /mnt/test/test/worm-xattr-unit/file-2: Permission denied
== Try to write to file before expiration
date: write error: Permission denied
== wait til expiration
== Try to write to file after expiration
== Try to move the file after expiration
== test deletion after expiration
== test creation with non integer value of a.a
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with non integer value of ....
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with non integer value of ...
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with no nsec value of 11.
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with no sec value of .11
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with . at start and end value .11.
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with invalid format of two characters
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with too large of a nsecs value 1.18446744073709551615
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with nsecs > NSECS_PER_SEC value 1.1000000000
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== test creation with sec > U64_MAX value of 184467440737095516159.1
setfattr: /mnt/test/test/worm-xattr-unit/file-1: Invalid argument
== removing files after expiration

View File

@@ -342,7 +342,8 @@ if [ -n "$T_INSMOD" ]; then
msg "removing and reinserting scoutfs module"
test -e /sys/module/scoutfs && cmd rmmod scoutfs
cmd modprobe libcrc32c
cmd insmod "$T_KMOD/src/scoutfs.ko"
T_MODULE="$T_KMOD/src/scoutfs.ko"
cmd insmod "$T_MODULE"
fi
nr_globs=${#T_TRACE_GLOB[@]}

View File

@@ -7,9 +7,11 @@ simple-release-extents.sh
setattr_more.sh
offline-extent-waiting.sh
move-blocks.sh
format-version-forward-back.sh
enospc.sh
srch-basic-functionality.sh
simple-xattr-unit.sh
worm-xattr-unit.sh
totl-xattr-tag.sh
lock-refleak.sh
lock-shrink-consistency.sh

View File

@@ -0,0 +1,176 @@
#
# Test our basic ability to work with different format versions.
#
# The current code being tested has a range of supported format
# versions. For each of the older supported format versions we have a
# git hash of the commit before the next greater version was introduced.
# We build versions of the scoutfs utility and kernel module for the
# last commit in tree that had a lesser supported version as its max
# supported version. We use those binaries to test forward and back
# compat as new and old code works with a persistent volume with a given
# format version.
#
mount_has_format_version()
{
local mnt="$1"
local vers="$2"
local sysfs_fmt_vers="$(t_sysfs_path_from_mnt $SCR)/format_version"
test "$(cat $sysfs_fmt_vers)" == "$vers"
}
SCR="/mnt/scoutfs.scratch"
MIN=$(modinfo $T_MODULE | awk '($1 == "scoutfs_format_version_min:"){print $2}')
MAX=$(modinfo $T_MODULE | awk '($1 == "scoutfs_format_version_max:"){print $2}')
echo "min: $MIN max: $MAX" > "$T_TMP.log"
test "$MIN" -gt 0 -a "$MAX" -gt 0 -a "$MIN" -le "$MAX" || \
t_fail "parsed bad versions, min: $MIN max: $MAX"
test "$MIN" == "$MAX" && \
t_skip "only one supported format version: $MIN"
# prepare dir and wipe any weird old partial state
builds="$T_RESULTS/format_version_builds"
mkdir -p "$builds"
echo "== ensuring utils and module for old versions"
declare -A commits
commits[1]=730a84a
for vers in $(seq $MIN $((MAX - 1))); do
dir="$builds/$vers"
platform="$(uname -rp)"
buildmark="$dir/buildmark"
commit="${commits[$vers]}"
test -n "$commit" || \
t_fail "no commit for vers $vers"
# have our files for this version
test "$(cat $buildmark 2>&1)" == "$platform" && \
continue
# build as one big sequence of commands that can return failure
(
set -o pipefail
rm -rf $dir &&
mkdir -p $dir/building &&
cd "$T_TESTS/.." &&
git archive --format=tar "$commit" | tar -C "$dir/building" -xf - &&
cd - &&
find $dir &&
make -C "$dir/building" -j &&
mv $dir/building/utils/src/scoutfs $dir &&
mv $dir/building/kmod/src/scoutfs.ko $dir &&
rm -rf $dir/building &&
echo "$platform" > "$buildmark" &&
find $dir &&
cat $buildmark
) || t_fail "version $vers build failed"
done
echo "== unmounting test fs and removing test module"
t_quiet t_umount_all
t_quiet rmmod scoutfs
echo "== testing combinations of old and new format versions"
mkdir -p "$SCR"
for vers in $(seq $MIN $((MAX - 1))); do
old_scoutfs="$builds/$vers/scoutfs"
old_module="$builds/$vers/scoutfs.ko"
echo "mkfs $vers" >> "$T_TMP.log"
t_quiet $old_scoutfs mkfs -f -Q 0,127.0.0.1,53000 "$T_EX_META_DEV" "$T_EX_DATA_DEV" \
|| t_fail "mkfs $vers failed"
echo "mount $vers with $vers" >> "$T_TMP.log"
t_quiet insmod $old_module
t_quiet mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR"
t_quiet mount_has_format_version "$SCR" "$vers"
echo "creating first in $vers" >> "$T_TMP.log"
t_quiet touch "$SCR/file-1"
stat "$SCR"/file-* > "$T_TMP.stat" || \
t_fail "stat in $vers failed"
echo "remounting $vers fs with $MAX" >> "$T_TMP.log"
t_quiet umount "$SCR"
rmmod scoutfs
insmod "$T_MODULE"
t_quiet mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR"
t_quiet mount_has_format_version "$SCR" "$vers"
echo "verifying stat in $vers with $MAX" >> "$T_TMP.log"
diff -u "$T_TMP.stat" <(stat "$SCR"/file-*)
echo "del first and create second in $vers" >> "$T_TMP.log"
t_quiet rm -f "$SCR/file-1"
t_quiet touch "$SCR/file-2"
stat "$SCR"/file-* > "$T_TMP.stat" || \
t_fail "stat in $vers failed"
echo "remounting $vers fs with $vers" >> "$T_TMP.log"
t_quiet umount "$SCR"
rmmod scoutfs
insmod "$old_module"
t_quiet mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR"
t_quiet mount_has_format_version "$SCR" "$vers"
echo "verifying stat in $vers with $vers" >> "$T_TMP.log"
diff -u "$T_TMP.stat" <(stat "$SCR"/file-*)
echo "changing format vers to $MAX" >> "$T_TMP.log"
t_quiet umount "$SCR"
rmmod scoutfs
t_quiet scoutfs change-format-version -F -V $MAX $T_EX_META_DEV "$T_EX_DATA_DEV"
echo "mount fs $MAX with old $vers should fail" >> "$T_TMP.log"
insmod "$old_module"
mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR" >> "$T_TMP.log" 2>&1
if [ "$?" == "0" ]; then
umount "$SCR"
t_fail "old code ver $vers able to mount new ver $MAX"
fi
echo "remounting $MAX fs with $MAX" >> "$T_TMP.log"
rmmod scoutfs
insmod "$T_MODULE"
t_quiet mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR"
t_quiet mount_has_format_version "$SCR" "$MAX"
echo "verifying stat in $MAX with $MAX" >> "$T_TMP.log"
diff -u "$T_TMP.stat" <(stat "$SCR"/file-*)
echo "create third in changed $MAX" >> "$T_TMP.log"
t_quiet touch "$SCR/file-3"
stat "$SCR"/file-* > "$T_TMP.stat" || \
t_fail "stat in $MAX failed"
echo "remounting $MAX fs with $MAX again" >> "$T_TMP.log"
t_quiet umount "$SCR"
t_quiet mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
"$T_EX_DATA_DEV" "$SCR"
t_quiet mount_has_format_version "$SCR" "$MAX"
echo "verifying stat in $MAX with $MAX again" >> "$T_TMP.log"
diff -u "$T_TMP.stat" <(stat "$SCR"/file-*)
echo "done with old vers $vers" >> "$T_TMP.log"
t_quiet umount "$SCR"
rmmod scoutfs
done
echo "== restoring test module and mount"
insmod "$T_MODULE"
t_mount_all
t_pass

View File

@@ -0,0 +1,84 @@
t_require_commands touch rm setfattr
touch "$T_D0/file-1"
SECS=$(date '+%s')
NSECS=$(date '+%N')
DELAY=10
EXP=$(( $SECS + $DELAY ))
EXP_DELAY=$(( $DELAY + 2 ))
echo "== test creation of worm xattr without .hide"
setfattr -n scoutfs.worm.v1_expiration -v $EXP.$NSECS "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation of worm xattr on a directory"
setfattr -n scoutfs.worm.v1_expiration -v $EXP.$NSECS "$T_D0" 2>&1 | t_filter_fs
echo "== test creation of worm xattr"
setfattr -n scoutfs.hide.worm.v1_expiration -v $EXP.$NSECS "$T_D0/file-1"
echo "== test value is set correctly"
diff -u --ignore-all-space <(echo "$EXP.$NSECS") <(getfattr --absolute-names --only-values -n scoutfs.hide.worm.v1_expiration -m - "$T_D0/file-1")
echo "== test deletion of xattr 1 sec after setting"
sleep 1
setfattr -x scoutfs.hide.worm.v1_expiration "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test setting xattr to a previous time after setting"
setfattr -n scoutfs.hide.worm.v1_expiration -v $SECS.$NSECS "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test deletion before expiration"
rm -f "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== Try to move the file prior to expiration"
mv $T_D0/file-1 $T_D0/file-2 2>&1 | t_filter_fs
echo "== Try to write to file before expiration"
date >> $T_D0/file-1
echo "== wait til expiration"
sleep $EXP_DELAY
echo "== Try to write to file after expiration"
date >> $T_D0/file-1
echo "== Try to move the file after expiration"
mv $T_D0/file-1 $T_D0/file-2
mv $T_D0/file-2 $T_D0/file-1
echo "== test deletion after expiration"
setfattr -x scoutfs.hide.worm.v1_expiration "$T_D0/file-1"
echo "== test creation with non integer value of a.a"
setfattr -n scoutfs.hide.worm.v1_expiration -v a.a "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with non integer value of ...."
setfattr -n scoutfs.hide.worm.v1_expiration -v .... "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with non integer value of ..."
setfattr -n scoutfs.hide.worm.v1_expiration -v ... "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with no nsec value of 11."
setfattr -n scoutfs.hide.worm.v1_expiration -v 11. "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with no sec value of .11"
setfattr -n scoutfs.hide.worm.v1_expiration -v .11 "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with . at start and end value .11."
setfattr -n scoutfs.hide.worm.v1_expiration -v .11. "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with invalid format of two characters"
setfattr -n scoutfs.hide.worm.v1_expiration -v 11 "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with too large of a nsecs value 1.18446744073709551615"
setfattr -n scoutfs.hide.worm.v1_expiration -v 1.18446744073709551615 "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with nsecs > NSECS_PER_SEC value 1.1000000000"
setfattr -n scoutfs.hide.worm.v1_expiration -v 1.1000000000 "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== test creation with sec > U64_MAX value of 184467440737095516159.1"
setfattr -n scoutfs.hide.worm.v1_expiration -v 184467440737095516159.1 "$T_D0/file-1" 2>&1 | t_filter_fs
echo "== removing files after expiration"
rm -f "$T_D0/file-1"
t_pass

View File

@@ -197,6 +197,17 @@ name, total value, and a count of contributing attributes can be read
with the
.IB READ_XATTR_TOTALS
ioctl.
.TP
.B .worm.
Attributes with the .worm. flag are used to maintain the worm policy
set on a file. This is a time-based retention policy, which allows a user
to set a policy to store data for a specified amount of time. When a time
based retention policy is set, files can be created and read, but not
modified or deleted. The only supported version at the moment is
v1_expiration. The v1_expiration policy will also prevent the ROOT user
from modifying or deleting of the file. The value of the extended attribute
is sec.nsec timeval in wall clock GMT. The hidden tag must also be used,
so that the worm attribute is kept private to this scoutfs volume.
.RE
.SH FORMAT VERSION

View File

@@ -119,6 +119,16 @@ static int do_change_fmt_vers(struct change_fmt_vers_args *args)
goto out;
}
if (le64_to_cpu(meta_super->fmt_vers) > args->fmt_vers ||
le64_to_cpu(data_super->fmt_vers) > args->fmt_vers) {
ret = -EPERM;
printf("Downgrade of Meta Format Version: %llu and Data Format Version: %llu to Format Version: %llu is not allowed\n",
le64_to_cpu(meta_super->fmt_vers),
le64_to_cpu(data_super->fmt_vers),
args->fmt_vers);
goto out;
}
if (le64_to_cpu(meta_super->fmt_vers) != args->fmt_vers) {
meta_super->fmt_vers = cpu_to_le64(args->fmt_vers);

View File

@@ -262,7 +262,10 @@ static int do_mkfs(struct mkfs_args *args)
inode.ctime.nsec = inode.atime.nsec;
inode.mtime.sec = inode.atime.sec;
inode.mtime.nsec = inode.atime.nsec;
btree_append_item(bt, &key, &inode, sizeof(inode));
if (args->fmt_vers == 1)
btree_append_item(bt, &key, &inode, SCOUTFS_INODE_FMT_V1_BYTES);
else
btree_append_item(bt, &key, &inode, SCOUTFS_INODE_FMT_V2_BYTES);
ret = write_block(meta_fd, SCOUTFS_BLOCK_MAGIC_BTREE, fsid, 1, blkno,
SCOUTFS_BLOCK_LG_SHIFT, &bt->hdr);

View File

@@ -14,6 +14,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <argp.h>
#include <stdbool.h>
#include "sparse.h"
#include "parse.h"
@@ -69,6 +70,15 @@ static void print_inode(struct scoutfs_key *key, void *val, int val_len)
le32_to_cpu(inode->ctime.nsec),
le64_to_cpu(inode->mtime.sec),
le32_to_cpu(inode->mtime.nsec));
if (val_len == SCOUTFS_INODE_FMT_V1_BYTES) {
printf ("\n");
} else {
printf(" worm_bits %llx worm_expiration %llu.%08u\n",
le64_to_cpu(inode->worm_bits),
le64_to_cpu(inode->worm_expiration.sec),
le32_to_cpu(inode->worm_expiration.nsec));
}
}
static void print_orphan(struct scoutfs_key *key, void *val, int val_len)