Merge pull request #1 from agrover/use-argp

Rework scoutfs command-line parsing
This commit is contained in:
Andy Grover
2021-01-13 11:14:08 -08:00
committed by GitHub
46 changed files with 2113 additions and 1221 deletions

View File

@@ -274,8 +274,8 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
struct super_block *sb = inode->i_sb;
struct scoutfs_ioctl_release args;
struct scoutfs_lock *lock = NULL;
loff_t start;
loff_t end_inc;
u64 sblock;
u64 eblock;
u64 online;
u64 offline;
u64 isize;
@@ -286,9 +286,11 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
trace_scoutfs_ioc_release(sb, scoutfs_ino(inode), &args);
if (args.count == 0)
if (args.length == 0)
return 0;
if ((args.block + args.count) < args.block)
if (((args.offset + args.length) < args.offset) ||
(args.offset & SCOUTFS_BLOCK_SM_MASK) ||
(args.length & SCOUTFS_BLOCK_SM_MASK))
return -EINVAL;
@@ -321,23 +323,24 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
inode_dio_wait(inode);
/* drop all clean and dirty cached blocks in the range */
start = args.block << SCOUTFS_BLOCK_SM_SHIFT;
end_inc = ((args.block + args.count) << SCOUTFS_BLOCK_SM_SHIFT) - 1;
truncate_inode_pages_range(&inode->i_data, start, end_inc);
truncate_inode_pages_range(&inode->i_data, args.offset,
args.offset + args.length - 1);
sblock = args.offset >> SCOUTFS_BLOCK_SM_SHIFT;
eblock = (args.offset + args.length - 1) >> SCOUTFS_BLOCK_SM_SHIFT;
ret = scoutfs_data_truncate_items(sb, inode, scoutfs_ino(inode),
args.block,
args.block + args.count - 1, true,
sblock,
eblock, true,
lock);
if (ret == 0) {
scoutfs_inode_get_onoff(inode, &online, &offline);
isize = i_size_read(inode);
if (online == 0 && isize) {
start = (isize + SCOUTFS_BLOCK_SM_SIZE - 1)
sblock = (isize + SCOUTFS_BLOCK_SM_SIZE - 1)
>> SCOUTFS_BLOCK_SM_SHIFT;
ret = scoutfs_data_truncate_items(sb, inode,
scoutfs_ino(inode),
start, U64_MAX,
sblock, U64_MAX,
false, lock);
}
}
@@ -459,23 +462,24 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg)
trace_scoutfs_ioc_stage(sb, scoutfs_ino(inode), &args);
end_size = args.offset + args.count;
end_size = args.offset + args.length;
/* verify arg constraints that aren't dependent on file */
if (args.count < 0 || (end_size < args.offset) ||
args.offset & SCOUTFS_BLOCK_SM_MASK)
if (args.length < 0 || (end_size < args.offset) ||
args.offset & SCOUTFS_BLOCK_SM_MASK) {
return -EINVAL;
}
if (args.count == 0)
if (args.length == 0)
return 0;
/* the iocb is really only used for the file pointer :P */
init_sync_kiocb(&kiocb, file);
kiocb.ki_pos = args.offset;
kiocb.ki_left = args.count;
kiocb.ki_nbytes = args.count;
kiocb.ki_left = args.length;
kiocb.ki_nbytes = args.length;
iov.iov_base = (void __user *)(unsigned long)args.buf_ptr;
iov.iov_len = args.count;
iov.iov_len = args.length;
ret = mnt_want_write_file(file);
if (ret)
@@ -514,11 +518,11 @@ static long scoutfs_ioc_stage(struct file *file, unsigned long arg)
written = 0;
do {
ret = generic_file_buffered_write(&kiocb, &iov, 1, pos, &pos,
args.count, written);
args.length, written);
BUG_ON(ret == -EIOCBQUEUED);
if (ret > 0)
written += ret;
} while (ret > 0 && written < args.count);
} while (ret > 0 && written < args.length);
si->staging = false;
current->backing_dev_info = NULL;

View File

