mirror of
https://github.com/versity/scoutfs.git
synced 2026-05-02 02:45:43 +00:00
Compare commits
21 Commits
v1.21
...
zab/quot_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18f00a8acc | ||
|
|
40b62b5033 | ||
|
|
d4b1cd5931 | ||
|
|
555b307fe6 | ||
|
|
9859a75d4e | ||
|
|
c6ad865054 | ||
|
|
19ea848bca | ||
|
|
13151b7c66 | ||
|
|
535352fbd4 | ||
|
|
941e053eb3 | ||
|
|
12fe44c0be | ||
|
|
b06a56b1ee | ||
|
|
6817b403e5 | ||
|
|
b89139b0fe | ||
|
|
bd4dad757b | ||
|
|
b2448f461a | ||
|
|
4091e2cc55 | ||
|
|
45cd62974a | ||
|
|
5d744b78bd | ||
|
|
1328f1a2cb | ||
|
|
1f5c68cd30 |
@@ -1,27 +1,6 @@
|
||||
Versity ScoutFS Release Notes
|
||||
=============================
|
||||
|
||||
---
|
||||
v1.21
|
||||
\
|
||||
*Jul 1, 2024*
|
||||
|
||||
This release adds features that rely on incompatible changes to
|
||||
structure the file system. The process of advancing the format version
|
||||
to enable these features is described in scoutfs(5).
|
||||
|
||||
Added the ".indx." extended attribute tag which can be used to determine
|
||||
the sorting of files in a global index.
|
||||
|
||||
Added ScoutFS quotas which let rules define file size and count limits
|
||||
in terms of ".totl." extended attribute totals.
|
||||
|
||||
Added the project ID file attribute which is inherited from parent
|
||||
directories on creation. ScoutFS quota rules can reference project IDs.
|
||||
|
||||
Add a retention attribute for files which prevents modification once
|
||||
enabled.
|
||||
|
||||
---
|
||||
v1.20
|
||||
\
|
||||
|
||||
@@ -9,7 +9,6 @@ CFLAGS_scoutfs_trace.o = -I$(src) # define_trace.h double include
|
||||
|
||||
scoutfs-y += \
|
||||
acl.o \
|
||||
attr_x.o \
|
||||
avl.o \
|
||||
alloc.o \
|
||||
block.o \
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Versity Software, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "format.h"
|
||||
#include "super.h"
|
||||
#include "inode.h"
|
||||
#include "ioctl.h"
|
||||
#include "lock.h"
|
||||
#include "trans.h"
|
||||
#include "attr_x.h"
|
||||
|
||||
static int validate_attr_x_input(struct super_block *sb, struct scoutfs_ioctl_inode_attr_x *iax)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX__UNKNOWN) ||
|
||||
(iax->x_flags & SCOUTFS_IOC_IAX_F__UNKNOWN))
|
||||
return -EINVAL;
|
||||
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_RETENTION) &&
|
||||
(ret = scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_RETENTION)))
|
||||
return ret;
|
||||
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_PROJECT_ID) &&
|
||||
(ret = scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_PROJECT_ID)))
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the mask indicates interest in the given attr then set the field
|
||||
* to the caller's value and return the new size if it didn't already
|
||||
* include the attr field.
|
||||
*/
|
||||
#define fill_attr(size, iax, bit, field, val) \
|
||||
({ \
|
||||
__typeof__(iax) _iax = (iax); \
|
||||
__typeof__(size) _size = (size); \
|
||||
\
|
||||
if (_iax->x_mask & (bit)) { \
|
||||
_iax->field = (val); \
|
||||
_size = max(_size, offsetof(struct scoutfs_ioctl_inode_attr_x, field) + \
|
||||
sizeof_field(struct scoutfs_ioctl_inode_attr_x, field)); \
|
||||
} \
|
||||
\
|
||||
_size; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Returns -errno on error, or >= number of bytes filled by the
|
||||
* response. 0 can be returned if no attributes are requested in the
|
||||
* input x_mask.
|
||||
*/
|
||||
int scoutfs_get_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *iax)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
size_t size = 0;
|
||||
u64 offline;
|
||||
u64 online;
|
||||
u64 bits;
|
||||
int ret;
|
||||
|
||||
if (iax->x_mask == 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = validate_attr_x_input(sb, iax);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_META_SEQ,
|
||||
meta_seq, scoutfs_inode_meta_seq(inode));
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_DATA_SEQ,
|
||||
data_seq, scoutfs_inode_data_seq(inode));
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_DATA_VERSION,
|
||||
data_version, scoutfs_inode_data_version(inode));
|
||||
if (iax->x_mask & (SCOUTFS_IOC_IAX_ONLINE_BLOCKS | SCOUTFS_IOC_IAX_OFFLINE_BLOCKS)) {
|
||||
scoutfs_inode_get_onoff(inode, &online, &offline);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_ONLINE_BLOCKS,
|
||||
online_blocks, online);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_OFFLINE_BLOCKS,
|
||||
offline_blocks, offline);
|
||||
}
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_CTIME, ctime_sec, inode->i_ctime.tv_sec);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_CTIME, ctime_nsec, inode->i_ctime.tv_nsec);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_CRTIME, crtime_sec, si->crtime.tv_sec);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_CRTIME, crtime_nsec, si->crtime.tv_nsec);
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_SIZE, size, i_size_read(inode));
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX__BITS) {
|
||||
bits = 0;
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_RETENTION) &&
|
||||
(scoutfs_inode_get_flags(inode) & SCOUTFS_INO_FLAG_RETENTION))
|
||||
bits |= SCOUTFS_IOC_IAX_B_RETENTION;
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX__BITS, bits, bits);
|
||||
}
|
||||
size = fill_attr(size, iax, SCOUTFS_IOC_IAX_PROJECT_ID,
|
||||
project_id, scoutfs_inode_get_proj(inode));
|
||||
|
||||
ret = size;
|
||||
unlock:
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
|
||||
inode_unlock(inode);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool valid_attr_changes(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *iax)
|
||||
{
|
||||
/* provided data_version must be non-zero */
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_DATA_VERSION) && (iax->data_version == 0))
|
||||
return false;
|
||||
|
||||
/* can only set size or data version in new regular files */
|
||||
if (((iax->x_mask & SCOUTFS_IOC_IAX_SIZE) ||
|
||||
(iax->x_mask & SCOUTFS_IOC_IAX_DATA_VERSION)) &&
|
||||
(!S_ISREG(inode->i_mode) || scoutfs_inode_data_version(inode) != 0))
|
||||
return false;
|
||||
|
||||
/* must provide non-zero data_version with non-zero size */
|
||||
if (((iax->x_mask & SCOUTFS_IOC_IAX_SIZE) && (iax->size > 0)) &&
|
||||
(!(iax->x_mask & SCOUTFS_IOC_IAX_DATA_VERSION) || (iax->data_version == 0)))
|
||||
return false;
|
||||
|
||||
/* must provide non-zero size when setting offline extents to that size */
|
||||
if ((iax->x_flags & SCOUTFS_IOC_IAX_F_SIZE_OFFLINE) &&
|
||||
(!(iax->x_mask & SCOUTFS_IOC_IAX_SIZE) || (iax->size == 0)))
|
||||
return false;
|
||||
|
||||
/* the retention bit only applies to regular files */
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_RETENTION) && !S_ISREG(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int scoutfs_set_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *iax)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
LIST_HEAD(ind_locks);
|
||||
bool set_data_seq;
|
||||
int ret;
|
||||
|
||||
/* initially all setting is root only, could loosen with finer grained checks */
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iax->x_mask == 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = validate_attr_x_input(sb, iax);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* check for errors before making any changes */
|
||||
if (!valid_attr_changes(inode, iax)) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* retention prevents modification unless also clearing retention */
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0 && !((iax->x_mask & SCOUTFS_IOC_IAX_RETENTION) &&
|
||||
!(iax->bits & SCOUTFS_IOC_IAX_B_RETENTION)))
|
||||
goto unlock;
|
||||
|
||||
/* setting only so we don't see 0 data seq with nonzero data_version */
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_DATA_VERSION) && (iax->data_version > 0))
|
||||
set_data_seq = true;
|
||||
else
|
||||
set_data_seq = false;
|
||||
|
||||
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, set_data_seq, true);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(inode, lock);
|
||||
if (ret < 0)
|
||||
goto release;
|
||||
|
||||
/* creating offline extent first, it might fail */
|
||||
if (iax->x_flags & SCOUTFS_IOC_IAX_F_SIZE_OFFLINE) {
|
||||
ret = scoutfs_data_init_offline_extent(inode, iax->size, lock);
|
||||
if (ret)
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* make all changes once they're all checked and will succeed */
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_DATA_VERSION)
|
||||
scoutfs_inode_set_data_version(inode, iax->data_version);
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_SIZE)
|
||||
i_size_write(inode, iax->size);
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_CTIME) {
|
||||
inode->i_ctime.tv_sec = iax->ctime_sec;
|
||||
inode->i_ctime.tv_nsec = iax->ctime_nsec;
|
||||
}
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_CRTIME) {
|
||||
si->crtime.tv_sec = iax->crtime_sec;
|
||||
si->crtime.tv_nsec = iax->crtime_nsec;
|
||||
}
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_RETENTION) {
|
||||
scoutfs_inode_set_flags(inode, ~SCOUTFS_INO_FLAG_RETENTION,
|
||||
(iax->bits & SCOUTFS_IOC_IAX_B_RETENTION) ?
|
||||
SCOUTFS_INO_FLAG_RETENTION : 0);
|
||||
}
|
||||
if (iax->x_mask & SCOUTFS_IOC_IAX_PROJECT_ID)
|
||||
scoutfs_inode_set_proj(inode, iax->project_id);
|
||||
|
||||
scoutfs_update_inode_item(inode, lock, &ind_locks);
|
||||
ret = 0;
|
||||
release:
|
||||
scoutfs_release_trans(sb);
|
||||
unlock:
|
||||
scoutfs_inode_index_unlock(sb, &ind_locks);
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
inode_unlock(inode);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef _SCOUTFS_ATTR_X_H_
|
||||
#define _SCOUTFS_ATTR_X_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include "ioctl.h"
|
||||
|
||||
int scoutfs_get_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *iax);
|
||||
int scoutfs_set_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *iax);
|
||||
|
||||
#endif
|
||||
@@ -683,7 +683,6 @@ int scoutfs_block_read_ref(struct super_block *sb, struct scoutfs_block_ref *ref
|
||||
struct scoutfs_block_header *hdr;
|
||||
struct block_private *bp = NULL;
|
||||
bool retried = false;
|
||||
__le32 crc = 0;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
@@ -696,9 +695,7 @@ retry:
|
||||
|
||||
/* corrupted writes might be a sign of a stale reference */
|
||||
if (!test_bit(BLOCK_BIT_CRC_VALID, &bp->bits)) {
|
||||
crc = block_calc_crc(hdr, SCOUTFS_BLOCK_LG_SIZE);
|
||||
if (hdr->crc != crc) {
|
||||
trace_scoutfs_block_stale(sb, ref, hdr, magic, le32_to_cpu(crc));
|
||||
if (hdr->crc != block_calc_crc(hdr, SCOUTFS_BLOCK_LG_SIZE)) {
|
||||
ret = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
@@ -708,7 +705,6 @@ retry:
|
||||
|
||||
if (hdr->magic != cpu_to_le32(magic) || hdr->fsid != cpu_to_le64(sbi->fsid) ||
|
||||
hdr->seq != ref->seq || hdr->blkno != ref->blkno) {
|
||||
trace_scoutfs_block_stale(sb, ref, hdr, magic, 0);
|
||||
ret = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -586,12 +586,6 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (create && !si->staging) {
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* convert unwritten to written, could be staging */
|
||||
if (create && ext.map && (ext.flags & SEF_UNWRITTEN)) {
|
||||
un.start = iblock;
|
||||
@@ -1165,9 +1159,9 @@ out:
|
||||
* on regular files with no data extents. It's used to restore a file
|
||||
* with an offline extent which can then trigger staging.
|
||||
*
|
||||
* The caller must take care of cluster locking, transactions, inode
|
||||
* updates, and index updates (so that they can atomically make this
|
||||
* change along with other metadata changes).
|
||||
* The caller has taken care of locking the inode. We're updating the
|
||||
* inode offline count as we create the offline extent so we take care
|
||||
* of the index locking, updating, and transaction.
|
||||
*/
|
||||
int scoutfs_data_init_offline_extent(struct inode *inode, u64 size,
|
||||
struct scoutfs_lock *lock)
|
||||
@@ -1181,6 +1175,7 @@ int scoutfs_data_init_offline_extent(struct inode *inode, u64 size,
|
||||
.lock = lock,
|
||||
};
|
||||
const u64 count = DIV_ROUND_UP(size, SCOUTFS_BLOCK_SM_SIZE);
|
||||
LIST_HEAD(ind_locks);
|
||||
u64 on;
|
||||
u64 off;
|
||||
int ret;
|
||||
@@ -1193,10 +1188,28 @@ int scoutfs_data_init_offline_extent(struct inode *inode, u64 size,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* we're updating meta_seq with offline block count */
|
||||
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, false, true);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(inode, lock);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
down_write(&si->extent_sem);
|
||||
ret = scoutfs_ext_insert(sb, &data_ext_ops, &args,
|
||||
0, count, 0, SEF_OFFLINE);
|
||||
up_write(&si->extent_sem);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
scoutfs_update_inode_item(inode, lock, &ind_locks);
|
||||
|
||||
unlock:
|
||||
scoutfs_release_trans(sb);
|
||||
scoutfs_inode_index_unlock(sb, &ind_locks);
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -1264,9 +1277,6 @@ int scoutfs_data_move_blocks(struct inode *from, u64 from_off,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!is_stage && (ret = scoutfs_inode_check_retention(to)))
|
||||
goto out;
|
||||
|
||||
if ((from_off & SCOUTFS_BLOCK_SM_MASK) ||
|
||||
(to_off & SCOUTFS_BLOCK_SM_MASK) ||
|
||||
((byte_len & SCOUTFS_BLOCK_SM_MASK) &&
|
||||
|
||||
@@ -933,16 +933,12 @@ static int scoutfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
hash = dirent_name_hash(dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
ret = lookup_dirent(sb, scoutfs_ino(dir), dentry->d_name.name, dentry->d_name.len, hash,
|
||||
&dent, dir_lock);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
goto out;
|
||||
|
||||
if (should_orphan(inode)) {
|
||||
ret = scoutfs_lock_orphan(sb, SCOUTFS_LOCK_WRITE_ONLY, 0, scoutfs_ino(inode),
|
||||
@@ -1643,10 +1639,6 @@ static int scoutfs_rename_common(struct inode *old_dir,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if ((old_inode && (ret = scoutfs_inode_check_retention(old_inode))) ||
|
||||
(new_inode && (ret = scoutfs_inode_check_retention(new_inode))))
|
||||
goto out_unlock;
|
||||
|
||||
if (should_orphan(new_inode)) {
|
||||
ret = scoutfs_lock_orphan(sb, SCOUTFS_LOCK_WRITE_ONLY, 0, scoutfs_ino(new_inode),
|
||||
&orph_lock);
|
||||
|
||||
@@ -109,10 +109,6 @@ retry:
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_complete_truncate(inode, scoutfs_inode_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -225,10 +221,6 @@ retry:
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_complete_truncate(inode, scoutfs_inode_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
* The format version defines the format of structures on devices,
|
||||
* structures that are communicated over the wire, and the protocol
|
||||
* behind the structures.
|
||||
*
|
||||
* Builds can have unique pre-release formats that are incompatible with
|
||||
* every other build. This lets people experiment with formats without
|
||||
* accidentally corrupting data with release builds.
|
||||
*/
|
||||
#define SCOUTFS_FORMAT_VERSION_MIN 1
|
||||
#define SCOUTFS_FORMAT_VERSION_MIN 0x8cf3b46619eb9975ULL
|
||||
#define SCOUTFS_FORMAT_VERSION_MIN_STR __stringify(SCOUTFS_FORMAT_VERSION_MIN)
|
||||
#define SCOUTFS_FORMAT_VERSION_MAX 2
|
||||
#define SCOUTFS_FORMAT_VERSION_MAX 0x8cf3b46619eb9975ULL
|
||||
#define SCOUTFS_FORMAT_VERSION_MAX_STR __stringify(SCOUTFS_FORMAT_VERSION_MAX)
|
||||
|
||||
#define SCOUTFS_FORMAT_VERSION_FEAT_RETENTION 2
|
||||
#define SCOUTFS_FORMAT_VERSION_FEAT_PROJECT_ID 2
|
||||
#define SCOUTFS_FORMAT_VERSION_FEAT_QUOTA 2
|
||||
#define SCOUTFS_FORMAT_VERSION_FEAT_INDX_TAG 2
|
||||
#define SCOUTFS_FORMAT_VER_PREREL 0x8000000000000000ULL
|
||||
|
||||
/* statfs(2) f_type */
|
||||
#define SCOUTFS_SUPER_MAGIC 0x554f4353 /* "SCOU" */
|
||||
@@ -189,6 +189,11 @@ struct scoutfs_key {
|
||||
#define skxt_b _sk_second
|
||||
#define skxt_c _sk_third
|
||||
|
||||
/* xattr index */
|
||||
#define skxi_a _sk_first
|
||||
#define skxi_b _sk_second
|
||||
#define skxi_ino _sk_third
|
||||
|
||||
/* inode */
|
||||
#define ski_ino _sk_first
|
||||
|
||||
@@ -891,6 +896,7 @@ struct scoutfs_inode {
|
||||
__le64 next_readdir_pos;
|
||||
__le64 next_xattr_id;
|
||||
__le64 version;
|
||||
__le64 proj;
|
||||
__le32 nlink;
|
||||
__le32 uid;
|
||||
__le32 gid;
|
||||
@@ -901,38 +907,9 @@ struct scoutfs_inode {
|
||||
struct scoutfs_timespec ctime;
|
||||
struct scoutfs_timespec mtime;
|
||||
struct scoutfs_timespec crtime;
|
||||
__le64 proj;
|
||||
};
|
||||
|
||||
#define SCOUTFS_INODE_FMT_V1_BYTES offsetof(struct scoutfs_inode, proj)
|
||||
|
||||
/*
|
||||
* There are so few versions that we don't mind doing this work inline
|
||||
* so that both utils and kernel can share these. Mounting has already
|
||||
* checked that the format version is within the supported min and max,
|
||||
* so these functions only deal with size variance within that band.
|
||||
*/
|
||||
/* Returns the native written inode size for the given format version, 0 for bad version */
|
||||
static inline int scoutfs_inode_vers_bytes(__u64 fmt_vers)
|
||||
{
|
||||
if (fmt_vers == 1)
|
||||
return SCOUTFS_INODE_FMT_V1_BYTES;
|
||||
else
|
||||
return sizeof(struct scoutfs_inode);
|
||||
}
|
||||
/*
|
||||
* Returns true if bytes is a valid inode size to read from the given
|
||||
* version. The given version must be greater than the version that
|
||||
* introduced the size.
|
||||
*/
|
||||
static inline int scoutfs_inode_valid_vers_bytes(__u64 fmt_vers, int bytes)
|
||||
{
|
||||
return (bytes == sizeof(struct scoutfs_inode) && fmt_vers == SCOUTFS_FORMAT_VERSION_MAX) ||
|
||||
(bytes == SCOUTFS_INODE_FMT_V1_BYTES);
|
||||
}
|
||||
|
||||
#define SCOUTFS_INO_FLAG_TRUNCATE 0x1
|
||||
#define SCOUTFS_INO_FLAG_RETENTION 0x2
|
||||
#define SCOUTFS_INO_FLAG_TRUNCATE 0x1
|
||||
|
||||
#define SCOUTFS_ROOT_INO 1
|
||||
|
||||
|
||||
162
kmod/src/inode.c
162
kmod/src/inode.c
@@ -91,7 +91,7 @@ static void scoutfs_inode_ctor(void *obj)
|
||||
|
||||
init_rwsem(&si->extent_sem);
|
||||
mutex_init(&si->item_mutex);
|
||||
seqlock_init(&si->seqlock);
|
||||
seqcount_init(&si->seqcount);
|
||||
si->staging = false;
|
||||
scoutfs_per_task_init(&si->pt_data_lock);
|
||||
atomic64_set(&si->data_waitq.changed, 0);
|
||||
@@ -250,7 +250,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, int inode_bytes)
|
||||
static void load_inode(struct inode *inode, struct scoutfs_inode *cinode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
@@ -275,10 +275,10 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode, int in
|
||||
si->offline_blocks = le64_to_cpu(cinode->offline_blocks);
|
||||
si->next_readdir_pos = le64_to_cpu(cinode->next_readdir_pos);
|
||||
si->next_xattr_id = le64_to_cpu(cinode->next_xattr_id);
|
||||
si->proj = le64_to_cpu(cinode->proj);
|
||||
si->flags = le32_to_cpu(cinode->flags);
|
||||
si->crtime.tv_sec = le64_to_cpu(cinode->crtime.sec);
|
||||
si->crtime.tv_nsec = le32_to_cpu(cinode->crtime.nsec);
|
||||
si->proj = le64_to_cpu(cinode->proj);
|
||||
|
||||
/*
|
||||
* i_blocks is initialized from online and offline and is then
|
||||
@@ -299,24 +299,6 @@ void scoutfs_inode_init_key(struct scoutfs_key *key, u64 ino)
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Read an inode item into the caller's buffer and return the size that
|
||||
* we read. Returns errors if the inode size is unsupported or doesn't
|
||||
* make sense for the format version.
|
||||
*/
|
||||
static int lookup_inode_item(struct super_block *sb, struct scoutfs_key *key,
|
||||
struct scoutfs_inode *sinode, struct scoutfs_lock *lock)
|
||||
{
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
int ret;
|
||||
|
||||
ret = scoutfs_item_lookup_smaller_zero(sb, key, sinode, sizeof(struct scoutfs_inode), lock);
|
||||
if (ret >= 0 && !scoutfs_inode_valid_vers_bytes(sbi->fmt_vers, ret))
|
||||
return -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh the vfs inode fields if the lock indicates that the current
|
||||
* contents could be stale.
|
||||
@@ -352,12 +334,12 @@ 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 = lookup_inode_item(sb, &key, &sinode, lock);
|
||||
if (ret > 0) {
|
||||
load_inode(inode, &sinode, ret);
|
||||
ret = scoutfs_item_lookup_exact(sb, &key, &sinode,
|
||||
sizeof(sinode), lock);
|
||||
if (ret == 0) {
|
||||
load_inode(inode, &sinode);
|
||||
atomic64_set(&si->last_refreshed, refresh_gen);
|
||||
scoutfs_lock_add_coverage(sb, lock, &si->ino_lock_cov);
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
@@ -505,10 +487,6 @@ retry:
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
attr_size = (attr->ia_valid & ATTR_SIZE) ? attr->ia_size :
|
||||
i_size_read(inode);
|
||||
|
||||
@@ -589,9 +567,11 @@ static void set_trans_seq(struct inode *inode, u64 *seq)
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
|
||||
if (*seq != sbi->trans_seq) {
|
||||
write_seqlock(&si->seqlock);
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
*seq = sbi->trans_seq;
|
||||
write_sequnlock(&si->seqlock);
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,18 +593,22 @@ void scoutfs_inode_inc_data_version(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
write_seqlock(&si->seqlock);
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
si->data_version++;
|
||||
write_sequnlock(&si->seqlock);
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void scoutfs_inode_set_data_version(struct inode *inode, u64 data_version)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
write_seqlock(&si->seqlock);
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
si->data_version = data_version;
|
||||
write_sequnlock(&si->seqlock);
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off)
|
||||
@@ -633,7 +617,8 @@ void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off)
|
||||
|
||||
if (inode && (on || off)) {
|
||||
si = SCOUTFS_I(inode);
|
||||
write_seqlock(&si->seqlock);
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
|
||||
/* inode and extents out of sync, bad callers */
|
||||
if (((s64)si->online_blocks + on < 0) ||
|
||||
@@ -654,7 +639,8 @@ void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off)
|
||||
si->online_blocks,
|
||||
si->offline_blocks);
|
||||
|
||||
write_sequnlock(&si->seqlock);
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/* any time offline extents decreased we try and wake waiters */
|
||||
@@ -662,16 +648,16 @@ void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off)
|
||||
scoutfs_data_wait_changed(inode);
|
||||
}
|
||||
|
||||
static u64 read_seqlock_u64(struct inode *inode, u64 *val)
|
||||
static u64 read_seqcount_u64(struct inode *inode, u64 *val)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
unsigned seq;
|
||||
unsigned int seq;
|
||||
u64 v;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&si->seqlock);
|
||||
seq = read_seqcount_begin(&si->seqcount);
|
||||
v = *val;
|
||||
} while (read_seqretry(&si->seqlock, seq));
|
||||
} while (read_seqcount_retry(&si->seqcount, seq));
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -680,71 +666,45 @@ u64 scoutfs_inode_meta_seq(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
return read_seqlock_u64(inode, &si->meta_seq);
|
||||
return read_seqcount_u64(inode, &si->meta_seq);
|
||||
}
|
||||
|
||||
u64 scoutfs_inode_data_seq(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
return read_seqlock_u64(inode, &si->data_seq);
|
||||
return read_seqcount_u64(inode, &si->data_seq);
|
||||
}
|
||||
|
||||
u64 scoutfs_inode_data_version(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
return read_seqlock_u64(inode, &si->data_version);
|
||||
return read_seqcount_u64(inode, &si->data_version);
|
||||
}
|
||||
|
||||
void scoutfs_inode_get_onoff(struct inode *inode, s64 *on, s64 *off)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
unsigned seq;
|
||||
unsigned int seq;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&si->seqlock);
|
||||
seq = read_seqcount_begin(&si->seqcount);
|
||||
*on = SCOUTFS_I(inode)->online_blocks;
|
||||
*off = SCOUTFS_I(inode)->offline_blocks;
|
||||
} while (read_seqretry(&si->seqlock, seq));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get our private scoutfs inode flags, not the vfs i_flags.
|
||||
*/
|
||||
u32 scoutfs_inode_get_flags(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
unsigned seq;
|
||||
u32 flags;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&si->seqlock);
|
||||
flags = si->flags;
|
||||
} while (read_seqretry(&si->seqlock, seq));
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void scoutfs_inode_set_flags(struct inode *inode, u32 and, u32 or)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
write_seqlock(&si->seqlock);
|
||||
si->flags = (si->flags & and) | or;
|
||||
write_sequnlock(&si->seqlock);
|
||||
} while (read_seqcount_retry(&si->seqcount, seq));
|
||||
}
|
||||
|
||||
u64 scoutfs_inode_get_proj(struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
unsigned seq;
|
||||
unsigned int seq;
|
||||
u64 proj;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&si->seqlock);
|
||||
seq = read_seqcount_begin(&si->seqcount);
|
||||
proj = si->proj;
|
||||
} while (read_seqretry(&si->seqlock, seq));
|
||||
} while (read_seqcount_retry(&si->seqcount, seq));
|
||||
|
||||
return proj;
|
||||
}
|
||||
@@ -753,9 +713,11 @@ void scoutfs_inode_set_proj(struct inode *inode, u64 proj)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
write_seqlock(&si->seqlock);
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
si->proj = proj;
|
||||
write_sequnlock(&si->seqlock);
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static int scoutfs_iget_test(struct inode *inode, void *arg)
|
||||
@@ -867,7 +829,7 @@ out:
|
||||
return inode;
|
||||
}
|
||||
|
||||
static void store_inode(struct scoutfs_inode *cinode, struct inode *inode, int inode_bytes)
|
||||
static void store_inode(struct scoutfs_inode *cinode, struct inode *inode)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
u64 online_blocks;
|
||||
@@ -899,11 +861,11 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode, int i
|
||||
cinode->offline_blocks = cpu_to_le64(offline_blocks);
|
||||
cinode->next_readdir_pos = cpu_to_le64(si->next_readdir_pos);
|
||||
cinode->next_xattr_id = cpu_to_le64(si->next_xattr_id);
|
||||
cinode->proj = cpu_to_le64(si->proj);
|
||||
cinode->flags = cpu_to_le32(si->flags);
|
||||
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));
|
||||
cinode->proj = cpu_to_le64(si->proj);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -927,18 +889,15 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode, int i
|
||||
int scoutfs_dirty_inode_item(struct inode *inode, struct scoutfs_lock *lock)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
struct scoutfs_inode sinode;
|
||||
struct scoutfs_key key;
|
||||
int inode_bytes;
|
||||
int ret;
|
||||
|
||||
inode_bytes = scoutfs_inode_vers_bytes(sbi->fmt_vers);
|
||||
store_inode(&sinode, inode, inode_bytes);
|
||||
store_inode(&sinode, inode);
|
||||
|
||||
scoutfs_inode_init_key(&key, scoutfs_ino(inode));
|
||||
|
||||
ret = scoutfs_item_update(sb, &key, &sinode, inode_bytes, lock);
|
||||
ret = scoutfs_item_update(sb, &key, &sinode, sizeof(sinode), lock);
|
||||
if (!ret)
|
||||
trace_scoutfs_dirty_inode(inode);
|
||||
return ret;
|
||||
@@ -1140,11 +1099,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;
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
const u64 ino = scoutfs_ino(inode);
|
||||
struct scoutfs_inode sinode;
|
||||
struct scoutfs_key key;
|
||||
int inode_bytes;
|
||||
struct scoutfs_inode sinode;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
@@ -1153,17 +1110,15 @@ 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 = scoutfs_inode_vers_bytes(sbi->fmt_vers);
|
||||
|
||||
/* only race with other inode field stores once */
|
||||
store_inode(&sinode, inode, inode_bytes);
|
||||
store_inode(&sinode, inode);
|
||||
|
||||
ret = update_indices(sb, si, ino, inode->i_mode, &sinode, lock_list, lock);
|
||||
BUG_ON(ret);
|
||||
|
||||
scoutfs_inode_init_key(&key, ino);
|
||||
|
||||
err = scoutfs_item_update(sb, &key, &sinode, inode_bytes, lock);
|
||||
err = scoutfs_item_update(sb, &key, &sinode, sizeof(sinode), lock);
|
||||
if (err) {
|
||||
scoutfs_err(sb, "inode %llu update err %d", ino, err);
|
||||
BUG_ON(err);
|
||||
@@ -1531,12 +1486,10 @@ out:
|
||||
int scoutfs_new_inode(struct super_block *sb, struct inode *dir, umode_t mode, dev_t rdev,
|
||||
u64 ino, struct scoutfs_lock *lock, struct inode **inode_ret)
|
||||
{
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
struct scoutfs_inode_info *si;
|
||||
struct scoutfs_inode sinode;
|
||||
struct scoutfs_key key;
|
||||
struct scoutfs_inode sinode;
|
||||
struct inode *inode;
|
||||
int inode_bytes;
|
||||
int ret;
|
||||
|
||||
inode = new_inode(sb);
|
||||
@@ -1568,16 +1521,14 @@ int scoutfs_new_inode(struct super_block *sb, struct inode *dir, umode_t mode, d
|
||||
inode->i_rdev = rdev;
|
||||
set_inode_ops(inode);
|
||||
|
||||
inode_bytes = scoutfs_inode_vers_bytes(sbi->fmt_vers);
|
||||
|
||||
store_inode(&sinode, inode, inode_bytes);
|
||||
store_inode(&sinode, inode);
|
||||
scoutfs_inode_init_key(&key, scoutfs_ino(inode));
|
||||
|
||||
ret = scoutfs_omap_set(sb, ino);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_item_create(sb, &key, &sinode, inode_bytes, lock);
|
||||
ret = scoutfs_item_create(sb, &key, &sinode, sizeof(sinode), lock);
|
||||
if (ret < 0)
|
||||
scoutfs_omap_clear(sb, ino);
|
||||
out:
|
||||
@@ -1831,7 +1782,7 @@ static int try_delete_inode_items(struct super_block *sb, u64 ino)
|
||||
}
|
||||
|
||||
scoutfs_inode_init_key(&key, ino);
|
||||
ret = lookup_inode_item(sb, &key, &sinode, lock);
|
||||
ret = scoutfs_item_lookup_exact(sb, &key, &sinode, sizeof(sinode), lock);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT)
|
||||
ret = 0;
|
||||
@@ -2220,17 +2171,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an error if the inode has the retention flag set and can not
|
||||
* be modified. This mimics the errno returned by the vfs whan an
|
||||
* inode's immutable flag is set. The flag won't be set on older format
|
||||
* versions so we don't check the mounted format version here.
|
||||
*/
|
||||
int scoutfs_inode_check_retention(struct inode *inode)
|
||||
{
|
||||
return (scoutfs_inode_get_flags(inode) & SCOUTFS_INO_FLAG_RETENTION) ? -EPERM : 0;
|
||||
}
|
||||
|
||||
int scoutfs_inode_setup(struct super_block *sb)
|
||||
{
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
|
||||
@@ -48,7 +48,7 @@ struct scoutfs_inode_info {
|
||||
atomic64_t last_refreshed;
|
||||
|
||||
/* initialized once for slab object */
|
||||
seqlock_t seqlock;
|
||||
seqcount_t seqcount;
|
||||
bool staging; /* holder of i_mutex is staging */
|
||||
struct scoutfs_per_task pt_data_lock;
|
||||
struct scoutfs_data_waitq data_waitq;
|
||||
@@ -121,15 +121,11 @@ u64 scoutfs_inode_meta_seq(struct inode *inode);
|
||||
u64 scoutfs_inode_data_seq(struct inode *inode);
|
||||
u64 scoutfs_inode_data_version(struct inode *inode);
|
||||
void scoutfs_inode_get_onoff(struct inode *inode, s64 *on, s64 *off);
|
||||
u32 scoutfs_inode_get_flags(struct inode *inode);
|
||||
void scoutfs_inode_set_flags(struct inode *inode, u32 and, u32 or);
|
||||
u64 scoutfs_inode_get_proj(struct inode *inode);
|
||||
void scoutfs_inode_set_proj(struct inode *inode, u64 proj);
|
||||
|
||||
int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock);
|
||||
|
||||
int scoutfs_inode_check_retention(struct inode *inode);
|
||||
|
||||
int scoutfs_inode_refresh(struct inode *inode, struct scoutfs_lock *lock);
|
||||
#ifdef KC_LINUX_HAVE_RHEL_IOPS_WRAPPER
|
||||
int scoutfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
|
||||
269
kmod/src/ioctl.c
269
kmod/src/ioctl.c
@@ -42,7 +42,6 @@
|
||||
#include "alloc.h"
|
||||
#include "server.h"
|
||||
#include "counters.h"
|
||||
#include "attr_x.h"
|
||||
#include "totl.h"
|
||||
#include "wkic.h"
|
||||
#include "quota.h"
|
||||
@@ -549,41 +548,20 @@ out:
|
||||
static long scoutfs_ioc_stat_more(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct scoutfs_ioctl_inode_attr_x *iax = NULL;
|
||||
struct scoutfs_ioctl_stat_more *stm = NULL;
|
||||
int ret;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
struct scoutfs_ioctl_stat_more stm;
|
||||
|
||||
iax = kmalloc(sizeof(struct scoutfs_ioctl_inode_attr_x), GFP_KERNEL);
|
||||
stm = kmalloc(sizeof(struct scoutfs_ioctl_stat_more), GFP_KERNEL);
|
||||
if (!iax || !stm) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
stm.meta_seq = scoutfs_inode_meta_seq(inode);
|
||||
stm.data_seq = scoutfs_inode_data_seq(inode);
|
||||
stm.data_version = scoutfs_inode_data_version(inode);
|
||||
scoutfs_inode_get_onoff(inode, &stm.online_blocks, &stm.offline_blocks);
|
||||
stm.crtime_sec = si->crtime.tv_sec;
|
||||
stm.crtime_nsec = si->crtime.tv_nsec;
|
||||
|
||||
iax->x_mask = SCOUTFS_IOC_IAX_META_SEQ | SCOUTFS_IOC_IAX_DATA_SEQ |
|
||||
SCOUTFS_IOC_IAX_DATA_VERSION | SCOUTFS_IOC_IAX_ONLINE_BLOCKS |
|
||||
SCOUTFS_IOC_IAX_OFFLINE_BLOCKS | SCOUTFS_IOC_IAX_CRTIME;
|
||||
iax->x_flags = 0;
|
||||
ret = scoutfs_get_attr_x(inode, iax);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (copy_to_user((void __user *)arg, &stm, sizeof(stm)))
|
||||
return -EFAULT;
|
||||
|
||||
stm->meta_seq = iax->meta_seq;
|
||||
stm->data_seq = iax->data_seq;
|
||||
stm->data_version = iax->data_version;
|
||||
stm->online_blocks = iax->online_blocks;
|
||||
stm->offline_blocks = iax->offline_blocks;
|
||||
stm->crtime_sec = iax->crtime_sec;
|
||||
stm->crtime_nsec = iax->crtime_nsec;
|
||||
|
||||
if (copy_to_user((void __user *)arg, stm, sizeof(struct scoutfs_ioctl_stat_more)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
out:
|
||||
kfree(iax);
|
||||
kfree(stm);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool inc_wrapped(u64 *ino, u64 *iblock)
|
||||
@@ -640,19 +618,24 @@ static long scoutfs_ioc_data_waiting(struct file *file, unsigned long arg)
|
||||
* This is used when restoring files, it lets the caller set all the
|
||||
* inode attributes which are otherwise unreachable. Changing the file
|
||||
* size can only be done for regular files with a data_version of 0.
|
||||
*
|
||||
* We unconditionally fill the iax attributes from the sm set and let
|
||||
* set_attr_x check them.
|
||||
*/
|
||||
static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode *inode = file->f_inode;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_ioctl_setattr_more __user *usm = (void __user *)arg;
|
||||
struct scoutfs_ioctl_inode_attr_x *iax = NULL;
|
||||
struct scoutfs_ioctl_setattr_more sm;
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
LIST_HEAD(ind_locks);
|
||||
bool set_data_seq;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(file->f_mode & FMODE_WRITE)) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
@@ -663,38 +646,65 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_UNKNOWN) {
|
||||
if ((sm.i_size > 0 && sm.data_version == 0) ||
|
||||
((sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) && !sm.i_size) ||
|
||||
(sm.flags & SCOUTFS_IOC_SETATTR_MORE_UNKNOWN)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
iax = kzalloc(sizeof(struct scoutfs_ioctl_inode_attr_x), GFP_KERNEL);
|
||||
if (!iax) {
|
||||
ret = -ENOMEM;
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE,
|
||||
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret)
|
||||
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) ||
|
||||
scoutfs_inode_data_version(inode) != 0))) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
iax->x_mask = SCOUTFS_IOC_IAX_DATA_VERSION | SCOUTFS_IOC_IAX_CTIME |
|
||||
SCOUTFS_IOC_IAX_CRTIME | SCOUTFS_IOC_IAX_SIZE;
|
||||
iax->data_version = sm.data_version;
|
||||
iax->ctime_sec = sm.ctime_sec;
|
||||
iax->ctime_nsec = sm.ctime_nsec;
|
||||
iax->crtime_sec = sm.crtime_sec;
|
||||
iax->crtime_nsec = sm.crtime_nsec;
|
||||
iax->size = sm.i_size;
|
||||
/* create offline extents in potentially many transactions */
|
||||
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) {
|
||||
ret = scoutfs_data_init_offline_extent(inode, sm.i_size, lock);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE)
|
||||
iax->x_flags |= SCOUTFS_IOC_IAX_F_SIZE_OFFLINE;
|
||||
/* setting only so we don't see 0 data seq with nonzero data_version */
|
||||
set_data_seq = sm.data_version != 0 ? true : false;
|
||||
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, set_data_seq, false);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (sm.data_version)
|
||||
scoutfs_inode_set_data_version(inode, sm.data_version);
|
||||
if (sm.i_size)
|
||||
i_size_write(inode, sm.i_size);
|
||||
inode->i_ctime.tv_sec = sm.ctime_sec;
|
||||
inode->i_ctime.tv_nsec = sm.ctime_nsec;
|
||||
si->crtime.tv_sec = sm.crtime_sec;
|
||||
si->crtime.tv_nsec = sm.crtime_nsec;
|
||||
|
||||
ret = scoutfs_set_attr_x(inode, iax);
|
||||
scoutfs_update_inode_item(inode, lock, &ind_locks);
|
||||
ret = 0;
|
||||
|
||||
scoutfs_release_trans(sb);
|
||||
unlock:
|
||||
scoutfs_inode_index_unlock(sb, &ind_locks);
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
inode_unlock(inode);
|
||||
mnt_drop_write_file(file);
|
||||
out:
|
||||
kfree(iax);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1326,65 +1336,75 @@ out:
|
||||
return nr ?: ret;
|
||||
}
|
||||
|
||||
static long scoutfs_ioc_get_attr_x(struct file *file, unsigned long arg)
|
||||
static long scoutfs_ioc_get_project_id(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct scoutfs_ioctl_inode_attr_x __user *uiax = (void __user *)arg;
|
||||
struct scoutfs_ioctl_inode_attr_x *iax = NULL;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
u64 __user *uproj = (void __user *)arg;
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
u64 proj;
|
||||
int ret;
|
||||
|
||||
iax = kmalloc(sizeof(struct scoutfs_ioctl_inode_attr_x), GFP_KERNEL);
|
||||
if (!iax) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
if (!capable(CAP_DAC_READ_SEARCH))
|
||||
return -EPERM;
|
||||
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ,
|
||||
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret == 0) {
|
||||
proj = scoutfs_inode_get_proj(inode);
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ);
|
||||
}
|
||||
|
||||
ret = get_user(iax->x_mask, &uiax->x_mask) ?:
|
||||
get_user(iax->x_flags, &uiax->x_flags);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = scoutfs_get_attr_x(inode, iax);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* only copy results after dropping cluster locks (could fault) */
|
||||
if (ret > 0 && copy_to_user(uiax, iax, ret) != 0)
|
||||
if (ret == 0 && __put_user(proj, uproj))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
out:
|
||||
kfree(iax);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long scoutfs_ioc_set_attr_x(struct file *file, unsigned long arg)
|
||||
static long scoutfs_ioc_set_project_id(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct scoutfs_ioctl_inode_attr_x __user *uiax = (void __user *)arg;
|
||||
struct scoutfs_ioctl_inode_attr_x *iax = NULL;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
u64 __user *uproj = (void __user *)arg;
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
LIST_HEAD(ind_locks);
|
||||
u64 proj;
|
||||
int ret;
|
||||
|
||||
iax = kmalloc(sizeof(struct scoutfs_ioctl_inode_attr_x), GFP_KERNEL);
|
||||
if (!iax) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(iax, uiax, sizeof(struct scoutfs_ioctl_inode_attr_x))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (get_user(proj, uproj))
|
||||
return -EFAULT;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE,
|
||||
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = scoutfs_set_attr_x(inode, iax);
|
||||
if (scoutfs_inode_get_proj(inode) == proj) {
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, false, false);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = scoutfs_dirty_inode_item(inode, lock);
|
||||
if (ret < 0)
|
||||
goto out_release;
|
||||
|
||||
scoutfs_inode_set_proj(inode, proj);
|
||||
scoutfs_update_inode_item(inode, lock, &ind_locks);
|
||||
|
||||
ret = 0;
|
||||
out_release:
|
||||
scoutfs_release_trans(sb);
|
||||
scoutfs_inode_index_unlock(sb, &ind_locks);
|
||||
out_unlock:
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
|
||||
mnt_drop_write_file(file);
|
||||
out:
|
||||
kfree(iax);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1471,21 +1491,17 @@ struct read_index_buf {
|
||||
((PAGE_SIZE - sizeof(struct read_index_buf)) / \
|
||||
sizeof(struct scoutfs_ioctl_xattr_index_entry))
|
||||
|
||||
/*
|
||||
* This doesn't filter out duplicates, the caller filters them out to
|
||||
* catch duplicates between iteration calls.
|
||||
*/
|
||||
static int read_index_cb(struct scoutfs_key *key, void *val, unsigned int val_len, void *cb_arg)
|
||||
{
|
||||
struct read_index_buf *rib = cb_arg;
|
||||
struct scoutfs_ioctl_xattr_index_entry *ent = &rib->ents[rib->nr];
|
||||
u64 xid;
|
||||
|
||||
if (val_len != 0)
|
||||
return -EIO;
|
||||
|
||||
/* discard the xid, they're not exposed to ioctl callers */
|
||||
scoutfs_xattr_get_indx_key(key, &ent->major, &ent->minor, &ent->ino, &xid);
|
||||
ent->a = le64_to_cpu(key->skxi_a);
|
||||
ent->b = le64_to_cpu(key->skxi_b);
|
||||
ent->ino = le64_to_cpu(key->skxi_ino);
|
||||
|
||||
if (++rib->nr == rib->size)
|
||||
return rib->nr;
|
||||
@@ -1499,7 +1515,6 @@ static long scoutfs_ioc_read_xattr_index(struct file *file, unsigned long arg)
|
||||
struct scoutfs_ioctl_read_xattr_index __user *urxi = (void __user *)arg;
|
||||
struct scoutfs_ioctl_xattr_index_entry __user *uents;
|
||||
struct scoutfs_ioctl_xattr_index_entry *ent;
|
||||
struct scoutfs_ioctl_xattr_index_entry prev;
|
||||
struct scoutfs_ioctl_read_xattr_index rxi;
|
||||
struct read_index_buf *rib;
|
||||
struct page *page = NULL;
|
||||
@@ -1509,7 +1524,6 @@ static long scoutfs_ioc_read_xattr_index(struct file *file, unsigned long arg)
|
||||
struct scoutfs_key end;
|
||||
int copied = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EPERM;
|
||||
@@ -1530,8 +1544,8 @@ static long scoutfs_ioc_read_xattr_index(struct file *file, unsigned long arg)
|
||||
}
|
||||
rib = page_address(page);
|
||||
|
||||
scoutfs_xattr_init_indx_key(&first, rxi.first.major, rxi.first.minor, rxi.first.ino, 0);
|
||||
scoutfs_xattr_init_indx_key(&last, rxi.last.major, rxi.last.minor, rxi.last.ino, U64_MAX);
|
||||
scoutfs_xattr_init_indx_key(&first, rxi.first.a, rxi.first.b, rxi.first.ino);
|
||||
scoutfs_xattr_init_indx_key(&last, rxi.last.a, rxi.last.b, rxi.last.ino);
|
||||
scoutfs_xattr_indx_get_range(&start, &end);
|
||||
|
||||
if (scoutfs_key_compare(&first, &last) > 0) {
|
||||
@@ -1539,9 +1553,6 @@ static long scoutfs_ioc_read_xattr_index(struct file *file, unsigned long arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 0 ino doesn't exist, can't ever match entry to return */
|
||||
memset(&prev, 0, sizeof(prev));
|
||||
|
||||
while (copied < rxi.entries_nr) {
|
||||
rib->nr = 0;
|
||||
rib->size = min_t(u64, rxi.entries_nr - copied, READ_INDEX_BUF_MAX_ENTS);
|
||||
@@ -1552,32 +1563,20 @@ static long scoutfs_ioc_read_xattr_index(struct file *file, unsigned long arg)
|
||||
if (rib->nr == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Copy entries to userspace, skipping duplicate entries
|
||||
* that can result from multiple xattrs indexing an
|
||||
* inode at the same position and which can span
|
||||
* multiple cache iterations. (Comparing in order of
|
||||
* most likely to change to fail fast.)
|
||||
*/
|
||||
for (i = 0, ent = rib->ents; i < rib->nr; i++, ent++) {
|
||||
if (ent->ino == prev.ino && ent->minor == prev.minor &&
|
||||
ent->major == prev.major)
|
||||
continue;
|
||||
|
||||
if (copy_to_user(&uents[copied], ent, sizeof(*ent))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev = *ent;
|
||||
copied++;
|
||||
if (copy_to_user(&uents[copied], rib->ents, rib->nr * sizeof(rib->ents[0]))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
scoutfs_xattr_init_indx_key(&first, prev.major, prev.minor, prev.ino, U64_MAX);
|
||||
copied += rib->nr;
|
||||
|
||||
ent = &rib->ents[rib->nr - 1];
|
||||
scoutfs_xattr_init_indx_key(&first, ent->a, ent->b, ent->ino);
|
||||
scoutfs_key_inc(&first);
|
||||
}
|
||||
|
||||
ret = copied;
|
||||
|
||||
out:
|
||||
if (page)
|
||||
__free_page(page);
|
||||
@@ -1622,10 +1621,10 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return scoutfs_ioc_get_allocated_inos(file, arg);
|
||||
case SCOUTFS_IOC_GET_REFERRING_ENTRIES:
|
||||
return scoutfs_ioc_get_referring_entries(file, arg);
|
||||
case SCOUTFS_IOC_GET_ATTR_X:
|
||||
return scoutfs_ioc_get_attr_x(file, arg);
|
||||
case SCOUTFS_IOC_SET_ATTR_X:
|
||||
return scoutfs_ioc_set_attr_x(file, arg);
|
||||
case SCOUTFS_IOC_GET_PROJECT_ID:
|
||||
return scoutfs_ioc_get_project_id(file, arg);
|
||||
case SCOUTFS_IOC_SET_PROJECT_ID:
|
||||
return scoutfs_ioc_set_project_id(file, arg);
|
||||
case SCOUTFS_IOC_GET_QUOTA_RULES:
|
||||
return scoutfs_ioc_get_quota_rules(file, arg);
|
||||
case SCOUTFS_IOC_ADD_QUOTA_RULE:
|
||||
|
||||
@@ -673,82 +673,10 @@ struct scoutfs_ioctl_dirent {
|
||||
#define SCOUTFS_IOC_GET_REFERRING_ENTRIES \
|
||||
_IOW(SCOUTFS_IOCTL_MAGIC, 17, struct scoutfs_ioctl_get_referring_entries)
|
||||
|
||||
struct scoutfs_ioctl_inode_attr_x {
|
||||
__u64 x_mask;
|
||||
__u64 x_flags;
|
||||
__u64 meta_seq;
|
||||
__u64 data_seq;
|
||||
__u64 data_version;
|
||||
__u64 online_blocks;
|
||||
__u64 offline_blocks;
|
||||
__u64 ctime_sec;
|
||||
__u32 ctime_nsec;
|
||||
__u32 crtime_nsec;
|
||||
__u64 crtime_sec;
|
||||
__u64 size;
|
||||
__u64 bits;
|
||||
__u64 project_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Behavioral flags set in the x_flags field. These flags don't
|
||||
* necessarily correspond to specific attributes, but instead change the
|
||||
* behaviour of a _get_ or _set_ operation.
|
||||
*
|
||||
* @SCOUTFS_IOC_IAX_F_SIZE_OFFLINE: When setting i_size, also create
|
||||
* extents which are marked offline for the region of the file from
|
||||
* offset 0 to the new set size. This can only be set when setting the
|
||||
* size and has no effect if setting the size fails.
|
||||
*/
|
||||
#define SCOUTFS_IOC_IAX_F_SIZE_OFFLINE (1ULL << 0)
|
||||
#define SCOUTFS_IOC_IAX_F__UNKNOWN (U64_MAX << 1)
|
||||
|
||||
/*
|
||||
* Single-bit values stored in the @bits field. These indicate whether
|
||||
* the bit is set, or not. The main _IAX_ bits set in the mask indicate
|
||||
* whether this value bit is populated by _get or stored by _set.
|
||||
*/
|
||||
#define SCOUTFS_IOC_IAX_B_RETENTION (1ULL << 0)
|
||||
|
||||
/*
|
||||
* x_mask bits which indicate which attributes of the inode to populate
|
||||
* on return for _get or to set on the inode for _set. Each mask bit
|
||||
* corresponds to the matching named field in the attr_x struct passed
|
||||
* to the _get_ and _set_ calls.
|
||||
*
|
||||
* Each field can have different permissions or other attribute
|
||||
* requirements which can cause calls to fail. If _set_ fails then no
|
||||
* other attribute changes will have been made by the same call.
|
||||
*
|
||||
* @SCOUTFS_IOC_IAX_RETENTION: Mark a file for retention. When marked,
|
||||
* no modification can be made to the file other than changing extended
|
||||
* attributes outside the "user." prefix and clearing the retention
|
||||
* mark. This can only be set on regular files and requires root (the
|
||||
* CAP_SYS_ADMIN capability). Other attributes can be set with a
|
||||
* set_attr_x call on a retention inode as long as that call also
|
||||
* successfully clears the retention mark.
|
||||
*/
|
||||
#define SCOUTFS_IOC_IAX_META_SEQ (1ULL << 0)
|
||||
#define SCOUTFS_IOC_IAX_DATA_SEQ (1ULL << 1)
|
||||
#define SCOUTFS_IOC_IAX_DATA_VERSION (1ULL << 2)
|
||||
#define SCOUTFS_IOC_IAX_ONLINE_BLOCKS (1ULL << 3)
|
||||
#define SCOUTFS_IOC_IAX_OFFLINE_BLOCKS (1ULL << 4)
|
||||
#define SCOUTFS_IOC_IAX_CTIME (1ULL << 5)
|
||||
#define SCOUTFS_IOC_IAX_CRTIME (1ULL << 6)
|
||||
#define SCOUTFS_IOC_IAX_SIZE (1ULL << 7)
|
||||
#define SCOUTFS_IOC_IAX_RETENTION (1ULL << 8)
|
||||
#define SCOUTFS_IOC_IAX_PROJECT_ID (1ULL << 9)
|
||||
|
||||
/* single bit attributes that are packed in the bits field as _B_ */
|
||||
#define SCOUTFS_IOC_IAX__BITS (SCOUTFS_IOC_IAX_RETENTION)
|
||||
/* inverse of all the bits we understand */
|
||||
#define SCOUTFS_IOC_IAX__UNKNOWN (U64_MAX << 10)
|
||||
|
||||
#define SCOUTFS_IOC_GET_ATTR_X \
|
||||
_IOW(SCOUTFS_IOCTL_MAGIC, 18, struct scoutfs_ioctl_inode_attr_x)
|
||||
|
||||
#define SCOUTFS_IOC_SET_ATTR_X \
|
||||
_IOW(SCOUTFS_IOCTL_MAGIC, 19, struct scoutfs_ioctl_inode_attr_x)
|
||||
#define SCOUTFS_IOC_GET_PROJECT_ID \
|
||||
_IOR(SCOUTFS_IOCTL_MAGIC, 18, __u64)
|
||||
#define SCOUTFS_IOC_SET_PROJECT_ID \
|
||||
_IOW(SCOUTFS_IOCTL_MAGIC, 19, __u64)
|
||||
|
||||
/*
|
||||
* (These fields are documented in the order that they're displayed by
|
||||
@@ -821,15 +749,13 @@ struct scoutfs_ioctl_get_quota_rules {
|
||||
|
||||
/*
|
||||
* Inodes can be indexed in a global key space at a position determined
|
||||
* by a .indx. tagged xattr. The xattr name specifies the two index
|
||||
* position values, with major having the more significant comparison
|
||||
* order.
|
||||
* by a single scoutfs.hide.indx xattr per inode. The xattr sets the
|
||||
* two index position values, with a being higher significance.
|
||||
*/
|
||||
struct scoutfs_ioctl_xattr_index_entry {
|
||||
__u64 minor;
|
||||
__u64 a;
|
||||
__u64 b;
|
||||
__u64 ino;
|
||||
__u8 major;
|
||||
__u8 _pad[7];
|
||||
};
|
||||
|
||||
struct scoutfs_ioctl_read_xattr_index {
|
||||
|
||||
@@ -1720,8 +1720,8 @@ static int copy_val(void *dst, int dst_len, void *src, int src_len)
|
||||
* The amount of bytes copied is returned which can be 0 or truncated if
|
||||
* the caller's buffer isn't big enough.
|
||||
*/
|
||||
static int item_lookup(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, int len_limit, struct scoutfs_lock *lock)
|
||||
int scoutfs_item_lookup(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, struct scoutfs_lock *lock)
|
||||
{
|
||||
DECLARE_ITEM_CACHE_INFO(sb, cinf);
|
||||
struct cached_item *item;
|
||||
@@ -1741,8 +1741,6 @@ static int item_lookup(struct super_block *sb, struct scoutfs_key *key,
|
||||
item = item_rbtree_walk(&pg->item_root, key, NULL, NULL, NULL);
|
||||
if (!item || item->deletion)
|
||||
ret = -ENOENT;
|
||||
else if (len_limit > 0 && item->val_len > len_limit)
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = copy_val(val, val_len, item->val, item->val_len);
|
||||
|
||||
@@ -1751,38 +1749,13 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scoutfs_item_lookup(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, struct scoutfs_lock *lock)
|
||||
{
|
||||
return item_lookup(sb, key, val, val_len, 0, lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy an item's value into the caller's buffer. If the item's value
|
||||
* is larger than the caller's buffer then -EIO is returned. If the
|
||||
* item is smaller then the bytes from the end of the copied value to
|
||||
* the end of the buffer are zeroed. The number of value bytes copied
|
||||
* is returned, and 0 can be returned for an item with no value.
|
||||
*/
|
||||
int scoutfs_item_lookup_smaller_zero(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, struct scoutfs_lock *lock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = item_lookup(sb, key, val, val_len, val_len, lock);
|
||||
if (ret >= 0 && ret < val_len)
|
||||
memset(val + ret, 0, val_len - ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scoutfs_item_lookup_exact(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len,
|
||||
struct scoutfs_lock *lock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = item_lookup(sb, key, val, val_len, 0, lock);
|
||||
ret = scoutfs_item_lookup(sb, key, val, val_len, lock);
|
||||
if (ret == val_len)
|
||||
ret = 0;
|
||||
else if (ret >= 0)
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
int scoutfs_item_lookup(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, struct scoutfs_lock *lock);
|
||||
int scoutfs_item_lookup_smaller_zero(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len, struct scoutfs_lock *lock);
|
||||
int scoutfs_item_lookup_exact(struct super_block *sb, struct scoutfs_key *key,
|
||||
void *val, int val_len,
|
||||
struct scoutfs_lock *lock);
|
||||
|
||||
@@ -90,11 +90,6 @@ struct squota_info {
|
||||
#define DECLARE_QUOTA_INFO(sb, name) \
|
||||
struct squota_info *name = SCOUTFS_SB(sb)->squota_info
|
||||
|
||||
static inline int quota_unsupported(struct super_block *sb)
|
||||
{
|
||||
return scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_QUOTA);
|
||||
}
|
||||
|
||||
struct squota_check {
|
||||
struct rcu_head rcu;
|
||||
struct rhash_head head;
|
||||
@@ -104,9 +99,9 @@ struct squota_check {
|
||||
};
|
||||
|
||||
static const struct rhashtable_params check_ht_params = {
|
||||
.key_len = member_sizeof(struct squota_check, inp),
|
||||
.key_offset = offsetof(struct squota_check, inp),
|
||||
.head_offset = offsetof(struct squota_check, head),
|
||||
.key_len = member_sizeof(struct squota_check, inp),
|
||||
.key_offset = offsetof(struct squota_check, inp),
|
||||
.head_offset = offsetof(struct squota_check, head),
|
||||
};
|
||||
|
||||
static bool get_cached_check(struct squota_info *qtinf, struct squota_input *inp, int *result)
|
||||
@@ -252,7 +247,7 @@ static unsigned long scan_cached_checks(struct shrinker *shrink, struct shrink_c
|
||||
nr--;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
rcu_read_unlock();
|
||||
|
||||
if (retries == 0 && freed == 0)
|
||||
freed = SHRINK_STOP;
|
||||
@@ -928,9 +923,6 @@ int scoutfs_quota_check_inode(struct super_block *sb, struct inode *dir)
|
||||
{
|
||||
struct squota_input inp;
|
||||
|
||||
if (quota_unsupported(sb))
|
||||
return 0;
|
||||
|
||||
BUILD_BUG_ON(max(sizeof(uid_t), sizeof(gid_t)) > sizeof(u32));
|
||||
|
||||
init_inp(&inp, scoutfs_inode_get_proj(dir), from_kuid(&init_user_ns, current_fsuid()),
|
||||
@@ -945,9 +937,6 @@ int scoutfs_quota_check_data(struct super_block *sb, struct inode *inode)
|
||||
{
|
||||
struct squota_input inp;
|
||||
|
||||
if (quota_unsupported(sb))
|
||||
return 0;
|
||||
|
||||
init_inp(&inp, scoutfs_inode_get_proj(inode), i_uid_read(inode), i_gid_read(inode),
|
||||
SQ_OP_DATA);
|
||||
|
||||
@@ -971,9 +960,6 @@ int scoutfs_quota_get_rules(struct super_block *sb, u64 *iterator,
|
||||
int copied = 0;
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = quota_unsupported(sb)))
|
||||
return ret;
|
||||
|
||||
if (nr == 0)
|
||||
goto out;
|
||||
|
||||
@@ -1070,9 +1056,6 @@ int scoutfs_quota_mod_rule(struct super_block *sb, bool is_add,
|
||||
struct scoutfs_key key;
|
||||
int ret;
|
||||
|
||||
if ((ret = quota_unsupported(sb)))
|
||||
return ret;
|
||||
|
||||
ret = irule_to_rule(&rule, irule);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -1142,9 +1125,6 @@ void scoutfs_quota_invalidate(struct super_block *sb)
|
||||
DECLARE_QUOTA_INFO(sb, qtinf);
|
||||
struct squota_ruleset *rs;
|
||||
|
||||
if (quota_unsupported(sb))
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
spin_lock(&qtinf->lock);
|
||||
@@ -1190,9 +1170,6 @@ int scoutfs_quota_setup(struct super_block *sb)
|
||||
struct squota_info *qtinf = NULL;
|
||||
int ret;
|
||||
|
||||
if (quota_unsupported(sb))
|
||||
return 0;
|
||||
|
||||
qtinf = kzalloc(sizeof(struct squota_info), GFP_KERNEL);
|
||||
if (!qtinf) {
|
||||
ret = -ENOMEM;
|
||||
@@ -1232,9 +1209,9 @@ out:
|
||||
|
||||
static void free_cached_check(void *ptr, void *arg)
|
||||
{
|
||||
struct squota_check *chk = ptr;
|
||||
struct squota_check *chk = ptr;
|
||||
|
||||
kfree(chk);
|
||||
kfree(chk);
|
||||
}
|
||||
|
||||
void scoutfs_quota_destroy(struct super_block *sb)
|
||||
|
||||
@@ -2399,44 +2399,6 @@ TRACE_EVENT(scoutfs_block_dirty_ref,
|
||||
__entry->block_blkno, __entry->block_seq)
|
||||
);
|
||||
|
||||
TRACE_EVENT(scoutfs_block_stale,
|
||||
TP_PROTO(struct super_block *sb, struct scoutfs_block_ref *ref,
|
||||
struct scoutfs_block_header *hdr, u32 magic, u32 crc),
|
||||
|
||||
TP_ARGS(sb, ref, hdr, magic, crc),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
__field(__u64, ref_blkno)
|
||||
__field(__u64, ref_seq)
|
||||
__field(__u32, hdr_crc)
|
||||
__field(__u32, hdr_magic)
|
||||
__field(__u64, hdr_fsid)
|
||||
__field(__u64, hdr_seq)
|
||||
__field(__u64, hdr_blkno)
|
||||
__field(__u32, magic)
|
||||
__field(__u32, crc)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
SCSB_TRACE_ASSIGN(sb);
|
||||
__entry->ref_blkno = le64_to_cpu(ref->blkno);
|
||||
__entry->ref_seq = le64_to_cpu(ref->seq);
|
||||
__entry->hdr_crc = le32_to_cpu(hdr->crc);
|
||||
__entry->hdr_magic = le32_to_cpu(hdr->magic);
|
||||
__entry->hdr_fsid = le64_to_cpu(hdr->fsid);
|
||||
__entry->hdr_seq = le64_to_cpu(hdr->seq);
|
||||
__entry->hdr_blkno = le64_to_cpu(hdr->blkno);
|
||||
__entry->magic = magic;
|
||||
__entry->crc = crc;
|
||||
),
|
||||
|
||||
TP_printk(SCSBF" ref_blkno %llu ref_seq %016llx hdr_crc %08x hdr_magic %08x hdr_fsid %016llx hdr_seq %016llx hdr_blkno %llu magic %08x crc %08x",
|
||||
SCSB_TRACE_ARGS, __entry->ref_blkno, __entry->ref_seq, __entry->hdr_crc,
|
||||
__entry->hdr_magic, __entry->hdr_fsid, __entry->hdr_seq, __entry->hdr_blkno,
|
||||
__entry->magic, __entry->crc)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(scoutfs_block_class,
|
||||
TP_PROTO(struct super_block *sb, void *bp, u64 blkno, int refcount, int io_count,
|
||||
unsigned long bits, __u64 accessed),
|
||||
|
||||
@@ -330,7 +330,7 @@ static int scoutfs_read_super_from_bdev(struct super_block *sb,
|
||||
|
||||
if (le64_to_cpu(super->fmt_vers) < SCOUTFS_FORMAT_VERSION_MIN ||
|
||||
le64_to_cpu(super->fmt_vers) > SCOUTFS_FORMAT_VERSION_MAX) {
|
||||
scoutfs_err(sb, "super block has format version %llu outside of supported version range %u-%u",
|
||||
scoutfs_err(sb, "super block has format version %llu outside of supported version range %llu-%llu",
|
||||
le64_to_cpu(super->fmt_vers), SCOUTFS_FORMAT_VERSION_MIN,
|
||||
SCOUTFS_FORMAT_VERSION_MAX);
|
||||
ret = -EINVAL;
|
||||
@@ -669,6 +669,10 @@ static int __init scoutfs_module_init(void)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (SCOUTFS_FORMAT_VERSION_MIN & SCOUTFS_FORMAT_VER_PREREL) {
|
||||
printk(KERN_INFO "scoutfs module using incompatible pre-release format version 0x%016llx. This module can only mount volumes with this version, and volumes with this version will be incompatible with all other release builds.", SCOUTFS_FORMAT_VERSION_MIN);
|
||||
}
|
||||
|
||||
scoutfs_debugfs_root = debugfs_create_dir("scoutfs", NULL);
|
||||
if (!scoutfs_debugfs_root) {
|
||||
ret = -ENOMEM;
|
||||
|
||||
@@ -160,17 +160,4 @@ int scoutfs_write_super(struct super_block *sb,
|
||||
/* to keep this out of the ioctl.h public interface definition */
|
||||
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
/*
|
||||
* Returns 0 when supported, non-zero -errno when unsupported.
|
||||
*/
|
||||
static inline int scoutfs_fmt_vers_unsupported(struct super_block *sb, u64 vers)
|
||||
{
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
|
||||
if (sbi && (sbi->fmt_vers < vers))
|
||||
return -EOPNOTSUPP;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -81,18 +81,6 @@ static void init_xattr_key(struct scoutfs_key *key, u64 ino, u32 name_hash,
|
||||
#define SCOUTFS_XATTR_PREFIX "scoutfs."
|
||||
#define SCOUTFS_XATTR_PREFIX_LEN (sizeof(SCOUTFS_XATTR_PREFIX) - 1)
|
||||
|
||||
/*
|
||||
* We could have hidden the logic that needs this in a user-prefix
|
||||
* specific .set handler, but I wanted to make sure that we always
|
||||
* applied that logic from any call chains to _xattr_set. The
|
||||
* additional strcmp isn't so expensive given all the rest of the work
|
||||
* we're doing in here.
|
||||
*/
|
||||
static inline bool is_user(const char *name)
|
||||
{
|
||||
return !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
|
||||
}
|
||||
|
||||
#define HIDE_TAG "hide."
|
||||
#define INDX_TAG "indx."
|
||||
#define SRCH_TAG "srch."
|
||||
@@ -641,48 +629,23 @@ void scoutfs_xattr_indx_get_range(struct scoutfs_key *start, struct scoutfs_key
|
||||
end->sk_zone = SCOUTFS_XATTR_INDX_ZONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* .indx. keys are a bit funny because we're iterating over index keys
|
||||
* by major:minor:inode:xattr_id. That doesn't map nicely to the
|
||||
* comparison precedence of the key fields. We have to mess around a
|
||||
* little bit to get the major into the most significant key bits and
|
||||
* the low bits of xattr id into the least significant key bits.
|
||||
*/
|
||||
void scoutfs_xattr_init_indx_key(struct scoutfs_key *key, u8 major, u64 minor, u64 ino, u64 xid)
|
||||
void scoutfs_xattr_init_indx_key(struct scoutfs_key *key, u64 a, u64 b, u64 ino)
|
||||
{
|
||||
scoutfs_key_set_zeros(key);
|
||||
key->sk_zone = SCOUTFS_XATTR_INDX_ZONE;
|
||||
|
||||
key->_sk_first = cpu_to_le64(((u64)major << 56) | (minor >> 8));
|
||||
key->_sk_second = cpu_to_le64((minor << 56) | (ino >> 8));
|
||||
key->_sk_third = cpu_to_le64((ino << 56) | (xid >> 8));
|
||||
key->_sk_fourth = xid & 0xff;
|
||||
}
|
||||
|
||||
void scoutfs_xattr_get_indx_key(struct scoutfs_key *key, u8 *major, u64 *minor, u64 *ino, u64 *xid)
|
||||
{
|
||||
*major = le64_to_cpu(key->_sk_first) >> 56;
|
||||
*minor = (le64_to_cpu(key->_sk_first) << 8) | (le64_to_cpu(key->_sk_second) >> 56);
|
||||
*ino = (le64_to_cpu(key->_sk_second) << 8) | (le64_to_cpu(key->_sk_third) >> 56);
|
||||
*xid = (le64_to_cpu(key->_sk_third) << 8) | key->_sk_fourth;
|
||||
}
|
||||
|
||||
void scoutfs_xattr_set_indx_key_xid(struct scoutfs_key *key, u64 xid)
|
||||
{
|
||||
u8 major;
|
||||
u64 minor;
|
||||
u64 ino;
|
||||
u64 dummy;
|
||||
|
||||
scoutfs_xattr_get_indx_key(key, &major, &minor, &ino, &dummy);
|
||||
scoutfs_xattr_init_indx_key(key, major, minor, ino, xid);
|
||||
key->skxi_a = cpu_to_le64(a);
|
||||
key->skxi_b = cpu_to_le64(b);
|
||||
key->skxi_ino = cpu_to_le64(ino);
|
||||
}
|
||||
|
||||
/*
|
||||
* This initial parsing of the name doesn't yet have access to an xattr
|
||||
* id to put in the key. That's added later as the existing xattr is
|
||||
* found or a new xattr's id is allocated.
|
||||
* indx keys have a restricted name so that there can only be one xattr
|
||||
* that places in inode at a given position. This lets us emit index
|
||||
* items under CW cluster locks without reading to see if they exist or
|
||||
* not.
|
||||
*/
|
||||
#define REQUIRED_INDEX_PREFIX "scoutfs.hide.indx"
|
||||
|
||||
static int parse_indx_key(struct scoutfs_key *key, const char *name, int name_len, u64 ino)
|
||||
{
|
||||
u64 u64s[2];
|
||||
@@ -692,10 +655,10 @@ static int parse_indx_key(struct scoutfs_key *key, const char *name, int name_le
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (u64s[0] > U8_MAX)
|
||||
if (!xattr_names_equal(name, ret, REQUIRED_INDEX_PREFIX, sizeof(REQUIRED_INDEX_PREFIX) - 1))
|
||||
return -EINVAL;
|
||||
|
||||
scoutfs_xattr_init_indx_key(key, u64s[0], u64s[1], ino, 0);
|
||||
scoutfs_xattr_init_indx_key(key, u64s[0], u64s[1], ino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -764,18 +727,9 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_
|
||||
if (tgs->totl && ((ret = parse_totl_key(&tag_key, name, name_len)) != 0))
|
||||
return ret;
|
||||
|
||||
if (tgs->indx &&
|
||||
(ret = scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_INDX_TAG)))
|
||||
return ret;
|
||||
|
||||
if (tgs->indx && ((ret = parse_indx_key(&tag_key, name, name_len, ino)) != 0))
|
||||
return ret;
|
||||
|
||||
/* retention blocks user. xattr modification, all else allowed */
|
||||
ret = scoutfs_inode_check_retention(inode);
|
||||
if (ret < 0 && is_user(name))
|
||||
return ret;
|
||||
|
||||
/* allocate enough to always read an existing xattr's totl */
|
||||
xat_bytes_totl = first_item_bytes(name_len,
|
||||
max_t(size_t, size, SCOUTFS_XATTR_MAX_TOTL_U64));
|
||||
@@ -814,12 +768,6 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_
|
||||
/* found fields in key will also be used */
|
||||
found_parts = ret >= 0 ? xattr_nr_parts(xat) : 0;
|
||||
|
||||
/* use existing xattr's id or allocate new when creating */
|
||||
if (found_parts)
|
||||
id = le64_to_cpu(key.skx_id);
|
||||
else if (value)
|
||||
id = si->next_xattr_id++;
|
||||
|
||||
if (found_parts && tgs->totl) {
|
||||
/* parse old totl value before we clobber xat buf */
|
||||
val_len = ret - offsetof(struct scoutfs_xattr, name[xat->name_len]);
|
||||
@@ -849,6 +797,10 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_
|
||||
|
||||
/* prepare the xattr header, name, and start of value in first item */
|
||||
if (value) {
|
||||
if (found_parts)
|
||||
id = le64_to_cpu(key.skx_id);
|
||||
else
|
||||
id = si->next_xattr_id++;
|
||||
xat->name_len = name_len;
|
||||
xat->val_len = cpu_to_le16(size);
|
||||
memset(xat->__pad, 0, sizeof(xat->__pad));
|
||||
@@ -867,7 +819,6 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_
|
||||
}
|
||||
|
||||
if (tgs->indx) {
|
||||
scoutfs_xattr_set_indx_key_xid(&tag_key, id);
|
||||
if (value)
|
||||
ret = scoutfs_item_create_force(sb, &tag_key, NULL, 0, tag_lock, NULL);
|
||||
else
|
||||
@@ -878,6 +829,8 @@ int scoutfs_xattr_set_locked(struct inode *inode, const char *name, size_t name_
|
||||
}
|
||||
|
||||
if (tgs->srch && !(found_parts && value)) {
|
||||
if (found_parts)
|
||||
id = le64_to_cpu(key.skx_id);
|
||||
hash = scoutfs_hash64(name, name_len);
|
||||
ret = scoutfs_forest_srch_add(sb, hash, ino, id);
|
||||
if (ret < 0)
|
||||
@@ -1267,6 +1220,10 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino,
|
||||
|
||||
if ((tgs.totl || tgs.indx) && locked_zone != tag_key.sk_zone) {
|
||||
if (tag_lock) {
|
||||
if (release) {
|
||||
scoutfs_release_trans(sb);
|
||||
release = false;
|
||||
}
|
||||
scoutfs_unlock(sb, tag_lock, SCOUTFS_LOCK_WRITE_ONLY);
|
||||
tag_lock = NULL;
|
||||
}
|
||||
@@ -1309,7 +1266,7 @@ int scoutfs_xattr_drop(struct super_block *sb, u64 ino,
|
||||
if (tgs.indx) {
|
||||
ret = scoutfs_item_delete_force(sb, &tag_key, tag_lock, NULL);
|
||||
if (ret < 0)
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
|
||||
scoutfs_release_trans(sb);
|
||||
|
||||
@@ -32,8 +32,6 @@ 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);
|
||||
|
||||
void scoutfs_xattr_indx_get_range(struct scoutfs_key *start, struct scoutfs_key *end);
|
||||
void scoutfs_xattr_init_indx_key(struct scoutfs_key *key, u8 major, u64 minor, u64 ino, u64 xid);
|
||||
void scoutfs_xattr_get_indx_key(struct scoutfs_key *key, u8 *major, u64 *minor, u64 *ino, u64 *xid);
|
||||
void scoutfs_xattr_set_indx_key_xid(struct scoutfs_key *key, u64 xid);
|
||||
void scoutfs_xattr_init_indx_key(struct scoutfs_key *key, u64 a, u64 b, u64 ino);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -113,7 +113,6 @@ 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 |
|
||||
|
||||
@@ -147,9 +147,7 @@ t_filter_dmesg()
|
||||
# ignore systemd-journal rotating
|
||||
re="$re|systemd-journald.*"
|
||||
|
||||
# format vers back/compat tries bad mounts
|
||||
re="$re|scoutfs .* error.*outside of supported version.*"
|
||||
re="$re|scoutfs .* error.*could not get .*super.*"
|
||||
re="$re|incompatible pre-release format version"
|
||||
|
||||
egrep -v "($re)" | \
|
||||
ignore_harmless_unwind_kasan_stack_oob
|
||||
|
||||
@@ -29,12 +29,13 @@ t_mount_rid()
|
||||
}
|
||||
|
||||
#
|
||||
# Output the "f.$fsid.r.$rid" identifier string for the given path
|
||||
# in a mounted scoutfs volume.
|
||||
# Output the "f.$fsid.r.$rid" identifier string for the given mount
|
||||
# number, 0 is used by default if none is specified.
|
||||
#
|
||||
t_ident_from_mnt()
|
||||
t_ident()
|
||||
{
|
||||
local mnt="$1"
|
||||
local nr="${1:-0}"
|
||||
local mnt="$(eval echo \$T_M$nr)"
|
||||
local fsid
|
||||
local rid
|
||||
|
||||
@@ -44,38 +45,6 @@ t_ident_from_mnt()
|
||||
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.
|
||||
@@ -84,7 +53,7 @@ t_sysfs_path()
|
||||
{
|
||||
local nr="$1"
|
||||
|
||||
t_sysfs_path_from_ident $(t_ident $nr)
|
||||
echo "/sys/fs/scoutfs/$(t_ident $nr)"
|
||||
}
|
||||
|
||||
#
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
== 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
|
||||
@@ -1,11 +1,9 @@
|
||||
== default new files don't have project
|
||||
0
|
||||
== set new project on files and dirs
|
||||
8675309
|
||||
8675309
|
||||
== non-root can see id
|
||||
8675309
|
||||
== can use IDs around long width limits
|
||||
/mnt/test.0/test/projects/file: 1
|
||||
/mnt/test.0/test/projects/dir: 1
|
||||
== can use interesting IDs
|
||||
2147483647
|
||||
2147483648
|
||||
4294967295
|
||||
@@ -13,12 +11,12 @@
|
||||
9223372036854775808
|
||||
18446744073709551615
|
||||
== created files and dirs inherit project id
|
||||
8675309
|
||||
8675309
|
||||
/mnt/test.0/test/projects/dir/file: 1
|
||||
/mnt/test.0/test/projects/dir/sub: 1
|
||||
== inheritance continues
|
||||
8675309
|
||||
1
|
||||
== clearing project id stops inheritance
|
||||
0
|
||||
0
|
||||
/mnt/test.0/test/projects/dir/another-file: 0
|
||||
/mnt/test.0/test/projects/dir/another-sub: 0
|
||||
== o_tmpfile creations inherit dir
|
||||
8675309
|
||||
1
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
== setting retention on dir fails
|
||||
attr_x ioctl failed on '/mnt/test/test/retention-basic': Invalid argument (22)
|
||||
scoutfs: set-attr-x failed: Invalid argument (22)
|
||||
== set retention
|
||||
== get-attr-x shows retention
|
||||
1
|
||||
== unpriv can't clear retention
|
||||
attr_x ioctl failed on '/mnt/test/test/retention-basic/file-1': Operation not permitted (1)
|
||||
scoutfs: set-attr-x failed: Operation not permitted (1)
|
||||
== can set hidden scoutfs xattr in retention
|
||||
== setting user. xattr fails in retention
|
||||
setfattr: /mnt/test/test/retention-basic/file-1: Operation not permitted
|
||||
== file deletion fails in retention
|
||||
rm: cannot remove '/mnt/test/test/retention-basic/file-1': Operation not permitted
|
||||
== file rename fails in retention
|
||||
mv: cannot move '/mnt/test/test/retention-basic/file-1' to '/mnt/test/test/retention-basic/file-2': Operation not permitted
|
||||
== file write fails in retention
|
||||
date: write error: Operation not permitted
|
||||
== file truncate fails in retention
|
||||
truncate: failed to truncate '/mnt/test/test/retention-basic/file-1' at 0 bytes: Operation not permitted
|
||||
== setattr fails in retention
|
||||
touch: setting times of '/mnt/test/test/retention-basic/file-1': Operation not permitted
|
||||
== clear retention
|
||||
== file write
|
||||
== file rename
|
||||
== setattr
|
||||
== xattr deletion
|
||||
== cleanup
|
||||
@@ -73,7 +73,6 @@ $(basename $0) options:
|
||||
-t | Enabled trace events that match the given glob argument.
|
||||
| Multiple options enable multiple globbed events.
|
||||
-T <nr> | Multiply the original trace buffer size by nr during the run.
|
||||
-V <nr> | Set mkfs device format version.
|
||||
-X | xfstests git repo. Used by tests/xfstests.sh.
|
||||
-x | xfstests git branch to checkout and track.
|
||||
-y | xfstests ./check additional args
|
||||
@@ -177,11 +176,6 @@ while true; do
|
||||
T_TRACE_MULT="$2"
|
||||
shift
|
||||
;;
|
||||
-V)
|
||||
test -n "$2" || die "-V must have a format version argument"
|
||||
T_MKFS_FORMAT_VERSION="-V $2"
|
||||
shift
|
||||
;;
|
||||
-X)
|
||||
test -n "$2" || die "-X requires xfstests git repo dir argument"
|
||||
T_XFSTESTS_REPO="$2"
|
||||
@@ -350,7 +344,7 @@ if [ -n "$T_MKFS" ]; then
|
||||
done
|
||||
|
||||
msg "making new filesystem with $T_QUORUM quorum members"
|
||||
cmd scoutfs mkfs -f $quo $T_DATA_ALLOC_ZONE_BLOCKS $T_MKFS_FORMAT_VERSION \
|
||||
cmd scoutfs mkfs -f $quo $T_DATA_ALLOC_ZONE_BLOCKS \
|
||||
"$T_META_DEVICE" "$T_DATA_DEVICE"
|
||||
fi
|
||||
|
||||
@@ -358,8 +352,7 @@ if [ -n "$T_INSMOD" ]; then
|
||||
msg "removing and reinserting scoutfs module"
|
||||
test -e /sys/module/scoutfs && cmd rmmod scoutfs
|
||||
cmd modprobe libcrc32c
|
||||
T_MODULE="$T_KMOD/src/scoutfs.ko"
|
||||
cmd insmod "$T_MODULE"
|
||||
cmd insmod "$T_KMOD/src/scoutfs.ko"
|
||||
fi
|
||||
|
||||
if [ -n "$T_TRACE_MULT" ]; then
|
||||
|
||||
@@ -14,12 +14,10 @@ offline-extent-waiting.sh
|
||||
move-blocks.sh
|
||||
projects.sh
|
||||
large-fragmented-free.sh
|
||||
format-version-forward-back.sh
|
||||
enospc.sh
|
||||
srch-safe-merge-pos.sh
|
||||
srch-basic-functionality.sh
|
||||
simple-xattr-unit.sh
|
||||
retention-basic.sh
|
||||
totl-xattr-tag.sh
|
||||
quota.sh
|
||||
lock-refleak.sh
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
#
|
||||
# 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]=c3c4b080
|
||||
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" &&
|
||||
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_TMP.log" 2>&1 || 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 files in $vers" >> "$T_TMP.log"
|
||||
t_quiet touch "$SCR/file-"{1,2,3}
|
||||
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 "keep/update/del existing, create new in $vers" >> "$T_TMP.log"
|
||||
t_quiet touch "$SCR/file-2"
|
||||
t_quiet rm -f "$SCR/file-3"
|
||||
t_quiet touch "$SCR/file-4"
|
||||
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 "keep/update/del existing, create new in $MAX" >> "$T_TMP.log"
|
||||
t_quiet touch "$SCR/file-2"
|
||||
t_quiet rm -f "$SCR/file-4"
|
||||
t_quiet touch "$SCR/file-5"
|
||||
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
|
||||
@@ -1,52 +1,40 @@
|
||||
|
||||
# notable id to recognize in output
|
||||
ID=8675309
|
||||
|
||||
echo "== default new files don't have project"
|
||||
touch "$T_D0/file"
|
||||
scoutfs get-attr-x -p "$T_D0/file"
|
||||
scoutfs project-id -g "$T_D0/file"
|
||||
|
||||
echo "== set new project on files and dirs"
|
||||
mkdir "$T_D0/dir"
|
||||
scoutfs set-attr-x -p $ID "$T_D0/file"
|
||||
scoutfs set-attr-x -p $ID "$T_D0/dir"
|
||||
scoutfs get-attr-x -p "$T_D0/file"
|
||||
scoutfs get-attr-x -p "$T_D0/dir"
|
||||
scoutfs project-id -s 1 "$T_D0/file" "$T_D0/dir"
|
||||
scoutfs project-id -g "$T_D0/file" "$T_D0/dir"
|
||||
|
||||
echo "== non-root can see id"
|
||||
chmod 644 "$T_D0/file"
|
||||
setpriv --ruid=12345 --euid=12345 scoutfs get-attr-x -p "$T_D0/file"
|
||||
|
||||
echo "== can use IDs around long width limits"
|
||||
echo "== can use interesting IDs"
|
||||
touch "$T_D0/ids"
|
||||
for id in 0x7FFFFFFF 0x80000000 0xFFFFFFFF \
|
||||
0x7FFFFFFFFFFFFFFF 0x8000000000000000 0xFFFFFFFFFFFFFFFF; do
|
||||
scoutfs set-attr-x -p $id "$T_D0/ids"
|
||||
scoutfs get-attr-x -p "$T_D0/ids"
|
||||
scoutfs project-id -s $id "$T_D0/ids"
|
||||
scoutfs project-id -g "$T_D0/ids"
|
||||
done
|
||||
|
||||
echo "== created files and dirs inherit project id"
|
||||
touch "$T_D0/dir/file"
|
||||
mkdir "$T_D0/dir/sub"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/file"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/sub"
|
||||
scoutfs project-id -g "$T_D0/dir/file" "$T_D0/dir/sub"
|
||||
|
||||
echo "== inheritance continues"
|
||||
mkdir "$T_D0/dir/sub/more"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/sub/more"
|
||||
scoutfs project-id -g "$T_D0/dir/sub/more"
|
||||
|
||||
# .. just inherits 0 :)
|
||||
echo "== clearing project id stops inheritance"
|
||||
scoutfs set-attr-x -p 0 "$T_D0/dir"
|
||||
scoutfs project-id -s 0 "$T_D0/dir"
|
||||
touch "$T_D0/dir/another-file"
|
||||
mkdir "$T_D0/dir/another-sub"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/another-file"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/another-sub"
|
||||
scoutfs project-id -g "$T_D0/dir/another-file" "$T_D0/dir/another-sub"
|
||||
|
||||
echo "== o_tmpfile creations inherit dir"
|
||||
scoutfs set-attr-x -p $ID "$T_D0/dir"
|
||||
scoutfs project-id -s 1 "$T_D0/dir"
|
||||
o_tmpfile_linkat "$T_D0/dir" "$T_D0/dir/tmpfile"
|
||||
scoutfs get-attr-x -p "$T_D0/dir/tmpfile"
|
||||
|
||||
scoutfs project-id -g "$T_D0/dir/tmpfile"
|
||||
|
||||
t_pass
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
t_require_commands scoutfs touch rm setfattr
|
||||
|
||||
touch "$T_D0/file-1"
|
||||
|
||||
echo "== setting retention on dir fails"
|
||||
scoutfs set-attr-x -t 1 "$T_D0" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== set retention"
|
||||
scoutfs set-attr-x -t 1 "$T_D0/file-1"
|
||||
|
||||
echo "== get-attr-x shows retention"
|
||||
scoutfs get-attr-x -t "$T_D0/file-1"
|
||||
|
||||
echo "== unpriv can't clear retention"
|
||||
setpriv --ruid=12345 --euid=12345 scoutfs set-attr-x -t 0 "$T_D0/file-1" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== can set hidden scoutfs xattr in retention"
|
||||
setfattr -n scoutfs.hide.srch.retention_test -v val "$T_D0/file-1"
|
||||
|
||||
echo "== setting user. xattr fails in retention"
|
||||
setfattr -n user.retention_test -v val "$T_D0/file-1" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== file deletion fails in retention"
|
||||
rm -f "$T_D0/file-1" 2>&1 | t_filter_fs
|
||||
|
||||
echo "== file rename fails in retention"
|
||||
mv $T_D0/file-1 $T_D0/file-2 2>&1 | t_filter_fs
|
||||
|
||||
echo "== file write fails in retention"
|
||||
date >> $T_D0/file-1
|
||||
|
||||
echo "== file truncate fails in retention"
|
||||
truncate -s 0 $T_D0/file-1 2>&1 | t_filter_fs
|
||||
|
||||
echo "== setattr fails in retention"
|
||||
touch $T_D0/file-1 2>&1 | t_filter_fs
|
||||
|
||||
echo "== clear retention"
|
||||
scoutfs set-attr-x -t 0 "$T_D0/file-1"
|
||||
|
||||
echo "== file write"
|
||||
date >> $T_D0/file-1
|
||||
|
||||
echo "== file rename"
|
||||
mv $T_D0/file-1 $T_D0/file-2
|
||||
mv $T_D0/file-2 $T_D0/file-1
|
||||
|
||||
echo "== setattr"
|
||||
touch $T_D0/file-1 2>&1 | t_filter_fs
|
||||
|
||||
echo "== xattr deletion"
|
||||
setfattr -x scoutfs.hide.srch.retention_test "$T_D0/file-1"
|
||||
|
||||
echo "== cleanup"
|
||||
rm -f "$T_D0/file-1"
|
||||
|
||||
t_pass
|
||||
@@ -270,21 +270,6 @@ metadata that is bound to a specific volume and should not be
|
||||
transferred with the file by tools that read extended attributes, like
|
||||
.BR tar(1) .
|
||||
.TP
|
||||
.B .indx.
|
||||
Attributes with the .indx. tag dd the inode containing the attribute to
|
||||
a filesystem-wide index. The name of the extended attribute must end
|
||||
with strings representing two values separated by dots. The first value
|
||||
is an unsigned 8bit value and the second is an unsigned 64bit value.
|
||||
These attributes can only be modified with root privileges and the
|
||||
attributes can not have a value.
|
||||
.sp
|
||||
The inodes in the index are stored in increasing sort order of the
|
||||
values, with the first u8 value being most significant. Inodes can be
|
||||
at many positions as tracked by many extended attributes, and their
|
||||
position follows the creation, renaming, or deletion of the attributes.
|
||||
The index can be read with the read-xattr-index command which uses the
|
||||
underlying READ_XATTR_INDEX ioctl.
|
||||
.TP
|
||||
.B .srch.
|
||||
Attributes with the .srch. tag are indexed so that they can be
|
||||
found by the
|
||||
@@ -310,36 +295,6 @@ with the
|
||||
ioctl.
|
||||
.RE
|
||||
|
||||
.SH FILE RETENTION MODE
|
||||
A file can be set to retention mode by setting the
|
||||
.IB RETENTION
|
||||
attribute with the
|
||||
.IB SET_ATTR_X
|
||||
ioctl. This flag can only be set on regular files and requires root
|
||||
permission (the
|
||||
.IB CAP_SYS_ADMIN
|
||||
capability).
|
||||
.sp
|
||||
Once in retention mode all modifications of the file will fail. The
|
||||
only exceptions are that system extended attributes (all those without
|
||||
the "user." prefix) may be modified. The retention bit may be cleared
|
||||
with sufficient priveledges to remove the retention restrictions on
|
||||
other modifications.
|
||||
.RE
|
||||
|
||||
.SH PROJECT IDs
|
||||
All inodes have a project ID attribute that can be set via the
|
||||
SET_ATTR_X ioctl and displayed with the GET_ATTR_X ioctl. Project IDs
|
||||
are an unsigned 64bit value and the value of 0 is reserved to indicate
|
||||
that no project ID is assigned. If a project ID is set on a directory
|
||||
then all inodes created with it as the initial parent inheret that ID,
|
||||
for all file types. This includes files initially unlinked from the
|
||||
namespace when created with O_TMPFILE. Project IDs are only
|
||||
automatically inherited from the parent dir on initial creation.
|
||||
They're not changed as directory entry linkes to the inode are created
|
||||
or renamed.
|
||||
.RE
|
||||
|
||||
.SH FORMAT VERSION
|
||||
The format version defines the layout and use of structures stored on
|
||||
devices and passed over the network. The version is incremented for
|
||||
@@ -418,19 +373,6 @@ The version that a mount is using is shown in the
|
||||
file in the mount's sysfs directory, typically
|
||||
.I /sys/fs/scoutfs/f.FSID.r.RID/
|
||||
.RE
|
||||
.sp
|
||||
The defined format versions are:
|
||||
.RS
|
||||
.TP
|
||||
.sp
|
||||
.B 1
|
||||
Initial format version.
|
||||
.TP
|
||||
.B 2
|
||||
Added retention mode by setting the retention attribute. Added the
|
||||
project ID inode attribute. Added quota rules and enforcement. Added
|
||||
the .indx. extended attribute tag.
|
||||
.RE
|
||||
|
||||
.SH CORRUPTION DETECTION
|
||||
A
|
||||
|
||||
@@ -209,16 +209,6 @@ A path within a ScoutFS filesystem.
|
||||
.RE
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "get-attr-x FILE"
|
||||
.sp
|
||||
Display ScoutFS-specific attributes from a file. If no options are
|
||||
given than all the attributes that the command supports will be
|
||||
displayed. If attributes are specified with options then only those
|
||||
attributes are displayed. If only one attribute is specified then it
|
||||
will not have a label prefix in the display output. The --help option
|
||||
will list the attributes that the command supports. The file system may
|
||||
support a different set of attributes.
|
||||
.TP
|
||||
.BI "get-referring-entries [-p|--path PATH] INO"
|
||||
.sp
|
||||
@@ -516,15 +506,6 @@ A path within a ScoutFS filesystem.
|
||||
.RE
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "set-attr-x FILE"
|
||||
.sp
|
||||
Set ScoutFS-specific attributes on a file. Only the attributes that are
|
||||
spcified by options will be set. The --help option will list the
|
||||
attributes that the command understands. The file system may support a
|
||||
different set of attributes.
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "setattr FILE [-d, --data-version=VERSION [-s, --size=SIZE [-o, --offline]]] [-t, --ctime=TIMESPEC]"
|
||||
.sp
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <argp.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "ioctl.h"
|
||||
#include "parse.h"
|
||||
#include "cmd.h"
|
||||
|
||||
struct attr_x_args {
|
||||
bool set;
|
||||
char *filename;
|
||||
struct scoutfs_ioctl_inode_attr_x iax;
|
||||
};
|
||||
|
||||
#define pr(iax, name, label, fmt, args...) \
|
||||
do { \
|
||||
if ((iax->x_mask & SCOUTFS_IOC_IAX_##name)) { \
|
||||
if (__builtin_popcount(iax->x_mask) > 1) \
|
||||
printf(label ": " fmt "\n", ##args); \
|
||||
else \
|
||||
printf(fmt "\n", ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define prb(iax, name, label) \
|
||||
pr(iax, name, label, "%u", !!((iax)->bits & SCOUTFS_IOC_IAX_B_##name))
|
||||
|
||||
static int do_attr_x(struct attr_x_args *args)
|
||||
{
|
||||
struct scoutfs_ioctl_inode_attr_x *iax = &args->iax;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
int op;
|
||||
|
||||
if (args->set) {
|
||||
/* nothing to do if not setting */
|
||||
if (iax->x_mask == 0)
|
||||
return 0;
|
||||
op = SCOUTFS_IOC_SET_ATTR_X;
|
||||
} else {
|
||||
/* get all known if none specified */
|
||||
if (iax->x_mask == 0)
|
||||
iax->x_mask = ~SCOUTFS_IOC_IAX__UNKNOWN;
|
||||
op = SCOUTFS_IOC_GET_ATTR_X;
|
||||
}
|
||||
|
||||
fd = open(args->filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
args->filename, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, op, iax);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "attr_x ioctl failed on '%s': "
|
||||
"%s (%d)\n", args->filename, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!args->set) {
|
||||
pr(iax, META_SEQ, "meta_seq", "%llu", iax->meta_seq);
|
||||
pr(iax, DATA_SEQ, "data_seq", "%llu", iax->data_seq);
|
||||
pr(iax, DATA_VERSION, "data_version", "%llu", iax->data_version);
|
||||
pr(iax, ONLINE_BLOCKS, "online_blocks", "%llu", iax->online_blocks);
|
||||
pr(iax, OFFLINE_BLOCKS, "offline_blocks", "%llu", iax->offline_blocks);
|
||||
pr(iax, CTIME, "ctime", "%llu.%u", iax->ctime_sec, iax->ctime_nsec);
|
||||
pr(iax, CRTIME, "crtime", "%llu.%u", iax->crtime_sec, iax->crtime_nsec);
|
||||
pr(iax, SIZE, "size", "%llu", iax->size);
|
||||
prb(iax, RETENTION, "retention");
|
||||
pr(iax, PROJECT_ID, "project_id", "%llu", iax->project_id);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called for both get and set. The get calls won't have
|
||||
* arguments and are only setting the mask. The set calls parse the
|
||||
* value to set. We could have defaults by making set option arguments
|
||||
* optional, like setting the current time for timestamps, but that
|
||||
* hasn't been needed.
|
||||
*
|
||||
* Option value parsing places no constraints on the attributes or
|
||||
* values themselves once parsed. This lets us use the set command to
|
||||
* test the kernel's testing for invalid attribute combinations and
|
||||
* values.
|
||||
*/
|
||||
static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct attr_x_args *args = state->input;
|
||||
struct timespec ts;
|
||||
int ret;
|
||||
u64 x;
|
||||
|
||||
switch (key) {
|
||||
case 'm':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_META_SEQ;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.meta_seq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_DATA_SEQ;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.data_seq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_DATA_VERSION;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.data_version);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (args->iax.data_version == 0)
|
||||
argp_error(state, "data version must not be 0");
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_ONLINE_BLOCKS;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.online_blocks);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_OFFLINE_BLOCKS;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.offline_blocks);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_CTIME;
|
||||
if (arg) {
|
||||
ret = parse_timespec(arg, &ts);
|
||||
if (ret)
|
||||
return ret;
|
||||
args->iax.ctime_sec = ts.tv_sec;
|
||||
args->iax.ctime_nsec = ts.tv_nsec;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_CRTIME;
|
||||
if (arg) {
|
||||
ret = parse_timespec(arg, &ts);
|
||||
if (ret)
|
||||
return ret;
|
||||
args->iax.crtime_sec = ts.tv_sec;
|
||||
args->iax.crtime_nsec = ts.tv_nsec;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_SIZE;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_RETENTION;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &x);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (x)
|
||||
args->iax.bits |= SCOUTFS_IOC_IAX_B_RETENTION;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
args->iax.x_mask |= SCOUTFS_IOC_IAX_PROJECT_ID;
|
||||
if (arg) {
|
||||
ret = parse_u64(arg, &args->iax.project_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (!args->filename)
|
||||
args->filename = strdup_or_error(state, arg);
|
||||
else
|
||||
argp_error(state, "more than one argument given");
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->filename)
|
||||
argp_error(state, "no filename given");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The get options are derived from these by copying the struct and
|
||||
* modifying fields.
|
||||
*/
|
||||
static struct argp_option set_options[] = {
|
||||
{ "meta_seq", 'm', "SEQ", 0, "Inode Metadata change index sequence number"},
|
||||
{ "data_seq", 'd', "SEQ", 0, "File Data change index sequence number"},
|
||||
{ "data_version", 'v', "VERSION", 0, "File Data contents version"},
|
||||
{ "online_blocks", 'n', "COUNT", 0, "Online data block count"},
|
||||
{ "offline_blocks", 'f', "COUNT", 0, "Offline data block count"},
|
||||
{ "ctime", 'c', "SECS.NSECS", 0, "Inode change time (posix ctime)"},
|
||||
{ "crtime", 'r', "SECS.NSECS", 0, "ScoutFS creation time"},
|
||||
{ "size", 's', "SIZE", 0, "Inode i_size field"},
|
||||
{ "retention", 't', "0|1", 0, "Retention flag"},
|
||||
{ "project_id", 'p', "PROJECT_ID", 0, "Project ID"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp get_argp = {
|
||||
NULL, /* dynamically built */
|
||||
parse_opt,
|
||||
"FILE",
|
||||
"get extensible file attributes"
|
||||
};
|
||||
|
||||
static int get_attr_x_cmd(int argc, char **argv)
|
||||
{
|
||||
struct attr_x_args args = {0,};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&get_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_attr_x(&args);
|
||||
}
|
||||
|
||||
/*
|
||||
* The set options match the get arguments but don't take argument
|
||||
* values to set.
|
||||
*/
|
||||
static void build_get_options(void)
|
||||
{
|
||||
struct argp_option **opts = (struct argp_option **)&get_argp.options;
|
||||
int i;
|
||||
|
||||
*opts = calloc(array_size(set_options), sizeof(set_options[0]));
|
||||
assert(*opts);
|
||||
|
||||
memcpy(*opts, set_options, array_size(set_options) * sizeof(set_options[0]));
|
||||
|
||||
for (i = 0; i < array_size(set_options) - 1; i++)
|
||||
(*opts)[i].arg = NULL;
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) get_ctor(void)
|
||||
{
|
||||
build_get_options();
|
||||
|
||||
cmd_register_argp("get-attr-x", &get_argp, GROUP_AGENT, get_attr_x_cmd);
|
||||
}
|
||||
|
||||
static struct argp set_argp = {
|
||||
set_options,
|
||||
parse_opt,
|
||||
"FILE",
|
||||
"Set extensible file attributes"
|
||||
};
|
||||
|
||||
static int set_attr_x_cmd(int argc, char **argv)
|
||||
{
|
||||
struct attr_x_args args = {.set = true,};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&set_argp, argc, argv, 0, NULL, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_attr_x(&args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) set_ctor(void)
|
||||
{
|
||||
cmd_register_argp("set-attr-x", &set_argp, GROUP_AGENT, set_attr_x_cmd);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ static int do_change_fmt_vers(struct change_fmt_vers_args *args)
|
||||
|
||||
if (le64_to_cpu(meta_super->fmt_vers) < SCOUTFS_FORMAT_VERSION_MIN ||
|
||||
le64_to_cpu(meta_super->fmt_vers) > SCOUTFS_FORMAT_VERSION_MAX) {
|
||||
fprintf(stderr, "meta super block has format version %llu outside of supported version range %u-%u",
|
||||
fprintf(stderr, "meta super block has format version %llu outside of supported version range %llu-%llu",
|
||||
le64_to_cpu(meta_super->fmt_vers), SCOUTFS_FORMAT_VERSION_MIN,
|
||||
SCOUTFS_FORMAT_VERSION_MAX);
|
||||
ret = -EINVAL;
|
||||
@@ -105,7 +105,7 @@ static int do_change_fmt_vers(struct change_fmt_vers_args *args)
|
||||
|
||||
if (le64_to_cpu(data_super->fmt_vers) < SCOUTFS_FORMAT_VERSION_MIN ||
|
||||
le64_to_cpu(data_super->fmt_vers) > SCOUTFS_FORMAT_VERSION_MAX) {
|
||||
fprintf(stderr, "data super block has format version %llu outside of supported version range %u-%u",
|
||||
fprintf(stderr, "data super block has format version %llu outside of supported version range %llu-%llu",
|
||||
le64_to_cpu(data_super->fmt_vers), SCOUTFS_FORMAT_VERSION_MIN,
|
||||
SCOUTFS_FORMAT_VERSION_MAX);
|
||||
ret = -EINVAL;
|
||||
@@ -119,16 +119,6 @@ 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);
|
||||
|
||||
@@ -196,7 +186,7 @@ static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
return ret;
|
||||
if (args->fmt_vers < SCOUTFS_FORMAT_VERSION_MIN ||
|
||||
args->fmt_vers > SCOUTFS_FORMAT_VERSION_MAX)
|
||||
argp_error(state, "format-version %llu is outside supported range of %u-%u",
|
||||
argp_error(state, "format-version %llu is outside supported range of %llu-%llu",
|
||||
args->fmt_vers, SCOUTFS_FORMAT_VERSION_MIN,
|
||||
SCOUTFS_FORMAT_VERSION_MAX);
|
||||
break;
|
||||
|
||||
@@ -70,7 +70,7 @@ static void usage(void)
|
||||
fprintf(stderr, "Selected fs defaults to current working directory.\n");
|
||||
fprintf(stderr, "See <command> --help for more details.\n");
|
||||
|
||||
fprintf(stderr, "\nSupported format version: %u-%u\n",
|
||||
fprintf(stderr, "\nSupported format version: %llu-%llu\n",
|
||||
SCOUTFS_FORMAT_VERSION_MIN, SCOUTFS_FORMAT_VERSION_MAX);
|
||||
|
||||
fprintf(stderr, "\nCore admin:\n");
|
||||
|
||||
@@ -141,13 +141,4 @@ static inline void scoutfs_key_dec(struct scoutfs_key *key)
|
||||
key->sk_zone--;
|
||||
}
|
||||
|
||||
static inline void scoutfs_xattr_get_indx_key(struct scoutfs_key *key, u8 *major, u64 *minor,
|
||||
u64 *ino, u64 *xid)
|
||||
{
|
||||
*major = le64_to_cpu(key->_sk_first) >> 56;
|
||||
*minor = (le64_to_cpu(key->_sk_first) << 8) | (le64_to_cpu(key->_sk_second) >> 56);
|
||||
*ino = (le64_to_cpu(key->_sk_second) << 8) | (le64_to_cpu(key->_sk_third) >> 56);
|
||||
*xid = (le64_to_cpu(key->_sk_third) << 8) | key->_sk_fourth;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -274,9 +274,7 @@ 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;
|
||||
inode.crtime.sec = inode.atime.sec;
|
||||
inode.crtime.nsec = inode.atime.nsec;
|
||||
btree_append_item(bt, &key, &inode, scoutfs_inode_vers_bytes(args->fmt_vers));
|
||||
btree_append_item(bt, &key, &inode, sizeof(inode));
|
||||
|
||||
ret = write_block(meta_fd, SCOUTFS_BLOCK_MAGIC_BTREE, fsid, 1, blkno,
|
||||
SCOUTFS_BLOCK_LG_SHIFT, &bt->hdr);
|
||||
@@ -388,6 +386,10 @@ static int do_mkfs(struct mkfs_args *args)
|
||||
print_quorum_slots(super->qconf.slots, array_size(super->qconf.slots),
|
||||
" ");
|
||||
|
||||
if (SCOUTFS_FORMAT_VERSION_MIN & SCOUTFS_FORMAT_VER_PREREL)
|
||||
printf("This volume was created with the incompatible pre-release format version 0x%016llx. This volume will only be mountable by pre-release builds with this specific matching format version.\n",
|
||||
SCOUTFS_FORMAT_VERSION_MIN);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (super)
|
||||
@@ -458,7 +460,7 @@ static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
return ret;
|
||||
if (args->fmt_vers < SCOUTFS_FORMAT_VERSION_MIN ||
|
||||
args->fmt_vers > SCOUTFS_FORMAT_VERSION_MAX)
|
||||
argp_error(state, "format-version %llu is outside supported range of %u-%u",
|
||||
argp_error(state, "format-version %llu is outside supported range of %llu-%llu",
|
||||
args->fmt_vers, SCOUTFS_FORMAT_VERSION_MIN,
|
||||
SCOUTFS_FORMAT_VERSION_MAX);
|
||||
break;
|
||||
|
||||
@@ -110,13 +110,9 @@ static void print_xattr_totl(struct scoutfs_key *key, void *val, int val_len)
|
||||
|
||||
static void print_xattr_indx(struct scoutfs_key *key, void *val, int val_len)
|
||||
{
|
||||
u64 minor;
|
||||
u64 ino;
|
||||
u64 xid;
|
||||
u8 major;
|
||||
|
||||
scoutfs_xattr_get_indx_key(key, &major, &minor, &ino, &xid);
|
||||
printf(" xattr indx: major %u minor %llu ino %llu xid %llu", major, minor, ino, xid);
|
||||
printf(" xattr indx: a %llu b %llu ino %llu",
|
||||
le64_to_cpu(key->skxi_a), le64_to_cpu(key->skxi_b),
|
||||
le64_to_cpu(key->skxi_ino));
|
||||
}
|
||||
|
||||
static u8 *global_printable_name(u8 *name, int name_len)
|
||||
|
||||
155
utils/src/projects.c
Normal file
155
utils/src/projects.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <argp.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "parse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "ioctl.h"
|
||||
#include "cmd.h"
|
||||
#include "list.h"
|
||||
|
||||
struct str_head {
|
||||
struct list_head head;
|
||||
char str[0];
|
||||
};
|
||||
|
||||
struct proj_args {
|
||||
struct list_head paths;
|
||||
char *which;
|
||||
u64 proj;
|
||||
unsigned int cmd;
|
||||
bool have_proj;
|
||||
};
|
||||
|
||||
static bool single_entry(struct list_head *list)
|
||||
{
|
||||
return list->next->next == list;
|
||||
}
|
||||
|
||||
static int do_proj(struct proj_args *args)
|
||||
{
|
||||
struct str_head *shead;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(shead, &args->paths, head) {
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = get_path(shead->str, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, args->cmd, &args->proj);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "%s project ioctl failed: %s (%d)\n",
|
||||
args->which, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (args->cmd == SCOUTFS_IOC_GET_PROJECT_ID) {
|
||||
if (single_entry(&args->paths))
|
||||
printf("%llu\n", args->proj);
|
||||
else
|
||||
printf("%s: %llu\n", shead->str, args->proj);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool add_strdup_head(struct list_head *list, char *str)
|
||||
{
|
||||
struct str_head *shead;
|
||||
size_t bytes;
|
||||
|
||||
bytes = strlen(str) + 1;
|
||||
shead = malloc(offsetof(struct str_head, str[bytes]));
|
||||
if (!shead)
|
||||
return false;
|
||||
|
||||
memcpy(shead->str, str, bytes);
|
||||
list_add_tail(&shead->head, list);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int parse_proj_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct proj_args *args = state->input;
|
||||
int ret;
|
||||
|
||||
switch (key) {
|
||||
case 'g':
|
||||
args->cmd = SCOUTFS_IOC_GET_PROJECT_ID;
|
||||
args->which = "get";
|
||||
break;
|
||||
case 's':
|
||||
ret = parse_u64(arg, &args->proj);
|
||||
if (ret)
|
||||
argp_error(state, "error parsing project ID");
|
||||
args->cmd = SCOUTFS_IOC_SET_PROJECT_ID;
|
||||
args->which = "set";
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (!add_strdup_head(&args->paths, arg))
|
||||
argp_error(state, "error allocating memory for path");
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->cmd)
|
||||
argp_error(state, "must specify either -g (get) or -s (set)");
|
||||
if (list_empty(&args->paths))
|
||||
argp_error(state, "must final path arguments");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp_option proj_opts[] = {
|
||||
{ "get", 'g', NULL, 0, "Get and print existing project ID from inodes"},
|
||||
{ "set", 's', "ID", 0, "Set unsigned 64bit project ID on inodes (0 clears)"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp proj_argp = {
|
||||
proj_opts,
|
||||
parse_proj_opt,
|
||||
"",
|
||||
"Manipulate Project ID on inodes"
|
||||
};
|
||||
|
||||
static int proj_cmd(int argc, char **argv)
|
||||
{
|
||||
struct proj_args args = {
|
||||
.paths = LIST_HEAD_INIT(args.paths),
|
||||
.have_proj = false,
|
||||
};
|
||||
|
||||
return argp_parse(&proj_argp, argc, argv, 0, NULL, &args) ?:
|
||||
do_proj(&args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) proj_ctor(void)
|
||||
{
|
||||
cmd_register_argp("project-id", &proj_argp, GROUP_CORE, proj_cmd);
|
||||
}
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "cmd.h"
|
||||
#include "cmp.h"
|
||||
|
||||
#define ENTF "%u.%llu.%llu"
|
||||
#define ENTA(e) (e)->major, (e)->minor, (e)->ino
|
||||
#define ENTF "%llu.%llu.%llu"
|
||||
#define ENTA(e) (e)->a, (e)->b, (e)->ino
|
||||
|
||||
struct xattr_args {
|
||||
char *path;
|
||||
@@ -29,16 +29,14 @@ struct xattr_args {
|
||||
static int compare_entries(struct scoutfs_ioctl_xattr_index_entry *a,
|
||||
struct scoutfs_ioctl_xattr_index_entry *b)
|
||||
{
|
||||
return scoutfs_cmp(a->major, b->major) ?: scoutfs_cmp(a->minor, b->minor) ?:
|
||||
scoutfs_cmp(a->ino, b->ino);
|
||||
return scoutfs_cmp(a->a, b->a) ?: scoutfs_cmp(a->b, b->b) ?: scoutfs_cmp(a->ino, b->ino);
|
||||
}
|
||||
|
||||
static int parse_entry(struct scoutfs_ioctl_xattr_index_entry *ent, char *str)
|
||||
{
|
||||
int major;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(str, "%i.%lli.%lli", &major, &ent->minor, &ent->ino);
|
||||
ret = sscanf(str, "%lli.%lli.%lli", &ent->a, &ent->b, &ent->ino);
|
||||
if (ret != 3) {
|
||||
fprintf(stderr, "bad index position entry argument '%s', it must be "
|
||||
"in the form \"a.b.ino\" where each value can be prefixed by "
|
||||
@@ -46,14 +44,6 @@ static int parse_entry(struct scoutfs_ioctl_xattr_index_entry *ent, char *str)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (major < 0 || major > UCHAR_MAX) {
|
||||
fprintf(stderr, "initial major index position '%d' must be between 0 and 255, "
|
||||
"inclusive.\n", major);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ent->major = major;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,13 +102,13 @@ static int do_read_xattr_index(struct xattr_args *args)
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
ent = &ents[i];
|
||||
printf("%u.%llu = %llu\n",
|
||||
ent->major, ent->minor, ent->ino);
|
||||
printf("%llu.%llu = %llu\n",
|
||||
ent->a, ent->b, ent->ino);
|
||||
}
|
||||
|
||||
rxi.first = *ent;
|
||||
|
||||
if ((++rxi.first.ino == 0 && ++rxi.first.minor == 0 && ++rxi.first.major == 0) ||
|
||||
if ((++rxi.first.ino == 0 && ++rxi.first.b == 0 && ++rxi.first.a == 0) ||
|
||||
compare_entries(&rxi.first, &rxi.last) > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user