mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-05 03:44:05 +00:00
Compare commits
10 Commits
v1.26
...
zab/worm_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2a3042037 | ||
|
|
e740f93da7 | ||
|
|
8e8817a049 | ||
|
|
07c09272e8 | ||
|
|
f3ff89f27d | ||
|
|
0bd5fc4942 | ||
|
|
9291f1a231 | ||
|
|
1d55a4c099 | ||
|
|
f9fbef7aff | ||
|
|
096bf83b43 |
@@ -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) &&
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
178
kmod/src/inode.c
178
kmod/src/inode.c
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
137
kmod/src/xattr.c
137
kmod/src/xattr.c
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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)"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
#
|
||||
|
||||
4
tests/golden/format-version-forward-back
Normal file
4
tests/golden/format-version-forward-back
Normal 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
|
||||
41
tests/golden/worm-xattr-unit
Normal file
41
tests/golden/worm-xattr-unit
Normal 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
|
||||
@@ -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[@]}
|
||||
|
||||
@@ -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
|
||||
|
||||
176
tests/tests/format-version-forward-back.sh
Normal file
176
tests/tests/format-version-forward-back.sh
Normal 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
|
||||
84
tests/tests/worm-xattr-unit.sh
Normal file
84
tests/tests/worm-xattr-unit.sh
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user