@@ -176,8 +176,8 @@ struct scoutfs_ioctl_ino_path_result {
* an offline record is left behind to trigger demand staging if the
* file is read.
*
* The starting block offset and number of blocks to release are in
* units 4KB blocks.
* The starting file offset and number of bytes to release must be in
* multiples of 4KB.
*
* The specified range can extend past i_size and can straddle sparse
* regions or blocks that are already offline. The only change it makes
@@ -193,8 +193,8 @@ struct scoutfs_ioctl_ino_path_result {
* presentation of the data in the file.
*/
struct scoutfs_ioctl_release {
__u64 block;
__u64 count;
__u64 offset;
__u64 length;
__u64 data_version;
};
@@ -205,7 +205,7 @@ struct scoutfs_ioctl_stage {
__u64 data_version;
__u64 buf_ptr;
__u64 offset;
__s32 count;
__s32 length;
__u32 _pad;
};

View File

@@ -530,22 +530,22 @@ TRACE_EVENT(scoutfs_ioc_release,
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(__u64, ino)
__field(__u64, block)
__field(__u64, count)
__field(__u64, offset)
__field(__u64, length)
__field(__u64, vers)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
__entry->ino = ino;
__entry->block = args->block;
__entry->count = args->count;
__entry->offset = args->offset;
__entry->length = args->length;
__entry->vers = args->data_version;
),
TP_printk(SCSBF" ino %llu block %llu count %llu vers %llu",
SCSB_TRACE_ARGS, __entry->ino, __entry->block,
__entry->count, __entry->vers)
TP_printk(SCSBF" ino %llu offset %llu length %llu vers %llu",
SCSB_TRACE_ARGS, __entry->ino, __entry->offset,
__entry->length, __entry->vers)
);
DEFINE_EVENT(scoutfs_ino_ret_class, scoutfs_ioc_release_ret,
@@ -564,7 +564,7 @@ TRACE_EVENT(scoutfs_ioc_stage,
__field(__u64, ino)
__field(__u64, vers)
__field(__u64, offset)
__field(__s32, count)
__field(__s32, length)
),
TP_fast_assign(
@@ -572,12 +572,12 @@ TRACE_EVENT(scoutfs_ioc_stage,
__entry->ino = ino;
__entry->vers = args->data_version;
__entry->offset = args->offset;
__entry->count = args->count;
__entry->length = args->length;
),
TP_printk(SCSBF" ino %llu vers %llu offset %llu count %d",
TP_printk(SCSBF" ino %llu vers %llu offset %llu length %d",
SCSB_TRACE_ARGS, __entry->ino, __entry->vers,
__entry->offset, __entry->count)
__entry->offset, __entry->length)
);
TRACE_EVENT(scoutfs_ioc_data_wait_err,

View File

@@ -28,8 +28,8 @@ t_ident()
local fsid
local rid
fsid=$(scoutfs statfs -s fsid "$mnt")
rid=$(scoutfs statfs -s rid "$mnt")
fsid=$(scoutfs statfs -s fsid -p "$mnt")
rid=$(scoutfs statfs -s rid -p "$mnt")
echo "f.${fsid:0:6}.r.${rid:0:6}"
}

View File

@@ -21,5 +21,5 @@ t_require_mounts() {
local req="$1"
test "$T_NR_MOUNTS" -ge "$req" || \
t_fail "$req mounts required, only have $T_NR_MOUNTS"
t_skip "$req mounts required, only have $T_NR_MOUNTS"
}

View File

@@ -1,6 +1,6 @@
== create files
== waiter shows up in ioctl
offline wating should be empty:
offline waiting should be empty:
0
offline waiting should now have one known entry:
== multiple waiters on same block listed once
@@ -8,7 +8,7 @@ offline waiting still has one known entry:
== different blocks show up
offline waiting now has two known entries:
== staging wakes everyone
offline wating should be empty again:
offline waiting should be empty again:
0
== interruption does no harm
offline waiting should now have one known entry:

View File

@@ -1,9 +1,9 @@
== 0 data_version arg fails
setattr_more ioctl failed on '/mnt/test/test/setattr_more/file': Invalid argument (22)
scoutfs: setattr failed: Invalid argument (22)
setattr: data version must not be 0
Try `setattr --help' or `setattr --usage' for more information.
== args must specify size and offline
setattr_more ioctl failed on '/mnt/test/test/setattr_more/file': Invalid argument (22)
scoutfs: setattr failed: Invalid argument (22)
setattr: must provide size if using --offline option
Try `setattr --help' or `setattr --usage' for more information.
== only works on regular files
failed to open '/mnt/test/test/setattr_more/dir': Is a directory (21)
scoutfs: setattr failed: Is a directory (21)

View File

@@ -8,16 +8,16 @@
release ioctl failed: Invalid argument (22)
scoutfs: release failed: Invalid argument (22)
== releasing non-file fails
ioctl failed on '/mnt/test/test/simple-release-extents/file-char': Inappropriate ioctl for device (25)
release ioctl failed: Inappropriate ioctl for device (25)
scoutfs: release failed: Inappropriate ioctl for device (25)
ioctl failed: Inappropriate ioctl for device (25)
release: must provide file version --data-version
Try `release --help' or `release --usage' for more information.
== releasing a non-scoutfs file fails
ioctl failed on '/dev/null': Inappropriate ioctl for device (25)
release ioctl failed: Inappropriate ioctl for device (25)
scoutfs: release failed: Inappropriate ioctl for device (25)
ioctl failed: Inappropriate ioctl for device (25)
release: must provide file version --data-version
Try `release --help' or `release --usage' for more information.
== releasing bad version fails
release ioctl failed: Stale file handle (116)
scoutfs: release failed: Stale file handle (116)
release: must provide file version --data-version
Try `release --help' or `release --usage' for more information.
== verify small release merging
0 0 0: (0 0 1) (1 101 4)
0 0 1: (0 0 2) (2 102 3)

View File

@@ -4,8 +4,8 @@
== release+stage shouldn't change stat, data seq or vers
== stage does change meta_seq
== can't use stage to extend online file
stage returned -1, not 4096: error Invalid argument (22)
scoutfs: stage failed: Input/output error (5)
stage: must provide file version with --data-version
Try `stage --help' or `stage --usage' for more information.
== wrapped region fails
stage returned -1, not 4096: error Invalid argument (22)
scoutfs: stage failed: Input/output error (5)
@@ -18,6 +18,6 @@ scoutfs: stage failed: Input/output error (5)
== partial final block that writes to i_size does work
== zero length stage doesn't bring blocks online
== stage of non-regular file fails
ioctl failed on '/mnt/test/test/simple-staging/file-char': Inappropriate ioctl for device (25)
stage returned -1, not 1: error Inappropriate ioctl for device (25)
scoutfs: stage failed: Input/output error (5)
ioctl failed: Inappropriate ioctl for device (25)
stage: must provide file version with --data-version
Try `stage --help' or `stage --usage' for more information.

View File

@@ -161,9 +161,9 @@ for n in $(t_fs_nrs); do
echo "bash $gen $blocks $n $p $f > $path" >> $create
echo "cmp $path <(bash $gen $blocks $n $p $f)" >> $verify
echo "vers=\$(scoutfs stat -s data_version $path)" >> $release
echo "scoutfs release $path \$vers 0 $blocks" >> $release
echo "scoutfs release $path -V \$vers -o 0 -l $bytes" >> $release
echo "vers=\$(scoutfs stat -s data_version $path)" >> $stage
echo "scoutfs stage $path \$vers 0 $bytes <(bash $gen $blocks $n $p $f)" >> $stage
echo "scoutfs stage <(bash $gen $blocks $n $p $f) $path -V \$vers -o 0 -l $bytes " >> $stage
echo "rm -f $path" >> $unlink
echo "x=\$(scoutfs stat -s online_blocks $path)" >> $online

View File

@@ -9,14 +9,14 @@ t_require_commands scoutfs dd truncate touch mkdir rm rmdir
release_vers() {
local file="$1"
local vers="$2"
local block="$3"
local count="$4"
local offset="$3"
local length="$4"
if [ "$vers" == "stat" ]; then
vers=$(scoutfs stat -s data_version "$file")
fi
scoutfs release "$file" "$vers" "$block" "$count"
scoutfs release "$file" -V "$vers" -o "$offset" -l "$length"
}
# if vers is "stat" then we ask stat_more for the data_version
@@ -24,14 +24,14 @@ stage_vers() {
local file="$1"
local vers="$2"
local offset="$3"
local count="$4"
local length="$4"
local contents="$5"
if [ "$vers" == "stat" ]; then
vers=$(scoutfs stat -s data_version "$file")
fi
scoutfs stage "$file" "$vers" "$offset" "$count" "$contents"
scoutfs stage "$contents" "$file" -V "$vers" -o "$offset" -l "$length"
}
echo_blocks()
@@ -57,15 +57,15 @@ dd if=/dev/zero of="$FILE" bs=4K count=1 conv=notrunc oflag=append status=none
echo_blocks "$FILE"
echo "== release"
release_vers "$FILE" stat 0 2
release_vers "$FILE" stat 0 8K
echo_blocks "$FILE"
echo "== duplicate release"
release_vers "$FILE" stat 0 2
release_vers "$FILE" stat 0 8K
echo_blocks "$FILE"
echo "== duplicate release past i_size"
release_vers "$FILE" stat 0 16
release_vers "$FILE" stat 0 64K
echo_blocks "$FILE"
echo "== stage"

View File

@@ -169,32 +169,32 @@ rm -rf "$T_D0/dir"
echo "== inode indexes match after syncing existing"
t_sync_seq_index
scoutfs walk-inodes meta_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes meta_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- meta_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- meta_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
scoutfs walk-inodes data_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes data_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- data_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- data_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
echo "== inode indexes match after copying and syncing"
mkdir "$T_D0/dir"
cp -ar /boot/conf* "$T_D0/dir"
t_sync_seq_index
scoutfs walk-inodes meta_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes meta_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- meta_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- meta_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
scoutfs walk-inodes data_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes data_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- data_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- data_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
echo "== inode indexes match after removing and syncing"
rm -f "$T_D1/dir/conf*"
t_sync_seq_index
scoutfs walk-inodes meta_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes meta_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- meta_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- meta_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
scoutfs walk-inodes data_seq 0 -1 "$T_M0" > "$T_TMP.0"
scoutfs walk-inodes data_seq 0 -1 "$T_M1" > "$T_TMP.1"
scoutfs walk-inodes -p "$T_M0" -- data_seq 0 -1 > "$T_TMP.0"
scoutfs walk-inodes -p "$T_M1" -- data_seq 0 -1 > "$T_TMP.1"
diff -u "$T_TMP.0" "$T_TMP.1"
t_pass

View File

@@ -30,7 +30,7 @@ echo "== create files and sync"
dd if=/dev/zero of="$DIR/truncate" bs=4096 count=1 status=none
dd if=/dev/zero of="$DIR/stage" bs=4096 count=1 status=none
vers=$(scoutfs stat -s data_version "$DIR/stage")
scoutfs release "$DIR/stage" $vers 0 1
scoutfs release "$DIR/stage" -V $vers -o 0 -l 4K
dd if=/dev/zero of="$DIR/release" bs=4096 count=1 status=none
touch "$DIR/write_end"
mkdir "$DIR"/{mknod_dir,link_dir,unlink_dir,symlink_dir,rename_dir}
@@ -41,9 +41,9 @@ sync; sync
echo "== modify files"
truncate -s 0 "$DIR/truncate"
vers=$(scoutfs stat -s data_version "$DIR/stage")
scoutfs stage "$DIR/stage" $vers 0 4096 /dev/zero
scoutfs stage /dev/zero "$DIR/stage" -V $vers -o 0 -l 4096
vers=$(scoutfs stat -s data_version "$DIR/release")
scoutfs release "$DIR/release" $vers 0 1
scoutfs release "$DIR/release" -V $vers -o 0 -l 4K
dd if=/dev/zero of="$DIR/write_end" bs=4096 count=1 status=none conv=notrunc
touch $DIR/mknod_dir/mknod_file
touch $DIR/link_dir/link_targ

View File

@@ -9,7 +9,7 @@ FILE="$T_D0/file"
echo "== race writing and index walking"
for i in $(seq 1 10); do
dd if=/dev/zero of="$FILE" bs=4K count=1 status=none conv=notrunc &
scoutfs walk-inodes data_seq 0 -1 "$T_M0" > /dev/null &
scoutfs walk-inodes -p "$T_M0" -- data_seq 0 -1 > /dev/null &
wait
done

View File

@@ -24,7 +24,7 @@ expect_wait()
shift
done
scoutfs data-waiting 0 0 "$file" > $T_TMP.wait.output
scoutfs data-waiting -B 0 -I 0 -p "$file" > $T_TMP.wait.output
diff -u $T_TMP.wait.expected $T_TMP.wait.output
}
@@ -37,9 +37,9 @@ ino=$(stat -c "%i" "$DIR/file")
vers=$(scoutfs stat -s data_version "$DIR/file")
echo "== waiter shows up in ioctl"
echo "offline wating should be empty:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
echo "offline waiting should be empty:"
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
cat "$DIR/file" > /dev/null &
sleep .1
echo "offline waiting should now have one known entry:"
@@ -58,13 +58,13 @@ echo "offline waiting now has two known entries:"
expect_wait "$DIR/file" "read" $ino 0 $ino 1
echo "== staging wakes everyone"
scoutfs stage "$DIR/file" "$vers" 0 $BYTES "$DIR/golden"
scoutfs stage "$DIR/golden" "$DIR/file" -V "$vers" -o 0 -l $BYTES
sleep .1
echo "offline wating should be empty again:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
echo "offline waiting should be empty again:"
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
echo "== interruption does no harm"
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
cat "$DIR/file" > /dev/null 2>&1 &
pid="$!"
sleep .1
@@ -74,7 +74,7 @@ kill "$pid"
# silence terminated message
wait "$pid" 2> /dev/null
echo "offline waiting should be empty again:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
echo "== EIO injection for waiting readers works"
ino=$(stat -c "%i" "$DIR/file")
@@ -86,23 +86,23 @@ dd if="$DIR/file" bs=$BS skip=1 of=/dev/null 2>&1 | \
pid2="$!"
sleep .1
echo "offline waiting should now have two known entries:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
expect_wait "$DIR/file" "read" $ino 0 $ino 1
scoutfs data-wait-err "$DIR" "$ino" "$vers" 0 $((BS*2)) read -5
scoutfs data-wait-err -p "$DIR" -I "$ino" -V "$vers" -F 0 -C $((BS*2)) -O read -E -5
sleep .1
echo "offline waiting should now have 0 known entries:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
# silence terminated message
wait "$pid" 2> /dev/null
wait "$pid2" 2> /dev/null
cat $T_TMP.cat1
cat $T_TMP.cat2
echo "offline waiting should be empty again:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
echo "== readahead while offline does no harm"
xfs_io -c "fadvise -w 0 $BYTES" "$DIR/file"
scoutfs stage "$DIR/file" "$vers" 0 $BYTES "$DIR/golden"
scoutfs stage "$DIR/golden" "$DIR/file" -V "$vers" -o 0 -l $BYTES
cmp "$DIR/file" "$DIR/golden"
echo "== waiting on interesting blocks works"
@@ -113,65 +113,65 @@ for base in $(echo 0 $(($BLOCKS / 2)) $(($BLOCKS - 2))); do
done
done
for b in $blocks; do
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
dd if="$DIR/file" of=/dev/null \
status=none bs=$BS count=1 skip=$b 2> /dev/null &
sleep .1
scoutfs stage "$DIR/file" "$vers" 0 $BYTES "$DIR/golden"
scoutfs stage "$DIR/golden" "$DIR/file" -V "$vers" -o 0 -l $BYTES
sleep .1
echo "offline waiting is empty at block $b"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
done
echo "== contents match when staging blocks forward"
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
cat "$DIR/file" > "$DIR/forward" &
for b in $(seq 0 1 $((BLOCKS - 1))); do
dd if="$DIR/golden" of="$DIR/block" status=none bs=$BS skip=$b count=1
scoutfs stage "$DIR/file" "$vers" $((b * $BS)) $BS "$DIR/block"
scoutfs stage "$DIR/block" "$DIR/file" -V "$vers" -o $((b * $BS)) -l $BS
done
sleep .1
cmp "$DIR/golden" "$DIR/forward"
echo "== contents match when staging blocks backwards"
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
cat "$DIR/file" > "$DIR/backward" &
for b in $(seq $((BLOCKS - 1)) -1 0); do
dd if="$DIR/golden" of="$DIR/block" status=none bs=$BS skip=$b count=1
scoutfs stage "$DIR/file" "$vers" $((b * $BS)) $BS "$DIR/block"
scoutfs stage "$DIR/block" "$DIR/file" -V "$vers" -o $((b * $BS)) -l $BS
done
sleep .1
cmp "$DIR/golden" "$DIR/backward"
echo "== truncate to same size doesn't wait"
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
truncate -s "$BYTES" "$DIR/file" &
sleep .1
echo "offline wating should be empty:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
echo "== truncating does wait"
truncate -s "$BS" "$DIR/file" &
sleep .1
echo "truncate should be waiting for first block:"
expect_wait "$DIR/file" "change_size" $ino 0
scoutfs stage "$DIR/file" "$vers" 0 $BYTES "$DIR/golden"
scoutfs stage "$DIR/golden" "$DIR/file" -V "$vers" -o 0 -l $BYTES
sleep .1
echo "trunate should no longer be waiting:"
scoutfs data-waiting 0 0 "$DIR" | wc -l
scoutfs data-waiting -B 0 -I 0 -p "$DIR" | wc -l
cat "$DIR/golden" > "$DIR/file"
vers=$(scoutfs stat -s data_version "$DIR/file")
echo "== writing waits"
dd if=/dev/urandom of="$DIR/other" bs=$BS count=$BLOCKS status=none
scoutfs release "$DIR/file" "$vers" 0 $BLOCKS
scoutfs release "$DIR/file" -V "$vers" -o 0 -l $BYTES
# overwrite, not truncate+write
dd if="$DIR/other" of="$DIR/file" \
bs=$BS count=$BLOCKS conv=notrunc status=none &
sleep .1
echo "should be waiting for write"
expect_wait "$DIR/file" "write" $ino 0
scoutfs stage "$DIR/file" "$vers" 0 $BYTES "$DIR/golden"
scoutfs stage "$DIR/golden" "$DIR/file" -V "$vers" -o 0 -l $BYTES
cmp "$DIR/file" "$DIR/other"
echo "== cleanup"

View File

@@ -8,63 +8,63 @@ FILE="$T_D0/file"
echo "== 0 data_version arg fails"
touch "$FILE"
scoutfs setattr -d 0 -s 1 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 0 -s 1 "$FILE" 2>&1 | t_filter_fs
rm "$FILE"
echo "== args must specify size and offline"
touch "$FILE"
scoutfs setattr -d 1 -o -s 0 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -o -s 0 "$FILE" 2>&1 | t_filter_fs
rm "$FILE"
echo "== only works on regular files"
mkdir "$T_D0/dir"
scoutfs setattr -d 1 -s 1 -f "$T_D0/dir" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -s 1 "$T_D0/dir" 2>&1 | t_filter_fs
rmdir "$T_D0/dir"
mknod "$T_D0/char" c 1 3
scoutfs setattr -d 1 -s 1 -f "$T_D0/char" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -s 1 "$T_D0/char" 2>&1 | t_filter_fs
rm "$T_D0/char"
echo "== non-zero file size fails"
echo contents > "$FILE"
scoutfs setattr -d 1 -s 1 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -s 1 "$FILE" 2>&1 | t_filter_fs
rm "$FILE"
echo "== non-zero file data_version fails"
touch "$FILE"
truncate -s 1M "$FILE"
truncate -s 0 "$FILE"
scoutfs setattr -d 1 -o -s 1 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -o -s 1 "$FILE" 2>&1 | t_filter_fs
rm "$FILE"
echo "== large size is set"
touch "$FILE"
scoutfs setattr -d 1 -s 578437695752307201 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -s 578437695752307201 "$FILE" 2>&1 | t_filter_fs
stat -c "%s" "$FILE"
rm "$FILE"
echo "== large data_version is set"
touch "$FILE"
scoutfs setattr -d 578437695752307201 -s 1 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 578437695752307201 -s 1 "$FILE" 2>&1 | t_filter_fs
scoutfs stat -s data_version "$FILE"
rm "$FILE"
echo "== large ctime is set"
touch "$FILE"
# only doing 32bit sec 'cause stat gets confused
scoutfs setattr -c 67305985.999999999 -d 1 -s 1 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -t 67305985.999999999 -V 1 -s 1 "$FILE" 2>&1 | t_filter_fs
TZ=GMT stat -c "%z" "$FILE"
rm "$FILE"
echo "== large offline extents are created"
touch "$FILE"
scoutfs setattr -d 1 -o -s $((10007 * 4096)) -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -o -s $((10007 * 4096)) "$FILE" 2>&1 | t_filter_fs
filefrag -v -b4096 "$FILE" 2>&1 | t_filter_fs
rm "$FILE"
# had a bug where we were creating extents that were too long
echo "== correct offline extent length"
touch "$FILE"
scoutfs setattr -d 1 -o -s 4000000000 -f "$FILE" 2>&1 | t_filter_fs
scoutfs setattr -V 1 -o -s 4000000000 "$FILE" 2>&1 | t_filter_fs
scoutfs stat -s offline_blocks "$FILE"
rm "$FILE"

View File

@@ -14,7 +14,7 @@ query_index() {
local first="${2:-0}"
local last="${3:--1}"
scoutfs walk-inodes $which $first $last "$T_M0"
scoutfs walk-inodes -p "$T_M0" -- $which $first $last
}
# print the major in the index for the ino if it's found
@@ -22,7 +22,7 @@ ino_major() {
local which="$1"
local ino="$2"
scoutfs walk-inodes $which 0 -1 "$T_M0" | \
scoutfs walk-inodes -p "$T_M0" -- $which 0 -1 | \
awk '($4 == "'$ino'") {print $2}'
}

View File

@@ -23,14 +23,14 @@ create_file() {
release_vers() {
local file="$1"
local vers="$2"
local block="$3"
local count="$4"
local offset="$3"
local length="$4"
if [ "$vers" == "stat" ]; then
vers=$(scoutfs stat -s data_version "$file")
fi
scoutfs release "$file" "$vers" "$block" "$count"
scoutfs release "$file" -V "$vers" -o "$offset" -l "$length"
}
FILE="$T_D0/file"
@@ -38,41 +38,41 @@ CHAR="$FILE-char"
echo "== simple whole file multi-block releasing"
create_file "$FILE" 65536
release_vers "$FILE" stat 0 16
release_vers "$FILE" stat 0 64K
rm "$FILE"
echo "== release last block that straddles i_size"
create_file "$FILE" 6144
release_vers "$FILE" stat 1 1
release_vers "$FILE" stat 4K 4K
rm "$FILE"
echo "== release entire file past i_size"
create_file "$FILE" 8192
release_vers "$FILE" stat 0 100
release_vers "$FILE" stat 0 400K
# not deleting for the following little tests
echo "== releasing offline extents is fine"
release_vers "$FILE" stat 0 100
release_vers "$FILE" stat 0 400K
echo "== 0 count is fine"
release_vers "$FILE" stat 0 0
echo "== release past i_size is fine"
release_vers "$FILE" stat 100 1
release_vers "$FILE" stat 400K 4K
echo "== wrapped blocks fails"
release_vers "$FILE" stat $vers 0x8000000000000000 0x8000000000000000
echo "== releasing non-file fails"
mknod "$CHAR" c 1 3
release_vers "$CHAR" stat 0 1 2>&1 | t_filter_fs
release_vers "$CHAR" stat 0 4K 2>&1 | t_filter_fs
rm "$CHAR"
echo "== releasing a non-scoutfs file fails"
release_vers "/dev/null" stat 0 1
release_vers "/dev/null" stat 0 4K
echo "== releasing bad version fails"
release_vers "$FILE" 0 0 1
release_vers "$FILE" 0 0 4K
rm "$FILE"
@@ -108,9 +108,9 @@ for c in $(seq 0 4); do
start=$(fiemap_file "$FILE" | \
awk '($1 == "0:"){print substr($4, 0, length($4)- 2)}')
release_vers "$FILE" stat $a 1
release_vers "$FILE" stat $b 1
release_vers "$FILE" stat $c 1
release_vers "$FILE" stat $(($a * 4))K 4K
release_vers "$FILE" stat $(($b * 4))K 4K
release_vers "$FILE" stat $(($c * 4))K 4K
echo -n "$a $b $c:"

View File

@@ -29,14 +29,14 @@ create_file() {
release_vers() {
local file="$1"
local vers="$2"
local block="$3"
local count="$4"
local offset="$3"
local length="$4"
if [ "$vers" == "stat" ]; then
vers=$(scoutfs stat -s data_version "$file")
fi
scoutfs release "$file" "$vers" "$block" "$count"
scoutfs release "$file" -V "$vers" -o "$offset" -l "$length"
}
# if vers is "stat" then we ask stat_more for the data_version
@@ -44,14 +44,14 @@ stage_vers() {
local file="$1"
local vers="$2"
local offset="$3"
local count="$4"
local length="$4"
local contents="$5"
if [ "$vers" == "stat" ]; then
vers=$(scoutfs stat -s data_version "$file")
fi
scoutfs stage "$file" "$vers" "$offset" "$count" "$contents"
scoutfs stage "$contents" "$file" -V "$vers" -o "$offset" -l "$length"
}
FILE="$T_D0/file"
@@ -60,7 +60,7 @@ CHAR="$FILE-char"
echo "== create/release/stage single block file"
create_file "$FILE" 4096
cp "$FILE" "$T_TMP"
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
# make sure there only offline extents
fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
stage_vers "$FILE" stat 0 4096 "$T_TMP"
@@ -70,7 +70,7 @@ rm -f "$FILE"
echo "== create/release/stage larger file"
create_file "$FILE" $((4096 * 4096))
cp "$FILE" "$T_TMP"
release_vers "$FILE" stat 0 4096
release_vers "$FILE" stat 0 16M
# make sure there only offline extents
fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
stage_vers "$FILE" stat 0 $((4096 * 4096)) "$T_TMP"
@@ -83,7 +83,7 @@ cp "$FILE" "$T_TMP"
nr=1
while [ "$nr" -lt 10 ]; do
echo "attempt $nr" >> $seqres.full 2>&1
release_vers "$FILE" stat 0 1024
release_vers "$FILE" stat 0 4096K
sync
echo 3 > /proc/sys/vm/drop_caches
stage_vers "$FILE" stat 0 $((4096 * 1024)) "$T_TMP"
@@ -100,7 +100,7 @@ sync
stat "$FILE" > "$T_TMP.before"
scoutfs stat -s data_seq "$FILE" >> "$T_TMP.before"
scoutfs stat -s data_version "$FILE" >> "$T_TMP.before"
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
stage_vers "$FILE" stat 0 4096 "$T_TMP"
stat "$FILE" > "$T_TMP.after"
scoutfs stat -s data_seq "$FILE" >> "$T_TMP.after"
@@ -110,7 +110,7 @@ rm -f "$FILE"
echo "== stage does change meta_seq"
create_file "$FILE" 4096
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
sync
before=$(scoutfs stat -s meta_seq "$FILE")
stage_vers "$FILE" stat 0 4096 "$T_TMP"
@@ -121,7 +121,7 @@ rm -f "$FILE"
# XXX this now waits, demand staging should be own test
#echo "== can't write to offline"
#create_file "$FILE" 4096
#release_vers "$FILE" stat 0 1
#release_vers "$FILE" stat 0 4K
## make sure there only offline extents
#fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
#dd if=/dev/zero of="$FILE" conv=notrunc bs=4096 count=1 2>&1 | t_filter_fs
@@ -144,13 +144,13 @@ rm -f "$FILE"
echo "== wrapped region fails"
create_file "$FILE" 4096
stage_vers "$FILE" stat 0xFFFFFFFFFFFFFFFF 4096 /dev/zero
stage_vers "$FILE" stat 0xFFFFFFFFFFFFF000 4096 /dev/zero
rm -f "$FILE"
echo "== non-block aligned offset fails"
create_file "$FILE" 4096
cp "$FILE" "$T_TMP"
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
stage_vers "$FILE" stat 1 4095 "$T_TMP"
fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
rm -f "$FILE"
@@ -158,7 +158,7 @@ rm -f "$FILE"
echo "== non-block aligned len within block fails"
create_file "$FILE" 4096
cp "$FILE" "$T_TMP"
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
stage_vers "$FILE" stat 0 1024 "$T_TMP"
fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
rm -f "$FILE"
@@ -166,14 +166,14 @@ rm -f "$FILE"
echo "== partial final block that writes to i_size does work"
create_file "$FILE" 2048
cp "$FILE" "$T_TMP"
release_vers "$FILE" stat 0 1
release_vers "$FILE" stat 0 4K
stage_vers "$FILE" stat 0 2048 "$T_TMP"
cmp "$FILE" "$T_TMP"
rm -f "$FILE"
echo "== zero length stage doesn't bring blocks online"
create_file "$FILE" $((4096 * 100))
release_vers "$FILE" stat 0 100
release_vers "$FILE" stat 0 400K
stage_vers "$FILE" stat 4096 0 /dev/zero
fiemap_file "$FILE" | grep "^[ 0-9]*:" | grep -v "unknown"
rm -f "$FILE"
@@ -188,7 +188,7 @@ rm -f "$FILE"
#create_file "$FILE" 4096
#cp "$FILE" "$T_TMP"
#sync
#release_vers "$FILE" stat 0 1
#release_vers "$FILE" stat 0 4K
#md5sum "$FILE" 2>&1 | t_filter_fs
#stage_vers "$FILE" stat 0 4096 "$T_TMP"
#cmp "$FILE" "$T_TMP"

View File

@@ -17,7 +17,7 @@ diff_srch_find()
local n="$1"
sync
scoutfs search-xattrs -n "$n" -f "$T_M0" > "$T_TMP.srch"
scoutfs search-xattrs "$n" -p "$T_M0" > "$T_TMP.srch"
find_xattrs -d "$T_D0" -m "$T_M0" -n "$n" > "$T_TMP.find"
diff -u "$T_TMP.srch" "$T_TMP.find"

View File

@@ -29,7 +29,7 @@ release_file() {
local path="$1"
local vers=$(scoutfs stat -s data_version "$path")
scoutfs release "$path" "$vers" 0 $FILE_BLOCKS
scoutfs release "$path" -V "$vers" -o 0 -l $FILE_BYTES
}
stage_file() {
@@ -38,8 +38,8 @@ stage_file() {
local off=0
for a in $(seq 1 $NR_FRAGS); do
scoutfs stage "$path" "$vers" $off $FRAG_BYTES \
<(gen $FRAG_BLOCKS $a $a $a)
scoutfs stage <(gen $FRAG_BLOCKS $a $a $a) "$path" -V "$vers" \
-o $off -l $FRAG_BYTES
((off+=$FRAG_BYTES))
done
}

View File

@@ -15,7 +15,7 @@ release_file() {
local vers=$(scoutfs stat -s data_version "$path")
echo "releasing $path" >> "$T_TMP.log"
scoutfs release "$path" "$vers" 0 $BLOCKS
scoutfs release "$path" -V "$vers" -o 0 -l $BYTES
echo "released $path" >> "$T_TMP.log"
}
@@ -24,8 +24,8 @@ stage_file() {
local vers=$(scoutfs stat -s data_version "$path")
echo "staging $path" >> "$T_TMP.log"
scoutfs stage "$path" "$vers" 0 $BYTES \
"$DIR/good/$(basename $path)"
scoutfs stage "$DIR/good/$(basename $path)" "$path" -V "$vers" -o 0 -l $BYTES
echo "staged $path" >> "$T_TMP.log"
}

View File

@@ -19,7 +19,6 @@ endif
SCOUTFS_FORMAT_HASH := $(shell cat $(HASH_FILES) | md5sum | cut -b1-16)
CFLAGS := -Wall -O2 -Werror -D_FILE_OFFSET_BITS=64 -g -msse4.2 \
-Wpadded \
-fno-strict-aliasing \
-DSCOUTFS_FORMAT_HASH=0x$(SCOUTFS_FORMAT_HASH)LLU
@@ -47,7 +46,7 @@ endif
$(BIN): $(OBJ)
$(QU) [BIN $@]
$(VE)gcc -o $@ $^ -luuid -lm -lcrypto
$(VE)gcc -o $@ $^ -luuid -lm -lcrypto -lblkid
%.o %.d: %.c Makefile sparse.sh
$(QU) [CC $<]

View File

@@ -3,51 +3,302 @@
scoutfs \- scoutfs management utility
.SH DESCRIPTION
The
.b
scoutfs
utility provides commands to manage a scoutfs filesystem.
.B scoutfs
utility provides commands to create and manage a ScoutFS filesystem.
.SH COMMANDS
Note: Commands taking the
.B --path
option will, when the option is omitted, fall back to using the value of the
.I SCOUTFS_MOUNT_PATH
environment variable. If that variable is also absent the current working
directory will be used.
.TP
.BI "counters [\-t\] <sysfs topdir>"
.BI "df [-h|--human-readable] [-p|--path PATH]"
.sp
Displays the counters and their values for a mounted scoutfs filesystem.
Each counter and its value are printed on a line to stdout with
sufficient spaces seperating the name and value to align the values
after
Display available and used space on the ScoutFS data and metadata devices.
.RS 1.0i
.PD 0
.TP
.sp
.B "\-t"
Format the counters into a table that fills the display instead of
printing one counter per line. The names and values are padded to
create columns that fill the current width of the terminal.
.B "-h, --human-readable"
Output sizes in human-readable size units (e.g. 500G, 1.2P) rather than number
of ScoutFS allocation blocks.
.TP
.B "sysfs topdir"
Specify the mount's sysfs directory in which to find the
.B counters/
directory when then contains files for each counter.
The sysfs directory is typically
of the form
.I /sys/fs/scoutfs/f.<fsid>.r.<rid>/
\&.
.B "-p, --path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "data-waiting <ino> <iblock> <path>"
.BI "mkfs META-DEVICE DATA-DEVICE {-Q|--quorum-count} NUM [-m|--max-meta-size SIZE] [-d|--max-data-size SIZE] [-f|--force]"
.sp
Displays all the files and blocks for which there is a task blocked waiting on
Initialize a new ScoutFS filesystem on the target devices. Since ScoutFS uses
separate block devices for its metadata and data storage, two are required.
.sp
If
.B --force
option is not given, mkfs will check for existing filesystem signatures. It is
recommended to use
.B wipefs(8)
to remove non-ScoutFS filesystem signatures before proceeding, and
.B --force
to overwrite a previous ScoutFS filesystem.
.RS 1.0i
.PD 0
.TP
.sp
.B META-DEVICE
The path to the block device to be used for ScoutFS metadata. If possible, use
a faster block device for the metadata device.
.TP
.B DATA-DEVICE
The path to the block device to be used for ScoutFS file data. If possible, use
a larger block device for the data device.
.TP
.B "-Q, --quorum-count NUM"
The number of mounts needed to reach quorum and elect one
to be the server. Mounts of the filesystem will hang until a quorum of
mounts are operational.
.sp
Mounts with the
.B server_addr
mount option participate in quorum. The safest quorum number is the
smallest majority of an odd number of participating mounts. For
example,
two out of three total mounts. This ensures that there can only be one
set of mounts that can establish quorum.
.TP
.B "-m, --max-meta-size SIZE"
Limit the space used by ScoutFS on the metadata device to the
given size, rather than using the entire block device. Size is given as
an integer followed by a units digit: "K", "M", "G", "T", "P", to denote
kibibytes, mebibytes, etc.
.TP
.B "-d, --max-data-size SIZE"
Same as previous, but for limiting the size of the data device.
.TP
.B "-f, --force"
Ignore presence of existing data on the data and metadata devices.
.RE
.PD
.TP
.BI "stat FILE [-s|--single-field FIELD-NAME]"
.sp
Display ScoutFS-specific metadata fields for the given file.
.RS 1.0i
.PD 0
.TP
.sp
.B "FILE"
Path to the file.
.TP
.B "-s, --single-field FIELD-NAME"
Only output a single field's value instead of the default: all the stats with
one stat per line.
.sp
.TP
.RE
.PD
The fields are:
.RS 1.0i
.PD 0
.TP
.B "meta_seq"
The metadata change sequence. This changes each time the inode's metadata
is changed.
.TP
.B "data_seq"
The data change sequence. This changes each time the inode's data
is changed.
.TP
.B "data_version"
The data version changes every time the contents of the file changes,
or the file grows or shrinks.
.TP
.B "online_blocks"
The number of 4Kb data blocks that contain data and can be read.
.TP
.B "offline_blocks"
The number of 4Kb data blocks that are offline and would need to be
staged to be read.
.RE
.PD
.TP
.BI "statfs [-s|--single-field FIELD-NAME] [-p|--path PATH]"
.sp
Display ScoutFS-specific filesystem-wide metadata fields.
.RS 1.0i
.PD 0
.TP
.sp
.B "-s, --single-field FIELD-NAME"
Only ontput a single stat instead of all the stats with one stat per
line. The possible stat names are those given in the output.
.TP
.B "-p, --path PATH"
A path within a ScoutFS filesystem.
.sp
.TP
.RE
.PD
The fields are:
.RS 1.0i
.PD 0
.TP
.B "fsid"
The unique 64bit filesystem identifier for this filesystem.
.TP
.B "rid"
The unique 64bit random identifier for this mount of the filesystem.
This is generated for every new mount of the file system.
.TP
.B "committed_seq"
All seqs up to and including this seq have been
committed. Can be compared with meta_seq and data_seq from inodes in
.B stat
to discover if changes to a file have been committed to disk.
.TP
.B "total_meta_blocks"
The total number of 64K metadata blocks in the filesystem.
.TP
.B "total_data_blocks"
The total number of 4K data blocks in the filesystem.
.RE
.PD
.TP
.BI "counters [-t|--table] SYSFS-DIR"
.sp
Display the counters and their values for a mounted ScoutFS filesystem.
.RS 1.0i
.PD 0
.sp
.TP
.B SYSFS-DIR
The mount's sysfs directory in which to find the
.B counters/
directory when then contains files for each counter.
The sysfs directory is
of the form
.I /sys/fs/scoutfs/f.<fsid>.r.<rid>/
\&.
.TP
.B "-t, --table"
Format the counters into a columnar table that fills the width of the display
instead of printing one counter per line.
.RE
.PD
.TP
.BI "search-xattrs XATTR-NAME [-p|--path PATH]"
.sp
Display the inode numbers of inodes in the filesystem which may have
an extended attribute with the given name.
.sp
The results may contain false positives. The returned inode numbers
should be checked to verify that the extended attribute is in fact
present on the inode.
.RS 1.0i
.PD 0
.TP
.sp
.B XATTR-NAME
The full name of the extended attribute to search for as
described in the
.BR xattr (7)
manual page.
.TP
.B "-p|--path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "list-hidden-xattrs FILE"
.sp
Display extended attributes starting with the
.BR scoutfs.
prefix and containing the
.BR hide.
tag
which makes them invisible to
.BR listxattr (2) .
The names of each attribute are output, one per line. Their order
is not specified.
.RS 1.0i
.PD 0
.TP
.sp
.B "FILE"
The path to a file within a ScoutFS filesystem. File permissions must allow
reading.
.RE
.PD
.TP
.BI "walk-inodes {meta_seq|data_seq} FIRST-INODE LAST-INODE [-p|--path PATH]"
.sp
Walk an inode index in the file system and output the inode numbers
that are found between the first and last positions in the index.
.RS 1.0i
.PD 0
.sp
.TP
.BR meta_seq , data_seq
Which index to walk.
.TP
.B "FIRST-INODE"
An integer index value giving starting position of the index walk.
.I 0
is the first possible position.
.TP
.B "LAST-INODE"
An integer index value giving the last position to include in the index walk.
.I \-1
can be given to indicate the last possible position.
.TP
.B "-p|--path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "ino-path INODE-NUM [-p|--path PATH]"
.sp
Display all paths that reference an inode number.
.sp
Ongoing filesystem changes, such as renaming a common parent of multiple paths,
can cause displayed paths to be inconsistent.
.RS 1.0i
.PD 0
.sp
.TP
.B "INODE-NUM"
The inode number of the target inode.
.TP
.B "-p|--path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "data-waiting {-I|--inode} INODE-NUM {-B|--block} BLOCK-NUM [-p|--path PATH]"
.sp
Display all the files and blocks for which there is a task blocked waiting on
offline data.
.sp
The results are sorted by the file's inode number and the
logical block offset that is being waited on.
.sp
Each line of output specifies a block in a file that has a task waiting
Each line of output describes a block in a file that has a task waiting
and is formatted as:
.I "ino <nr> iblock <nr> ops [str]"
\&. The ops string indicates blocked operations seperated by commas and can
include
include
.B read
for a read operation,
.B write
@@ -58,156 +309,151 @@ for a truncate or extending write.
.PD 0
.sp
.TP
.B "ino"
.B "-I, --inode INODE-NUM"
Start iterating over waiting tasks from the given inode number.
Specifying 0 will show all waiting tasks.
Value of 0 will show all waiting tasks.
.TP
.B "iblock"
.B "-B, --block BLOCK-NUM"
Start iterating over waiting tasks from the given logical block number
in the starting inode. Specifying 0 will show blocks in the first inode
in the starting inode. Value of 0 will show blocks in the first inode
and then continue to show all blocks with tasks waiting in all the
remaining inodes.
.TP
.B "-p, --path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "data-wait-err {-I|--inode} INODE-NUM {-V|--version} VER-NUM {-F|--offset} OFF-NUM {-C|--count} COUNT {-O|--op} OP {-E|--err} ERR [-p|--path PATH]"
.sp
Return error from matching waiters.
.RS 1.0i
.PD 0
.sp
.TP
.B "-C, --count COUNT"
Count.
.TP
.B "-E, --err ERR"
Error.
.TP
.B "-F, --offset OFF-NUM"
Offset. May be expressed in bytes, or with KMGTP (Kibi, Mibi, etc.) size
suffixes.
.TP
.B "-I, --inode INODE-NUM"
Inode number.
.TP
.B "-O, --op OP"
Operation. One of: "read", "write", "change_size".
.TP
.B "-p, --path PATH"
A path within a ScoutFS filesystem.
.RE
.PD
.TP
.BI "stage ARCHIVE-FILE FILE {-V|--version} VERSION [-o, --offset OFF-NUM] [-l, --length LENGTH]"
.sp
.B Stage
(i.e. return to online) the previously-offline contents of a file by copying a
region from another file, the archive, and without updating regular inode
metadata. Any operations that are blocked by the existence of an offline
region will proceed once the region has been staged.
.RS 1.0i
.PD 0
.TP
.sp
.B "ARCHIVE-FILE"
The source file for the file contents being staged.
.TP
.B "FILE"
The regular file whose contents will be staged.
.TP
.B "-V, --version VERSION"
The data_version of the contents to be staged. It must match the
current data_version of the file.
.TP
.B "-o, --offset OFF-NUM"
The starting byte offset of the region to write. May be expressed in bytes, or with
KMGTP (Kibi, Mibi, etc.) size suffixes. Default is 0.
.TP
.B "-l, --length LENGTH"
Length of range (bytes or KMGTP units) of file to stage. Default is the file's
total size.
.RE
.PD
.TP
.BI "release FILE {-V|--version} VERSION [-o, --offset OFF-NUM] [-l, --length LENGTH]"
.sp
.B Release
the given region of the file. That is, remove the region's backing data and
leave an offline data region. Future attempts to read or write the offline
region will block until the region is restored by a
.B stage
write. This is used by userspace archive managers to free data space in the
ScoutFS filesystem once the file data has been archived.
.sp
Note: This only works on regular files with write permission. Releasing regions
that are already offline or sparse, including regions extending past the end of
the file, will silently succeed.
.RS 1.0i
.PD 0
.TP
.sp
.B "path"
A path to any inode in the target filesystem, typically the root
directory.
The path to the regular file whose region will be released.
.TP
.B "-V, --version VERSION"
The data_version of the contents to be released. It must match the current
data_version of the file. This ensures that a release operation is truncating
the same version of the data that was archived. (Use the
.BI "stat"
subcommand to obtain data version for a file.)
.TP
.B "-o, --offset OFF-NUM"
The starting byte offset of the region to write. May be expressed in bytes, or with
KMGTP (Kibi, Mibi, etc.) size suffixes. Default is 0.
.TP
.B "-l, --length LENGTH"
Length of range (bytes or KMGTP units) of file to stage. Default is the file's
total size.
.RE
.PD
.TP
.BI "find-xattrs <\-n\ name> <\-f path>"
.BI "setattr FILE [-d, --data-version=VERSION [-s, --size=SIZE [-o, --offline]]] [-t, --ctime=TIMESPEC]"
.sp
Displays the inode numbers of inodes in the filesystem which may have
an extended attribute with the given name.
.sp
The results may contain false positives. The returned inode numbers
should be checked to verify that the extended attribute is in fact
present on the inode.
.RS 1.0i
.PD 0
.TP
.sp
.B "-n name"
Specifies the full name of the extended attribute to search for as
described in the
.BR xattr (7)
manual page.
.TP
.B "-f path"
Specifies the path to any inode in the filesystem to search.
.RE
.PD
.TP
.BI "ino-path <ino> <path>"
.sp
Displays all the paths to links to the given inode number.
.sp
All the relative paths from the root directory to each link of the
target inode are output, one result per line. Each output path is
guaranteed to have been a valid path to a link at some point in the
past. An individual path won't be corrupted by a rename that occurs
during the search. The set of paths can be modified while the search is
running. A rename of a parent directory of all the paths, for example,
can result in output where the parent directory name component changes
in the middle of outputting all the paths.
Set ScoutFS-specific attributes on a newly created zero-length file.
.RS 1.0i
.PD 0
.sp
.TP
.B "ino"
The inode number of the target inode to resolve.
.B "-V, --data-version=VERSION"
Set data version.
.TP
.B "path"
A path to any inode in the target filesystem, typically the root
directory.
.B "-o, --offline"
Set file contents as offline, not sparse. Requires
.I --size
option also be present.
.TP
.B "-s, --size=SIZE"
Set file size. May be expressed in bytes, or with
KMGTP (Kibi, Mibi, etc.) size suffixes. Requires
.I --data-version
option also be present.
.TP
.B "-t, --ctime=TIMESPEC"
Set creation time using
.I "<seconds-since-epoch>.<nanoseconds>"
format.
.RE
.PD
.TP
.BI "listxattr-hidden <\-f path>"
.sp
Displays all the extended attributes starting with the
.BR scoutfs.
prefix and which contain the
.BR hide.
tag
which makes them invisible to
.BR listxattr (2)
\&.
The names of each attribute are output, one name per line. Their order
is determined by internal indexing implementation details and should not
be relied on.
.RS 1.0i
.PD 0
.TP
.sp
.B "-f path"
The path to the file whose extended attributes will be listed. The
user must have read permission to the inode.
.RE
.PD
.TP
.BI "mkfs <\-Q nr> <meta_dev_path> <data_dev_path> [-M meta_size] [-D data_size]"
.sp
Initialize a new empty filesystem in the target devices by writing empty
structures and a new superblock. Since ScoutFS uses separate block
devices for its metadata and data storage, both must be given.
.sp
This
.B unconditionally destroys
the contents of the devices, regardless of what they contain or who may be
using them. It simply writes new data structures into known offsets.
.B Be very careful that the devices do not contain data and are not actively in use.
.RS 1.0i
.PD 0
.TP
.sp
.B "-Q nr"
Specify the number of mounts needed to reach quorum and elect a mount
to start the server. Mounts of the filesystem will hang until this many
mounts are operational and can elect a server amongst themselves.
.sp
Mounts with the
.B server_addr
mount option participate in quorum. The safest quorum number is the
smallest majority of an odd number of participating mounts. For
example,
two out of three total mounts. This ensures that there can only be one
set of mounts that can establish quorum.
.sp
Degenerate quorums are possible, for example by specifying half of an
even number of mounts or less than half of the mount count, down to even
just one mount establishing quorum. These minority quorums carry the
risk of multiple quorums being established concurrently. Each quorum's
elected servers race to fence each other and can have the unlikely
outcome of continually racing to fence each other resulting in a
persistent loss of service.
.TP
.B "meta_dev_path"
The path to the device to be used for ScoutFS metadata. If possible,
use a faster block device for the metadata device. Its contents will be
unconditionally destroyed.
.TP
.B "data_dev_path"
The path to the device to be used for ScoutFS file data. If possible,
use a larger block device for the data device. Its contents will be
unconditionally destroyed.
.TP
.B "-M meta_size"
Limit the space used by the filesystem on the metadata device to the
given size, rather than using the entire block device. Size is given as
an integer followed by a units digit: "K", "M", "G", "T", "P", to denote
kibibytes, mebibytes, etc.
.TP
.B "-D data_size"
Same as previous, but for limiting the size of the data device.
.RE
.PD
.TP
.BI "print <path>"
.BI "print META-DEVICE"
.sp
Prints out all of the metadata in the file system. This makes no effort
to ensure that the structures are consistent as they're traversed and
@@ -217,236 +463,21 @@ output.
.PD 0
.TP
.sp
.B "path"
The path to the metadata device for filesystem whose metadata will
be printed. The command reads from the buffer cache of the device which
may not reflect the current blocks in the filesystem that may have been
written through another host or device. The local device's cache can be
manually flushed before printing, perhaps with the
.B \--flushbufs
command in the
.BR blockdev (8)
command.
.RE
.PD
.TP
.BI "release <path> <vers> <4KB block offset> <4KB block count>"
.sp
.B Release
the given logical block region of the file. That is, truncate away
any data blocks but leave behind offline data regions and do not change
the main inode metadata. Future attempts to read or write the block
region
will block until the region is restored by a
.B stage
write. This is used by userspace archive managers to store file data
in a remote archive tier.
.sp
This only works on regular files and with write permission. Releasing
regions that are already offline or are sparse, including past the end
of the file, silently succeed.
.RS 1.0i
.PD 0
.TP
.sp
.B "path"
The path to the regular file whose region will be released.
.TP
.B "version"
The current data version of the contents of the file. This ensures
that a release operation is truncating the version of the data that it
expects. It can't throw away data that was newly written while it was
performing its release operation. An inode's data_version is read
by the SCOUTFS_IOC_STATFS_MORE
ioctl.
.TP
.B "4KB block offset"
The 64bit logical block offset of the start of the region in units of 4KB.
.TP
.B "4KB block count"
The 64bit length of the region to release in units of 4KB blocks.
.RE
.PD
.TP
.BI "setattr <\-c ctime> <\-d data_version> -o <\-s i_size> <\-f path>
.sp
Set scoutfs specific metadata on a newly created inode without updating
other inode metadata.
.RS 1.0i
.PD 0
.TP
.sp
.B "-c ctime"
Specify the inode's creation GMT timespec with 64bit seconds and 32bit
nanoseconds formatted as
.B sec.nsec
\&.
.TP
.B "-d data_version"
Specify the inode's data version. This can only be set on regular files whose
current data_version is 0.
.TP
.B "-o"
Create an offline region for all of the file's data up to the specified
file size. This can only be set on regular files whose data_version is
0 and i_size must also be specified.
.TP
.B "-s i_size"
Set the inode's i_size. This can only be set on regular files whose
data_version is 0.
.TP
.B "-f path"
The file whose metadata will be set.
.RE
.PD
.TP
.BI "stage <file> <vers> <offset> <count> <archive file>"
.sp
.B Stage
the contents of the file by reading a region of another archive file and writing it
into the file region without updating regular inode metadata. Any tasks
that are blocked by the offline region will proceed once it has been
staged.
.RS 1.0i
.PD 0
.TP
.sp
.B "file"
The regular file whose contents will be staged.
.TP
.B "vers"
The data_version of the contents to be staged. It must match the
current data_version of the file.
.TP
.B "offset"
The starting byte offset of the region to write. This must be aligned
to 4KB blocks.
.TP
.B "count"
The length of the region to write in bytes. A length of 0 is a noop
and will immediately return success. The length must be a multiple
of 4KB blocks unless it is writing the final partial block in which
case it must end at i_size.
.TP
.B "archive file"
A file whose contents will be read and written as the staged region.
The start of the archive file will be used as the start of the region.
.RE
.PD
.TP
.BI "stat [-s single] <path>"
.sp
Display scoutfs metadata fields for the given inode.
.RS 1.0i
.PD 0
.TP
.sp
.B "-s single"
Only ontput a single stat instead of all the stats with one stat per
line. The possible stat names are those given in the output.
.TP
.B "path"
The path to the file whose inode field will be output.
.sp
.TP
.RE
.PD
The fields are as follows:
.RS 1.0i
.PD 0
.TP
.B "meta_seq"
The metadata change sequence. This changes each time the inode's metadata
is changed during a mount's transaction.
.TP
.B "data_seq"
The data change sequence. This changes each time the inode's data
is changed during a mount's transaction.
.TP
.B "data_version"
The data version changes every time any contents of the file changes,
including size changes. It can change many times during a syscall in a
transactions.
.TP
.B "online_blocks"
The number of 4Kb data blocks that contain data and can be read.
.TP
.B "online_blocks"
The number of 4Kb data blocks that are offline and would need to be
staged to be read.
.RE
.PD
.TP
.BI "statfs [-s single] <path>"
.sp
Display scoutfs metadata fields for a scoutfs filesystem.
.RS 1.0i
.PD 0
.TP
.sp
.B "-s single"
Only ontput a single stat instead of all the stats with one stat per
line. The possible stat names are those given in the output.
.TP
.B "path"
The path to any inode in the filesystem.
.sp
.TP
.RE
.PD
The fields are as follows:
.RS 1.0i
.PD 0
.TP
.B "fsid"
The unique 64bit filesystem identifier for this filesystem.
.TP
.B "rid"
The unique 64bit random identifier for this mount of the filesystem.
This is generated for every new mount of the file system.
.RE
.PD
.TP
.BI "walk-inodes <index> <first> <last> <path>"
.sp
Walks an inode index in the file system and outputs the inode numbers
that are found within the first and last positions in the index.
.RS 1.0i
.PD 0
.sp
.TP
.B "index"
Specifies the index to walk. The currently supported indices are
.B meta_seq
and
.B data_seq
\&.
.TP
.B "first"
The starting position of the index walk.
.I 0
is the first possible position in every index.
.TP
.B "last"
The last position to include in the index walk.
.I \-1
can be given as shorthand for the U64_MAX last possible position in
every index.
.TP
.B "path"
A path to any inode in the filesystem, typically the root directory.
.B "META-DEVICE"
The path to the metadata device for the filesystem whose metadata will be
printed. Since this command reads via the host's buffer cache, it may not
reflect the current blocks in the filesystem possibly written to the shared
block devices from another host, unless
.B blockdev \--flushbufs
command is used first.
.RE
.PD
.SH SEE ALSO
.BR scoutfs (5),
.BR xattr (7).
.BR xattr (7),
.BR blockdev (8),
.BR wipefs (8)
.SH AUTHORS
Zach Brown <zab@versity.com>

View File

@@ -16,6 +16,7 @@ BuildRequires: git
BuildRequires: gzip
BuildRequires: libuuid-devel
BuildRequires: openssl-devel
BuildRequires: libblkid-devel
#Requires: kmod-scoutfs = %{version}

94
utils/src/blkid.c Normal file
View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <blkid/blkid.h>
#include "util.h"
#include "format.h"
#include "blkid.h"
static int check_bdev_blkid(int fd, char *devname, char *usage)
{
blkid_probe pr;
int ret = 0;
pr = blkid_new_probe_from_filename(devname);
if (!pr) {
fprintf(stderr, "%s: failed to create a new libblkid probe\n", devname);
goto out;
}
/* enable partitions probing (superblocks are enabled by default) */
ret = blkid_probe_enable_partitions(pr, true);
if (ret == -1) {
fprintf(stderr, "%s: blkid_probe_enable_partitions() failed\n", devname);
goto out;
}
ret = blkid_do_fullprobe(pr);
if (ret == -1) {
fprintf(stderr, "%s: blkid_do_fullprobe() failed", devname);
goto out;
} else if (ret == 0) {
const char *type;
if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
fprintf(stderr, "%s: appears to contain an existing "
"%s superblock\n", devname, type);
ret = -1;
goto out;
}
if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
fprintf(stderr, "%s: appears to contain a partition "
"table (%s)\n", devname, type);
ret = -1;
goto out;
}
} else {
/* return 0 if ok */
ret = 0;
}
out:
blkid_free_probe(pr);
return ret;
}
static int check_bdev_scoutfs(int fd, char *devname, char *usage)
{
struct scoutfs_super_block *super = NULL;
int ret;
ret = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, (void **)&super);
if (ret)
return ret;
if (le32_to_cpu(super->hdr.magic) == SCOUTFS_SUPER_MAGIC) {
fprintf(stderr, "%s: appears to contain an existing "
"ScoutFS superblock\n", devname);
ret = -EINVAL;
}
free(super);
return ret;
}
/*
* Returns -1 on error, 0 otherwise.
*/
int check_bdev(int fd, char *devname, char *usage)
{
return check_bdev_blkid(fd, devname, usage) ?:
/* Our sig is not in blkid (yet) so check explicitly for us. */
check_bdev_scoutfs(fd, devname, usage);
}

6
utils/src/blkid.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _BLKID_H_
#define _BLKID_H_
int check_bdev(int fd, char *path, char *usage);
#endif

View File

@@ -4,35 +4,37 @@
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <argp.h>
#include "cmd.h"
#include "util.h"
static struct command {
static struct argp_command {
char *name;
char *opts;
char *summary;
struct argp *argp;
int group;
int (*func)(int argc, char **argv);
} cmds[100], *next_cmd = cmds;
} argp_cmds[100], *next_argp_cmd = argp_cmds;
#define cmd_for_each(com) for (com = cmds; com->func; com++)
#define cmd_for_each(com) for (com = argp_cmds; com->func; com++)
void cmd_register(char *name, char *opts, char *summary,
void cmd_register_argp(char *name, struct argp *argp, int group,
int (*func)(int argc, char **argv))
{
struct command *com = next_cmd++;
struct argp_command *com = next_argp_cmd++;
assert((com - cmds) < array_size(cmds));
assert((com - argp_cmds) < array_size(argp_cmds));
com->name = name;
com->opts = opts;
com->summary = summary;
com->argp = argp;
com->group = group;
com->func = func;
}
static struct command *find_command(char *name)
static struct argp_command *find_command(char *name)
{
struct command *com;
struct argp_command *com;
cmd_for_each(com) {
if (!strcmp(name, com->name))
@@ -42,28 +44,47 @@ static struct command *find_command(char *name)
return NULL;
}
static void usage(void)
static void print_cmds_for_group(int group)
{
struct command *com;
struct argp_command *com;
int largest = 0;
fprintf(stderr, "usage: scoutfs <command> [<args>]\n"
"Commands:\n");
/* Base alignment on all groups */
cmd_for_each(com)
largest = max(strlen(com->name), largest);
cmd_for_each(com) {
fprintf(stderr, " %*s %s\n %*s %s\n",
largest, com->name, com->opts,
largest, "", com->summary);
if (com->group == group) {
fprintf(stderr, " %*s %s\n %*s %s\n",
largest, com->name, com->argp->args_doc,
largest, "", com->argp->doc);
}
}
}
static void usage(void)
{
fprintf(stderr, "usage: scoutfs <command> [<args>]\n\n");
fprintf(stderr, "Selected fs defaults to current working directory.\n");
fprintf(stderr, "See <command> --help for more details.\n");
fprintf(stderr, "\nCore admin:\n");
print_cmds_for_group(GROUP_CORE);
fprintf(stderr, "\nAdditional Information:\n");
print_cmds_for_group(GROUP_INFO);
fprintf(stderr, "\nSearch Acceleration:\n");
print_cmds_for_group(GROUP_SEARCH);
fprintf(stderr, "\nArchival Agent Support:\n");
print_cmds_for_group(GROUP_AGENT);
fprintf(stderr, "\nDebugging commands:\n");
print_cmds_for_group(GROUP_DEBUG);
}
/* this returns a positive unix return code on error for some reason */
char cmd_execute(int argc, char **argv)
{
struct command *com = NULL;
struct argp_command *com = NULL;
int ret;
if (argc > 1) {

View File

@@ -1,7 +1,13 @@
#ifndef _CMD_H_
#define _CMD_H_
void cmd_register(char *name, char *opts, char *summary,
#define GROUP_CORE 0
#define GROUP_INFO 1
#define GROUP_SEARCH 2
#define GROUP_AGENT 3
#define GROUP_DEBUG 4
void cmd_register_argp(char *name, struct argp *argp, int group,
int (*func)(int argc, char **argv));
char cmd_execute(int argc, char **argv);

View File

@@ -12,7 +12,10 @@
#include <dirent.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "cmd.h"
@@ -37,7 +40,12 @@ static int cmp_counter_names(const void *A, const void *B)
return strcmp(a->name, b->name);
}
static int counters_cmd(int argc, char **argv)
struct counters_args {
char *sysfs_path;
bool tabular;
};
static int do_counters(struct counters_args *args)
{
unsigned int *name_wid = NULL;
unsigned int *val_wid = NULL;
@@ -50,9 +58,7 @@ static int counters_cmd(int argc, char **argv)
unsigned int rows = 0;
unsigned int cols = 0;
unsigned int nr = 0;
char *dir_arg = NULL;
struct dirent *dent;
bool table = false;
struct winsize ws;
DIR *dirp = NULL;
int dir_fd = -1;
@@ -64,28 +70,16 @@ static int counters_cmd(int argc, char **argv)
int r;
int c;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-t") == 0)
table = true;
else
dir_arg = argv[i];
}
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
if (ret < 0)
ret = ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
if (ret < 0)
table = false;
args->tabular = false;
if (dir_arg == NULL) {
printf("scoutfs counter-table: need mount sysfs dir (i.e. /sys/fs/scoutfs/$fr)\n");
return -EINVAL;
}
ret = snprintf(path, PATH_MAX, "%s/counters", dir_arg);
ret = snprintf(path, PATH_MAX, "%s/counters", args->sysfs_path);
if (ret < 1 || ret >= PATH_MAX) {
ret = -EINVAL;
fprintf(stderr, "invalid counter dir path '%s'\n", dir_arg);
fprintf(stderr, "invalid counter dir path '%s'\n", args->sysfs_path);
goto out;
}
@@ -120,6 +114,7 @@ static int counters_cmd(int argc, char **argv)
goto out;
}
memset(&ctrs[nr], 0, (alloced - nr) * sizeof(*ctrs));
memset(&name_wid[nr], 0, (alloced - nr) * sizeof(*name_wid));
}
ctr = &ctrs[nr];
@@ -191,7 +186,7 @@ static int counters_cmd(int argc, char **argv)
* one column of counters and use the max field widths from the
* initial counter reads.
*/
if (table) {
if (args->tabular) {
min_rows = 1;
cols = ws.ws_col / (name_wid[0] + 1 + val_wid[0] + 2);
max_rows = nr / cols;
@@ -276,9 +271,58 @@ out:
return ret;
};
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct counters_args *args = state->input;
switch (key) {
case 't':
args->tabular = true;
break;
case ARGP_KEY_ARG:
if (!args->sysfs_path)
args->sysfs_path = strdup_or_error(state, arg);
else
argp_error(state, "more than one argument given");
break;
case ARGP_KEY_FINI:
if (!args->sysfs_path)
argp_error(state, "no sysfs path argument given");
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "table", 't', NULL, 0, "Output in table format" },
{ NULL }
};
static struct argp argp = {
options,
parse_opt,
"SYSFS-DIR",
"Show counters for a mounted volume"
};
static int counters_cmd(int argc, char *argv[])
{
struct counters_args counters_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &counters_args);
if (ret)
return ret;
return do_counters(&counters_args);
}
static void __attribute__((constructor)) counters_ctor(void)
{
cmd_register("counters", "[-t] <sysfs dir>",
"show [tablular] counters for a given mounted volume",
counters_cmd);
cmd_register_argp("counters", &argp, GROUP_INFO, counters_cmd);
}

View File

@@ -1,7 +1,7 @@
#ifndef _DEV_H_
#define _DEV_H_
#define BASE_SIZE_FMT "%.2f %s"
#define BASE_SIZE_FMT "%.2f%s"
#define BASE_SIZE_ARGS(sz) size_flt(sz, 1), size_str(sz, 1)
#define SIZE_FMT "%llu (%.2f %s)"

View File

@@ -7,20 +7,28 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <assert.h>
#include <stdbool.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "cmd.h"
#include "dev.h"
#define ROWS 3
#define COLS 6
#define CHARS 20
static int df_cmd(int argc, char **argv)
struct df_args {
char *path;
bool human_readable;
};
static int do_df(struct df_args *args)
{
struct scoutfs_ioctl_alloc_detail ad;
struct scoutfs_ioctl_alloc_detail_entry *ade = NULL;
@@ -36,18 +44,9 @@ static int df_cmd(int argc, char **argv)
int r;
int c;
if (argc != 2) {
fprintf(stderr, "must specify path\n");
return -EINVAL;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[1], strerror(errno), errno);
return ret;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
sfm.valid_bytes = sizeof(struct scoutfs_ioctl_statfs_more);
ret = ioctl(fd, SCOUTFS_IOC_STATFS_MORE, &sfm);
@@ -96,18 +95,38 @@ static int df_cmd(int argc, char **argv)
snprintf(cells[1][0], CHARS, "MetaData");
snprintf(cells[1][1], CHARS, "64KB");
snprintf(cells[1][2], CHARS, "%llu", sfm.total_meta_blocks);
snprintf(cells[1][3], CHARS, "%llu", sfm.total_meta_blocks - meta_free);
snprintf(cells[1][4], CHARS, "%llu", meta_free);
if (args->human_readable) {
snprintf(cells[1][2], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS(sfm.total_meta_blocks * SCOUTFS_BLOCK_LG_SIZE));
snprintf(cells[1][3], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS((sfm.total_meta_blocks - meta_free)
* SCOUTFS_BLOCK_LG_SIZE));
snprintf(cells[1][4], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS(meta_free * SCOUTFS_BLOCK_LG_SIZE));
} else {
snprintf(cells[1][2], CHARS, "%llu", sfm.total_meta_blocks);
snprintf(cells[1][3], CHARS, "%llu", sfm.total_meta_blocks - meta_free);
snprintf(cells[1][4], CHARS, "%llu", meta_free);
}
snprintf(cells[1][5], CHARS, "%llu",
((sfm.total_meta_blocks - meta_free) * 100) /
sfm.total_meta_blocks);
snprintf(cells[2][0], CHARS, "Data");
snprintf(cells[2][1], CHARS, "4KB");
snprintf(cells[2][2], CHARS, "%llu", sfm.total_data_blocks);
snprintf(cells[2][3], CHARS, "%llu", sfm.total_data_blocks - data_free);
snprintf(cells[2][4], CHARS, "%llu", data_free);
if (args->human_readable) {
snprintf(cells[2][2], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS(sfm.total_data_blocks * SCOUTFS_BLOCK_SM_SIZE));
snprintf(cells[2][3], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS((sfm.total_data_blocks - data_free)
* SCOUTFS_BLOCK_SM_SIZE));
snprintf(cells[2][4], CHARS, BASE_SIZE_FMT,
BASE_SIZE_ARGS(data_free * SCOUTFS_BLOCK_SM_SIZE));
} else {
snprintf(cells[2][2], CHARS, "%llu", sfm.total_data_blocks);
snprintf(cells[2][3], CHARS, "%llu", sfm.total_data_blocks - data_free);
snprintf(cells[2][4], CHARS, "%llu", data_free);
}
snprintf(cells[2][5], CHARS, "%llu",
((sfm.total_data_blocks - data_free) * 100) /
sfm.total_data_blocks);
@@ -131,8 +150,51 @@ out:
return ret;
}
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct df_args *args = state->input;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case 'h':
args->human_readable = true;
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ "human-readable", 'h', NULL, 0, "Print sizes in human readable format (e.g., 1KB 234MB 2GB)"},
{ NULL }
};
static struct argp argp = {
options,
parse_opt,
"",
"Show metadata and data block usage"
};
static int df_cmd(int argc, char **argv)
{
struct df_args df_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &df_args);
if (ret)
return ret;
return do_df(&df_args);
}
static void __attribute__((constructor)) df_ctor(void)
{
cmd_register("df", "<path>",
"show metadata and data block usage", df_cmd);
cmd_register_argp("df", &argp, GROUP_CORE, df_cmd);
}

