mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
Add CI stat/create/unlink test for multiple directory forms
Merge of master-melb:xfs-cmds:31347a by kenmcd. Add genhashnames target
This commit is contained in:
+4
-1
@@ -10,7 +10,7 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \
|
||||
mmapcat append_reader append_writer dirperf metaperf \
|
||||
devzero feature alloc fault fstest t_access_root \
|
||||
godown resvtest writemod makeextents itrash \
|
||||
multi_open_unlink dmiperf unwritten_sync
|
||||
multi_open_unlink dmiperf unwritten_sync genhashnames
|
||||
|
||||
LINUX_TARGETS = loggen xfsctl bstat t_mtab getdevicesize \
|
||||
preallo_rw_pattern_reader preallo_rw_pattern_writer ftrunc trunc \
|
||||
@@ -52,6 +52,9 @@ truncfile: truncfile.o $(LIBTEST)
|
||||
dbtest: dbtest.o $(LIBTEST)
|
||||
$(LINKTEST) $(LIBTEST) $(LIBGDBM) $(LDLIBS)
|
||||
|
||||
genhashnames: genhashnames.o
|
||||
$(LINKTEST)
|
||||
|
||||
nametest: nametest.o $(LIBTEST)
|
||||
$(LINKTEST) $(LIBTEST) $(LDLIBS)
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Creates a bunch of files in the specified directory with the same
|
||||
* hashvalue on-disk.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <xfs/libxfs.h>
|
||||
|
||||
#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
|
||||
|
||||
/*
|
||||
* Implement a simple hash on a character string.
|
||||
* Rotate the hash value by 7 bits, then XOR each character in.
|
||||
* This is implemented with some source-level loop unrolling.
|
||||
*/
|
||||
static uint32_t xfs_da_hashname(const char *name, int namelen)
|
||||
{
|
||||
uint32_t hash;
|
||||
|
||||
/*
|
||||
* Do four characters at a time as long as we can.
|
||||
*/
|
||||
for (hash = 0; namelen >= 4; namelen -= 4, name += 4)
|
||||
hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
|
||||
(name[3] << 0) ^ rol32(hash, 7 * 4);
|
||||
|
||||
/*
|
||||
* Now do the rest of the characters.
|
||||
*/
|
||||
switch (namelen) {
|
||||
case 3:
|
||||
return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
|
||||
rol32(hash, 7 * 3);
|
||||
case 2:
|
||||
return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
|
||||
case 1:
|
||||
return (name[0] << 0) ^ rol32(hash, 7 * 1);
|
||||
default: /* case 0: */
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_invalid_char(char c)
|
||||
{
|
||||
return (c <= ' ' || c > '~' || c == '/' || (c >= 'A' && c <= 'Z'));
|
||||
}
|
||||
|
||||
static char random_char(void)
|
||||
{
|
||||
char c;
|
||||
|
||||
/* get a character of "0"-"9" or "a"-"z" */
|
||||
|
||||
c = lrand48() % 36;
|
||||
|
||||
return (c >= 10) ? c + 87 : c + 48;
|
||||
}
|
||||
|
||||
static int generate_names(const char *path, long amount, uint32_t desired_hash)
|
||||
{
|
||||
char **names;
|
||||
char fullpath[PATH_MAX];
|
||||
char *filename;
|
||||
long count;
|
||||
long i;
|
||||
uint32_t base_hash;
|
||||
uint32_t hash;
|
||||
|
||||
names = malloc(amount * sizeof(char *));
|
||||
if (!names) {
|
||||
fprintf(stderr, "genhashnames: malloc(%lu) failed!\n",
|
||||
amount * sizeof(char *));
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(fullpath, path);
|
||||
filename = fullpath + strlen(fullpath);
|
||||
if (filename[-1] != '/')
|
||||
*filename++ = '/';
|
||||
|
||||
for (count = 0; count < amount; count++) {
|
||||
for (;;) {
|
||||
base_hash = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
filename[i] = random_char();
|
||||
base_hash = filename[i] ^ rol32(base_hash, 7);
|
||||
}
|
||||
while (i < 200) {
|
||||
filename[i] = random_char();
|
||||
base_hash = filename[i] ^ rol32(base_hash, 7);
|
||||
i++;
|
||||
hash = rol32(base_hash, 3) ^ desired_hash;
|
||||
|
||||
filename[i] = (hash >> 28) |
|
||||
(random_char() & 0xf0);
|
||||
if (is_invalid_char(filename[i]))
|
||||
continue;
|
||||
|
||||
filename[i + 1] = (hash >> 21) & 0x7f;
|
||||
if (is_invalid_char(filename[i + 1]))
|
||||
continue;
|
||||
filename[i + 2] = (hash >> 14) & 0x7f;
|
||||
if (is_invalid_char(filename[i + 2]))
|
||||
continue;
|
||||
filename[i + 3] = (hash >> 7) & 0x7f;
|
||||
if (is_invalid_char(filename[i + 3]))
|
||||
continue;
|
||||
filename[i + 4] = (hash ^ (filename[i] >> 4))
|
||||
& 0x7f;
|
||||
if (is_invalid_char(filename[i + 4]))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i < NAME_MAX)
|
||||
break;
|
||||
}
|
||||
filename[i + 5] = '\0';
|
||||
if (xfs_da_hashname(filename, i + 5) != desired_hash) {
|
||||
fprintf(stderr, "genhashnames: Hash mismatch!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (strcmp(fullpath, names[i]) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == count) {
|
||||
names[count] = strdup(fullpath);
|
||||
puts(fullpath);
|
||||
} else
|
||||
count--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: genhashnames <directory> <amount> "
|
||||
"[seed] [hashvalue]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
long seed;
|
||||
uint32_t desired_hash;
|
||||
|
||||
if (argc < 3 || argc > 5)
|
||||
usage();
|
||||
|
||||
if (argc >= 4)
|
||||
seed = strtol(argv[3], NULL, 0);
|
||||
else {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
seed = tv.tv_usec / 1000 + (tv.tv_sec % 1000) * 1000;
|
||||
}
|
||||
srand48(seed);
|
||||
|
||||
/*
|
||||
* always generate hash from random so if hash is specified, random
|
||||
* sequence is the same as a randomly generated hash of the same value.
|
||||
*/
|
||||
desired_hash = (uint32_t)mrand48();
|
||||
if (argc >= 5)
|
||||
desired_hash = (uint32_t)strtoul(argv[4], NULL, 0);
|
||||
|
||||
fprintf(stderr, "seed = %ld, hash = 0x%08x\n", seed, desired_hash);
|
||||
|
||||
return generate_names(argv[1], strtol(argv[2], NULL, 0), desired_hash);
|
||||
}
|
||||
+43
-19
@@ -15,7 +15,7 @@
|
||||
* along with this program; if not, write the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "global.h"
|
||||
|
||||
/*
|
||||
@@ -25,21 +25,21 @@
|
||||
*
|
||||
* Given an input file of a list of filenames (one per line)
|
||||
* It does a number of iterations of operations
|
||||
* chosen pseudo-randomly in certain percentages:
|
||||
* creating (open),
|
||||
* deleting (unlink) and
|
||||
* chosen pseudo-randomly in certain percentages:
|
||||
* creating (open),
|
||||
* deleting (unlink) and
|
||||
* looking up (stat)
|
||||
* on a pseudo-randomly chosen filename (from input file).
|
||||
* on a pseudo-randomly chosen filename (from input file).
|
||||
*
|
||||
* The percentage thresholds for operation selection change
|
||||
* every <number-of-names> iterations.
|
||||
* every <number-of-names> iterations.
|
||||
* e.g.
|
||||
* If had 100 names then:
|
||||
* iterations:
|
||||
* 1-100: pct_remove = 33; pct_create = 33;
|
||||
* 1-100: pct_remove = 33; pct_create = 33;
|
||||
* 101-200: pct_remove = 60; pct_create = 20;
|
||||
* 201-300: pct_remove = 20; pct_create = 60;
|
||||
* 301-400: pct_remove = 33; pct_create = 33;
|
||||
* 301-400: pct_remove = 33; pct_create = 33;
|
||||
* 401-500: pct_remove = 60; pct_create = 20;
|
||||
* 501-600: pct_remove = 20; pct_create = 60;
|
||||
* etc...
|
||||
@@ -53,7 +53,7 @@
|
||||
*
|
||||
* The operation is done and any error codes are
|
||||
* verified considering whether file exists (info.exists)
|
||||
* or not. The stat(3) call also compares inode number.
|
||||
* or not. The stat(3) call also compares inode number.
|
||||
*/
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ int good_adds, good_rms, good_looks, good_tot; /* ops that suceeded */
|
||||
int bad_adds, bad_rms, bad_looks, bad_tot; /* ops that failed */
|
||||
|
||||
int verbose;
|
||||
int mixcase;
|
||||
|
||||
int auto_lookup(struct info *);
|
||||
int auto_create(struct info *);
|
||||
@@ -82,7 +83,7 @@ void usage(void);
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v]\n");
|
||||
printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v] [-c]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -96,17 +97,18 @@ main(int argc, char *argv[])
|
||||
struct info *ip;
|
||||
int seed, linedots;
|
||||
|
||||
linedots = zeroout = verbose = 0;
|
||||
linedots = zeroout = verbose = mixcase = 0;
|
||||
seed = (int)time(NULL) % 1000;
|
||||
iterations = 100000;
|
||||
sourcefile = "input";
|
||||
while ((ch = getopt(argc, argv, "l:i:s:zv")) != EOF) {
|
||||
while ((ch = getopt(argc, argv, "l:i:s:zvc")) != EOF) {
|
||||
switch (ch) {
|
||||
case 'l': sourcefile = optarg; break;
|
||||
case 's': seed = atoi(optarg); break;
|
||||
case 'i': iterations = atoi(optarg); break;
|
||||
case 'z': zeroout++; break;
|
||||
case 'v': verbose++; break;
|
||||
case 'c': mixcase++; break;
|
||||
default: usage(); break;
|
||||
}
|
||||
}
|
||||
@@ -137,8 +139,8 @@ main(int argc, char *argv[])
|
||||
* Allocate space for the info table and fill it in.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add up number of lines in file
|
||||
/*
|
||||
* Add up number of lines in file
|
||||
* and replace '\n' by '\0'
|
||||
*/
|
||||
totalnames = 0;
|
||||
@@ -152,14 +154,14 @@ main(int argc, char *argv[])
|
||||
printf("no names found in input file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
table = (struct info *)calloc(totalnames+1, sizeof(struct info));
|
||||
if (table == NULL) {
|
||||
perror("calloc");
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Copy over names from file (in <table_data>) into name fields
|
||||
* Copy over names from file (in <table_data>) into name fields
|
||||
* of info structures in <table>.
|
||||
*/
|
||||
ip = table;
|
||||
@@ -303,13 +305,35 @@ main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *get_name(struct info *ip)
|
||||
{
|
||||
static char path[PATH_MAX];
|
||||
int i;
|
||||
char *p;
|
||||
|
||||
if (!mixcase)
|
||||
return ip->name;
|
||||
|
||||
/* pick a random character to change case in path */
|
||||
strcpy(path, ip->name);
|
||||
p = strrchr(path, '/');
|
||||
if (!p)
|
||||
p = path;
|
||||
p += random() % strlen(p);
|
||||
if (islower(*p))
|
||||
*p = toupper(*p);
|
||||
else
|
||||
*p = tolower(*p);
|
||||
return path;
|
||||
}
|
||||
|
||||
int
|
||||
auto_lookup(struct info *ip)
|
||||
{
|
||||
struct stat64 statb;
|
||||
int retval;
|
||||
|
||||
retval = stat64(ip->name, &statb);
|
||||
retval = stat64(get_name(ip), &statb);
|
||||
if (retval >= 0) {
|
||||
good_looks++;
|
||||
retval = 0;
|
||||
@@ -352,7 +376,7 @@ auto_create(struct info *ip)
|
||||
struct stat64 statb;
|
||||
int retval;
|
||||
|
||||
retval = open(ip->name, O_RDWR|O_EXCL|O_CREAT, 0666);
|
||||
retval = open(get_name(ip), O_RDWR|O_EXCL|O_CREAT, 0666);
|
||||
if (retval >= 0) {
|
||||
close(retval);
|
||||
good_adds++;
|
||||
@@ -400,7 +424,7 @@ auto_remove(struct info *ip)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = unlink(ip->name);
|
||||
retval = unlink(get_name(ip));
|
||||
if (retval >= 0) {
|
||||
good_rms++;
|
||||
retval = 0;
|
||||
|
||||
Reference in New Issue
Block a user