mirror of
https://github.com/versity/scoutfs.git
synced 2026-09-04 15:17:35 +00:00
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>
This commit is contained in:
committed by
Zach Brown
parent
f9fbef7aff
commit
1d55a4c099
@@ -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;
|
||||
|
||||
@@ -856,8 +856,15 @@ 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 (SCOUTFS_INODE_FMT_V2_BYTES - \
|
||||
offsetof(struct scoutfs_inode, worm_bits))
|
||||
|
||||
#define SCOUTFS_INO_FLAG_TRUNCATE 0x1
|
||||
|
||||
#define SCOUTFS_ROOT_INO 1
|
||||
|
||||
+96
-8
@@ -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,20 @@ 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;
|
||||
}
|
||||
|
||||
void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
|
||||
{
|
||||
*key = (struct scoutfs_key) {
|
||||
@@ -285,12 +339,6 @@ void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
|
||||
};
|
||||
}
|
||||
|
||||
/* Returns the max inode size given format version */
|
||||
static int max_inode_fmt_ver_bytes(struct super_block *sb)
|
||||
{
|
||||
return sizeof(struct scoutfs_inode);
|
||||
}
|
||||
|
||||
static int lookup_inode_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
struct scoutfs_lock *lock,
|
||||
struct scoutfs_inode_payload_wrapper *inode_payload)
|
||||
@@ -481,6 +529,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);
|
||||
|
||||
@@ -771,11 +824,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);
|
||||
|
||||
@@ -807,6 +862,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));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1449,6 +1513,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);
|
||||
|
||||
@@ -2008,6 +2074,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
|
||||
@@ -144,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
|
||||
|
||||
+6
-1
@@ -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;
|
||||
|
||||
+125
-12
@@ -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) {
|
||||
|
||||
+4
-3
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-1
@@ -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