Compare commits

..

21 Commits

Author SHA1 Message Date
Zach Brown
18f00a8acc scoutfs_item_create checks wrong lock mode
scoutfs_item_create() was checking that its lock had a read mode, when
it should have been checking for a write mode.  This worked out because
callers with write mode locks are also protecting reads.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
40b62b5033 Have item cache show unprotected lock
The item cache has a bit of safety checks that make sure that an
operation is performed while holding a lock that covers the item.  It
dumped a stack trace via WARN when that wasn't true, but it didn't
include any details about the keys or lock modes involved.

This adds a message that's printed once which includes the keys and
modes when an operation is attempted that isn't protected.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
d4b1cd5931 Use incompatible pre-release format version
Change the format version so that this code is incompatible with
existing releases, and vice versa.  We're distributing test builds and
might change the format based on feedback and we don't want it to be
possible to corrupt volumes with the changes in these pre-release
builds.

This is a one-off patch for this feature branch.  We may work this into
the build more formally if it works out.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
555b307fe6 Remove scoutfs_data_wait_check_iter
scoutfs_data_wait_check_iter() was checking the contiguous region of the
file starting at its pos and extending for iter_iov_count() bytes.  The
caller can do that with the previous _data_wait_check() method by
providing the same count that _check_iter() was using.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
9859a75d4e Check for all offline in scoutfs_file_write_iter
When we write to file contents we change the data_version.  To stage old
contents into an offline region the data_version of the file must match
the archived copy.  When writing we have to make sure that there is no
offline data so that we don't increase the data_version which will
prevent staging of any other file regions because the data_versions no
longer match.

scoutfs_file_write_iter was only checking for offline data in its write
region, not the entire file.  Fix it to match the _aio_write method and
check the whole file.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
c6ad865054 Fix uninit written in scoutfs_file_write_iter
scoutfs_file_write_iter tried to track written bytes and return those
unless there was an error.  But written was uninitialized if we got
errors in any of the calls leading up to performing the write.  The
bytes written were also not being passed to the generic_write_sync
helper.  This fixes up all those inconsistencies and makes it look like
the write_iter path in other filesystems.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
19ea848bca Add indx xattr tag support to utils
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
13151b7c66 Add indx xattr tag support
Add support for the indx xattr tag which lets xattrs determine the sort
order of by their inode number in a global index.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
535352fbd4 Add quota tests
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:57:17 -07:00
Zach Brown
941e053eb3 Add quota support to utils
Add scoutfs cli commands for managing quotas and add its persistent
structures to the print command.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:55:05 -07:00
Zach Brown
12fe44c0be Add quota support
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:55:05 -07:00
Zach Brown
b06a56b1ee Add project ID tests
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
6817b403e5 Add o_tmpfile_linkat test binary
Add a test binary that uses o_tmpfile and linkat to create a file in a
given dir.  We have something similar, but it's weirdly specific to a
given test.  This is a simpler building block that could be used by more
tests.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
b89139b0fe Add project ID support to utils
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
bd4dad757b Add project support
Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
b2448f461a Drop caches in totl test
Now that the _READ_XATTR_TOTALS ioctl uses the weak item cache we have
to drop caches before each attempt to read the xattrs that we just wrote
and synced.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
4091e2cc55 read_xattr_totls ioctl uses weak item cache
Change the read_xattr_totls ioctl to use the weak item cache instead of
manually reading and merging the fs items for the xattr totals on every
call.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
45cd62974a Extract .totl. item merging into own functions
The _READ_XATTR_TOTALS ioctl had manual code for merging the .totl.
total and value while reading fs items.  We're going to want to do this
in another reader so let's put these in their own funcions that clearly
isolate the logic of merging the fs items into a coherent result.

We can get rid of some of the totl_read_ counters that tracked which
items we were merging.  They weren't adding much value and conflated the
reading ioctl interface with the merging logic.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
5d744b78bd Pre-declare scoutfs_lock in forest.h
Definitions in forest.h use lock pointers.  Pre-declare the struct so it
doesn't break inclusion without lock.h, following current practice in
the header.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
1328f1a2cb Add scoutfs_forest_read_items_roots
Add a forest item reading interface that lets the caller specify the net
roots instead of always getting them from a network request.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
Zach Brown
1f5c68cd30 Add the weak item cache
Add the weak item cache that is used for reads that can handle results
being a little behind.  This gives us a lot more freedom to implement
the cache that biases concurrent reads.

Signed-off-by: Zach Brown <zab@versity.com>
2024-05-09 11:54:54 -07:00
42 changed files with 475 additions and 1658 deletions

View File

@@ -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
\

View File

@@ -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 \

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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) &&

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)

View File

@@ -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),

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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)"
}
#

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View 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);
}

View File

@@ -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;
}