View File

@@ -8,44 +8,32 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "parse.h"
#include "cmd.h"
static int ino_path_cmd(int argc, char **argv)
struct ino_args {
char *path;
u64 ino;
};
static int do_ino_path(struct ino_args *args)
{
struct scoutfs_ioctl_ino_path args;
struct scoutfs_ioctl_ino_path ioctl_args;
struct scoutfs_ioctl_ino_path_result *res;
unsigned int result_bytes;
char *endptr = NULL;
u64 ino;
int ret;
int fd;
if (argc != 3) {
fprintf(stderr, "must specify ino and path\n");
return -EINVAL;
}
ino = strtoull(argv[1], &endptr, 0);
if (*endptr != '\0' ||
((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing inode number '%s'\n",
argv[1]);
return -EINVAL;
}
fd = open(argv[2], O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[2], strerror(errno), errno);
return ret;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
result_bytes = offsetof(struct scoutfs_ioctl_ino_path_result,
path[PATH_MAX]);
@@ -57,13 +45,13 @@ static int ino_path_cmd(int argc, char **argv)
goto out;
}
args.ino = ino;
args.dir_ino = 0;
args.dir_pos = 0;
args.result_ptr = (intptr_t)res;
args.result_bytes = result_bytes;
ioctl_args.ino = args->ino;
ioctl_args.dir_ino = 0;
ioctl_args.dir_pos = 0;
ioctl_args.result_ptr = (intptr_t)res;
ioctl_args.result_bytes = result_bytes;
for (;;) {
ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &args);
ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &ioctl_args);
if (ret < 0) {
ret = -errno;
if (ret == -ENOENT)
@@ -73,10 +61,10 @@ static int ino_path_cmd(int argc, char **argv)
printf("%.*s\n", res->path_bytes, res->path);
args.dir_ino = res->dir_ino;
args.dir_pos = res->dir_pos;
if (++args.dir_pos == 0) {
if (++args.dir_ino == 0)
ioctl_args.dir_ino = res->dir_ino;
ioctl_args.dir_pos = res->dir_pos;
if (++ioctl_args.dir_pos == 0) {
if (++ioctl_args.dir_ino == 0)
break;
}
}
@@ -92,8 +80,60 @@ out:
return ret;
};
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct ino_args *args = state->input;
int ret;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case ARGP_KEY_ARG:
if (args->ino)
argp_error(state, "more than one argument given");
ret = parse_u64(arg, &args->ino);
if (ret)
argp_error(state, "inode parse error");
break;
case ARGP_KEY_FINI:
if (!args->ino) {
argp_error(state, "must provide inode number");
}
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ NULL }
};
static struct argp argp = {
options,
parse_opt,
"INODE-NUM",
"Print paths that refer to inode number"
};
static int ino_path_cmd(int argc, char **argv)
{
struct ino_args ino_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &ino_args);
if (ret)
return ret;
return do_ino_path(&ino_args);
}
static void __attribute__((constructor)) ino_path_ctor(void)
{
cmd_register("ino-path", "<ino> <path>",
"print paths that refer to inode #", ino_path_cmd);
cmd_register_argp("ino-path", &argp, GROUP_SEARCH, ino_path_cmd);
}

