From: Barry Naujok Date: Tue, 24 Jun 2008 16:29:51 +0000 (+0000) Subject: Add CI stat/create/unlink test for multiple directory forms X-Git-Tag: v1.1.0~372 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=2c8dbd49c8fe71b380f6c8eaed0dde18f852ed30;p=xfstests-dev.git Add CI stat/create/unlink test for multiple directory forms Merge of master-melb:xfs-cmds:31347a by kenmcd. Add genhashnames target --- diff --git a/src/Makefile b/src/Makefile index fa181657..ee792739 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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) diff --git a/src/genhashnames.c b/src/genhashnames.c new file mode 100644 index 00000000..12801796 --- /dev/null +++ b/src/genhashnames.c @@ -0,0 +1,179 @@ +/* + * Creates a bunch of files in the specified directory with the same + * hashvalue on-disk. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 " + "[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); +} diff --git a/src/nametest.c b/src/nametest.c index 3536c9b8..1f114475 100644 --- a/src/nametest.c +++ b/src/nametest.c @@ -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 iterations. + * every 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 ) into name fields + * Copy over names from file (in ) into name fields * of info structures in . */ 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;