xref: /dragonfly/usr.bin/undo/undo.c (revision dcb5d66b)
195d7e54dSMatthew Dillon /*
295d7e54dSMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
395d7e54dSMatthew Dillon  *
495d7e54dSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
595d7e54dSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
695d7e54dSMatthew Dillon  *
795d7e54dSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
895d7e54dSMatthew Dillon  * modification, are permitted provided that the following conditions
995d7e54dSMatthew Dillon  * are met:
1095d7e54dSMatthew Dillon  *
1195d7e54dSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1295d7e54dSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1395d7e54dSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1495d7e54dSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
1595d7e54dSMatthew Dillon  *    the documentation and/or other materials provided with the
1695d7e54dSMatthew Dillon  *    distribution.
1795d7e54dSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
1895d7e54dSMatthew Dillon  *    contributors may be used to endorse or promote products derived
1995d7e54dSMatthew Dillon  *    from this software without specific, prior written permission.
2095d7e54dSMatthew Dillon  *
2195d7e54dSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2295d7e54dSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2395d7e54dSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2495d7e54dSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2595d7e54dSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2695d7e54dSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2795d7e54dSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2895d7e54dSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2995d7e54dSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3095d7e54dSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3195d7e54dSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3295d7e54dSMatthew Dillon  * SUCH DAMAGE.
3395d7e54dSMatthew Dillon  *
34f95efecaSThomas Nikolajsen  * $DragonFly: src/usr.bin/undo/undo.c,v 1.6 2008/07/17 21:34:47 thomas Exp $
3595d7e54dSMatthew Dillon  */
3695d7e54dSMatthew Dillon /*
3795d7e54dSMatthew Dillon  * UNDO - retrieve an older version of a file.
3895d7e54dSMatthew Dillon  */
3995d7e54dSMatthew Dillon 
4095d7e54dSMatthew Dillon #include <sys/types.h>
4195d7e54dSMatthew Dillon #include <sys/stat.h>
4295d7e54dSMatthew Dillon #include <sys/wait.h>
43bf31f16dSMatthew Dillon #include <sys/tree.h>
44*dcb5d66bSTomohiro Kusumi #include <sys/ioctl.h>
4595d7e54dSMatthew Dillon #include <stdio.h>
4695d7e54dSMatthew Dillon #include <stdlib.h>
4795d7e54dSMatthew Dillon #include <stdarg.h>
4895d7e54dSMatthew Dillon #include <string.h>
4995d7e54dSMatthew Dillon #include <unistd.h>
5095d7e54dSMatthew Dillon #include <fcntl.h>
5195d7e54dSMatthew Dillon #include <errno.h>
5202318f07STomohiro Kusumi #include <err.h>
5395d7e54dSMatthew Dillon #include <vfs/hammer/hammer_disk.h>
5495d7e54dSMatthew Dillon #include <vfs/hammer/hammer_ioctl.h>
5595d7e54dSMatthew Dillon 
56bf31f16dSMatthew Dillon /*
57bf31f16dSMatthew Dillon  * Sorted list of transaction ids
58bf31f16dSMatthew Dillon  */
59bf31f16dSMatthew Dillon struct undo_hist_entry;
60bf31f16dSMatthew Dillon RB_HEAD(undo_hist_entry_rb_tree, undo_hist_entry);
61bf31f16dSMatthew Dillon RB_PROTOTYPE2(undo_hist_entry_rb_tree, undo_hist_entry, rbnode,
62bf31f16dSMatthew Dillon 	undo_hist_entry_compare, hammer_tid_t);
63bf31f16dSMatthew Dillon 
64bf31f16dSMatthew Dillon struct undo_hist_entry {
65bf31f16dSMatthew Dillon 	RB_ENTRY(undo_hist_entry) rbnode;
66bf31f16dSMatthew Dillon 	struct hammer_ioc_hist_entry tse;
67b9a33d3fSMatthew Dillon 	ino_t inum;
68bf31f16dSMatthew Dillon };
69bf31f16dSMatthew Dillon 
7095d7e54dSMatthew Dillon enum undo_type { TYPE_FILE, TYPE_DIFF, TYPE_RDIFF, TYPE_HISTORY };
71b9a33d3fSMatthew Dillon enum undo_cmd { CMD_DUMP, CMD_ITERATEALL };
72b9a33d3fSMatthew Dillon 
73b9a33d3fSMatthew Dillon #define UNDO_FLAG_MULT		0x0001
74b9a33d3fSMatthew Dillon #define UNDO_FLAG_INOCHG	0x0002
7591b6127fSTomohiro Kusumi #define UNDO_FLAG_TID_INDEX1	0x0004
7691b6127fSTomohiro Kusumi #define UNDO_FLAG_TID_INDEX2	0x0008
7795d7e54dSMatthew Dillon 
78bf31f16dSMatthew Dillon static int undo_hist_entry_compare(struct undo_hist_entry *he1,
79bf31f16dSMatthew Dillon 		    struct undo_hist_entry *he2);
80740ed10dSTomohiro Kusumi static void doiterate(const char *filename, int flags,
81b9a33d3fSMatthew Dillon 		   struct hammer_ioc_hist_entry ts1,
82965778c8SMatthew Dillon 		   struct hammer_ioc_hist_entry ts2,
83b9a33d3fSMatthew Dillon 		   enum undo_cmd cmd, enum undo_type type);
84740ed10dSTomohiro Kusumi static int doiterate_dump(const char *filename, int flags,
85791d7504STomohiro Kusumi 		   struct undo_hist_entry_rb_tree *ptse_tree,
86791d7504STomohiro Kusumi 		   struct hammer_ioc_hist_entry ts1,
87791d7504STomohiro Kusumi 		   struct hammer_ioc_hist_entry ts2,
88791d7504STomohiro Kusumi 		   enum undo_type type);
89740ed10dSTomohiro Kusumi static int doiterate_iterall(const char *filename, int flags,
90791d7504STomohiro Kusumi 		   struct undo_hist_entry_rb_tree *ptse_tree,
91791d7504STomohiro Kusumi 		   enum undo_type type);
92740ed10dSTomohiro Kusumi static void dogenerate(const char *filename, int flags,
931d9f6aa1SMatthew Dillon 		   struct hammer_ioc_hist_entry ts1,
945728bf33STomohiro Kusumi 		   struct hammer_ioc_hist_entry ts2,
955728bf33STomohiro Kusumi 		   int idx, enum undo_type type);
96603468eaSTomohiro Kusumi static void __collect_history(int fd, int *error,
97bf31f16dSMatthew Dillon 		   struct undo_hist_entry_rb_tree *tse_tree);
98603468eaSTomohiro Kusumi static void collect_history(const char *filename, int *errorp,
99603468eaSTomohiro Kusumi 		   struct undo_hist_entry_rb_tree *dir_tree);
100b9a33d3fSMatthew Dillon static void collect_dir_history(const char *filename, int *error,
101b9a33d3fSMatthew Dillon 		   struct undo_hist_entry_rb_tree *dir_tree);
102b9a33d3fSMatthew Dillon static void clean_tree(struct undo_hist_entry_rb_tree *tree);
103965778c8SMatthew Dillon static hammer_tid_t parse_delta_time(const char *timeStr, int *flags,
104965778c8SMatthew Dillon 		   int ind_flag);
10528a07adbSTomohiro Kusumi static FILE *_fopen(const char *filename, const char *mode);
10695d7e54dSMatthew Dillon static void runcmd(int fd, const char *cmd, ...);
1074362c066STomohiro Kusumi static char *timestamp(struct hammer_ioc_hist_entry *hen);
10895d7e54dSMatthew Dillon static void usage(void);
10995d7e54dSMatthew Dillon 
11095d7e54dSMatthew Dillon static int VerboseOpt;
111740ed10dSTomohiro Kusumi static const char *OutFileName = NULL;
112740ed10dSTomohiro Kusumi static const char *OutFilePostfix = NULL;
11395d7e54dSMatthew Dillon 
114bf31f16dSMatthew Dillon RB_GENERATE2(undo_hist_entry_rb_tree, undo_hist_entry, rbnode,
115bf31f16dSMatthew Dillon 	undo_hist_entry_compare, hammer_tid_t, tse.tid);
116bf31f16dSMatthew Dillon 
117bf31f16dSMatthew Dillon 
11895d7e54dSMatthew Dillon int
main(int ac,char ** av)11995d7e54dSMatthew Dillon main(int ac, char **av)
12095d7e54dSMatthew Dillon {
121b9a33d3fSMatthew Dillon 	enum undo_cmd cmd;
12295d7e54dSMatthew Dillon 	enum undo_type type;
1231d9f6aa1SMatthew Dillon 	struct hammer_ioc_hist_entry ts1;
1241d9f6aa1SMatthew Dillon 	struct hammer_ioc_hist_entry ts2;
12595d7e54dSMatthew Dillon 	int c;
126965778c8SMatthew Dillon 	int count_t;
127b9a33d3fSMatthew Dillon 	int flags;
12895d7e54dSMatthew Dillon 
1291d9f6aa1SMatthew Dillon 	bzero(&ts1, sizeof(ts1));
1301d9f6aa1SMatthew Dillon 	bzero(&ts2, sizeof(ts2));
1311d9f6aa1SMatthew Dillon 
13295d7e54dSMatthew Dillon 	cmd = CMD_DUMP;
13395d7e54dSMatthew Dillon 	type = TYPE_FILE;
134965778c8SMatthew Dillon 	count_t = 0;
135965778c8SMatthew Dillon 	flags = 0;
13695d7e54dSMatthew Dillon 
13759f01588SMatthew Dillon 	while ((c = getopt(ac, av, "adDiuvo:t:")) != -1) {
13895d7e54dSMatthew Dillon 		switch(c) {
13995d7e54dSMatthew Dillon 		case 'd':
14095d7e54dSMatthew Dillon 			type = TYPE_DIFF;
14195d7e54dSMatthew Dillon 			break;
14295d7e54dSMatthew Dillon 		case 'D':
14395d7e54dSMatthew Dillon 			type = TYPE_RDIFF;
14495d7e54dSMatthew Dillon 			break;
14595d7e54dSMatthew Dillon 		case 'i':
14695d7e54dSMatthew Dillon 			if (type != TYPE_FILE)
14795d7e54dSMatthew Dillon 				usage();
14895d7e54dSMatthew Dillon 			type = TYPE_HISTORY;
149b9a33d3fSMatthew Dillon 			cmd = CMD_ITERATEALL;
15095d7e54dSMatthew Dillon 			break;
15159f01588SMatthew Dillon 		case 'a':
15295d7e54dSMatthew Dillon 			cmd = CMD_ITERATEALL;
15395d7e54dSMatthew Dillon 			break;
15495d7e54dSMatthew Dillon 		case 'u':
155740ed10dSTomohiro Kusumi 			OutFilePostfix = ".undo";
15695d7e54dSMatthew Dillon 			break;
15795d7e54dSMatthew Dillon 		case 'v':
15895d7e54dSMatthew Dillon 			++VerboseOpt;
15995d7e54dSMatthew Dillon 			break;
16095d7e54dSMatthew Dillon 		case 'o':
161740ed10dSTomohiro Kusumi 			OutFileName = optarg;
16295d7e54dSMatthew Dillon 			break;
16395d7e54dSMatthew Dillon 		case 't':
164965778c8SMatthew Dillon 			/*
165965778c8SMatthew Dillon 			 * Parse one or two -t options.  If two are specified
166965778c8SMatthew Dillon 			 * -d is implied (but may be overridden)
167965778c8SMatthew Dillon 			 */
168965778c8SMatthew Dillon 			++count_t;
169965778c8SMatthew Dillon 			if (count_t == 1) {
170965778c8SMatthew Dillon 				ts1.tid = parse_delta_time(optarg, &flags,
17191b6127fSTomohiro Kusumi 							UNDO_FLAG_TID_INDEX1);
172965778c8SMatthew Dillon 			} else if (count_t == 2) {
173965778c8SMatthew Dillon 				ts2.tid = parse_delta_time(optarg, &flags,
17491b6127fSTomohiro Kusumi 							UNDO_FLAG_TID_INDEX2);
175965778c8SMatthew Dillon 				if (type == TYPE_FILE)
176965778c8SMatthew Dillon 					type = TYPE_DIFF;
177965778c8SMatthew Dillon 			} else {
17895d7e54dSMatthew Dillon 				usage();
179965778c8SMatthew Dillon 			}
18095d7e54dSMatthew Dillon 			break;
18195d7e54dSMatthew Dillon 		default:
18295d7e54dSMatthew Dillon 			usage();
18395d7e54dSMatthew Dillon 			/* NOT REACHED */
18495d7e54dSMatthew Dillon 			break;
18595d7e54dSMatthew Dillon 		}
18695d7e54dSMatthew Dillon 	}
18795d7e54dSMatthew Dillon 
18895d7e54dSMatthew Dillon 	/*
18995d7e54dSMatthew Dillon 	 * Option validation
19095d7e54dSMatthew Dillon 	 */
191740ed10dSTomohiro Kusumi 	if (OutFileName && OutFilePostfix) {
19295d7e54dSMatthew Dillon 		fprintf(stderr, "The -o option may not be combined with -u\n");
19395d7e54dSMatthew Dillon 		usage();
19495d7e54dSMatthew Dillon 	}
19595d7e54dSMatthew Dillon 
19695d7e54dSMatthew Dillon 	ac -= optind;
19795d7e54dSMatthew Dillon 	av += optind;
198b9a33d3fSMatthew Dillon 	if (ac > 1)
199b9a33d3fSMatthew Dillon 		flags |= UNDO_FLAG_MULT;
20095d7e54dSMatthew Dillon 
20195d7e54dSMatthew Dillon 	if (ac == 0)
20295d7e54dSMatthew Dillon 		usage();
20395d7e54dSMatthew Dillon 
20495d7e54dSMatthew Dillon 	/*
20595d7e54dSMatthew Dillon 	 * Validate the output template, if specified.
20695d7e54dSMatthew Dillon 	 */
207740ed10dSTomohiro Kusumi 	if (OutFileName && (flags & UNDO_FLAG_MULT)) {
208740ed10dSTomohiro Kusumi 		const char *ptr = OutFileName;
20995d7e54dSMatthew Dillon 		int didStr = 0;
21095d7e54dSMatthew Dillon 
21195d7e54dSMatthew Dillon 		while ((ptr = strchr(ptr, '%')) != NULL) {
21295d7e54dSMatthew Dillon 			if (ptr[1] == 's') {
21395d7e54dSMatthew Dillon 				if (didStr) {
21495d7e54dSMatthew Dillon 					fprintf(stderr, "Malformed output "
21595d7e54dSMatthew Dillon 							"template\n");
21695d7e54dSMatthew Dillon 					usage();
21795d7e54dSMatthew Dillon 				}
21895d7e54dSMatthew Dillon 				didStr = 1;
21995d7e54dSMatthew Dillon 				++ptr;
22095d7e54dSMatthew Dillon 			} else if (ptr[1] != '%') {
22195d7e54dSMatthew Dillon 				fprintf(stderr, "Malformed output template\n");
22295d7e54dSMatthew Dillon 				usage();
22395d7e54dSMatthew Dillon 			} else {
22495d7e54dSMatthew Dillon 				ptr += 2;
22595d7e54dSMatthew Dillon 			}
22695d7e54dSMatthew Dillon 		}
22795d7e54dSMatthew Dillon 	}
22895d7e54dSMatthew Dillon 
22995d7e54dSMatthew Dillon 	while (ac) {
230740ed10dSTomohiro Kusumi 		doiterate(*av, flags, ts1, ts2, cmd, type);
23195d7e54dSMatthew Dillon 		++av;
23295d7e54dSMatthew Dillon 		--ac;
23395d7e54dSMatthew Dillon 	}
23495d7e54dSMatthew Dillon 	return(0);
23595d7e54dSMatthew Dillon }
23695d7e54dSMatthew Dillon 
23795d7e54dSMatthew Dillon /*
238b9a33d3fSMatthew Dillon  * Iterate through a file's history.  If cmd == CMD_DUMP we take the
239965778c8SMatthew Dillon  * next-to-last transaction id, unless another given.  Otherwise if
240965778c8SMatthew Dillon  * cmd == CMD_ITERATEALL we scan all transaction ids.
241b9a33d3fSMatthew Dillon  *
242b9a33d3fSMatthew Dillon  * Also iterate through the directory's history to locate other inodes that
243b9a33d3fSMatthew Dillon  * used the particular file name.
24495d7e54dSMatthew Dillon  */
24595d7e54dSMatthew Dillon static
24695d7e54dSMatthew Dillon void
doiterate(const char * filename,int flags,struct hammer_ioc_hist_entry ts1,struct hammer_ioc_hist_entry ts2,enum undo_cmd cmd,enum undo_type type)247740ed10dSTomohiro Kusumi doiterate(const char *filename, int flags,
248b9a33d3fSMatthew Dillon 	  struct hammer_ioc_hist_entry ts1,
249965778c8SMatthew Dillon 	  struct hammer_ioc_hist_entry ts2,
250b9a33d3fSMatthew Dillon 	  enum undo_cmd cmd, enum undo_type type)
25195d7e54dSMatthew Dillon {
252b9a33d3fSMatthew Dillon 	struct undo_hist_entry_rb_tree dir_tree;
253bf31f16dSMatthew Dillon 	struct undo_hist_entry_rb_tree tse_tree;
254791d7504STomohiro Kusumi 	struct undo_hist_entry *tse;
25528c26f7eSAlexander Polakov 	struct stat sb;
25695d7e54dSMatthew Dillon 	char *path = NULL;
257b9a33d3fSMatthew Dillon 	int error;
25895d7e54dSMatthew Dillon 
259b9a33d3fSMatthew Dillon 	RB_INIT(&dir_tree);
260bf31f16dSMatthew Dillon 	RB_INIT(&tse_tree);
261bf31f16dSMatthew Dillon 
262b9a33d3fSMatthew Dillon 	/*
263b9a33d3fSMatthew Dillon 	 * Use the directory history to locate all possible versions of
264b9a33d3fSMatthew Dillon 	 * the file.
265b9a33d3fSMatthew Dillon 	 */
266b9a33d3fSMatthew Dillon 	collect_dir_history(filename, &error, &dir_tree);
267791d7504STomohiro Kusumi 	RB_FOREACH(tse, undo_hist_entry_rb_tree, &dir_tree) {
268791d7504STomohiro Kusumi 		asprintf(&path, "%s@@0x%016jx", filename, (uintmax_t)tse->tse.tid);
269edb9a6c8STomohiro Kusumi 		if (stat(path, &sb) == 0 && (sb.st_mode & S_IFIFO)) {
270644be772STomohiro Kusumi 			fprintf(stderr, "Warning: fake transaction id %s@@0x%016jx\n",
271644be772STomohiro Kusumi 				filename,
272791d7504STomohiro Kusumi 				(uintmax_t)tse->tse.tid);
273fbfdee92STomohiro Kusumi 			free(path);
27428c26f7eSAlexander Polakov 			continue;
27528c26f7eSAlexander Polakov 		}
276603468eaSTomohiro Kusumi 		collect_history(path, &error, &tse_tree);
27712767dfbSSimon Schubert 		free(path);
278b9a33d3fSMatthew Dillon 	}
279603468eaSTomohiro Kusumi 	collect_history(filename, &error, &tse_tree);
280791d7504STomohiro Kusumi 
281791d7504STomohiro Kusumi 	switch (cmd) {
282791d7504STomohiro Kusumi 	case CMD_DUMP:
283740ed10dSTomohiro Kusumi 		if (doiterate_dump(filename, flags, &tse_tree, ts1, ts2, type) == -1)
284791d7504STomohiro Kusumi 			printf("%s: No UNDO history found\n", filename);
285791d7504STomohiro Kusumi 		break;
286791d7504STomohiro Kusumi 	case CMD_ITERATEALL:
287740ed10dSTomohiro Kusumi 		if (doiterate_iterall(filename, flags, &tse_tree, type) == -1)
288827bb210STomohiro Kusumi 			printf("%s: No UNDO history found\n", filename);
289791d7504STomohiro Kusumi 		break;
290791d7504STomohiro Kusumi 	default:
291791d7504STomohiro Kusumi 		fprintf(stderr, "Invalid command %d\n", cmd);
292791d7504STomohiro Kusumi 		break;
293791d7504STomohiro Kusumi 	}
294791d7504STomohiro Kusumi 
295791d7504STomohiro Kusumi 	clean_tree(&dir_tree);
296791d7504STomohiro Kusumi 	clean_tree(&tse_tree);
297791d7504STomohiro Kusumi }
298791d7504STomohiro Kusumi 
299791d7504STomohiro Kusumi static
300791d7504STomohiro Kusumi int
doiterate_dump(const char * filename,int flags,struct undo_hist_entry_rb_tree * ptse_tree,struct hammer_ioc_hist_entry ts1,struct hammer_ioc_hist_entry ts2,enum undo_type type)301740ed10dSTomohiro Kusumi doiterate_dump(const char *filename, int flags,
302791d7504STomohiro Kusumi 		struct undo_hist_entry_rb_tree *ptse_tree,
303791d7504STomohiro Kusumi 		struct hammer_ioc_hist_entry ts1,
304791d7504STomohiro Kusumi 		struct hammer_ioc_hist_entry ts2,
305791d7504STomohiro Kusumi 		enum undo_type type)
306791d7504STomohiro Kusumi {
307791d7504STomohiro Kusumi 	struct undo_hist_entry *tse1;
308791d7504STomohiro Kusumi 	struct undo_hist_entry *tse2;
309791d7504STomohiro Kusumi 
310965778c8SMatthew Dillon 	/*
311965778c8SMatthew Dillon 	 * Find entry if tid set to placeholder index
312965778c8SMatthew Dillon 	 */
31391b6127fSTomohiro Kusumi 	if (flags & UNDO_FLAG_TID_INDEX1) {
314791d7504STomohiro Kusumi 		tse1 = RB_MAX(undo_hist_entry_rb_tree, ptse_tree);
315965778c8SMatthew Dillon 		while (tse1 && ts1.tid--) {
316965778c8SMatthew Dillon 			tse1 = RB_PREV(undo_hist_entry_rb_tree,
317791d7504STomohiro Kusumi 				       ptse_tree, tse1);
318965778c8SMatthew Dillon 		}
319965778c8SMatthew Dillon 		if (tse1)
320965778c8SMatthew Dillon 			ts1 = tse1->tse;
321965778c8SMatthew Dillon 		else
322965778c8SMatthew Dillon 			ts1.tid = 0;
323965778c8SMatthew Dillon 	}
32491b6127fSTomohiro Kusumi 	if (flags & UNDO_FLAG_TID_INDEX2) {
325791d7504STomohiro Kusumi 		tse2 = RB_MAX(undo_hist_entry_rb_tree, ptse_tree);
326965778c8SMatthew Dillon 		while (tse2 && ts2.tid--) {
327965778c8SMatthew Dillon 			tse2 = RB_PREV(undo_hist_entry_rb_tree,
328791d7504STomohiro Kusumi 				       ptse_tree, tse2);
329965778c8SMatthew Dillon 		}
330965778c8SMatthew Dillon 		if (tse2)
331965778c8SMatthew Dillon 			ts2 = tse2->tse;
332965778c8SMatthew Dillon 		else
333965778c8SMatthew Dillon 			ts2.tid = 0;
334965778c8SMatthew Dillon 	}
335965778c8SMatthew Dillon 
336965778c8SMatthew Dillon 	/*
337965778c8SMatthew Dillon 	 * Single entry, most recent prior to current
338965778c8SMatthew Dillon 	 */
339b9a33d3fSMatthew Dillon 	if (ts1.tid == 0) {
340791d7504STomohiro Kusumi 		tse2 = RB_MAX(undo_hist_entry_rb_tree, ptse_tree);
34189b353f3SSimon Schubert 		if (tse2) {
34289b353f3SSimon Schubert 			ts2 = tse2->tse;
343b9a33d3fSMatthew Dillon 			tse1 = RB_PREV(undo_hist_entry_rb_tree,
344791d7504STomohiro Kusumi 				       ptse_tree, tse2);
345b9a33d3fSMatthew Dillon 			if (tse1)
346b9a33d3fSMatthew Dillon 				ts1 = tse1->tse;
347b9a33d3fSMatthew Dillon 		}
348b9a33d3fSMatthew Dillon 	}
349791d7504STomohiro Kusumi 
350791d7504STomohiro Kusumi 	if (ts1.tid) {
351740ed10dSTomohiro Kusumi 		dogenerate(filename, 0, ts1, ts2, 0, type);
352791d7504STomohiro Kusumi 		return(0);
353b9a33d3fSMatthew Dillon 	}
354791d7504STomohiro Kusumi 	return(-1);
355791d7504STomohiro Kusumi }
356791d7504STomohiro Kusumi 
357791d7504STomohiro Kusumi static
358791d7504STomohiro Kusumi int
doiterate_iterall(const char * filename,int flags,struct undo_hist_entry_rb_tree * ptse_tree,enum undo_type type)359740ed10dSTomohiro Kusumi doiterate_iterall(const char *filename, int flags,
360791d7504STomohiro Kusumi 		struct undo_hist_entry_rb_tree *ptse_tree,
361791d7504STomohiro Kusumi 		enum undo_type type)
362791d7504STomohiro Kusumi {
363791d7504STomohiro Kusumi 	struct undo_hist_entry *tse1;
364791d7504STomohiro Kusumi 	struct undo_hist_entry *tse2;
365791d7504STomohiro Kusumi 	struct hammer_ioc_hist_entry tid_max;
366791d7504STomohiro Kusumi 	int i;
367791d7504STomohiro Kusumi 
368791d7504STomohiro Kusumi 	if (RB_ROOT(ptse_tree) == NULL)
369791d7504STomohiro Kusumi 		return(-1);
370791d7504STomohiro Kusumi 
371b9a33d3fSMatthew Dillon 	/*
372b9a33d3fSMatthew Dillon 	 * Iterate entire history
373b9a33d3fSMatthew Dillon 	 */
374b9a33d3fSMatthew Dillon 	printf("%s: ITERATE ENTIRE HISTORY\n", filename);
37595d7e54dSMatthew Dillon 
376bf31f16dSMatthew Dillon 	tse1 = NULL;
377004f5c6fSMatthew Dillon 	i = 0;
378791d7504STomohiro Kusumi 	RB_FOREACH(tse2, undo_hist_entry_rb_tree, ptse_tree) {
379bf31f16dSMatthew Dillon 		if (tse1) {
380740ed10dSTomohiro Kusumi 			dogenerate(filename, flags, tse1->tse, tse2->tse, i, type);
381004f5c6fSMatthew Dillon 		}
382b9a33d3fSMatthew Dillon 		if (tse1 && tse2->inum != tse1->inum)
383b9a33d3fSMatthew Dillon 			flags |= UNDO_FLAG_INOCHG;
384b9a33d3fSMatthew Dillon 		else
385b9a33d3fSMatthew Dillon 			flags &= ~UNDO_FLAG_INOCHG;
386bf31f16dSMatthew Dillon 		tse1 = tse2;
387bf31f16dSMatthew Dillon 		++i;
388bf31f16dSMatthew Dillon 	}
389791d7504STomohiro Kusumi 
3908debe471SSimon Schubert 	/*
3918debe471SSimon Schubert 	 * There is no delta to print for the last pair,
3928debe471SSimon Schubert 	 * because they are identical.
3938debe471SSimon Schubert 	 */
3948debe471SSimon Schubert 	if (type != TYPE_DIFF && type != TYPE_RDIFF) {
395791d7504STomohiro Kusumi 		tid_max.tid = HAMMER_MAX_TID;
396791d7504STomohiro Kusumi 		tid_max.time32 = 0;
397740ed10dSTomohiro Kusumi 		dogenerate(filename, flags, tse1->tse, tid_max, i, type);
3988debe471SSimon Schubert 	}
399791d7504STomohiro Kusumi 	return(0);
40095d7e54dSMatthew Dillon }
40195d7e54dSMatthew Dillon 
40295d7e54dSMatthew Dillon /*
40395d7e54dSMatthew Dillon  * Generate output for a file as-of ts1 (ts1 may be 0!), if diffing then
40495d7e54dSMatthew Dillon  * through ts2.
40595d7e54dSMatthew Dillon  */
40695d7e54dSMatthew Dillon static
40795d7e54dSMatthew Dillon void
dogenerate(const char * filename,int flags,struct hammer_ioc_hist_entry ts1,struct hammer_ioc_hist_entry ts2,int idx,enum undo_type type)408740ed10dSTomohiro Kusumi dogenerate(const char *filename, int flags,
4091d9f6aa1SMatthew Dillon 	   struct hammer_ioc_hist_entry ts1,
4105728bf33STomohiro Kusumi 	   struct hammer_ioc_hist_entry ts2,
4115728bf33STomohiro Kusumi 	   int idx, enum undo_type type)
41295d7e54dSMatthew Dillon {
41395d7e54dSMatthew Dillon 	struct stat st;
41495d7e54dSMatthew Dillon 	const char *elm;
41595d7e54dSMatthew Dillon 	char *ipath1 = NULL;
41695d7e54dSMatthew Dillon 	char *ipath2 = NULL;
41795d7e54dSMatthew Dillon 	FILE *fi;
41895d7e54dSMatthew Dillon 	FILE *fp;
41995d7e54dSMatthew Dillon 	char *buf;
42095d7e54dSMatthew Dillon 	char *path;
421b9a33d3fSMatthew Dillon 	time_t t;
422b9a33d3fSMatthew Dillon 	struct tm *tp;
423b9a33d3fSMatthew Dillon 	char datestr[64];
42495d7e54dSMatthew Dillon 	int n;
42595d7e54dSMatthew Dillon 
42695d7e54dSMatthew Dillon 	/*
42795d7e54dSMatthew Dillon 	 * Open the input file.  If ts1 is 0 try to locate the most recent
42895d7e54dSMatthew Dillon 	 * version of the file prior to the current version.
42995d7e54dSMatthew Dillon 	 */
4301d9f6aa1SMatthew Dillon 	if (ts1.tid == 0)
43159f01588SMatthew Dillon 		asprintf(&ipath1, "%s", filename);
432bf31f16dSMatthew Dillon 	else
4337a7776eeSSascha Wildner 		asprintf(&ipath1, "%s@@0x%016jx", filename, (uintmax_t)ts1.tid);
43495d7e54dSMatthew Dillon 
435bf31f16dSMatthew Dillon 	if (ts2.tid == 0)
43695d7e54dSMatthew Dillon 		asprintf(&ipath2, "%s", filename);
437bf31f16dSMatthew Dillon 	else
4387a7776eeSSascha Wildner 		asprintf(&ipath2, "%s@@0x%016jx", filename, (uintmax_t)ts2.tid);
439bf31f16dSMatthew Dillon 
440bf31f16dSMatthew Dillon 	if (lstat(ipath1, &st) < 0 && lstat(ipath2, &st) < 0) {
441b9a33d3fSMatthew Dillon 		if (idx == 0 || VerboseOpt) {
442bf31f16dSMatthew Dillon 			fprintf(stderr, "Unable to access either %s or %s\n",
443bf31f16dSMatthew Dillon 				ipath1, ipath2);
44495d7e54dSMatthew Dillon 		}
445bf31f16dSMatthew Dillon 		free(ipath1);
446bf31f16dSMatthew Dillon 		free(ipath2);
447fbfdee92STomohiro Kusumi 		return;
44895d7e54dSMatthew Dillon 	}
44995d7e54dSMatthew Dillon 
45095d7e54dSMatthew Dillon 	/*
45195d7e54dSMatthew Dillon 	 * elm is the last component of the input file name
45295d7e54dSMatthew Dillon 	 */
45395d7e54dSMatthew Dillon 	if ((elm = strrchr(filename, '/')) != NULL)
45495d7e54dSMatthew Dillon 		++elm;
45595d7e54dSMatthew Dillon 	else
45695d7e54dSMatthew Dillon 		elm = filename;
45795d7e54dSMatthew Dillon 
45895d7e54dSMatthew Dillon 	/*
45995d7e54dSMatthew Dillon 	 * Where do we stuff our output?
46095d7e54dSMatthew Dillon 	 */
461740ed10dSTomohiro Kusumi 	if (OutFileName) {
462b9a33d3fSMatthew Dillon 		if (flags & UNDO_FLAG_MULT) {
463740ed10dSTomohiro Kusumi 			asprintf(&path, OutFileName, elm);
46428a07adbSTomohiro Kusumi 			fp = _fopen(path, "w");
46595d7e54dSMatthew Dillon 			free(path);
46695d7e54dSMatthew Dillon 		} else {
46728a07adbSTomohiro Kusumi 			fp = _fopen(OutFileName, "w");
46895d7e54dSMatthew Dillon 		}
469740ed10dSTomohiro Kusumi 	} else if (OutFilePostfix) {
47095d7e54dSMatthew Dillon 		if (idx >= 0) {
47195d7e54dSMatthew Dillon 			asprintf(&path, "%s%s.%04d", filename,
472740ed10dSTomohiro Kusumi 				 OutFilePostfix, idx);
47395d7e54dSMatthew Dillon 		} else {
474740ed10dSTomohiro Kusumi 			asprintf(&path, "%s%s", filename, OutFilePostfix);
47595d7e54dSMatthew Dillon 		}
47628a07adbSTomohiro Kusumi 		fp = _fopen(path, "w");
47795d7e54dSMatthew Dillon 		free(path);
47895d7e54dSMatthew Dillon 	} else {
479b9a33d3fSMatthew Dillon 		if ((flags & UNDO_FLAG_MULT) && type == TYPE_FILE) {
48095d7e54dSMatthew Dillon 			if (idx >= 0) {
4817a7776eeSSascha Wildner 				printf("\n>>> %s %04d 0x%016jx %s\n\n",
4827a7776eeSSascha Wildner 				       filename, idx, (uintmax_t)ts1.tid,
4837a7776eeSSascha Wildner 				       timestamp(&ts1));
48495d7e54dSMatthew Dillon 			} else {
4857a7776eeSSascha Wildner 				printf("\n>>> %s ---- 0x%016jx %s\n\n",
4867a7776eeSSascha Wildner 				       filename, (uintmax_t)ts1.tid,
4877a7776eeSSascha Wildner 				       timestamp(&ts1));
48895d7e54dSMatthew Dillon 			}
48995d7e54dSMatthew Dillon 		} else if (idx >= 0 && type == TYPE_FILE) {
4907a7776eeSSascha Wildner 			printf("\n>>> %s %04d 0x%016jx %s\n\n",
4917a7776eeSSascha Wildner 			       filename, idx, (uintmax_t)ts1.tid,
4927a7776eeSSascha Wildner 			       timestamp(&ts1));
49395d7e54dSMatthew Dillon 		}
49495d7e54dSMatthew Dillon 		fp = stdout;
49595d7e54dSMatthew Dillon 	}
49695d7e54dSMatthew Dillon 
49795d7e54dSMatthew Dillon 	switch(type) {
49895d7e54dSMatthew Dillon 	case TYPE_FILE:
499fbfdee92STomohiro Kusumi 		buf = malloc(8192);
50002318f07STomohiro Kusumi 		if (buf == NULL)
50102318f07STomohiro Kusumi 			err(1, "malloc");
50295d7e54dSMatthew Dillon 		if ((fi = fopen(ipath1, "r")) != NULL) {
50395d7e54dSMatthew Dillon 			while ((n = fread(buf, 1, 8192, fi)) > 0)
50495d7e54dSMatthew Dillon 				fwrite(buf, 1, n, fp);
50595d7e54dSMatthew Dillon 			fclose(fi);
50695d7e54dSMatthew Dillon 		}
507fbfdee92STomohiro Kusumi 		free(buf);
50895d7e54dSMatthew Dillon 		break;
50995d7e54dSMatthew Dillon 	case TYPE_DIFF:
510bf31f16dSMatthew Dillon 		printf("diff -N -r -u %s %s (to %s)\n",
5111d9f6aa1SMatthew Dillon 		       ipath1, ipath2, timestamp(&ts2));
51295d7e54dSMatthew Dillon 		fflush(stdout);
513edb9a6c8STomohiro Kusumi 		runcmd(fileno(fp), "/usr/bin/diff", "diff", "-N", "-r", "-u",
514edb9a6c8STomohiro Kusumi 			ipath1, ipath2, NULL);
51595d7e54dSMatthew Dillon 		break;
51695d7e54dSMatthew Dillon 	case TYPE_RDIFF:
517bf31f16dSMatthew Dillon 		printf("diff -N -r -u %s %s\n", ipath2, ipath1);
51895d7e54dSMatthew Dillon 		fflush(stdout);
519edb9a6c8STomohiro Kusumi 		runcmd(fileno(fp), "/usr/bin/diff", "diff", "-N", "-r", "-u",
520edb9a6c8STomohiro Kusumi 			ipath2, ipath1, NULL);
52195d7e54dSMatthew Dillon 		break;
52295d7e54dSMatthew Dillon 	case TYPE_HISTORY:
523b9a33d3fSMatthew Dillon 		t = (time_t)ts1.time32;
524b9a33d3fSMatthew Dillon 		tp = localtime(&t);
525b9a33d3fSMatthew Dillon 		strftime(datestr, sizeof(datestr), "%d-%b-%Y %H:%M:%S", tp);
5267a7776eeSSascha Wildner 		printf("\t0x%016jx %s", (uintmax_t)ts1.tid, datestr);
527b9a33d3fSMatthew Dillon 		if (flags & UNDO_FLAG_INOCHG)
528b9a33d3fSMatthew Dillon 			printf(" inode-change");
529b9a33d3fSMatthew Dillon 		if (lstat(ipath1, &st) < 0)
530b9a33d3fSMatthew Dillon 			printf(" file-deleted");
531b9a33d3fSMatthew Dillon 		printf("\n");
53295d7e54dSMatthew Dillon 		break;
53395d7e54dSMatthew Dillon 	}
53495d7e54dSMatthew Dillon 
53595d7e54dSMatthew Dillon 	if (fp != stdout)
53695d7e54dSMatthew Dillon 		fclose(fp);
53795d7e54dSMatthew Dillon }
53895d7e54dSMatthew Dillon 
5391d9f6aa1SMatthew Dillon static
540b9a33d3fSMatthew Dillon void
clean_tree(struct undo_hist_entry_rb_tree * tree)541b9a33d3fSMatthew Dillon clean_tree(struct undo_hist_entry_rb_tree *tree)
54295d7e54dSMatthew Dillon {
543bf31f16dSMatthew Dillon 	struct undo_hist_entry *tse;
544bf31f16dSMatthew Dillon 
545b9a33d3fSMatthew Dillon 	while ((tse = RB_ROOT(tree)) != NULL) {
546b9a33d3fSMatthew Dillon 		RB_REMOVE(undo_hist_entry_rb_tree, tree, tse);
547bf31f16dSMatthew Dillon 		free(tse);
548bf31f16dSMatthew Dillon 	}
54995d7e54dSMatthew Dillon }
55095d7e54dSMatthew Dillon 
5511d9f6aa1SMatthew Dillon static
552b9a33d3fSMatthew Dillon void
__collect_history(int fd,int * errorp,struct undo_hist_entry_rb_tree * tse_tree)553603468eaSTomohiro Kusumi __collect_history(int fd, int *errorp, struct undo_hist_entry_rb_tree *tse_tree)
554bf31f16dSMatthew Dillon {
555bf31f16dSMatthew Dillon 	struct hammer_ioc_history hist;
556bf31f16dSMatthew Dillon 	struct undo_hist_entry *tse;
557b9a33d3fSMatthew Dillon 	struct stat st;
558bf31f16dSMatthew Dillon 	int i;
559bf31f16dSMatthew Dillon 
560bf31f16dSMatthew Dillon 	/*
561bf31f16dSMatthew Dillon 	 * Setup
562bf31f16dSMatthew Dillon 	 */
56395d7e54dSMatthew Dillon 	bzero(&hist, sizeof(hist));
56495d7e54dSMatthew Dillon 	hist.beg_tid = HAMMER_MIN_TID;
56595d7e54dSMatthew Dillon 	hist.end_tid = HAMMER_MAX_TID;
56695d7e54dSMatthew Dillon 	hist.head.flags |= HAMMER_IOC_HISTORY_ATKEY;
56795d7e54dSMatthew Dillon 	hist.key = 0;
56895d7e54dSMatthew Dillon 	hist.nxt_key = HAMMER_MAX_KEY;
56995d7e54dSMatthew Dillon 
570bf31f16dSMatthew Dillon 	*errorp = 0;
571bf31f16dSMatthew Dillon 
572bf31f16dSMatthew Dillon 	/*
573b9a33d3fSMatthew Dillon 	 * Save the inode so inode changes can be reported.
574b9a33d3fSMatthew Dillon 	 */
575b9a33d3fSMatthew Dillon 	st.st_ino = 0;
576b9a33d3fSMatthew Dillon 	fstat(fd, &st);
577b9a33d3fSMatthew Dillon 
578b9a33d3fSMatthew Dillon 	/*
579bf31f16dSMatthew Dillon 	 * Collect a unique set of transaction ids
580bf31f16dSMatthew Dillon 	 */
58195d7e54dSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GETHISTORY, &hist) < 0) {
582bf31f16dSMatthew Dillon 		*errorp = errno;
5837e82238eSTomohiro Kusumi 		return;
58495d7e54dSMatthew Dillon 	}
58595d7e54dSMatthew Dillon 	for (;;) {
58695d7e54dSMatthew Dillon 		for (i = 0; i < hist.count; ++i) {
587bf31f16dSMatthew Dillon 			tse = malloc(sizeof(*tse));
588bf31f16dSMatthew Dillon 			tse->tse = hist.hist_ary[i];
589b9a33d3fSMatthew Dillon 			tse->inum = st.st_ino;
590bf31f16dSMatthew Dillon 			if (RB_INSERT(undo_hist_entry_rb_tree, tse_tree, tse)) {
591bf31f16dSMatthew Dillon 				free(tse);
592bf31f16dSMatthew Dillon 			}
59395d7e54dSMatthew Dillon 		}
59495d7e54dSMatthew Dillon 		if (hist.head.flags & HAMMER_IOC_HISTORY_EOF)
59595d7e54dSMatthew Dillon 			break;
59695d7e54dSMatthew Dillon 		if (hist.head.flags & HAMMER_IOC_HISTORY_NEXT_KEY) {
59795d7e54dSMatthew Dillon 			hist.key = hist.nxt_key;
59895d7e54dSMatthew Dillon 			hist.nxt_key = HAMMER_MAX_KEY;
59995d7e54dSMatthew Dillon 		}
60095d7e54dSMatthew Dillon 		if (hist.head.flags & HAMMER_IOC_HISTORY_NEXT_TID)
60195d7e54dSMatthew Dillon 			hist.beg_tid = hist.nxt_tid;
60295d7e54dSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GETHISTORY, &hist) < 0) {
603bf31f16dSMatthew Dillon 			*errorp = errno;
60495d7e54dSMatthew Dillon 			break;
60595d7e54dSMatthew Dillon 		}
60695d7e54dSMatthew Dillon 	}
607b9a33d3fSMatthew Dillon }
608b9a33d3fSMatthew Dillon 
609b9a33d3fSMatthew Dillon static
610b9a33d3fSMatthew Dillon void
collect_history(const char * filename,int * errorp,struct undo_hist_entry_rb_tree * dir_tree)611603468eaSTomohiro Kusumi collect_history(const char *filename, int *errorp,
612603468eaSTomohiro Kusumi 		struct undo_hist_entry_rb_tree *dir_tree)
613603468eaSTomohiro Kusumi {
614603468eaSTomohiro Kusumi 	int fd;
615603468eaSTomohiro Kusumi 
616603468eaSTomohiro Kusumi 	fd = open(filename, O_RDONLY);
617603468eaSTomohiro Kusumi 	if (fd == -1) {
618603468eaSTomohiro Kusumi 		*errorp = errno;
619603468eaSTomohiro Kusumi 		return;
620603468eaSTomohiro Kusumi 	}
621603468eaSTomohiro Kusumi 	__collect_history(fd, errorp, dir_tree);
622603468eaSTomohiro Kusumi 	close(fd);
623603468eaSTomohiro Kusumi }
624603468eaSTomohiro Kusumi 
625603468eaSTomohiro Kusumi static
626603468eaSTomohiro Kusumi void
collect_dir_history(const char * filename,int * errorp,struct undo_hist_entry_rb_tree * dir_tree)627b9a33d3fSMatthew Dillon collect_dir_history(const char *filename, int *errorp,
628b9a33d3fSMatthew Dillon 		    struct undo_hist_entry_rb_tree *dir_tree)
629b9a33d3fSMatthew Dillon {
630b9a33d3fSMatthew Dillon 	char *dirname;
631b9a33d3fSMatthew Dillon 
632b9a33d3fSMatthew Dillon 	if (strrchr(filename, '/')) {
633b9a33d3fSMatthew Dillon 		dirname = strdup(filename);
634b9a33d3fSMatthew Dillon 		*strrchr(dirname, '/') = 0;
635b9a33d3fSMatthew Dillon 	} else {
636b9a33d3fSMatthew Dillon 		dirname = strdup(".");
637b9a33d3fSMatthew Dillon 	}
638603468eaSTomohiro Kusumi 
639603468eaSTomohiro Kusumi 	collect_history(dirname, errorp, dir_tree);
640fbfdee92STomohiro Kusumi 	free(dirname);
64195d7e54dSMatthew Dillon }
64295d7e54dSMatthew Dillon 
64395d7e54dSMatthew Dillon static
64495d7e54dSMatthew Dillon hammer_tid_t
parse_delta_time(const char * timeStr,int * flags,int ind_flag)645965778c8SMatthew Dillon parse_delta_time(const char *timeStr, int *flags, int ind_flag)
64695d7e54dSMatthew Dillon {
64795d7e54dSMatthew Dillon 	hammer_tid_t tid;
64895d7e54dSMatthew Dillon 
64995d7e54dSMatthew Dillon 	tid = strtoull(timeStr, NULL, 0);
650965778c8SMatthew Dillon 	if (timeStr[0] == '+')
651965778c8SMatthew Dillon 		++timeStr;
652965778c8SMatthew Dillon 	if (timeStr[0] >= '0' && timeStr[0] <= '9' && timeStr[1] != 'x')
653965778c8SMatthew Dillon 		*flags |= ind_flag;
65495d7e54dSMatthew Dillon 	return(tid);
65595d7e54dSMatthew Dillon }
65695d7e54dSMatthew Dillon 
65728a07adbSTomohiro Kusumi static
65828a07adbSTomohiro Kusumi FILE*
_fopen(const char * filename,const char * mode)65928a07adbSTomohiro Kusumi _fopen(const char *filename, const char *mode)
66028a07adbSTomohiro Kusumi {
66128a07adbSTomohiro Kusumi 	FILE *fp;
66228a07adbSTomohiro Kusumi 
66328a07adbSTomohiro Kusumi 	fp = fopen(filename, mode);
66402318f07STomohiro Kusumi 	if (fp == NULL)
66502318f07STomohiro Kusumi 		err(1, "%s", filename);
66628a07adbSTomohiro Kusumi 	return(fp);
66728a07adbSTomohiro Kusumi }
66828a07adbSTomohiro Kusumi 
66995d7e54dSMatthew Dillon static void
runcmd(int fd,const char * cmd,...)67095d7e54dSMatthew Dillon runcmd(int fd, const char *cmd, ...)
67195d7e54dSMatthew Dillon {
67295d7e54dSMatthew Dillon 	va_list va;
67395d7e54dSMatthew Dillon 	pid_t pid;
67495d7e54dSMatthew Dillon 	char **av;
67595d7e54dSMatthew Dillon 	int ac;
67695d7e54dSMatthew Dillon 	int i;
67795d7e54dSMatthew Dillon 
67895d7e54dSMatthew Dillon 	va_start(va, cmd);
67995d7e54dSMatthew Dillon 	for (ac = 0; va_arg(va, void *) != NULL; ++ac)
68095d7e54dSMatthew Dillon 		;
68195d7e54dSMatthew Dillon 	va_end(va);
68295d7e54dSMatthew Dillon 
68395d7e54dSMatthew Dillon 	av = malloc((ac + 1) * sizeof(char *));
68495d7e54dSMatthew Dillon 	va_start(va, cmd);
68595d7e54dSMatthew Dillon 	for (i = 0; i < ac; ++i)
68695d7e54dSMatthew Dillon 		av[i] = va_arg(va, char *);
68795d7e54dSMatthew Dillon 	va_end(va);
68895d7e54dSMatthew Dillon 	av[i] = NULL;
68995d7e54dSMatthew Dillon 
69095d7e54dSMatthew Dillon 	if ((pid = fork()) < 0) {
69102318f07STomohiro Kusumi 		err(1, "fork");
69295d7e54dSMatthew Dillon 	} else if (pid == 0) {
69395d7e54dSMatthew Dillon 		if (fd != 1) {
69495d7e54dSMatthew Dillon 			dup2(fd, 1);
69595d7e54dSMatthew Dillon 			close(fd);
69695d7e54dSMatthew Dillon 		}
69795d7e54dSMatthew Dillon 		execv(cmd, av);
69895d7e54dSMatthew Dillon 		_exit(1);
69995d7e54dSMatthew Dillon 	} else {
70095d7e54dSMatthew Dillon 		while (waitpid(pid, NULL, 0) != pid)
70195d7e54dSMatthew Dillon 			;
70295d7e54dSMatthew Dillon 	}
70395d7e54dSMatthew Dillon 	free(av);
70495d7e54dSMatthew Dillon }
70595d7e54dSMatthew Dillon 
70695d7e54dSMatthew Dillon /*
70795d7e54dSMatthew Dillon  * Convert tid to timestamp.
70895d7e54dSMatthew Dillon  */
70995d7e54dSMatthew Dillon static char *
timestamp(struct hammer_ioc_hist_entry * hen)7104362c066STomohiro Kusumi timestamp(struct hammer_ioc_hist_entry *hen)
71195d7e54dSMatthew Dillon {
71295d7e54dSMatthew Dillon 	static char timebuf[64];
7131d9f6aa1SMatthew Dillon 	time_t t = (time_t)hen->time32;
71495d7e54dSMatthew Dillon 	struct tm *tp;
71595d7e54dSMatthew Dillon 
71695d7e54dSMatthew Dillon 	tp = localtime(&t);
7171d9f6aa1SMatthew Dillon 	strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tp);
71895d7e54dSMatthew Dillon 	return(timebuf);
71995d7e54dSMatthew Dillon }
72095d7e54dSMatthew Dillon 
721bf31f16dSMatthew Dillon static
722bf31f16dSMatthew Dillon int
undo_hist_entry_compare(struct undo_hist_entry * he1,struct undo_hist_entry * he2)723bf31f16dSMatthew Dillon undo_hist_entry_compare(struct undo_hist_entry *he1,
724bf31f16dSMatthew Dillon 			struct undo_hist_entry *he2)
725bf31f16dSMatthew Dillon {
726bf31f16dSMatthew Dillon 	if (he1->tse.tid < he2->tse.tid)
727bf31f16dSMatthew Dillon 		return(-1);
728bf31f16dSMatthew Dillon 	if (he1->tse.tid > he2->tse.tid)
729bf31f16dSMatthew Dillon 		return(1);
730bf31f16dSMatthew Dillon 	return(0);
731bf31f16dSMatthew Dillon }
732bf31f16dSMatthew Dillon 
73395d7e54dSMatthew Dillon static void
usage(void)73495d7e54dSMatthew Dillon usage(void)
73595d7e54dSMatthew Dillon {
73659f01588SMatthew Dillon 	fprintf(stderr, "undo [-adDiuv] [-o outfile] "
737bf31f16dSMatthew Dillon 			"[-t transaction-id] [-t transaction-id] path...\n"
73859f01588SMatthew Dillon 			"    -a       Iterate all historical segments\n"
73959f01588SMatthew Dillon 			"    -d       Forward diff\n"
74095d7e54dSMatthew Dillon 			"    -D       Reverse diff\n"
74195d7e54dSMatthew Dillon 			"    -i       Dump history transaction ids\n"
74295d7e54dSMatthew Dillon 			"    -u       Generate .undo files\n"
74395d7e54dSMatthew Dillon 			"    -v       Verbose\n"
7448360ec08SThomas Nikolajsen 			"    -o file  Output to the specified file\n"
74502a9630cSThomas Nikolajsen 			"    -t TID   Retrieve as of transaction-id, TID\n"
746965778c8SMatthew Dillon 			"             (a second `-t TID' to diff two)\n"
747965778c8SMatthew Dillon 			"    transaction ids must be prefixed with 0x, and\n"
748965778c8SMatthew Dillon 			"    otherwise may specify an index starting at 0\n"
749965778c8SMatthew Dillon 			"    and iterating backwards through the history.\n"
750965778c8SMatthew Dillon 	);
75195d7e54dSMatthew Dillon 	exit(1);
75295d7e54dSMatthew Dillon }
75395d7e54dSMatthew Dillon 
754