View File

@@ -7,56 +7,31 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "cmd.h"
static struct option long_ops[] = {
{ "file", 1, NULL, 'f' },
{ NULL, 0, NULL, 0}
struct list_hidden_xattr_args {
char *filename;
};
static int listxattr_hidden_cmd(int argc, char **argv)
static int do_list_hidden_xattrs(struct list_hidden_xattr_args *args)
{
struct scoutfs_ioctl_listxattr_hidden lxh;
char *path = NULL;
char *buf = NULL;
char *name;
int fd = -1;
int bytes;
int len;
int ret;
int c;
int i;
while ((c = getopt_long(argc, argv, "f:", long_ops, NULL)) != -1) {
switch (c) {
case 'f':
path = strdup(optarg);
if (!path) {
fprintf(stderr, "path mem alloc failed\n");
ret = -ENOMEM;
goto out;
}
break;
case '?':
default:
ret = -EINVAL;
goto out;
}
}
if (path == NULL) {
fprintf(stderr, "must specify -f path to file\n");
ret = -EINVAL;
goto out;
}
memset(&lxh, 0, sizeof(lxh));
lxh.id_pos = 0;
lxh.hash_pos = 0;
@@ -69,11 +44,11 @@ static int listxattr_hidden_cmd(int argc, char **argv)
}
lxh.buf_ptr = (unsigned long)buf;
fd = open(path, O_RDONLY);
fd = open(args->filename, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
args->filename, strerror(errno), errno);
goto out;
}
@@ -139,9 +114,50 @@ out:
return ret;
};
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct list_hidden_xattr_args *args = state->input;
switch (key) {
case ARGP_KEY_ARG:
if (args->filename)
argp_error(state, "more than one filename argument given");
args->filename = strdup_or_error(state, arg);
break;
case ARGP_KEY_FINI:
if (!args->filename) {
argp_error(state, "must specify filename");
}
break;
default:
break;
}
return 0;
}
static struct argp argp = {
NULL,
parse_opt,
"FILE",
"Print the names of hidden xattrs on a file"
};
static int list_hidden_xattrs_cmd(int argc, char **argv)
{
struct list_hidden_xattr_args list_hidden_xattr_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &list_hidden_xattr_args);
if (ret)
return ret;
return do_list_hidden_xattrs(&list_hidden_xattr_args);
}
static void __attribute__((constructor)) listxattr_hidden_ctor(void)
{
cmd_register("listxattr-hidden", "-f <path>",
"print the names of hidden xattrs on the file",
listxattr_hidden_cmd);
cmd_register_argp("list-hidden-xattrs", &argp, GROUP_INFO, list_hidden_xattrs_cmd);
}

