1 /*\
2 |*|  Parity Archive - A way to restore missing files in a set.
3 |*|
4 |*|  Copyright (C) 2001  Willem Monsuwe (willem@stack.nl)
5 |*|
6 |*|  File format by Stefan Wehlus -
7 |*|   initial idea by Tobias Rieper, further suggestions by Kilroy Balore
8 |*|
9 |*|  In short: Files are XORed with each other and the result is stored.
10 |*|            From this, one missing file can be recreated.
11 \*/
12 
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include "fileops.h"
17 #include "rwpar.h"
18 #include "backend.h"
19 #include "checkpar.h"
20 #include "makepar.h"
21 #include "interface.h"
22 
23 struct cmdline cmd;
24 
25 /*\
26 |*| Print usage
27 \*/
28 int
usage(void)29 usage(void)
30 {
31 	printf(
32 "Usage:\n"
33 "   par c(heck)   [options] <par file>         : Check parity archive\n"
34 "   par r(ecover) [options] <par file>         : Restore missing volumes\n"
35 "   par a(dd)     [options] <par file> [files] : Add files to parity archive\n"
36 " Advanced:\n"
37 "   par m(ix)     [options] : Try to restore from all parity files at once\n"
38 "   par i(nteractive) [<par files>] : Interactive mode (very bare-bones)\n"
39 "\n"
40 "Options: (Can be turned off with '+')\n"
41 "    -m   : Move existing files out of the way\n"
42 "    -f   : Fix faulty filenames\n"
43 "    -p<n>: Number of files per parity volume\n"
44 " or -n<n>: Number of parity volumes to create\n"
45 "    -d   : Search for duplicate files\n"
46 "    -k   : Keep broken files\n"
47 "    -s   : Be smart if filenames are consistently different.\n"
48 "    +i   : Do not add following files to parity volumes\n"
49 "    +c   : Do not create parity volumes\n"
50 "    +C   : Ignore case in filename comparisons\n"
51 "    +H   : Do not check control hashes\n"
52 "    -v,+v: Increase or decrease verbosity\n"
53 "    -h,-?: Display this help\n"
54 "    --   : Always treat following arguments as files\n"
55 "\n"
56 	);
57 	return 0;
58 }
59 
60 /*\ Sanity check \*/
61 static int
check_sizes(void)62 check_sizes(void)
63 {
64 	int fail = 0;
65 	if (sizeof(u8) != 1) {
66 		fprintf(stderr, "u8 isn't 8 bits wide.\n");
67 		fail++;
68 	}
69 	if (sizeof(u16) != 2) {
70 		fprintf(stderr, "u16 isn't 16 bits wide.\n");
71 		fail++;
72 	}
73 	if (sizeof(u32) != 4) {
74 		fprintf(stderr, "u32 isn't 32 bits wide.\n");
75 		fail++;
76 	}
77 	if (sizeof(i64) != 8) {
78 		fprintf(stderr, "u64 isn't 64 bits wide.\n");
79 		fail++;
80 	}
81 	return fail;
82 }
83 
84 /*\ In ui_text.h \*/
85 void ui_text(void);
86 
87 /*\
88 |*| Main loop.  Simple stuff.
89 \*/
90 int
main(int argc,char * argv[])91 main(int argc, char *argv[])
92 {
93 	par_t *par = 0;
94 	int fail = 0;
95 	char *p;
96 
97 	if (check_sizes())
98 		return -1;
99 
100 	/*\ Some defaults \*/
101 	memset(&cmd, 0, sizeof(cmd));
102 	cmd.volumes = 10;
103 	cmd.pervol = 1;
104 	cmd.pxx = 1;
105 	cmd.ctrl = 1;
106 	cmd.add = 1;
107 	cmd.usecase = 1;
108 
109 	if (argc == 1) return usage();
110 
111 	for (; argc > 1; argc--, argv++) {
112 		if (((argv[1][0] == '-') || (argv[1][0] == '+')) &&
113 		    argv[1][1] && !cmd.dash) {
114 			for (p = argv[1]; *p; p++) switch (*p) {
115 			case '-':
116 				if (p[1] == '-')
117 					cmd.dash = 1;
118 				else
119 					cmd.plus = 1;
120 				break;
121 			case '+':
122 				cmd.plus = 0;
123 				break;
124 			case 'm':
125 				cmd.move = cmd.plus;
126 				break;
127 			case 'i':
128 				cmd.add = cmd.plus;
129 				break;
130 			case 'f':
131 				cmd.fix = cmd.plus;
132 				break;
133 			case 'c':
134 				cmd.pxx = cmd.plus;
135 				break;
136 			case 'd':
137 				cmd.dupl = cmd.plus;
138 				break;
139 			case 'v':
140 				if (cmd.plus)
141 					cmd.loglevel++;
142 				else
143 					cmd.loglevel--;
144 				break;
145 			case 'p':
146 			case 'n':
147 				cmd.pervol = (*p == 'p');
148 				while (isspace(*++p))
149 					;
150 				if (!*p) {
151 					argv++;
152 					argc--;
153 					p = argv[1];
154 				}
155 				if (!isdigit(*p)) {
156 					fprintf(stderr, "Value expected!\n");
157 				} else {
158 					cmd.volumes = 0;
159 					do {
160 						cmd.volumes *= 10;
161 						cmd.volumes += *p - '0';
162 					} while (isdigit(*++p));
163 					p--;
164 				}
165 				break;
166 			case 'H':
167 				cmd.ctrl = cmd.plus;
168 				break;
169 			case 'C':
170 				cmd.usecase = cmd.plus;
171 				break;
172 			case 'k':
173 				cmd.keep = cmd.plus;
174 				break;
175 			case 's':
176 				cmd.smart = cmd.plus;
177 				break;
178 			case '?':
179 			case 'h':
180 				return usage();
181 			default:
182 				fprintf(stderr,
183 					"Unknown switch: '%c'\n", *p);
184 				break;
185 			}
186 			continue;
187 		}
188 		if (!cmd.action) {
189 			switch (argv[1][0]) {
190 			case 'c':
191 			case 'C':
192 				cmd.action = ACTION_CHECK;
193 				break;
194 			case 'm':
195 			case 'M':
196 				cmd.action = ACTION_MIX;
197 				break;
198 			case 'r':
199 			case 'R':
200 				cmd.action = ACTION_RESTORE;
201 				break;
202 			case 'a':
203 			case 'A':
204 				cmd.action = ACTION_ADD;
205 				break;
206 			case 'i':
207 			case 'I':
208 				cmd.action = ACTION_TEXT_UI;
209 				break;
210 			default:
211 				fprintf(stderr, "Unknown command: '%s'\n",
212 						argv[1]);
213 				break;
214 			}
215 			continue;
216 		}
217 		switch (cmd.action) {
218 		default:
219 			cmd.action = ACTION_CHECK;
220 			/*\ FALLTHROUGH \*/
221 		case ACTION_CHECK:
222 		case ACTION_RESTORE:
223 			fprintf(stderr, "Checking %s\n", argv[1]);
224 			par = read_par_header(unist(argv[1]), 0, 0, 0);
225 			if (!par) {
226 				fail = 2;
227 				continue;
228 			}
229 			if (check_par(par) < 0) fail = 1;
230 			free_par(par);
231 			par = 0;
232 			break;
233 		case ACTION_ADD:
234 			fprintf(stderr, "Adding to %s\n", argv[1]);
235 			par = read_par_header(unist(argv[1]), 1, 0, 0);
236 			if (!par) return 2;
237 			cmd.action = ACTION_ADDING;
238 			break;
239 		case ACTION_ADDING:
240 			par_add_file(par, find_file_name(unist(argv[1]), 1));
241 			break;
242 		case ACTION_MIX:
243 			fprintf(stderr, "Unknown argument: '%s'\n", argv[1]);
244 			break;
245 		case ACTION_TEXT_UI:
246 			par_load(unist(argv[1]));
247 			break;
248 		}
249 	}
250 	if (cmd.action == ACTION_TEXT_UI) {
251 		ui_text();
252 		return 0;
253 	}
254 	if (cmd.action == ACTION_MIX) {
255 		par = find_all_par_files();
256 		if (par) {
257 			fprintf(stderr, "\nChecking:\n");
258 			if (check_par(par) < 0)
259 				fail = 1;
260 			free_par(par);
261 			par = 0;
262 		} else {
263 			fail = 2;
264 		}
265 	}
266 	if (par) {
267 		if (cmd.pxx && !par_make_pxx(par))
268 			fail |= 1;
269 		if (!par->vol_number && !write_par_header(par))
270 			fail |= 1;
271 		free_par(par);
272 	}
273 	return fail;
274 }
275