ia64/xen-unstable

view tools/xenstore/xs_random.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
children a5d67e3fbff1 872cf6ee0594
line source
1 /* Random tests.
3 We check that the results from a real filesystem are the same.
4 */
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <stdlib.h>
9 #include <dirent.h>
10 #include <errno.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <sys/wait.h>
16 #include "xs.h"
17 #include "talloc.h"
18 #include "utils.h"
20 struct ops
21 {
22 char *name;
24 char **(*dir)(void *h, const char *path, unsigned int *num);
26 void *(*read)(void *h, const char *path, unsigned int *len);
28 bool (*write)(void *h, const char *path, const void *data,
29 unsigned int len, int createflags);
31 bool (*mkdir)(void *h, const char *path);
33 bool (*rm)(void *h, const char *path);
35 struct xs_permissions *(*get_perms)(void *h,
36 const char *path,
37 unsigned int *num);
39 bool (*set_perms)(void *h,
40 const char *path,
41 struct xs_permissions *perms,
42 unsigned int num);
44 bool (*transaction_start)(void *h, const char *subtree);
45 bool (*transaction_end)(void *h, bool abort);
47 /* Create and destroy a new handle. */
48 void *(*handle)(const char *path);
49 void (*close)(void *);
50 };
52 struct file_ops_info
53 {
54 const char *base;
55 char *transact_base;
56 char *transact;
57 };
59 static void convert_to_dir(const char *dirname)
60 {
61 char *tmpname = talloc_asprintf(dirname, "%s.tmp", dirname);
62 if (rename(dirname, tmpname) != 0)
63 barf_perror("Failed to rename %s to %s", dirname, tmpname);
64 if (mkdir(dirname, 0700) != 0)
65 barf_perror("Failed to mkdir %s", dirname);
66 if (rename(tmpname,talloc_asprintf(dirname, "%s/.DATA", dirname)) != 0)
67 barf_perror("Failed to rename into %s", dirname);
68 /* If perms exists, move it in. */
69 rename(talloc_asprintf(dirname, "%s.perms", dirname),
70 talloc_asprintf(dirname, "%s/.perms", dirname));
71 }
73 /* Files can be used as dirs, too. Convert them when they are. */
74 static void maybe_convert_to_directory(const char *filename)
75 {
76 struct stat st;
77 char *dirname = talloc_asprintf(filename, "%.*s",
78 strrchr(filename, '/') - filename,
79 filename);
80 if (lstat(dirname, &st) == 0 && S_ISREG(st.st_mode))
81 convert_to_dir(dirname);
82 }
84 static char *get_name(struct file_ops_info *info, const char *path)
85 {
86 if (info->transact_base)
87 return talloc_asprintf(path, "%s%s", info->transact_base,
88 path);
89 return talloc_asprintf(path, "%s%s", info->base, path);
90 }
92 static char *path_to_name(struct file_ops_info *info, const char *path)
93 {
94 char *filename = get_name(info, path);
95 maybe_convert_to_directory(filename);
96 return filename;
97 }
99 /* Is child a subnode of parent, or equal? */
100 static bool is_child(const char *child, const char *parent)
101 {
102 unsigned int len = strlen(parent);
104 /* / should really be "" for this algorithm to work, but that's a
105 * usability nightmare. */
106 if (streq(parent, "/"))
107 return true;
109 if (strncmp(child, parent, len) != 0)
110 return false;
112 return child[len] == '/' || child[len] == '\0';
113 }
115 static bool write_ok(struct file_ops_info *info, const char *path)
116 {
117 if (info->transact && !is_child(path, info->transact)) {
118 errno = EROFS;
119 return false;
120 }
121 return true;
122 }
124 static char **file_directory(struct file_ops_info *info,
125 const char *path, unsigned int *num)
126 {
127 char **ret;
128 DIR *dir;
129 struct dirent *dirent;
130 char *p, *dirname = path_to_name(info, path);
131 unsigned int i, len = 0;
132 struct stat st;
134 /* If it exists, but isn't a directory, we convert it. */
135 if (lstat(dirname, &st) == 0 && !S_ISDIR(st.st_mode))
136 convert_to_dir(dirname);
138 *num = 0;
139 dir = opendir(dirname);
140 if (!dir)
141 return NULL;;
143 /* Once to count them. */
144 while ((dirent = readdir(dir)) != NULL) {
145 if (strchr(dirent->d_name, '.'))
146 continue;
147 len += strlen(dirent->d_name) + 1;
148 (*num)++;
149 }
150 rewinddir(dir);
152 /* Now allocate and fill in. */
153 ret = malloc(sizeof(char *) * *num + len);
154 p = (char *)&ret[*num];
155 i = 0;
156 while ((dirent = readdir(dir)) != NULL) {
157 if (strchr(dirent->d_name, '.'))
158 continue;
159 ret[i] = p;
160 strcpy(p, dirent->d_name);
161 p += strlen(p) + 1;
162 i++;
163 }
164 closedir(dir);
166 return ret;
167 }
169 static char *filename_to_data(const char *filename)
170 {
171 struct stat st;
173 if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode))
174 return talloc_asprintf(filename, "%s/.DATA", filename);
175 return (char *)filename;
176 }
178 static void *file_read(struct file_ops_info *info,
179 const char *path, unsigned int *len)
180 {
181 void *ret;
182 char *filename = filename_to_data(path_to_name(info, path));
183 unsigned long size;
185 ret = grab_file(filename, &size);
186 /* Directory exists, .DATA doesn't. */
187 if (!ret && errno == ENOENT && strends(filename, ".DATA"))
188 errno = EISDIR;
189 *len = size;
190 return ret;
191 }
193 static struct xs_permissions *file_get_perms(struct file_ops_info *info,
194 const char *path,
195 unsigned int *num)
196 {
197 void *perms;
198 struct xs_permissions *ret;
199 char *filename = path_to_name(info, path);
200 char *permfile;
201 unsigned long size;
202 struct stat st;
204 if (lstat(filename, &st) != 0)
205 return NULL;
207 if (S_ISDIR(st.st_mode))
208 permfile = talloc_asprintf(path, "%s/.perms", filename);
209 else
210 permfile = talloc_asprintf(path, "%s.perms", filename);
212 perms = grab_file(permfile, &size);
213 if (!perms)
214 barf("Grabbing permissions for %s", permfile);
215 *num = xs_count_strings(perms, size);
217 ret = new_array(struct xs_permissions, *num);
218 if (!xs_strings_to_perms(ret, *num, perms))
219 barf("Reading permissions from %s", permfile);
220 release_file(perms, size);
221 return ret;
222 }
224 static void do_command(const char *cmd)
225 {
226 int ret;
228 ret = system(cmd);
229 if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
230 barf_perror("Failed '%s': %i", cmd, ret);
231 }
233 static void init_perms(const char *filename)
234 {
235 struct stat st;
236 char *permfile, *command;
238 if (lstat(filename, &st) != 0)
239 barf_perror("Failed to stat %s", filename);
241 if (S_ISDIR(st.st_mode))
242 permfile = talloc_asprintf(filename, "%s/.perms", filename);
243 else
244 permfile = talloc_asprintf(filename, "%s.perms", filename);
246 /* Leave permfile if it already exists. */
247 if (lstat(permfile, &st) == 0)
248 return;
250 /* Copy permissions from parent */
251 command = talloc_asprintf(filename, "cp %.*s/.perms %s",
252 strrchr(filename, '/') - filename,
253 filename, permfile);
254 do_command(command);
255 }
257 static bool file_set_perms(struct file_ops_info *info,
258 const char *path,
259 struct xs_permissions *perms,
260 unsigned int num)
261 {
262 unsigned int i;
263 char *filename = path_to_name(info, path);
264 char *permfile;
265 int fd;
266 struct stat st;
268 if (num < 1) {
269 errno = EINVAL;
270 return false;
271 }
273 if (!write_ok(info, path))
274 return false;
276 /* Check non-perm file exists/ */
277 if (lstat(filename, &st) != 0)
278 return false;
280 if (S_ISDIR(st.st_mode))
281 permfile = talloc_asprintf(path, "%s/.perms", filename);
282 else
283 permfile = talloc_asprintf(path, "%s.perms", filename);
285 fd = open(permfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
286 if (fd < 0)
287 return false;
289 for (i = 0; i < num; i++) {
290 char buffer[100];
292 if (!xs_perm_to_string(&perms[i], buffer)) {
293 int saved_errno = errno;
294 close(fd);
295 errno = saved_errno;
296 return false;
297 }
298 if (write(fd, buffer, strlen(buffer) + 1)
299 != (int)strlen(buffer) + 1)
300 barf_perror("Failed to write perm");
301 }
302 close(fd);
303 return true;
304 }
306 static char *parent_filename(const char *name)
307 {
308 char *slash = strrchr(name + 1, '/');
309 if (!slash)
310 return talloc_strdup(name, "/");
311 return talloc_asprintf(name, "%.*s", slash-name, name);
312 }
314 static void make_dirs(const char *filename)
315 {
316 struct stat st;
318 if (lstat(filename, &st) == 0 && S_ISREG(st.st_mode))
319 convert_to_dir(filename);
321 if (mkdir(filename, 0700) == 0) {
322 init_perms(filename);
323 return;
324 }
325 if (errno == EEXIST)
326 return;
328 make_dirs(parent_filename(filename));
329 if (mkdir(filename, 0700) != 0)
330 barf_perror("Failed to mkdir %s", filename);
331 init_perms(filename);
332 }
334 static bool file_write(struct file_ops_info *info,
335 const char *path, const void *data,
336 unsigned int len, int createflags)
337 {
338 char *filename = filename_to_data(path_to_name(info, path));
339 int fd;
341 /* Kernel isn't strict, but library is. */
342 if (createflags & ~(O_CREAT|O_EXCL)) {
343 errno = EINVAL;
344 return false;
345 }
347 if (!write_ok(info, path))
348 return false;
350 /* We regard it as existing if dir exists. */
351 if (strends(filename, ".DATA")) {
352 if (!createflags)
353 createflags = O_CREAT;
354 if (createflags & O_EXCL) {
355 errno = EEXIST;
356 return false;
357 }
358 }
360 if (createflags & O_CREAT)
361 make_dirs(parent_filename(filename));
363 fd = open(filename, createflags|O_TRUNC|O_WRONLY, 0600);
364 if (fd < 0) {
365 /* FIXME: Another hack. */
366 if (!(createflags & O_CREAT) && errno == EISDIR)
367 errno = EEXIST;
368 return false;
369 }
371 if (write(fd, data, len) != (int)len)
372 barf_perror("Bad write to %s", filename);
374 init_perms(filename);
375 close(fd);
376 return true;
377 }
379 static bool file_mkdir(struct file_ops_info *info, const char *path)
380 {
381 char *dirname = path_to_name(info, path);
383 if (!write_ok(info, path))
384 return false;
386 make_dirs(parent_filename(dirname));
387 if (mkdir(dirname, 0700) != 0)
388 return false;
390 init_perms(dirname);
391 return true;
392 }
394 static bool file_rm(struct file_ops_info *info, const char *path)
395 {
396 char *filename = path_to_name(info, path);
397 struct stat st;
399 if (info->transact && streq(info->transact, path)) {
400 errno = EINVAL;
401 return false;
402 }
404 if (lstat(filename, &st) != 0)
405 return false;
407 if (!write_ok(info, path))
408 return false;
410 if (streq(path, "/")) {
411 errno = EINVAL;
412 return false;
413 }
415 do_command(talloc_asprintf(path, "rm -f %s.perms; rm -r %s",
416 filename, filename));
417 return true;
418 }
420 static bool file_transaction_start(struct file_ops_info *info,
421 const char *subtree)
422 {
423 char *cmd;
424 char *filename = path_to_name(info, subtree);
425 struct stat st;
427 if (info->transact) {
428 errno = EBUSY;
429 return false;
430 }
432 if (lstat(filename, &st) != 0)
433 return false;
435 cmd = talloc_asprintf(NULL, "cp -r %s %s.transact",
436 info->base, info->base);
437 do_command(cmd);
438 talloc_free(cmd);
440 info->transact_base = talloc_asprintf(NULL, "%s.transact", info->base);
441 info->transact = talloc_strdup(NULL, subtree);
442 return true;
443 }
445 static bool file_transaction_end(struct file_ops_info *info, bool abort)
446 {
447 char *old, *cmd;
449 if (!info->transact) {
450 errno = ENOENT;
451 return false;
452 }
454 if (abort) {
455 cmd = talloc_asprintf(NULL, "rm -rf %s", info->transact_base);
456 do_command(cmd);
457 goto success;
458 }
460 old = talloc_asprintf(NULL, "rm -rf %s", info->base);
461 do_command(old);
462 talloc_free(old);
464 cmd = talloc_asprintf(NULL, "mv %s %s",
465 info->transact_base, info->base);
466 do_command(cmd);
468 success:
469 talloc_free(cmd);
470 talloc_free(info->transact);
471 talloc_free(info->transact_base);
472 info->transact = NULL;
473 info->transact_base = NULL;
474 return true;
475 }
477 static struct file_ops_info *file_handle(const char *dir)
478 {
479 struct file_ops_info *info = talloc(NULL, struct file_ops_info);
481 info->base = dir;
482 info->transact_base = NULL;
483 info->transact = NULL;
484 return info;
485 }
487 static void file_close(struct file_ops_info *handle)
488 {
489 talloc_free(handle);
490 }
492 static struct xs_handle *xs_handle(const char *dir __attribute__((unused)))
493 {
494 struct xs_handle *h;
496 h = xs_daemon_open();
497 if (!h)
498 barf_perror("Connecting to xs daemon");
499 return h;
500 }
502 static void xs_close(struct xs_handle *handle)
503 {
504 xs_daemon_close(handle);
505 }
507 struct ops file_ops = {
508 .name = "FILE",
509 .dir = (void *)file_directory,
510 .read = (void *)file_read,
511 .write = (void *)file_write,
512 .mkdir = (void *)file_mkdir,
513 .rm = (void *)file_rm,
514 .get_perms = (void *)file_get_perms,
515 .set_perms = (void *)file_set_perms,
516 .transaction_start = (void *)file_transaction_start,
517 .transaction_end = (void *)file_transaction_end,
518 .handle = (void *)file_handle,
519 .close = (void *)file_close,
520 };
522 struct ops xs_ops = {
523 .name = "XS",
524 .dir = (void *)xs_directory,
525 .read = (void *)xs_read,
526 .write = (void *)xs_write,
527 .mkdir = (void *)xs_mkdir,
528 .rm = (void *)xs_rm,
529 .get_perms = (void *)xs_get_permissions,
530 .set_perms = (void *)xs_set_permissions,
531 .transaction_start = (void *)xs_transaction_start,
532 .transaction_end = (void *)xs_transaction_end,
533 .handle = (void *)xs_handle,
534 .close = (void *)xs_close,
535 };
537 static int strptrcmp(const void *a, const void *b)
538 {
539 return strcmp(*(char **)a, *(char **)b);
540 }
542 static void sort_dir(char **dir, unsigned int num)
543 {
544 qsort(dir, num, sizeof(char *), strptrcmp);
545 }
547 static char *dump_dir(struct ops *ops,
548 void *h,
549 const char *node,
550 char **dir,
551 unsigned int numdirs,
552 unsigned int depth)
553 {
554 char *ret = talloc_strdup(node, "");
555 unsigned int i;
556 char spacing[depth+1];
558 memset(spacing, ' ', depth);
559 spacing[depth] = '\0';
561 sort_dir(dir, numdirs);
563 for (i = 0; i < numdirs; i++) {
564 struct xs_permissions *perms;
565 unsigned int j, numperms;
566 unsigned int len;
567 char *contents;
568 unsigned int subnum;
569 char **subdirs;
570 char *subret;
571 char *subnode = talloc_asprintf(node, "%s/%s", node, dir[i]);
573 perms = ops->get_perms(h, subnode, &numperms);
574 if (!perms)
575 return NULL;
576 ret = talloc_asprintf_append(ret, "%s%s: ", spacing, dir[i]);
577 for (j = 0; j < numperms; j++) {
578 char buffer[100];
579 if (!xs_perm_to_string(&perms[j], buffer))
580 barf("perm to string");
581 ret = talloc_asprintf_append(ret, "%s ", buffer);
582 }
583 free(perms);
584 ret = talloc_asprintf_append(ret, "\n");
586 /* Even directories can have contents. */
587 contents = ops->read(h, subnode, &len);
588 if (!contents) {
589 if (errno != EISDIR)
590 return NULL;
591 } else {
592 ret = talloc_asprintf_append(ret, " %s(%.*s)\n",
593 spacing, len, contents);
594 free(contents);
595 }
597 /* Every node is a directory. */
598 subdirs = ops->dir(h, subnode, &subnum);
599 if (!subdirs)
600 return NULL;
601 subret = dump_dir(ops, h, subnode, subdirs, subnum, depth+1);
602 if (!subret)
603 return NULL;
604 ret = talloc_asprintf_append(ret, "%s", subret);
605 free(subdirs);
606 }
607 return ret;
608 }
610 static char *dump(struct ops *ops, void *h)
611 {
612 char **subdirs;
613 unsigned int subnum;
614 char *ret = NULL, *root = talloc_strdup(NULL, "/");
616 subdirs = ops->dir(h, root, &subnum);
617 if (subdirs) {
618 ret = dump_dir(ops, h, talloc_strdup(root, ""), subdirs,
619 subnum, 0);
620 free(subdirs);
621 if (ret)
622 talloc_steal(NULL, ret);
623 }
624 talloc_free(root);
625 return ret;
626 }
628 /* jhash.h: Jenkins hash support.
629 *
630 * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net)
631 *
632 * http://burtleburtle.net/bob/hash/
633 *
634 * These are the credits from Bob's sources:
635 *
636 * lookup2.c, by Bob Jenkins, December 1996, Public Domain.
637 * hash(), hash2(), hash3, and mix() are externally useful functions.
638 * Routines to test the hash are included if SELF_TEST is defined.
639 * You can use this free for any purpose. It has no warranty.
640 *
641 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
642 *
643 * I've modified Bob's hash to be useful in the Linux kernel, and
644 * any bugs present are surely my fault. -DaveM
645 */
647 /* NOTE: Arguments are modified. */
648 #define __jhash_mix(a, b, c) \
649 { \
650 a -= b; a -= c; a ^= (c>>13); \
651 b -= c; b -= a; b ^= (a<<8); \
652 c -= a; c -= b; c ^= (b>>13); \
653 a -= b; a -= c; a ^= (c>>12); \
654 b -= c; b -= a; b ^= (a<<16); \
655 c -= a; c -= b; c ^= (b>>5); \
656 a -= b; a -= c; a ^= (c>>3); \
657 b -= c; b -= a; b ^= (a<<10); \
658 c -= a; c -= b; c ^= (b>>15); \
659 }
661 /* The golden ration: an arbitrary value */
662 #define JHASH_GOLDEN_RATIO 0x9e3779b9
664 /* The most generic version, hashes an arbitrary sequence
665 * of bytes. No alignment or length assumptions are made about
666 * the input key.
667 */
668 static inline u32 jhash(const void *key, u32 length, u32 initval)
669 {
670 u32 a, b, c, len;
671 const u8 *k = key;
673 len = length;
674 a = b = JHASH_GOLDEN_RATIO;
675 c = initval;
677 while (len >= 12) {
678 a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24));
679 b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24));
680 c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24));
682 __jhash_mix(a,b,c);
684 k += 12;
685 len -= 12;
686 }
688 c += length;
689 switch (len) {
690 case 11: c += ((u32)k[10]<<24);
691 case 10: c += ((u32)k[9]<<16);
692 case 9 : c += ((u32)k[8]<<8);
693 case 8 : b += ((u32)k[7]<<24);
694 case 7 : b += ((u32)k[6]<<16);
695 case 6 : b += ((u32)k[5]<<8);
696 case 5 : b += k[4];
697 case 4 : a += ((u32)k[3]<<24);
698 case 3 : a += ((u32)k[2]<<16);
699 case 2 : a += ((u32)k[1]<<8);
700 case 1 : a += k[0];
701 };
703 __jhash_mix(a,b,c);
705 return c;
706 }
708 /* A special optimized version that handles 1 or more of u32s.
709 * The length parameter here is the number of u32s in the key.
710 */
711 static inline u32 jhash2(u32 *k, u32 length, u32 initval)
712 {
713 u32 a, b, c, len;
715 a = b = JHASH_GOLDEN_RATIO;
716 c = initval;
717 len = length;
719 while (len >= 3) {
720 a += k[0];
721 b += k[1];
722 c += k[2];
723 __jhash_mix(a, b, c);
724 k += 3; len -= 3;
725 }
727 c += length * 4;
729 switch (len) {
730 case 2 : b += k[1];
731 case 1 : a += k[0];
732 };
734 __jhash_mix(a,b,c);
736 return c;
737 }
740 /* A special ultra-optimized versions that knows they are hashing exactly
741 * 3, 2 or 1 word(s).
742 *
743 * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally
744 * done at the end is not done here.
745 */
746 static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
747 {
748 a += JHASH_GOLDEN_RATIO;
749 b += JHASH_GOLDEN_RATIO;
750 c += initval;
752 __jhash_mix(a, b, c);
754 return c;
755 }
757 static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
758 {
759 return jhash_3words(a, b, 0, initval);
760 }
762 static inline u32 jhash_1word(u32 a, u32 initval)
763 {
764 return jhash_3words(a, 0, 0, initval);
765 }
767 static unsigned int get_randomness(int *state)
768 {
769 return jhash_1word((*state)++, *state * 1103515243);
770 }
772 static char *random_path(int *state)
773 {
774 unsigned int i;
775 char *ret = NULL;
777 if (get_randomness(state) % 20 == 0)
778 return talloc_strdup(NULL, "/");
780 for (i = 0; i < 1 || (get_randomness(state) % 2); i++) {
781 ret = talloc_asprintf_append(ret, "/%i",
782 get_randomness(state) % 15);
783 }
784 return ret;
785 }
787 static char *bool_to_errstring(bool result)
788 {
789 if (result)
790 return talloc_strdup(NULL, "OK");
792 /* Real daemon can never return this. */
793 if (errno == ENOTDIR)
794 errno = ENOENT;
795 return talloc_asprintf(NULL, "FAILED:%s", strerror(errno));
796 }
798 static char *linearize_dir(char **dir, unsigned int *num)
799 {
800 char *result = NULL;
801 unsigned int i;
803 if (!dir)
804 return bool_to_errstring(false);
806 if (!*num) {
807 free(dir);
808 return talloc_strdup(NULL, "");
809 }
811 sort_dir(dir, *num);
812 for (i = 0; i < *num; i++)
813 result = talloc_asprintf_append(result, "%s\n", dir[i]);
814 free(dir);
815 return result;
816 }
818 static char *linearize_read(char *read, unsigned int *size)
819 {
820 char *ret;
822 if (!read)
823 return bool_to_errstring(false);
825 ret = talloc_asprintf(NULL, "%i:%.*s", *size, *size, read);
826 free(read);
827 return ret;
828 }
830 static char *linearize_perms(struct xs_permissions *perms, unsigned int *size)
831 {
832 char *ret = NULL;
833 unsigned int i;
835 if (!perms)
836 return bool_to_errstring(false);
838 for (i = 0; i < *size; i++)
839 ret = talloc_asprintf_append(ret, "(%u %u)",
840 perms[i].id, perms[i].perms);
842 free(perms);
843 return ret;
844 }
846 static int random_flags(int *state)
847 {
848 switch (get_randomness(state) % 4) {
849 case 0:
850 return 0;
851 case 1:
852 return O_CREAT;
853 case 2:
854 return O_CREAT|O_EXCL;
855 default:
856 return get_randomness(state);
857 }
858 }
860 /* Do the next operation, return the results. */
861 static char *do_next_op(struct ops *ops, void *h, int state, bool verbose)
862 {
863 char *name;
864 unsigned int num;
865 char *ret;
867 if (verbose)
868 printf("State %i: ", state);
870 name = random_path(&state);
871 switch (get_randomness(&state) % 9) {
872 case 0:
873 if (verbose)
874 printf("DIR %s\n", name);
875 ret = linearize_dir(ops->dir(h, name, &num), &num);
876 break;
877 case 1:
878 if (verbose)
879 printf("READ %s\n", name);
880 ret = linearize_read(ops->read(h, name, &num), &num);
881 break;
882 case 2: {
883 int flags = random_flags(&state);
884 char *contents = talloc_asprintf(NULL, "%i",
885 get_randomness(&state));
886 unsigned int len = get_randomness(&state)%(strlen(contents)+1);
887 if (verbose)
888 printf("WRITE %s %s %.*s\n", name,
889 flags == O_CREAT ? "O_CREAT"
890 : flags == (O_CREAT|O_EXCL) ? "O_CREAT|O_EXCL"
891 : flags == 0 ? "0" : "CRAPFLAGS",
892 len, contents);
893 ret = bool_to_errstring(ops->write(h, name, contents, len,
894 flags));
895 talloc_steal(ret, contents);
896 break;
897 }
898 case 3:
899 if (verbose)
900 printf("MKDIR %s\n", name);
901 ret = bool_to_errstring(ops->mkdir(h, name));
902 break;
903 case 4:
904 if (verbose)
905 printf("RM %s\n", name);
906 ret = bool_to_errstring(ops->rm(h, name));
907 break;
908 case 5:
909 if (verbose)
910 printf("GETPERMS %s\n", name);
911 ret = linearize_perms(ops->get_perms(h, name, &num),
912 &num);
913 break;
914 case 6: {
915 unsigned int i, num = get_randomness(&state)%8;
916 struct xs_permissions perms[num];
918 if (verbose)
919 printf("SETPERMS %s: ", name);
920 for (i = 0; i < num; i++) {
921 perms[i].id = get_randomness(&state)%8;
922 perms[i].perms = get_randomness(&state)%4;
923 if (verbose)
924 printf("%i%c ", perms[i].id,
925 perms[i].perms == XS_PERM_WRITE ? 'W'
926 : perms[i].perms == XS_PERM_READ ? 'R'
927 : perms[i].perms ==
928 (XS_PERM_READ|XS_PERM_WRITE) ? 'B'
929 : 'N');
930 }
931 if (verbose)
932 printf("\n");
933 ret = bool_to_errstring(ops->set_perms(h, name, perms,
934 num));
935 break;
936 }
937 case 7: {
938 if (verbose)
939 printf("START %s\n", name);
940 ret = bool_to_errstring(ops->transaction_start(h, name));
941 if (streq(ret, "OK")) {
942 talloc_free(ret);
943 ret = talloc_asprintf(NULL, "OK:START-TRANSACT:%s",
944 name);
945 }
947 break;
948 }
949 case 8: {
950 bool abort = (get_randomness(&state) % 2);
952 if (verbose)
953 printf("STOP %s\n", abort ? "ABORT" : "COMMIT");
954 ret = bool_to_errstring(ops->transaction_end(h, abort));
955 if (streq(ret, "OK")) {
956 talloc_free(ret);
957 ret = talloc_strdup(NULL, "OK:STOP-TRANSACT");
958 }
959 break;
960 }
961 default:
962 barf("Impossible randomness");
963 }
965 talloc_steal(ret, name);
966 return ret;
967 }
969 static int daemon_pid;
971 static void cleanup_xs_ops(void)
972 {
973 char *cmd;
974 if (daemon_pid) {
975 struct xs_handle *h;
976 h = xs_daemon_open();
977 if (h) {
978 if (xs_shutdown(h)) {
979 waitpid(daemon_pid, NULL, 0);
980 daemon_pid = 0;
981 }
982 xs_daemon_close(h);
983 }
984 if (daemon_pid) {
985 kill(daemon_pid, SIGTERM);
986 waitpid(daemon_pid, NULL, 0);
987 }
988 }
990 cmd = talloc_asprintf(NULL, "rm -rf testsuite/tmp/*");
991 do_command(cmd);
992 talloc_free(cmd);
993 }
995 static void cleanup_file_ops(const char *dir)
996 {
997 char *cmd;
999 cmd = talloc_asprintf(NULL, "rm -rf %s %s.transact", dir, dir);
1000 do_command(cmd);
1001 talloc_free(cmd);
1004 static void cleanup(const char *dir)
1006 cleanup_xs_ops();
1007 cleanup_file_ops(dir);
1010 static void setup_file_ops(const char *dir)
1012 struct xs_permissions perm = { .id = 0, .perms = XS_PERM_READ };
1013 struct file_ops_info *h = file_handle(dir);
1014 if (mkdir(dir, 0700) != 0)
1015 barf_perror("Creating directory %s", dir);
1016 if (mkdir(talloc_asprintf(h, "%s/tool", dir), 0700) != 0)
1017 barf_perror("Creating directory %s/tool", dir);
1018 if (!file_set_perms(h, talloc_strdup(h, "/"), &perm, 1))
1019 barf_perror("Setting root perms in %s", dir);
1020 file_close(h);
1023 static void setup_xs_ops(void)
1025 int fds[2];
1027 /* Start daemon. */
1028 pipe(fds);
1029 if ((daemon_pid = fork())) {
1030 /* Child writes PID when its ready: we wait for that. */
1031 char buffer[20];
1032 close(fds[1]);
1033 if (read(fds[0], buffer, sizeof(buffer)) < 0)
1034 barf("Failed to summon daemon");
1035 close(fds[0]);
1036 } else {
1037 dup2(fds[1], STDOUT_FILENO);
1038 close(fds[0]);
1039 #if 1
1040 execlp("valgrind", "valgrind", "-q", "--suppressions=testsuite/vg-suppressions", "xenstored_test", "--output-pid",
1041 "--no-fork", NULL);
1042 #else
1043 execlp("./xenstored_test", "xenstored_test", "--output-pid",
1044 "--no-fork", NULL);
1045 #endif
1046 exit(1);
1050 static void setup(const char *dir)
1052 setup_file_ops(dir);
1053 setup_xs_ops();
1054 };
1056 struct simple_data
1058 unsigned int seed;
1059 bool print_progress;
1060 bool fast;
1061 struct ops *ops;
1062 const char *dir;
1063 };
1065 /* Just a random test. Don't care about results, just that it doesn't
1066 * go boom. */
1067 static unsigned int try_simple(const bool *trymap,
1068 unsigned int number,
1069 bool verbose,
1070 void *_data)
1072 unsigned int i, print;
1073 void *h;
1074 char *snapshot = NULL;
1075 struct simple_data *data = _data;
1077 if (data->ops == &xs_ops) {
1078 cleanup_xs_ops();
1079 setup_xs_ops();
1080 } else {
1081 cleanup_file_ops(data->dir);
1082 setup_file_ops(data->dir);
1084 h = data->ops->handle(data->dir);
1086 print = number / 76;
1087 if (!print)
1088 print = 1;
1090 for (i = 0; i < number; i++) {
1091 char *ret;
1093 if (data->print_progress) {
1094 if (i % print == 0) {
1095 printf(".");
1096 fflush(stdout);
1100 if (trymap && !trymap[i])
1101 continue;
1103 ret = do_next_op(data->ops, h, i + data->seed, verbose);
1104 if (verbose)
1105 printf("-> %.*s\n", strchr(ret, '\n') - ret, ret);
1106 if (streq(ret, "FAILED:Bad file descriptor"))
1107 goto out;
1108 if (kill(daemon_pid, 0) != 0)
1109 goto out;
1111 if (!data->fast) {
1112 if (strstarts(ret, "OK:START-TRANSACT:")) {
1113 void *pre = data->ops->handle(data->dir);
1115 snapshot = dump(data->ops, pre);
1116 if (!snapshot)
1117 goto out;
1118 data->ops->close(pre);
1119 } else if (streq(ret, "OK:STOP-TRANSACT")) {
1120 talloc_free(snapshot);
1121 snapshot = NULL;
1125 talloc_free(ret);
1127 if (snapshot) {
1128 void *pre = data->ops->handle(data->dir);
1129 char *contents;
1131 contents = dump(data->ops, pre);
1132 if (!contents)
1133 goto out;
1135 if (!streq(contents, snapshot))
1136 goto out;
1138 talloc_free(contents);
1139 data->ops->close(pre);
1142 out:
1143 data->ops->close(h);
1144 return i;
1147 /* Binary elimination: try eliminating all of them, then reduce. */
1148 static void reduce(bool *map,
1149 unsigned int number,
1150 unsigned int try_start, unsigned int try_num,
1151 unsigned int (*try)(const bool *map,
1152 unsigned int number,
1153 bool verbose,
1154 void *),
1155 void *data)
1157 bool newmap[number];
1159 if (try_num == 0)
1160 return;
1162 /* Try skipping everything between start and end. */
1163 memcpy(newmap, map, sizeof(newmap));
1164 memset(newmap + try_start, 0, try_num * sizeof(bool));
1166 /* We want the *same* failure: must fail at "number-1". */
1167 if (try(newmap, number, false, data) == number - 1) {
1168 memset(map + try_start, 0, try_num * sizeof(bool));
1169 return;
1172 if (try_num == 1)
1173 return;
1175 /* Try each half... */
1176 reduce(map, number, try_start, try_num/2, try, data);
1177 reduce(map, number, try_start + try_num/2, try_num - try_num/2,
1178 try, data);
1181 static void reduce_problem(unsigned int failed,
1182 unsigned int (*try)(const bool *map,
1183 unsigned int number,
1184 bool verbose,
1185 void *data),
1186 void *data)
1188 bool map[failed];
1190 memset(map, 1, sizeof(map));
1191 reduce(map, failed, 0, failed-1, try, data);
1193 printf("Cut down:\n");
1194 if (try(map, failed, true, data) != failed - 1) {
1195 printf("Except, that didn't actually fail. Bugger!");
1196 exit(2);
1198 exit(1);
1201 /* Just a random test. Don't care about results, just that it doesn't
1202 * go boom. */
1203 static void simple_test(const char *dir,
1204 unsigned int iters, unsigned int seed,
1205 bool fast, bool verbose)
1207 struct simple_data data;
1208 unsigned int try;
1210 data.seed = seed;
1211 data.print_progress = !verbose;
1212 data.fast = fast;
1213 data.ops = &xs_ops;
1214 data.dir = dir;
1216 try = try_simple(NULL, iters, verbose, &data);
1217 if (try == iters) {
1218 cleanup_xs_ops();
1219 exit(0);
1221 printf("Failed on iteration %u of seed %u\n", try + 1, seed);
1222 data.print_progress = false;
1223 reduce_problem(try + 1, try_simple, &data);
1226 static bool ops_equal(struct ops *a, void *ah,
1227 struct ops *b, void *bh,
1228 const char *node,
1229 struct ops **fail)
1231 char **dira = NULL, **dirb = NULL;
1232 char *dataa = NULL, *datab = NULL;
1233 unsigned int i, numa, numb, lena, lenb;
1234 struct xs_permissions *permsa = NULL, *permsb = NULL;
1235 unsigned int numpermsa, numpermsb;
1236 char *nodename;
1237 bool ret = false;
1239 /* Ignore tool/ dir. */
1240 if (streq(node, "/tool"))
1241 return true;
1243 /* FILE backend expects talloc'ed pointer. */
1244 nodename = talloc_strdup(NULL, node);
1245 permsa = a->get_perms(ah, nodename, &numpermsa);
1246 if (!permsa) {
1247 *fail = a;
1248 goto out;
1250 permsb = b->get_perms(bh, nodename, &numpermsb);
1251 if (!permsb) {
1252 *fail = b;
1253 goto out;
1255 if (numpermsa != numpermsb)
1256 goto out;
1257 for (i = 0; i < numpermsa; i++) {
1258 if (permsa[i].perms != permsb[i].perms)
1259 goto out;
1260 if (permsa[i].id != permsb[i].id)
1261 goto out;
1264 /* Non-pure-directory nodes contain data. */
1265 dataa = a->read(ah, nodename, &lena);
1266 if (!dataa && errno != EISDIR) {
1267 *fail = a;
1268 goto out;
1270 datab = b->read(bh, nodename, &lenb);
1271 if (!datab && errno != EISDIR) {
1272 *fail = b;
1273 goto out;
1276 if (dataa) {
1277 if (!datab)
1278 goto out;
1279 if (lena != lenb)
1280 goto out;
1282 if (memcmp(dataa, datab, lena) != 0)
1283 goto out;
1284 } else
1285 if (datab)
1286 goto out;
1288 /* Everything is a directory. */
1289 dira = a->dir(ah, nodename, &numa);
1290 if (!dira) {
1291 *fail = a;
1292 goto out;
1294 dirb = b->dir(bh, nodename, &numb);
1295 if (!dirb) {
1296 *fail = b;
1297 goto out;
1299 if (numa != numb)
1300 goto out;
1301 sort_dir(dira, numa);
1302 sort_dir(dirb, numb);
1303 for (i = 0; i < numa; i++) {
1304 char subnode[strlen(node) + 1 + strlen(dira[i]) + 1];
1306 if (!streq(dira[i], dirb[i]))
1307 goto out;
1309 strcpy(subnode, node);
1310 if (!streq(node, "/"))
1311 strcat(subnode, "/");
1312 strcat(subnode, dira[i]);
1313 if (!ops_equal(a, ah, b, bh, subnode, fail))
1314 goto out;
1317 ret = true;
1318 out:
1319 free(permsa);
1320 free(permsb);
1321 free(dataa);
1322 free(datab);
1323 free(dira);
1324 free(dirb);
1325 talloc_free(nodename);
1326 return ret;
1329 struct diff_data
1331 unsigned int seed;
1332 bool print_progress;
1333 bool fast;
1334 const char *dir;
1335 };
1337 /* Differential: try both file and xs backend, watch for differences. */
1338 static unsigned int try_diff(const bool *trymap,
1339 unsigned int number,
1340 bool verbose,
1341 void *_data)
1343 void *fileh, *xsh;
1344 char *transact = NULL;
1345 struct ops *fail;
1346 struct diff_data *data = _data;
1347 unsigned int i, print;
1349 cleanup(data->dir);
1350 setup(data->dir);
1352 fileh = file_handle(data->dir);
1353 xsh = xs_handle(data->dir);
1355 print = number / 76;
1356 if (!print)
1357 print = 1;
1359 for (i = 0; i < number; i++) {
1360 char *file, *xs;
1362 if (data->print_progress) {
1363 if (i % print == 0) {
1364 printf(".");
1365 fflush(stdout);
1368 if (trymap && !trymap[i])
1369 continue;
1371 if (verbose)
1372 printf("FILE: ");
1374 file = do_next_op(&file_ops, fileh, i+data->seed, verbose);
1375 if (verbose)
1376 printf("-> %.*s\n", strchr(file, '/') - file, file);
1378 if (verbose)
1379 printf("XS: ");
1380 xs = do_next_op(&xs_ops, xsh, i+data->seed, verbose);
1381 if (verbose)
1382 printf("-> %.*s\n", strchr(xs, '/') - xs, xs);
1384 if (!streq(file, xs))
1385 goto out;
1387 if (strstarts(file, "OK:START-TRANSACT:"))
1388 transact = talloc_strdup(NULL,
1389 file +
1390 strlen("OK:START-TRANSACT:"));
1391 else if (streq(file, "OK:STOP-TRANSACT")) {
1392 talloc_free(transact);
1393 transact = NULL;
1396 talloc_free(file);
1397 talloc_free(xs);
1399 if (data->fast)
1400 continue;
1402 fail = NULL;
1403 if (!ops_equal(&xs_ops, xsh, &file_ops, fileh, "/", &fail)) {
1404 if (fail)
1405 barf("%s failed during test\n", fail->name);
1406 if (verbose)
1407 printf("Trees differ:\nXS:%s\nFILE%s\n",
1408 dump(&xs_ops, xsh),
1409 dump(&file_ops, fileh));
1410 goto out;
1413 if (transact) {
1414 void *fileh_pre = file_handle(data->dir);
1415 void *xsh_pre = xs_handle(data->dir);
1417 fail = NULL;
1418 if (!ops_equal(&xs_ops, xsh_pre, &file_ops, fileh_pre,
1419 transact, &fail)) {
1420 if (fail)
1421 barf("%s failed during transact\n",
1422 fail->name);
1424 xs_daemon_close(xsh_pre);
1425 talloc_free(fileh_pre);
1426 goto out;
1428 xs_daemon_close(xsh_pre);
1429 talloc_free(fileh_pre);
1433 fail = NULL;
1434 if (data->fast)
1435 if (!ops_equal(&xs_ops, xsh, &file_ops, fileh, "/", &fail))
1436 barf("Final result not the same: try without --fast");
1437 out:
1438 file_ops.close(fileh);
1439 xs_ops.close(xsh);
1440 return i;
1443 /* Differential random test: compare results against file backend. */
1444 static void diff_test(const char *dir,
1445 unsigned int iters, unsigned int seed, bool fast,
1446 bool verbose)
1448 struct diff_data data;
1449 unsigned int try;
1451 data.seed = seed;
1452 data.print_progress = !verbose;
1453 data.fast = fast;
1454 data.dir = dir;
1456 try = try_diff(NULL, iters, verbose, &data);
1457 if (try == iters) {
1458 cleanup_xs_ops();
1459 exit(0);
1461 printf("Failed on iteration %u of seed %u\n", try + 1, seed);
1462 data.print_progress = false;
1463 reduce_problem(try + 1, try_diff, &data);
1466 struct fail_data
1468 unsigned int seed;
1469 bool print_progress;
1470 const char *dir;
1471 };
1473 /* Try xs with inserted failures: every op should either succeed or fail. */
1474 static unsigned int try_fail(const bool *trymap,
1475 unsigned int number,
1476 bool verbose,
1477 void *_data)
1479 unsigned int i, print, tried = 0, aborted = 0;
1480 struct fail_data *data = _data;
1481 struct xs_handle *tmpxsh;
1482 struct file_ops_info *tmpfileh;
1483 void *fileh, *xsh;
1484 struct ops *fail;
1485 char seed[20];
1487 /* Make sure failures off to shut down. */
1488 if (daemon_pid)
1489 kill(daemon_pid, SIGUSR1);
1490 cleanup(data->dir);
1491 setup(data->dir);
1493 fileh = file_handle(data->dir);
1494 xsh = xs_handle(data->dir);
1496 sprintf(seed, "%i", data->seed);
1497 free(xs_debug_command(xsh, "failtest", seed, strlen(seed)+1));
1499 print = number / 76;
1500 if (!print)
1501 print = 1;
1503 for (i = 0; i < number; i++) {
1504 unsigned int limit, failed;
1505 char *ret;
1507 /* A few times we fail due to other end OOM. */
1508 limit = 0;
1509 while (!xsh) {
1510 xsh = xs_handle(data->dir);
1511 if (!xsh && errno == ECONNREFUSED) {
1512 if (verbose)
1513 printf("Daemon refused connection\n");
1514 goto out;
1516 if (!xsh && limit++ == 5) {
1517 printf("Daemon failed conn 5 times\n");
1518 goto out;
1522 if (data->print_progress) {
1523 if (i % print == 0) {
1524 printf(".");
1525 fflush(stdout);
1528 if (trymap && !trymap[i])
1529 continue;
1531 if (verbose)
1532 printf("(%i) ", i);
1533 ret = do_next_op(&xs_ops, xsh, i + data->seed, verbose);
1534 if (streq(ret, "FAILED:Connection reset by peer")
1535 || streq(ret, "FAILED:Bad file descriptor")
1536 || streq(ret, "FAILED:Broken pipe")) {
1537 xs_close(xsh);
1538 xsh = NULL;
1539 failed = 2;
1540 } else if (strstarts(ret, "OK"))
1541 failed = 0;
1542 else
1543 failed = 1;
1545 tried++;
1546 if (xsh)
1547 aborted++;
1549 if (verbose)
1550 printf("-> %.*s\n", strchr(ret, '\n') - ret, ret);
1552 talloc_free(ret);
1554 /* Turn off failures using signal. */
1555 if (kill(daemon_pid, SIGUSR1) != 0) {
1556 if (verbose)
1557 printf("Failed to signal daemon\n");
1558 goto out;
1561 if (failed == 0) {
1562 /* Succeeded? Do same thing to file backend
1563 * to compare */
1564 try_applying:
1565 ret = do_next_op(&file_ops, fileh, i + data->seed,
1566 false);
1567 if (!strstarts(ret, "OK")) {
1568 if (!verbose)
1569 printf("File op failed on %i\n",
1570 i + data->seed);
1571 talloc_free(ret);
1572 goto out;
1574 talloc_free(ret);
1577 tmpxsh = xs_handle(data->dir);
1578 if (!tmpxsh) {
1579 if (verbose)
1580 printf("Failed to open signalled daemon");
1581 goto out;
1583 tmpfileh = file_handle(data->dir);
1585 fail = NULL;
1586 if (!ops_equal(&xs_ops, tmpxsh, &file_ops, tmpfileh, "/",
1587 &fail)) {
1588 xs_close(tmpxsh);
1589 file_close(tmpfileh);
1590 if (fail) {
1591 if (verbose)
1592 printf("%s failed\n", fail->name);
1593 goto out;
1595 /* Maybe op succeeded: try comparing after local op? */
1596 if (failed == 2) {
1597 failed = 0;
1598 if (verbose)
1599 printf("(Looks like it succeeded)\n");
1600 goto try_applying;
1602 if (verbose)
1603 printf("Two backends not equal\n");
1604 goto out;
1607 /* If we lost the xs handle, that ended the transaction */
1608 if (!xsh)
1609 file_transaction_end(fileh, true);
1611 /* Turn failures back on. */
1612 free(xs_debug_command(tmpxsh, "failtest", NULL, 0));
1613 xs_close(tmpxsh);
1614 file_close(tmpfileh);
1616 out:
1617 if (xsh)
1618 xs_close(xsh);
1619 return i;
1622 static void fail_test(const char *dir,
1623 unsigned int iters, unsigned int seed,
1624 bool fast __attribute__((unused)), bool verbose)
1626 struct fail_data data;
1627 unsigned int try;
1629 data.seed = seed;
1630 data.print_progress = !verbose;
1631 data.dir = dir;
1633 try = try_fail(NULL, iters, verbose, &data);
1634 if (try == iters) {
1635 cleanup_xs_ops();
1636 exit(0);
1638 printf("Failed on iteration %u of seed %u\n", try + 1, seed);
1639 fflush(stdout);
1640 data.print_progress = false;
1641 reduce_problem(try + 1, try_fail, &data);
1644 int main(int argc, char *argv[])
1646 bool verbose = false;
1647 bool simple = false;
1648 bool fast = false;
1649 bool fail = false;
1651 if (argv[1] && streq(argv[1], "--fail")) {
1652 fail = true;
1653 argv++;
1654 argc--;
1657 if (argv[1] && streq(argv[1], "--simple")) {
1658 simple = true;
1659 argv++;
1660 argc--;
1663 if (argv[1] && streq(argv[1], "--fast")) {
1664 fast = true;
1665 argv++;
1666 argc--;
1669 if (argv[1] && streq(argv[1], "--verbose")) {
1670 verbose = true;
1671 argv++;
1672 argc--;
1675 if (argc != 4)
1676 barf("Usage: xs_random [--fail|--simple] [--fast] [--verbose] <directory> <iterations> <seed>");
1678 talloc_enable_null_tracking();
1680 if (fail)
1681 fail_test(argv[1], atoi(argv[2]), atoi(argv[3]), fast, verbose);
1682 else if (simple)
1683 simple_test(argv[1], atoi(argv[2]), atoi(argv[3]), fast, verbose);
1684 else
1685 diff_test(argv[1], atoi(argv[2]), atoi(argv[3]), fast, verbose);
1686 exit(2);