View File

@@ -4,10 +4,20 @@
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <argp.h>
#include "cmd.h"
#include "util.h"
/*
* Ensure no compiler-added padding sneaks into structs defined in these
* headers.
*/
#pragma GCC diagnostic error "-Wpadded"
#include "format.h"
#include "ioctl.h"
#pragma GCC diagnostic pop
int main(int argc, char **argv)
{
/*

View File

@@ -11,12 +11,12 @@
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <inttypes.h>
#include <argp.h>
#include "sparse.h"
#include "cmd.h"
@@ -30,6 +30,7 @@
#include "bitops.h"
#include "btree.h"
#include "leaf_item_hash.h"
#include "blkid.h"
static int write_raw_block(int fd, u64 blkno, int shift, void *blk)
{
@@ -99,6 +100,15 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd,
return write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt);
}
struct mkfs_args {
unsigned long long quorum_count;
char *meta_device;
char *data_device;
unsigned long long max_meta_size;
unsigned long long max_data_size;
bool force;
};
/*
* Make a new file system by writing:
* - super blocks
@@ -108,19 +118,18 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd,
* Superblock is written to both metadata and data devices, everything else is
* written only to the metadata device.
*/
static int write_new_fs(char *meta_path, char *data_path,
int meta_fd, int data_fd,
u8 quorum_count,
u64 max_meta_size, u64 max_data_size)
static int do_mkfs(struct mkfs_args *args)
{
struct scoutfs_super_block *super;
struct scoutfs_super_block *super = NULL;
struct scoutfs_inode inode;
struct scoutfs_alloc_list_block *lblk;
struct scoutfs_btree_block *bt;
struct scoutfs_btree_block *bt = NULL;
struct scoutfs_key key;
struct timeval tv;
int meta_fd = -1;
int data_fd = -1;
char uuid_str[37];
void *zeros;
void *zeros = NULL;
u64 blkno;
u64 meta_size;
u64 data_size;
@@ -135,6 +144,33 @@ static int write_new_fs(char *meta_path, char *data_path,
gettimeofday(&tv, NULL);
meta_fd = open(args->meta_device, O_RDWR | O_EXCL);
if (meta_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
args->meta_device, strerror(errno), errno);
goto out;
}
if (!args->force) {
ret = check_bdev(meta_fd, args->meta_device, "meta");
if (ret)
return ret;
}
data_fd = open(args->data_device, O_RDWR | O_EXCL);
if (data_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
args->data_device, strerror(errno), errno);
goto out;
}
if (!args->force) {
ret = check_bdev(data_fd, args->data_device, "data");
if (ret)
return ret;
}
super = calloc(1, SCOUTFS_BLOCK_SM_SIZE);
bt = calloc(1, SCOUTFS_BLOCK_LG_SIZE);
zeros = calloc(1, SCOUTFS_BLOCK_SM_SIZE);
@@ -145,13 +181,13 @@ static int write_new_fs(char *meta_path, char *data_path,
goto out;
}
ret = device_size(meta_path, meta_fd, 2ULL * (1024 * 1024 * 1024),
max_meta_size, "meta", &meta_size);
ret = device_size(args->meta_device, meta_fd, 2ULL * (1024 * 1024 * 1024),
args->max_meta_size, "meta", &meta_size);
if (ret)
goto out;
ret = device_size(data_path, data_fd, 8ULL * (1024 * 1024 * 1024),
max_data_size, "data", &data_size);
ret = device_size(args->data_device, data_fd, 8ULL * (1024 * 1024 * 1024),
args->max_data_size, "data", &data_size);
if (ret)
goto out;
@@ -179,7 +215,7 @@ static int write_new_fs(char *meta_path, char *data_path,
super->total_data_blocks = cpu_to_le64(last_data - first_data + 1);
super->first_data_blkno = cpu_to_le64(first_data);
super->last_data_blkno = cpu_to_le64(last_data);
super->quorum_count = quorum_count;
super->quorum_count = args->quorum_count;
/* fs root starts with root inode and its index items */
blkno = next_meta++;
@@ -293,7 +329,7 @@ static int write_new_fs(char *meta_path, char *data_path,
if (fsync(data_fd)) {
ret = -errno;
fprintf(stderr, "failed to fsync '%s': %s (%d)\n",
data_path, strerror(errno), errno);
args->data_device, strerror(errno), errno);
goto out;
}
@@ -306,7 +342,7 @@ static int write_new_fs(char *meta_path, char *data_path,
if (fsync(meta_fd)) {
ret = -errno;
fprintf(stderr, "failed to fsync '%s': %s (%d)\n",
meta_path, strerror(errno), errno);
args->meta_device, strerror(errno), errno);
goto out;
}
@@ -321,8 +357,8 @@ static int write_new_fs(char *meta_path, char *data_path,
" 64KB metadata blocks: "SIZE_FMT"\n"
" 4KB data blocks: "SIZE_FMT"\n"
" quorum count: %u\n",
meta_path,
data_path,
args->meta_device,
args->data_device,
le64_to_cpu(super->hdr.fsid),
le64_to_cpu(super->format_hash),
uuid_str,
@@ -340,102 +376,106 @@ out:
free(bt);
if (zeros)
free(zeros);
if (meta_fd != -1)
close(meta_fd);
if (data_fd != -1)
close(data_fd);
return ret;
}
static struct option long_ops[] = {
{ "quorum_count", 1, NULL, 'Q' },
{ NULL, 0, NULL, 0}
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct mkfs_args *args = state->input;
int ret;
switch (key) {
case 'Q':
ret = parse_u64(arg, &args->quorum_count);
if (ret)
return ret;
break;
case 'f':
args->force = true;
break;
case 'm': /* max-meta-size */
{
u64 prev_val;
ret = parse_human(arg, &args->max_meta_size);
if (ret)
return ret;
prev_val = args->max_meta_size;
args->max_meta_size = round_down(args->max_meta_size, SCOUTFS_BLOCK_LG_SIZE);
if (args->max_meta_size != prev_val)
fprintf(stderr, "Meta dev size %llu rounded down to %llu bytes\n",
prev_val, args->max_meta_size);
break;
}
case 'd': /* max-data-size */
{
u64 prev_val;
ret = parse_human(arg, &args->max_data_size);
if (ret)
return ret;
prev_val = args->max_data_size;
args->max_data_size = round_down(args->max_data_size, SCOUTFS_BLOCK_SM_SIZE);
if (args->max_data_size != prev_val)
fprintf(stderr, "Data dev size %llu rounded down to %llu bytes\n",
prev_val, args->max_data_size);
break;
}
case ARGP_KEY_ARG:
if (!args->meta_device)
args->meta_device = strdup_or_error(state, arg);
else if (!args->data_device)
args->data_device = strdup_or_error(state, arg);
else
argp_error(state, "more than two arguments given");
break;
case ARGP_KEY_FINI:
if (!args->quorum_count)
argp_error(state, "must provide nonzero quorum count with --quorum-count|-Q option");
if (!args->meta_device)
argp_error(state, "no metadata device argument given");
if (!args->data_device)
argp_error(state, "no data device argument given");
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "quorum-count", 'Q', "NUM", 0, "Number of voters required to use the filesystem [Required]"},
{ "force", 'f', NULL, 0, "Overwrite existing data on block devices"},
{ "max-meta-size", 'm', "SIZE", 0, "Use a size less than the base metadata device size (bytes or KMGTP units)"},
{ "max-data-size", 'd', "SIZE", 0, "Use a size less than the base data device size (bytes or KMGTP units)"},
{ NULL }
};
static int mkfs_func(int argc, char *argv[])
static struct argp argp = {
options,
parse_opt,
"META-DEVICE DATA-DEVICE",
"Initialize a new ScoutFS filesystem"
};
static int mkfs_cmd(int argc, char *argv[])
{
unsigned long long ull;
u8 quorum_count = 0;
u64 max_data_size = 0;
u64 max_meta_size = 0;
char *end = NULL;
char *meta_path;
char *data_path;
int meta_fd;
int data_fd;
struct mkfs_args mkfs_args = {0};
int ret;
int c;
while ((c = getopt_long(argc, argv, "Q:D:M:", long_ops, NULL)) != -1) {
switch (c) {
case 'Q':
ull = strtoull(optarg, &end, 0);
if (*end != '\0' || ull == 0 ||
ull > SCOUTFS_QUORUM_MAX_COUNT) {
printf("scoutfs: invalid quorum count '%s'\n",
optarg);
return -EINVAL;
}
quorum_count = ull;
break;
case 'D':
ret = parse_human(optarg, &max_data_size);
if (ret < 0) {
printf("scoutfs: invalid data device size '%s'\n",
optarg);
return ret;
}
break;
case 'M':
ret = parse_human(optarg, &max_meta_size);
if (ret < 0) {
printf("scoutfs: invalid meta device size '%s'\n",
optarg);
return ret;
}
break;
case '?':
default:
return -EINVAL;
}
}
if (optind + 2 != argc) {
printf("scoutfs: mkfs: paths to metadata and data devices are required\n");
return -EINVAL;
}
meta_path = argv[optind];
data_path = argv[optind + 1];
if (!quorum_count) {
printf("provide quorum count with --quorum_count|-Q option\n");
return -EINVAL;
}
meta_fd = open(meta_path, O_RDWR | O_EXCL);
if (meta_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open metadata device '%s': %s (%d)\n",
meta_path, strerror(errno), errno);
ret = argp_parse(&argp, argc, argv, 0, NULL, &mkfs_args);
if (ret)
return ret;
}
data_fd = open(data_path, O_RDWR | O_EXCL);
if (data_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open data device '%s': %s (%d)\n",
data_path, strerror(errno), errno);
return ret;
}
ret = write_new_fs(meta_path, data_path, meta_fd, data_fd,
quorum_count, max_meta_size, max_data_size);
close(meta_fd);
close(data_fd);
return ret;
return do_mkfs(&mkfs_args);
}
static void __attribute__((constructor)) mkfs_ctor(void)
{
cmd_register("mkfs", "<path>", "write a new file system", mkfs_func);
cmd_register_argp("mkfs", &argp, GROUP_CORE, mkfs_cmd);
/* for lack of some other place to put these.. */
build_assert(sizeof(uuid_t) == SCOUTFS_UUID_BYTES);

View File

@@ -2,6 +2,7 @@
#define _PARSE_H_
#include <sys/time.h>
#include <argp.h>
int parse_human(char* str, u64 *val_ret);
int parse_u64(char *str, u64 *val_ret);
@@ -9,4 +10,13 @@ int parse_s64(char *str, s64 *val_ret);
int parse_u32(char *str, u32 *val_ret);
int parse_timespec(char *str, struct timespec *ts);
static inline char* strdup_or_error(const struct argp_state *state, char *str)
{
char *new = strdup(str);
if (!new)
argp_error(state, "memory allocation failed");
return new;
}
#endif

View File

@@ -12,8 +12,10 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "bitmap.h"
@@ -24,27 +26,6 @@
#include "srch.h"
#include "leaf_item_hash.h"
static void *read_block(int fd, u64 blkno, int shift)
{
size_t size = 1ULL << shift;
ssize_t ret;
void *buf;
buf = malloc(size);
if (!buf)
return NULL;
ret = pread(fd, buf, size, blkno << shift);
if (ret != size) {
fprintf(stderr, "read blkno %llu returned %zd: %s (%d)\n",
blkno, ret, strerror(errno), errno);
free(buf);
buf = NULL;
}
return buf;
}
static void print_block_header(struct scoutfs_block_header *hdr, int size)
{
u32 crc = crc_block(hdr, size);
@@ -465,9 +446,9 @@ static int print_btree_block(int fd, struct scoutfs_super_block *super,
int ret;
int i;
bt = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT);
if (!bt)
return -ENOMEM;
ret = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT, (void **)&bt);
if (ret)
return ret;
if (bt->level == level) {
printf("%s btree blkno %llu\n"
@@ -559,15 +540,16 @@ static int print_alloc_list_block(int fd, char *str,
u64 start;
u64 len;
int wid;
int ret;
int i;
blkno = le64_to_cpu(ref->blkno);
if (blkno == 0)
return 0;
lblk = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT);
if (!lblk)
return -ENOMEM;
ret = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, (void **)&lblk);
if (ret)
return ret;
printf("%s alloc_list_block blkno %llu\n", str, blkno);
print_block_header(&lblk->hdr, SCOUTFS_BLOCK_LG_SIZE);
@@ -617,11 +599,10 @@ static int print_srch_block(int fd, struct scoutfs_srch_ref *ref, int level)
if (blkno == 0)
return 0;
srp = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT);
if (!srp) {
ret = -ENOMEM;
ret = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, (void **)&srp);
if (ret)
goto out;
}
srb = (void *)srp;
printf("srch %sblock blkno %llu\n", level ? "parent " : "", blkno);
@@ -677,7 +658,6 @@ out:
struct print_recursion_args {
struct scoutfs_super_block *super;
int fd;
u8 __pad[4];
};
/* same as fs item but with a small header in the value */
@@ -763,9 +743,9 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super,
if (ref->blkno == 0)
return 0;
bt = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT);
if (!bt)
return -ENOMEM;
ret = read_block(fd, le64_to_cpu(ref->blkno), SCOUTFS_BLOCK_LG_SHIFT, (void **)&bt);
if (ret)
return ret;
node = avl_first(&bt->item_root);
while (node) {
@@ -828,11 +808,9 @@ static int print_quorum_blocks(int fd, struct scoutfs_super_block *super)
for (i = 0; i < SCOUTFS_QUORUM_BLOCKS; i++) {
blkno = SCOUTFS_QUORUM_BLKNO + i;
free(blk);
blk = read_block(fd, blkno, SCOUTFS_BLOCK_SM_SHIFT);
if (!blk) {
ret = -ENOMEM;
ret = read_block(fd, blkno, SCOUTFS_BLOCK_SM_SHIFT, (void **)&blk);
if (ret)
goto out;
}
if (blk->voter_rid != 0) {
printf("quorum block blkno %llu\n"
@@ -876,11 +854,15 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
uuid_unparse(super->uuid, uuid_str);
if (!(le64_to_cpu(super->flags) && SCOUTFS_FLAG_IS_META_BDEV))
fprintf(stderr,
"**** Printing metadata from a data device! Did you mean to do this? ****\n");
printf("super blkno %llu\n", blkno);
print_block_header(&super->hdr, SCOUTFS_BLOCK_SM_SIZE);
printf(" format_hash %llx uuid %s\n",
le64_to_cpu(super->format_hash), uuid_str);
printf(" flags: 0x%016llx\n", super->flags);
printf(" flags: 0x%016llx\n", le64_to_cpu(super->flags));
server_addr = alloc_addr_str(&super->server_addr);
if (!server_addr)
@@ -943,6 +925,10 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
free(server_addr);
}
struct print_args {
char *meta_device;
};
static int print_volume(int fd)
{
struct scoutfs_super_block *super = NULL;
@@ -952,9 +938,9 @@ static int print_volume(int fd)
int err;
int i;
super = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT);
if (!super)
return -ENOMEM;
ret = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, (void **)&super);
if (ret)
return ret;
print_super_block(super, SCOUTFS_SUPER_BLKNO);
@@ -1034,23 +1020,16 @@ static int print_volume(int fd)
return ret;
}
static int print_cmd(int argc, char **argv)
static int do_print(struct print_args *args)
{
char *path;
int ret;
int fd;
if (argc != 2) {
printf("scoutfs print: a single path argument is required\n");
return -EINVAL;
}
path = argv[1];
fd = open(path, O_RDONLY);
fd = open(args->meta_device, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
args->meta_device, strerror(errno), errno);
return ret;
}
@@ -1059,8 +1038,49 @@ static int print_cmd(int argc, char **argv)
return ret;
};
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct print_args *args = state->input;
switch (key) {
case ARGP_KEY_ARG:
if (!args->meta_device)
args->meta_device = strdup_or_error(state, arg);
else
argp_error(state, "more than one argument given");
break;
case ARGP_KEY_FINI:
if (!args->meta_device)
argp_error(state, "no metadata device argument given");
break;
default:
break;
}
return 0;
}
static struct argp argp = {
NULL,
parse_opt,
"META-DEV",
"Print metadata structures"
};
static int print_cmd(int argc, char **argv)
{
struct print_args print_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &print_args);
if (ret)
return ret;
return do_print(&print_args);
}
static void __attribute__((constructor)) print_ctor(void)
{
cmd_register("print", "<device>", "print metadata structures",
print_cmd);
cmd_register_argp("print", &argp, GROUP_DEBUG, print_cmd);
}

