mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-05 11:45:09 +00:00
scoutfs-tests: add find_xattrs
Add a utility that mimics our search_xattrs ioctl with directory entry walking and fgetxattr as efficiently as it can so we can use it to test large file populations. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@@ -3,3 +3,4 @@ src/createmany
|
||||
src/dumb_setxattr
|
||||
src/handle_cat
|
||||
src/bulk_create_paths
|
||||
src/find_xattrs
|
||||
|
||||
@@ -5,7 +5,8 @@ SHELL := /usr/bin/bash
|
||||
BIN := src/createmany \
|
||||
src/dumb_setxattr \
|
||||
src/handle_cat \
|
||||
src/bulk_create_paths
|
||||
src/bulk_create_paths \
|
||||
src/find_xattrs
|
||||
|
||||
DEPS := $(wildcard src/*.d)
|
||||
|
||||
|
||||
215
tests/src/find_xattrs.c
Normal file
215
tests/src/find_xattrs.c
Normal file
@@ -0,0 +1,215 @@
|
||||
#define _GNU_SOURCE /* getopt_long_only */
|
||||
/* reallocarray */
|
||||
/* open_by_handle_at */
|
||||
#define _ATFILE_SOURCE /* openat glibc < 2.10 */
|
||||
#define _POSIX_C_SOURCE 200809L /* openat glibc >= 2.10 */
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <endian.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* We need to test our srch subsystem with a large number of files. The
|
||||
* search_xattrs ioctl prints out a sorted list of inodes which may
|
||||
* contain a given xattr.
|
||||
*
|
||||
* This mimics that behaviour as efficiently as it can. We walk the
|
||||
* name space to find the inode numbers of files to check and then open
|
||||
* them in inode number order and print out any that contain the xattr.
|
||||
*/
|
||||
|
||||
#define error_exit(cond, fmt, args...) \
|
||||
do { \
|
||||
if (cond) { \
|
||||
printf("error: "fmt"\n", ##args); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ERRF " errno %d (%s)"
|
||||
#define ERRA errno, strerror(errno)
|
||||
|
||||
enum {
|
||||
OPT_DIR = 1,
|
||||
OPT_MOUNT,
|
||||
OPT_NAME,
|
||||
};
|
||||
|
||||
static struct option long_opts[] = {
|
||||
{"dir", required_argument, 0, OPT_DIR},
|
||||
{"mount", required_argument, 0, OPT_MOUNT},
|
||||
{"name", required_argument, 0, OPT_NAME},
|
||||
};
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("usage:\n"
|
||||
" --dir PATH | walk files starting with PATH directory\n"
|
||||
" --mount PATH | open by inode requires fs mount point PATH\n"
|
||||
" --name NAME | look for xattr named NAME\n");
|
||||
}
|
||||
|
||||
#define GROWTH (4 * 1024 * 1024)
|
||||
#define NR_LIMIT ((SIZE_MAX - GROWTH) / sizeof(uint64_t))
|
||||
|
||||
static void readdir_inos(int dfd, uint64_t **inos, size_t *nr)
|
||||
{
|
||||
struct dirent *dent;
|
||||
size_t bytes;
|
||||
DIR *dirp;
|
||||
int fd;
|
||||
|
||||
dirp = fdopendir(dfd);
|
||||
error_exit(dirp == NULL, "fdopendir(%d) failed"ERRF, dfd, ERRA);
|
||||
|
||||
while ((dent = readdir(dirp)) != NULL) {
|
||||
|
||||
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
|
||||
continue;
|
||||
|
||||
error_exit(dent->d_type == DT_UNKNOWN,
|
||||
"entry %s has unknown d_type",
|
||||
dent->d_name);
|
||||
|
||||
if (dent->d_type == DT_DIR) {
|
||||
/* descend into dir, don't use its ino */
|
||||
fd = openat(dfd, dent->d_name, O_RDONLY);
|
||||
error_exit(fd < 0,
|
||||
"openat(%d, %s, O_RDONLY) failed"ERRF,
|
||||
dfd, dent->d_name, ERRA);
|
||||
|
||||
readdir_inos(fd, inos, nr);
|
||||
close(fd);
|
||||
} else {
|
||||
/* record ino of all non-directory entries */
|
||||
(*inos)[*nr] = dent->d_ino;
|
||||
(*nr)++;
|
||||
|
||||
error_exit(*nr == NR_LIMIT,
|
||||
"reached limit of %zu inodes\n",
|
||||
NR_LIMIT);
|
||||
|
||||
if ((*nr % GROWTH) == 0) {
|
||||
bytes = (*nr + GROWTH) * sizeof((*inos)[0]);
|
||||
*inos = realloc(*inos, bytes);
|
||||
error_exit(*inos == NULL,
|
||||
"%zu element ino array failed"ERRF,
|
||||
*nr + GROWTH, ERRA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
#define FILEID_SCOUTFS 0x81
|
||||
struct our_handle {
|
||||
struct file_handle handle;
|
||||
__le64 scoutfs_ino;
|
||||
};
|
||||
|
||||
int open_by_ino(int mfd, uint64_t ino)
|
||||
{
|
||||
int fd;
|
||||
struct our_handle handle = {
|
||||
.handle.handle_bytes = sizeof(struct our_handle),
|
||||
.handle.handle_type = FILEID_SCOUTFS,
|
||||
.scoutfs_ino = htole64(ino),
|
||||
};
|
||||
|
||||
fd = open_by_handle_at(mfd, &handle.handle, O_RDONLY);
|
||||
error_exit(fd < 1, "open_by_handle_at(%d, %"PRIu64") failed"ERRF,
|
||||
mfd, ino, ERRA);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int cmp_inos(const void *A, const void *B)
|
||||
{
|
||||
uint64_t a = *(uint64_t *)A;
|
||||
uint64_t b = *(uint64_t *)B;
|
||||
|
||||
return a < b ? -1 : b > a ? 1 : 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *dir = NULL;
|
||||
char *mount = NULL;
|
||||
char *name = NULL;
|
||||
uint64_t *inos = NULL;
|
||||
size_t nr = 0;
|
||||
char junk;
|
||||
int dfd;
|
||||
int mfd;
|
||||
int fd;
|
||||
int opt;
|
||||
int i;
|
||||
|
||||
while ((opt = getopt_long_only(argc, argv, "", long_opts, NULL)) != -1){
|
||||
switch(opt) {
|
||||
case OPT_DIR:
|
||||
dir = strdup(optarg);
|
||||
break;
|
||||
case OPT_MOUNT:
|
||||
mount = strdup(optarg);
|
||||
break;
|
||||
case OPT_NAME:
|
||||
name = strdup(optarg);
|
||||
break;
|
||||
case '?':
|
||||
printf("Unknown option '%c'\n", optopt);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
error_exit(!dir, "must specify search dir path with -d");
|
||||
error_exit(!mount, "must specify mount point path with -m");
|
||||
error_exit(!name, "must specify xattr name with -n");
|
||||
|
||||
inos = calloc(GROWTH, sizeof(inos[0]));
|
||||
nr = 0;
|
||||
error_exit(inos == NULL, "ino array allocation error");
|
||||
|
||||
mfd = open(mount, O_RDONLY);
|
||||
error_exit(mfd < 1, "mount open(%s) failed"ERRF, mount, ERRA);
|
||||
|
||||
dfd = open(dir, O_RDONLY);
|
||||
error_exit(dfd < 1, "dir open(%s) failed"ERRF, dir, ERRA);
|
||||
|
||||
/* namespace walk to get all the non-dir inode numbers */
|
||||
readdir_inos(dfd, &inos, &nr);
|
||||
|
||||
/* sort by inode numbers so accesses are efficient */
|
||||
qsort(inos, nr, sizeof(inos[0]), cmp_inos);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
/* hard links can give us duplicate inos */
|
||||
if (i > 0 && inos[i - 1] == inos[i])
|
||||
continue;
|
||||
|
||||
/* and check each inode for the xattr */
|
||||
fd = open_by_ino(mfd, inos[i]);
|
||||
if (fgetxattr(fd, name, &junk, 0) >= 0)
|
||||
printf("%"PRIu64"\n", inos[i]);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
close(mfd);
|
||||
close(dfd);
|
||||
free(dir);
|
||||
free(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user