View File

@@ -7,38 +7,36 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "cmd.h"
static struct option long_ops[] = {
{ "name", 1, NULL, 'n' },
{ "file", 1, NULL, 'f' },
{ NULL, 0, NULL, 0}
};
/*
* There are significant constant costs to each search call, we
* want to get the inodes in as few calls as possible.
*/
#define BATCH_SIZE 1000000
static int search_xattrs_cmd(int argc, char **argv)
struct xattr_args {
char *name;
char *path;
};
static int do_search_xattrs(struct xattr_args *args)
{
struct scoutfs_ioctl_search_xattrs sx;
char *path = NULL;
char *name = NULL;
struct scoutfs_ioctl_search_xattrs sx = {0};
u64 *inos = NULL;
int fd = -1;
int ret;
int c;
int i;
memset(&sx, 0, sizeof(sx));
inos = malloc(BATCH_SIZE * sizeof(inos[0]));
if (!inos) {
fprintf(stderr, "inos mem alloc failed\n");
@@ -46,56 +44,15 @@ static int search_xattrs_cmd(int argc, char **argv)
goto out;
}
while ((c = getopt_long(argc, argv, "f:n:", long_ops, NULL)) != -1) {
switch (c) {
case 'f':
path = strdup(optarg);
if (!path) {
fprintf(stderr, "path mem alloc failed\n");
ret = -ENOMEM;
goto out;
}
break;
case 'n':
name = strdup(optarg);
if (!name) {
fprintf(stderr, "name mem alloc failed\n");
ret = -ENOMEM;
goto out;
}
break;
case '?':
default:
ret = -EINVAL;
goto out;
}
}
if (path == NULL) {
fprintf(stderr, "must specify -f path to file\n");
ret = -EINVAL;
goto out;
}
if (name == NULL) {
fprintf(stderr, "must specify -n xattr name to search for\n");
ret = -EINVAL;
goto out;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
goto out;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
sx.next_ino = 0;
sx.last_ino = U64_MAX;
sx.name_ptr = (unsigned long)name;
sx.name_ptr = (unsigned long)args->name;
sx.inodes_ptr = (unsigned long)inos;
sx.name_bytes = strlen(name);
sx.name_bytes = strlen(args->name);
sx.nr_inodes = BATCH_SIZE;
do {
@@ -119,16 +76,63 @@ static int search_xattrs_cmd(int argc, char **argv)
out:
if (fd >= 0)
close(fd);
free(path);
free(name);
free(inos);
return ret;
};
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct xattr_args *args = state->input;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case ARGP_KEY_ARG:
if (args->name)
argp_error(state, "more than one name argument given");
args->name = strdup_or_error(state, arg);
break;
case ARGP_KEY_FINI:
if (!args->name) {
argp_error(state, "must provide xattr containing .srch. scoutfs tag");
}
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ NULL }
};
static struct argp argp = {
options,
parse_opt,
"XATTR-NAME",
"Print inode numbers of inodes which may have given xattr"
};
static int search_xattrs_cmd(int argc, char **argv)
{
struct xattr_args xattr_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &xattr_args);
if (ret)
return ret;
return do_search_xattrs(&xattr_args);
}
static void __attribute__((constructor)) search_xattrs_ctor(void)
{
cmd_register("search-xattrs", "-n name -f <path>",
"print inode numbers of inodes which may have given xattr",
search_xattrs_cmd);
cmd_register_argp("search-xattrs", &argp, GROUP_INFO, search_xattrs_cmd);
}

View File

@@ -7,8 +7,9 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <assert.h>
#include <argp.h>
#include <stdbool.h>
#include "sparse.h"
#include "util.h"
@@ -17,83 +18,40 @@
#include "parse.h"
#include "cmd.h"
static struct option long_ops[] = {
{ "ctime", 1, NULL, 'c' },
{ "data_version", 1, NULL, 'd' },
{ "file", 1, NULL, 'f' },
{ "offline", 0, NULL, 'o' },
{ "i_size", 1, NULL, 's' },
{ NULL, 0, NULL, 0}
struct setattr_args {
char *filename;
struct timespec ctime;
u64 data_version;
u64 i_size;
bool offline;
};
static int setattr_more_cmd(int argc, char **argv)
static int do_setattr(struct setattr_args *args)
{
struct scoutfs_ioctl_setattr_more sm;
struct timespec ctime;
char *path = NULL;
int ret;
struct scoutfs_ioctl_setattr_more sm = {0};
int fd = -1;
int c;
int ret;
memset(&sm, 0, sizeof(sm));
while ((c = getopt_long(argc, argv, "c:d:f:os:", long_ops, NULL)) != -1) {
switch (c) {
case 'c':
ret = parse_timespec(optarg, &ctime);
if (ret)
goto out;
break;
case 'd':
ret = parse_u64(optarg, &sm.data_version);
if (ret)
goto out;
break;
case 'f':
path = strdup(optarg);
if (!path) {
fprintf(stderr, "path mem alloc failed\n");
ret = -ENOMEM;
goto out;
}
break;
case 'o':
sm.flags |= SCOUTFS_IOC_SETATTR_MORE_OFFLINE;
break;
case 's':
ret = parse_u64(optarg, &sm.i_size);
if (ret)
goto out;
break;
case '?':
default:
ret = -EINVAL;
goto out;
}
}
if (path == NULL) {
fprintf(stderr, "must specify -f path to file\n");
ret = -EINVAL;
goto out;
}
fd = open(path, O_WRONLY);
fd = open(args->filename, O_WRONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
args->filename, strerror(errno), errno);
goto out;
}
sm.ctime_sec = ctime.tv_sec;
sm.ctime_nsec = ctime.tv_nsec;
sm.ctime_sec = args->ctime.tv_sec;
sm.ctime_nsec = args->ctime.tv_nsec;
sm.data_version = args->data_version;
if (args->offline)
sm.flags |= SCOUTFS_IOC_SETATTR_MORE_OFFLINE;
sm.i_size = args->i_size;
ret = ioctl(fd, SCOUTFS_IOC_SETATTR_MORE, &sm);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "setattr_more ioctl failed on '%s': "
"%s (%d)\n", path, strerror(errno), errno);
"%s (%d)\n", args->filename, strerror(errno), errno);
goto out;
}
@@ -104,9 +62,83 @@ out:
return ret;
}
static int parse_opt(int key, char *arg, struct argp_state *state)
{
struct setattr_args *args = state->input;
int ret;
switch (key) {
case 't': /* timespec */
ret = parse_timespec(arg, &args->ctime);
if (ret)
return ret;
break;
case 'V': /* data version */
ret = parse_u64(arg, &args->data_version);
if (ret)
return ret;
if (args->data_version == 0)
argp_error(state, "data version must not be 0");
break;
case 's': /* size */
ret = parse_human(arg, &args->i_size);
if (ret)
return ret;
break;
case 'o': /* offline */
args->offline = true;
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");
if (args->i_size && !args->data_version) {
argp_error(state, "must provide data-version if using --size option");
}
if (!args->i_size && args->offline) {
argp_error(state, "must provide size if using --offline option");
}
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "ctime", 't', "TIMESPEC", 0, "Set creation time using \"<seconds-since-epoch>.<nanoseconds>\" format"},
{ "data-version", 'V', "VERSION", 0, "Set data version"},
{ "size", 's', "SIZE", 0, "Set file size (bytes or KMGTP units). Requires --data-version"},
{ "offline", 'o', NULL, 0, "Set file contents as offline, not sparse. Requires --size"},
{ NULL }
};
static struct argp argp = {
options,
parse_opt,
"FILE",
"Set attributes on newly-created zero-length file"
};
static int setattr_cmd(int argc, char **argv)
{
struct setattr_args setattr_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &setattr_args);
if (ret)
return ret;
return do_setattr(&setattr_args);
}
static void __attribute__((constructor)) setattr_more_ctor(void)
{
cmd_register("setattr", "-c ctime -d data_version -o -s i_size -f <path>",
"set attributes on file with no data",
setattr_more_cmd);
cmd_register_argp("setattr", &argp, GROUP_AGENT, setattr_cmd);
}

View File

@@ -8,82 +8,50 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <argp.h>
#include "sparse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "parse.h"
#include "cmd.h"
static int stage_cmd(int argc, char **argv)
struct stage_args {
char *archive_path;
char *path;
u64 data_version;
u64 offset;
u64 length;
};
static int do_stage(struct stage_args *args)
{
struct scoutfs_ioctl_stage args;
struct scoutfs_ioctl_stage ioctl_args;
unsigned int buf_len = 1024 * 1024;
unsigned int bytes;
char *endptr = NULL;
char *buf = NULL;
int afd = -1;
int fd = -1;
u64 offset;
u64 count;
u64 vers;
int ret;
if (argc != 6) {
fprintf(stderr, "must specify moar args\n");
return -EINVAL;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[1], strerror(errno), errno);
return ret;
}
vers = strtoull(argv[2], &endptr, 0);
if (*endptr != '\0' ||
((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing data version '%s'\n",
argv[2]);
ret = -EINVAL;
goto out;
}
offset = strtoull(argv[3], &endptr, 0);
if (*endptr != '\0' ||
((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing offset '%s'\n",
argv[3]);
ret = -EINVAL;
goto out;
}
count = strtoull(argv[4], &endptr, 0);
if (*endptr != '\0' ||
((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing count '%s'\n",
argv[4]);
ret = -EINVAL;
goto out;
}
if (count > INT_MAX) {
fprintf(stderr, "count %llu too large, limited to %d\n",
count, INT_MAX);
ret = -EINVAL;
goto out;
}
afd = open(argv[5], O_RDONLY);
afd = open(args->archive_path, O_RDONLY);
if (afd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[5], strerror(errno), errno);
args->archive_path, strerror(errno), errno);
goto out;
}
fd = open(args->path, O_RDWR);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
args->path, strerror(errno), errno);
return ret;
}
buf = malloc(buf_len);
if (!buf) {
fprintf(stderr, "couldn't allocate %u byte buffer\n", buf_len);
@@ -91,9 +59,9 @@ static int stage_cmd(int argc, char **argv)
goto out;
}
while (count) {
while (args->length) {
bytes = min(count, buf_len);
bytes = min(args->length, buf_len);
ret = read(afd, buf, bytes);
if (ret <= 0) {
@@ -105,15 +73,15 @@ static int stage_cmd(int argc, char **argv)
bytes = ret;
args.data_version = vers;
args.buf_ptr = (unsigned long)buf;
args.offset = offset;
args.count = bytes;
ioctl_args.data_version = args->data_version;
ioctl_args.buf_ptr = (unsigned long)buf;
ioctl_args.offset = args->offset;
ioctl_args.length = bytes;
count -= bytes;
offset += bytes;
args->length -= bytes;
args->offset += bytes;
ret = ioctl(fd, SCOUTFS_IOC_STAGE, &args);
ret = ioctl(fd, SCOUTFS_IOC_STAGE, &ioctl_args);
if (ret != bytes) {
fprintf(stderr, "stage returned %d, not %u: error %s (%d)\n",
ret, bytes, strerror(errno), errno);
@@ -132,79 +100,203 @@ out:
return ret;
};
static void __attribute__((constructor)) stage_ctor(void)
static int parse_stage_opts(int key, char *arg, struct argp_state *state)
{
cmd_register("stage", "<file> <vers> <offset> <count> <archive file>",
"write archive file contents to offline region", stage_cmd);
struct stage_args *args = state->input;
int ret;
switch (key) {
case 'V':
ret = parse_u64(arg, &args->data_version);
if (ret)
return ret;
break;
case 'o': /* offset */
ret = parse_human(arg, &args->offset);
if (ret)
return ret;
break;
case 'l': /* length */
ret = parse_human(arg, &args->length);
if (ret)
return ret;
break;
case ARGP_KEY_ARG:
if (!args->archive_path)
args->archive_path = strdup_or_error(state, arg);
else if (!args->path)
args->path = strdup_or_error(state, arg);
else
argp_error(state, "more than two arguments given");
break;
case ARGP_KEY_FINI:
if (!args->archive_path) {
argp_error(state, "must provide archive file path");
}
if (!args->path) {
argp_error(state, "must provide to-stage file path");
}
if (!args->data_version) {
argp_error(state, "must provide file version with --data-version");
}
if (!args->length) {
struct stat statbuf = {0};
ret = stat(args->archive_path, &statbuf);
if (ret < 0)
argp_failure(state, 1, -errno, "Could not get file size");
args->length = statbuf.st_size;
}
break;
default:
break;
}
return 0;
}
static int release_cmd(int argc, char **argv)
static struct argp_option options[] = {
{ "data-version", 'V', "VERSION", 0, "Data version of the file [Required]"},
{ "offset", 'o', "OFFSET", 0, "Offset (bytes or KMGTP units) in file to stage (default: 0)"},
{ "length", 'l', "LENGTH", 0, "Length of range (bytes or KMGTP units) of file to stage. (default: size of ARCHIVE-FILE)"},
{ NULL }
};
static struct argp stage_argp = {
options,
parse_stage_opts,
"ARCHIVE-FILE STAGE-FILE --data-version VERSION",
"Write archive file contents to an offline file"
};
static int stage_cmd(int argc, char **argv)
{
struct scoutfs_ioctl_release args;
char *endptr = NULL;
u64 block;
u64 count;
u64 vers;
struct stage_args stage_args = {NULL};
int ret;
ret = argp_parse(&stage_argp, argc, argv, 0, NULL, &stage_args);
if (ret)
return ret;
return do_stage(&stage_args);
}
static void __attribute__((constructor)) stage_ctor(void)
{
cmd_register_argp("stage", &stage_argp, GROUP_AGENT, stage_cmd);
}
struct release_args {
char *path;
u64 data_version;
u64 offset;
u64 length;
};
static int do_release(struct release_args *args)
{
struct scoutfs_ioctl_release ioctl_args = {0};
int ret;
int fd;
if (argc != 5) {
fprintf(stderr, "must specify path, data version, offset, and count\n");
return -EINVAL;
}
fd = open(argv[1], O_RDWR);
fd = open(args->path, O_RDWR);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[1], strerror(errno), errno);
args->path, strerror(errno), errno);
return ret;
}
vers = strtoull(argv[2], &endptr, 0);
if (*endptr != '\0' ||
((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing data version '%s'\n",
argv[2]);
ret = -EINVAL;
goto out;
}
assert(args->offset % SCOUTFS_BLOCK_SM_SIZE == 0);
assert(args->length % SCOUTFS_BLOCK_SM_SIZE == 0);
block = strtoull(argv[3], &endptr, 0);
if (*endptr != '\0' ||
((block == LLONG_MIN || block == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing starting 4K block offset '%s'\n",
argv[3]);
ret = -EINVAL;
goto out;
}
ioctl_args.offset = args->offset;
ioctl_args.length = args->length;
ioctl_args.data_version = args->data_version;
count = strtoull(argv[4], &endptr, 0);
if (*endptr != '\0' ||
((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) {
fprintf(stderr, "error parsing length '%s'\n",
argv[4]);
ret = -EINVAL;
goto out;
}
args.block = block;
args.count = count;
args.data_version = vers;
ret = ioctl(fd, SCOUTFS_IOC_RELEASE, &args);
ret = ioctl(fd, SCOUTFS_IOC_RELEASE, &ioctl_args);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "release ioctl failed: %s (%d)\n",
strerror(errno), errno);
}
out:
close(fd);
return ret;
};
static int parse_release_opts(int key, char *arg, struct argp_state *state)
{
struct release_args *args = state->input;
int ret;
switch (key) {
case 'V':
ret = parse_u64(arg, &args->data_version);
if (ret)
return ret;
break;
case 'o': /* offset */
ret = parse_human(arg, &args->offset);
if (ret)
return ret;
break;
case 'l': /* length */
ret = parse_human(arg, &args->length);
if (ret)
return ret;
break;
case ARGP_KEY_ARG:
if (args->path)
argp_error(state, "more than one argument given");
args->path = strdup_or_error(state, arg);
break;
case ARGP_KEY_FINI:
if (!args->path) {
argp_error(state, "must provide file path");
}
if (!args->data_version) {
argp_error(state, "must provide file version --data-version");
}
if (!args->length) {
int ret;
struct stat statbuf = {0};
ret = stat(args->path, &statbuf);
if (ret < 0)
argp_failure(state, 1, -errno, "Could not get file size");
args->length = round_up(statbuf.st_size, SCOUTFS_BLOCK_SM_SIZE);
}
break;
default:
break;
}
return 0;
}
static struct argp release_argp = {
options,
parse_release_opts,
"FILE --data-version VERSION",
"Mark file region offline and free extents"
};
static int release_cmd(int argc, char **argv)
{
struct release_args release_args = {NULL};
int ret;
ret = argp_parse(&release_argp, argc, argv, 0, NULL, &release_args);
if (ret)
return ret;
return do_release(&release_args);
}
static void __attribute__((constructor)) release_ctor(void)
{
cmd_register("release", "<path> <vers> <4K block offset> <block count>",
"mark file region offline and free extents", release_cmd);
cmd_register_argp("release", &release_argp, GROUP_AGENT, release_cmd);
}

View File

@@ -7,10 +7,12 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <assert.h>
#include <argp.h>
#include <stdbool.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
@@ -101,12 +103,13 @@ static void print_fs_field(void *st, size_t off)
typedef void (*print_field_t)(void *st, size_t off);
static struct option long_ops[] = {
{ "single_field", 1, NULL, 's' },
{ NULL, 0, NULL, 0}
struct stat_args {
char *path;
char *single_field;
bool is_inode;
};
static int do_stat(int argc, char **argv, int is_inode)
static int do_stat(struct stat_args *args)
{
union {
struct scoutfs_ioctl_stat_more stm;
@@ -115,17 +118,13 @@ static int do_stat(int argc, char **argv, int is_inode)
struct stat_more_field *single = NULL;
struct stat_more_field *fields;
struct stat_more_field *fi;
char *single_name = NULL;
print_field_t pr = NULL;
char *path;
int cmd;
int ret;
int fd;
int i;
int c;
memset(&st, 0, sizeof(st));
if (is_inode) {
if (args->is_inode) {
cmd = SCOUTFS_IOC_STAT_MORE;
fields = inode_fields;
st.stm.valid_bytes = sizeof(struct scoutfs_ioctl_stat_more);
@@ -137,89 +136,141 @@ static int do_stat(int argc, char **argv, int is_inode)
pr = print_fs_field;
}
while ((c = getopt_long(argc, argv, "s:", long_ops, NULL)) != -1) {
switch (c) {
case 's':
single_name = strdup(optarg);
assert(single_name);
break;
case '?':
default:
return -EINVAL;
}
}
if (single_name) {
if (args->single_field) {
for_each_field(fi, fields) {
if (strcmp(fi->name, single_name) == 0) {
if (strcmp(fi->name, args->single_field) == 0) {
single = fi;
break;
}
}
if (!single) {
fprintf(stderr, "unknown field: '%s'\n", single_name);
fprintf(stderr, "unknown field: '%s'\n", args->single_field);
return -EINVAL;
}
}
if (optind >= argc) {
fprintf(stderr, "must specify at least one path argument\n");
return -EINVAL;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
for (i = optind; i < argc; i++) {
path = argv[i];
fd = open(path, O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
continue;
}
ret = ioctl(fd, cmd, &st);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "ioctl failed on '%s': "
"%s (%d)\n", path, strerror(errno), errno);
} else if (single) {
pr(&st, single->offset);
ret = ioctl(fd, cmd, &st);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "ioctl failed: %s (%d)\n", strerror(errno), errno);
} else if (single) {
pr(&st, single->offset);
printf("\n");
} else {
for_each_field(fi, fields) {
printf("%-17s ", fi->name);
pr(&st, fi->offset);
printf("\n");
} else {
printf("%-17s %s\n", "path", path);
for_each_field(fi, fields) {
printf("%-17s ", fi->name);
pr(&st, fi->offset);
printf("\n");
}
}
close(fd);
}
return 0;
}
static int stat_parse_opt(int key, char *arg, struct argp_state *state)
{
struct stat_args *args = state->input;
switch (key) {
case 's':
args->single_field = strdup_or_error(state, arg);
break;
case ARGP_KEY_ARG:
if (!args->path)
args->path = strdup_or_error(state, arg);
else
argp_error(state, "more than one argument");
break;
case ARGP_KEY_FINI:
if (!args->path)
argp_error(state, "missing operand");
break;
default:
break;
}
return 0;
}
static struct argp_option stat_options[] = {
{ "single-field", 's', "FIELD-NAME", 0, "Specify single field to print" },
{ NULL }
};
static struct argp stat_argp = {
stat_options,
stat_parse_opt,
"FILE",
"Show ScoutFS extra inode information"
};
static int stat_more_cmd(int argc, char **argv)
{
return do_stat(argc, argv, 1);
struct stat_args stat_args = {NULL};
int ret;
ret = argp_parse(&stat_argp, argc, argv, 0, NULL, &stat_args);
if (ret)
return ret;
stat_args.is_inode = true;
return do_stat(&stat_args);
}
static struct argp_option statfs_options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ "single-field", 's', "FIELD-NAME", 0, "Specify single field to print" },
{ NULL }
};
static int statfs_parse_opt(int key, char *arg, struct argp_state *state)
{
struct stat_args *args = state->input;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case 's':
args->single_field = strdup_or_error(state, arg);
break;
default:
break;
}
return 0;
}
static struct argp statfs_argp = {
statfs_options,
statfs_parse_opt,
"",
"Show ScoutFS file system information"
};
static int statfs_more_cmd(int argc, char **argv)
{
return do_stat(argc, argv, 0);
struct stat_args stat_args = {NULL};
int ret;
ret = argp_parse(&statfs_argp, argc, argv, 0, NULL, &stat_args);
if (ret)
return ret;
stat_args.is_inode = false;
return do_stat(&stat_args);
}
static void __attribute__((constructor)) stat_more_ctor(void)
{
cmd_register("stat", "<path>",
"show scoutfs inode information", stat_more_cmd);
cmd_register_argp("stat", &stat_argp, GROUP_INFO, stat_more_cmd);
}
static void __attribute__((constructor)) statfs_more_ctor(void)
{
cmd_register("statfs", "<path>",
"show scoutfs file system information", statfs_more_cmd);
cmd_register_argp("statfs", &statfs_argp, GROUP_INFO, statfs_more_cmd);
}

100
utils/src/util.c Normal file
View File

@@ -0,0 +1,100 @@
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>
#include "util.h"
#define ENV_PATH "SCOUTFS_MOUNT_PATH"
static int open_path(char *path, int flags)
{
wordexp_t exp_result;
int ret;
ret = wordexp(path, &exp_result, WRDE_NOCMD | WRDE_SHOWERR | WRDE_UNDEF);
if (ret) {
fprintf(stderr, "wordexp() failure for \"%s\": %d\n", path, ret);
ret = -EINVAL;
goto out;
}
ret = open(exp_result.we_wordv[0], flags);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
path, strerror(errno), errno);
}
out:
wordfree(&exp_result);
return ret;
}
/*
* 1. if path option given, use that
* 2. if env var, use that
* 3. if cwd is in a scoutfs fs, use that
* 4. else error
*/
int get_path(char *path, int flags)
{
char *env_path;
char *cur_dir_path;
int ret;
if (path)
return open_path(path, flags);
env_path = getenv(ENV_PATH);
if (env_path)
return open_path(path, flags);
cur_dir_path = get_current_dir_name();
if (!cur_dir_path) {
ret = -errno;
return ret;
}
ret = open_path(cur_dir_path, flags);
free(cur_dir_path);
// TODO: check this is within a scoutfs mount?
return ret;
}
int read_block(int fd, u64 blkno, int shift, void **ret_val)
{
size_t size = 1ULL << shift;
void *buf;
int ret;
*ret_val = NULL;
buf = malloc(size);
if (!buf)
return -ENOMEM;
ret = pread(fd, buf, size, blkno << shift);
if (ret == -1) {
fprintf(stderr, "read blkno %llu returned %d: %s (%d)\n",
blkno, ret, strerror(errno), errno);
free(buf);
return -errno;
} else if (ret != size) {
fprintf(stderr, "incomplete pread\n");
free(buf);
return -EINVAL;
} else {
*ret_val = buf;
return 0;
}
}

View File

@@ -111,4 +111,7 @@ static inline int memcmp_lens(const void *a, int a_len,
return memcmp(a, b, len) ?: a_len - b_len;
}
int get_path(char *path, int flags);
int read_block(int fd, u64 blkno, int shift, void **ret_val);
#endif

View File

@@ -8,6 +8,8 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <argp.h>
#include <stdbool.h>
#include "sparse.h"
#include "util.h"
@@ -31,7 +33,16 @@
(((ops) & (bit)) ? (str) : ""), \
(((ops) & (bit)) && ((ops) & ~(((bit) << 1) - 1)) ? "," : "")
static int waiting_cmd(int argc, char **argv)
struct waiting_args {
char *path;
bool inode_set;
u64 inode;
bool blkno_set;
u64 blkno;
};
static int do_waiting(struct waiting_args *args)
{
struct scoutfs_ioctl_data_waiting_entry dwe[16];
struct scoutfs_ioctl_data_waiting idw;
@@ -39,25 +50,13 @@ static int waiting_cmd(int argc, char **argv)
int fd;
int i;
if (argc != 4) {
fprintf(stderr, "must specify ino, iblock, and path\n");
return -EINVAL;
}
ret = parse_u64(argv[1], &idw.after_ino) ?:
parse_u64(argv[2], &idw.after_iblock);
if (ret)
return ret;
fd = open(argv[3], O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[3], strerror(errno), errno);
return ret;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
idw.flags = 0;
idw.after_ino = args->inode;
idw.after_iblock = args->blkno;
idw.ents_ptr = (unsigned long)dwe;
idw.ents_nr = array_size(dwe);
@@ -91,59 +90,114 @@ static int waiting_cmd(int argc, char **argv)
return ret;
};
static void __attribute__((constructor)) waiting_ctor(void)
static int waiting_parse_opt(int key, char *arg, struct argp_state *state)
{
cmd_register("data-waiting", "<ino> <iblock> <path>",
"print ops waiting for data blocks", waiting_cmd);
}
static int data_wait_err_cmd(int argc, char **argv)
{
struct scoutfs_ioctl_data_wait_err args;
int fd = -1;
struct waiting_args *args = state->input;
int ret;
memset(&args, 0, sizeof(args));
if (argc != 8) {
fprintf(stderr, "must specify path, ino, version, offset, count,op, and err\n");
return -EINVAL;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case 'I': /* inode */
ret = parse_u64(arg, &args->inode);
if (ret)
argp_error(state, "inode parse error");
args->inode_set = true;
break;
case 'B': /* blkno */
ret = parse_u64(arg, &args->blkno);
if (ret)
argp_error(state, "blkno parse error");
args->blkno_set = true;
break;
case ARGP_KEY_FINI:
if (!args->inode_set)
argp_error(state, "no inode given");
if (!args->blkno_set)
argp_error(state, "no blkno given");
break;
default:
break;
}
ret = parse_u64(argv[2], &args.ino) ?:
parse_u64(argv[3], &args.data_version) ?:
parse_u64(argv[4], &args.offset) ?:
parse_u64(argv[5], &args.count) ?:
parse_s64(argv[7], &args.err);
return 0;
}
static struct argp_option waiting_options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ "inode", 'I', "INODE-NUM", 0, "Inode number [Required]"},
{ "block", 'B', "BLKNO-NUM", 0, "Block number [Required]"},
{ NULL }
};
static struct argp waiting_argp = {
waiting_options,
waiting_parse_opt,
"--inode INODE-NUM --block BLOCK-NUM",
"Print operations waiting for data blocks"
};
static int waiting_cmd(int argc, char **argv)
{
struct waiting_args waiting_args = {NULL};
int ret;
ret = argp_parse(&waiting_argp, argc, argv, 0, NULL, &waiting_args);
if (ret)
return ret;
if ((args.err >= 0) || (args.err < -MAX_ERRNO)) {
fprintf(stderr, "err %lld invalid\n", args.err);
ret = -EINVAL;
goto out;
}
return do_waiting(&waiting_args);
}
if (!strcmp(argv[6], "read")) {
args.op = SCOUTFS_IOC_DWO_READ;
} else if (!strcmp(argv[6], "write")) {
args.op = SCOUTFS_IOC_DWO_WRITE;
} else if (!strcmp(argv[6], "change_size")) {
args.op = SCOUTFS_IOC_DWO_CHANGE_SIZE;
static void __attribute__((constructor)) waiting_ctor(void)
{
cmd_register_argp("data-waiting", &waiting_argp, GROUP_AGENT, waiting_cmd);
}
struct wait_err_args {
char *path;
bool inode_set;
u64 inode;
bool version_set;
u64 version;
bool offset_set;
u64 offset;
bool count_set;
u64 count;
char *op;
bool err_set;
s64 err;
};
static int do_wait_err(struct wait_err_args *args)
{
struct scoutfs_ioctl_data_wait_err dwe = {0};
int fd = -1;
int ret;
dwe.ino = args->inode;
dwe.data_version = args->version;
dwe.offset = args->offset;
dwe.count = args->count;
if (!strcmp(args->op, "read")) {
dwe.op = SCOUTFS_IOC_DWO_READ;
} else if (!strcmp(args->op, "write")) {
dwe.op = SCOUTFS_IOC_DWO_WRITE;
} else if (!strcmp(args->op, "change_size")) {
dwe.op = SCOUTFS_IOC_DWO_CHANGE_SIZE;
} else {
fprintf(stderr, "invalid data wait op: '%s'\n", argv[6]);
fprintf(stderr, "invalid data wait op: '%s'\n", args->op);
return -EINVAL;
}
dwe.err = args->err;
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[1], strerror(errno), errno);
return ret;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
ret = ioctl(fd, SCOUTFS_IOC_DATA_WAIT_ERR, &args);
ret = ioctl(fd, SCOUTFS_IOC_DATA_WAIT_ERR, &dwe);
if (ret < 0) {
fprintf(stderr, "data_wait_err returned %d: error %s (%d)\n",
ret, strerror(errno), errno);
@@ -158,9 +212,104 @@ out:
return ret;
};
static int wait_err_parse_opt(int key, char *arg, struct argp_state *state)
{
struct wait_err_args *args = state->input;
int ret;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case 'I': /* inode */
ret = parse_u64(arg, &args->inode);
if (ret)
argp_error(state, "inode parse error");
args->inode_set = true;
break;
case 'V': /* version */
ret = parse_u64(arg, &args->version);
if (ret)
argp_error(state, "version parse error");
args->version_set = true;
break;
case 'F': /* offset */
ret = parse_human(arg, &args->offset);
if (ret)
argp_error(state, "version parse error");
args->offset_set = true;
break;
case 'C': /* count */
ret = parse_u64(arg, &args->count);
if (ret)
argp_error(state, "count parse error");
args->count_set = true;
break;
case 'O': /* op */
args->op = strdup_or_error(state, arg);
break;
case 'E': /* err */
ret = parse_s64(arg, &args->err);
if (ret)
argp_error(state, "error parse error");
if ((args->err >= 0) || (args->err < -MAX_ERRNO))
argp_error(state, "errno out of range");
args->err_set = true;
break;
case ARGP_KEY_FINI:
if (!args->inode_set)
argp_error(state, "no inode given");
if (!args->version_set)
argp_error(state, "no version given");
if (!args->offset_set)
argp_error(state, "no offset given");
if (!args->count_set)
argp_error(state, "no count given");
if (!args->op)
argp_error(state, "no operation given");
if (!args->err_set)
argp_error(state, "no error given");
break;
default:
break;
}
return 0;
}
static struct argp_option wait_err_options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ "inode", 'I', "INODE-NUM", 0, "Inode number [Required]"},
{ "version", 'V', "VER-NUM", 0, "Version [Required]"},
{ "offset", 'F', "OFF-NUM", 0, "Offset (bytes or KMGTP units) [Required]"},
{ "count", 'C', "COUNT", 0, "Count [Required]"},
{ "op", 'O', "OP", 0, "Operation: \"read\", \"write\", \"change_size\" [Required]"},
{ "err", 'E', "ERR", 0, "Error [Required]"},
{ NULL }
};
static struct argp wait_err_argp = {
wait_err_options,
wait_err_parse_opt,
"--inode INODE-NUM --version VER-NUM "
"--offset OFF-NUM --count COUNT --op OP --err ERR",
"Return error from matching waiters"
};
static int wait_err_cmd(int argc, char **argv)
{
struct wait_err_args wait_err_args = {NULL};
int ret;
ret = argp_parse(&wait_err_argp, argc, argv, 0, NULL, &wait_err_args);
if (ret)
return ret;
return do_wait_err(&wait_err_args);
}
static void __attribute__((constructor)) data_wait_err_ctor(void)
{
cmd_register("data-wait-err", "<path> <ino> <vers> <offset> <count> <op> <err>",
"return error from matching waiters",
data_wait_err_cmd);
cmd_register_argp("data-wait-err", &wait_err_argp, GROUP_AGENT, wait_err_cmd);
}

View File

@@ -8,8 +8,10 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <argp.h>
#include "sparse.h"
#include "parse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
@@ -66,7 +68,14 @@ static int parse_walk_entry(struct scoutfs_ioctl_walk_inodes_entry *ent,
return 0;
}
static int walk_inodes_cmd(int argc, char **argv)
struct walk_inodes_args {
char *path;
char *index;
char *first_entry;
char *last_entry;
};
static int do_walk_inodes(struct walk_inodes_args *args)
{
struct scoutfs_ioctl_walk_inodes_entry ents[128];
struct scoutfs_ioctl_walk_inodes walk;
@@ -75,44 +84,35 @@ static int walk_inodes_cmd(int argc, char **argv)
int fd;
int i;
if (argc != 5) {
fprintf(stderr, "must specify seq and path\n");
return -EINVAL;
}
if (!strcasecmp(argv[1], "meta_seq"))
if (!strcasecmp(args->index, "meta_seq"))
walk.index = SCOUTFS_IOC_WALK_INODES_META_SEQ;
else if (!strcasecmp(argv[1], "data_seq"))
else if (!strcasecmp(args->index, "data_seq"))
walk.index = SCOUTFS_IOC_WALK_INODES_DATA_SEQ;
else {
fprintf(stderr, "unknown index '%s', try 'meta_seq' or "
"'data_seq'\n", argv[1]);
"'data_seq'\n", args->index);
return -EINVAL;
}
ret = parse_walk_entry(&walk.first, argv[2]);
ret = parse_walk_entry(&walk.first, args->first_entry);
if (ret) {
fprintf(stderr, "invalid first position '%s', try '1.2.3' or "
"'-1'\n", argv[2]);
"'-1'\n", args->first_entry);
return -EINVAL;
}
ret = parse_walk_entry(&walk.last, argv[3]);
ret = parse_walk_entry(&walk.last, args->last_entry);
if (ret) {
fprintf(stderr, "invalid last position '%s', try '1.2.3' or "
"'-1'\n", argv[3]);
"'-1'\n", args->last_entry);
return -EINVAL;
}
fd = open(argv[4], O_RDONLY);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
argv[4], strerror(errno), errno);
return ret;
}
fd = get_path(args->path, O_RDONLY);
if (fd < 0)
return fd;
walk.entries_ptr = (unsigned long)ents;
walk.nr_entries = array_size(ents);
@@ -149,8 +149,65 @@ static int walk_inodes_cmd(int argc, char **argv)
return ret;
};
static int walk_inodes_parse_opt(int key, char *arg, struct argp_state *state)
{
struct walk_inodes_args *args = state->input;
switch (key) {
case 'p':
args->path = strdup_or_error(state, arg);
break;
case ARGP_KEY_ARG:
if (!args->index)
args->index = strdup_or_error(state, arg);
else if (!args->first_entry)
args->first_entry = strdup_or_error(state, arg);
else if (!args->last_entry)
args->last_entry = strdup_or_error(state, arg);
else
argp_error(state, "more than three arguments given");
break;
case ARGP_KEY_FINI:
if (!args->index)
argp_error(state, "no index given");
if (!args->first_entry)
argp_error(state, "no first entry given");
if (!args->last_entry)
argp_error(state, "no last entry given");
break;
default:
break;
}
return 0;
}
static struct argp_option options[] = {
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
{ NULL }
};
static struct argp argp = {
options,
walk_inodes_parse_opt,
"<meta_seq|data_seq> FIRST-ENTRY LAST-ENTRY",
"Print range of indexed inodes"
};
static int walk_inodes_cmd(int argc, char **argv)
{
struct walk_inodes_args walk_inodes_args = {NULL};
int ret;
ret = argp_parse(&argp, argc, argv, 0, NULL, &walk_inodes_args);
if (ret)
return ret;
return do_walk_inodes(&walk_inodes_args);
}
static void __attribute__((constructor)) walk_inodes_ctor(void)
{
cmd_register("walk-inodes", "<index> <first> <last> <path>",
"print range of indexed inodes", walk_inodes_cmd);
cmd_register_argp("walk-inodes", &argp, GROUP_SEARCH, walk_inodes_cmd);
}