1 /*
2  * test_rel.c
3  *
4  * Copyright (C) 1997 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  */
11 
12 #include "config.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #ifdef HAVE_GETOPT_H
18 #include <getopt.h>
19 #endif
20 #include <fcntl.h>
21 
22 #include <et/com_err.h>
23 #include <ss/ss.h>
24 #include <ext2fs/ext2_fs.h>
25 #include <ext2fs/ext2fs.h>
26 #include <ext2fs/irel.h>
27 #include <ext2fs/brel.h>
28 
29 #include "test_rel.h"
30 
31 extern ss_request_table test_cmds;
32 
33 ext2_irel irel = NULL;
34 ext2_brel brel = NULL;
35 
36 /*
37  * Helper function which parses an inode number.
38  */
39 static int parse_inode(const char *request, const char *desc,
40 		       const char *str, ext2_ino_t *ino)
41 {
42 	char *tmp;
43 
44 	*ino = strtoul(str, &tmp, 0);
45 	if (*tmp) {
46 		com_err(request, 0, "Bad %s - %s", desc, str);
47 		return 1;
48 	}
49 	return 0;
50 }
51 
52 /*
53  * Helper function which parses a block number.
54  */
55 static int parse_block(const char *request, const char *desc,
56 		       const char *str, blk_t *blk)
57 {
58 	char *tmp;
59 
60 	*blk = strtoul(str, &tmp, 0);
61 	if (*tmp) {
62 		com_err(request, 0, "Bad %s - %s", desc, str);
63 		return 1;
64 	}
65 	return 0;
66 }
67 
68 /*
69  * Helper function which assures that a brel table is open
70  */
71 static int check_brel(char *request)
72 {
73 	if (brel)
74 		return 0;
75 	com_err(request, 0, "A block relocation table must be open.");
76 	return 1;
77 }
78 
79 /*
80  * Helper function which assures that an irel table is open
81  */
82 static int check_irel(char *request)
83 {
84 	if (irel)
85 		return 0;
86 	com_err(request, 0, "An inode relocation table must be open.");
87 	return 1;
88 }
89 
90 /*
91  * Helper function which displays a brel entry
92  */
93 static void display_brel_entry(blk_t old,
94 			       struct ext2_block_relocate_entry *ent)
95 {
96 	printf("Old= %u, New= %u, Owner= %u:%u\n", old, ent->new,
97 	       ent->owner.block_ref, ent->offset);
98 }
99 
100 /*
101  * Helper function which displays an irel entry
102  */
103 static void display_irel_entry(ext2_ino_t old,
104 			       struct ext2_inode_relocate_entry *ent,
105 			       int do_refs)
106 {
107 	struct ext2_inode_reference ref;
108 	errcode_t	retval;
109 	int		first = 1;
110 
111 	printf("Old= %lu, New= %lu, Original=%lu, Max_refs=%u\n", old,
112 	       ent->new, ent->orig, ent->max_refs);
113 	if (!do_refs)
114 		return;
115 
116 	retval = ext2fs_irel_start_iter_ref(irel, old);
117 	if (retval) {
118 		printf("\tCouldn't get references: %s\n",
119 		       error_message(retval));
120 		return;
121 	}
122 	while (1) {
123 		retval = ext2fs_irel_next_ref(irel, &ref);
124 		if (retval) {
125 			printf("(%s) ", error_message(retval));
126 			break;
127 		}
128 		if (ref.block == 0)
129 			break;
130 		if (first) {
131 			fputc('\t', stdout);
132 			first = 0;
133 		} else
134 			printf(", ");
135 		printf("%u:%u", ref.block, ref.offset);
136 	}
137 	if (!first)
138 		fputc('\n', stdout);
139 }
140 
141 /*
142  * These are the actual command table procedures
143  */
144 void do_brel_ma_create(int argc, char **argv)
145 {
146 	const char *usage = "Usage: %s name max_blocks\n";
147 	errcode_t	retval;
148 	blk_t		max_blk;
149 
150 	if (argc < 3) {
151 		printf(usage, argv[0]);
152 		return;
153 	}
154 	if (parse_block(argv[0], "max_blocks", argv[2], &max_blk))
155 		return;
156 	retval = ext2fs_brel_memarray_create(argv[1], max_blk, &brel);
157 	if (retval) {
158 		com_err(argv[0], retval, "while opening memarray brel");
159 		return;
160 	}
161 	return;
162 }
163 
164 void do_brel_free(int argc, char **argv)
165 {
166 	if (check_brel(argv[0]))
167 		return;
168 	ext2fs_brel_free(brel);
169 	brel = NULL;
170 	return;
171 }
172 
173 void do_brel_put(int argc, char **argv)
174 {
175 	const char *usage = "usage: %s old_block new_block [owner] [offset]";
176 	errcode_t retval;
177 	struct ext2_block_relocate_entry ent;
178 	blk_t	old, new, offset=0, owner=0;
179 
180 	if (check_brel(argv[0]))
181 		return;
182 
183 	if (argc < 3) {
184 		printf(usage, argv[0]);
185 		return;
186 	}
187 	if (parse_block(argv[0], "old block", argv[1], &old))
188 		return;
189 	if (parse_block(argv[0], "new block", argv[2], &new))
190 		return;
191 	if (argc > 3 &&
192 	    parse_block(argv[0], "owner block", argv[3], &owner))
193 		return;
194 	if (argc > 4 &&
195 	    parse_block(argv[0], "offset", argv[4], &offset))
196 		return;
197 	if (offset > 65535) {
198 		printf("Offset too large.\n");
199 		return;
200 	}
201 	ent.new = new;
202 	ent.offset = (__u16) offset;
203 	ent.flags = 0;
204 	ent.owner.block_ref = owner;
205 
206 	retval = ext2fs_brel_put(brel, old, &ent);
207 	if (retval) {
208 		com_err(argv[0], retval, "while calling ext2fs_brel_put");
209 		return;
210 	}
211 	return;
212 }
213 
214 void do_brel_get(int argc, char **argv)
215 {
216 	const char *usage = "%s block";
217 	errcode_t retval;
218 	struct ext2_block_relocate_entry ent;
219 	blk_t	blk;
220 
221 	if (check_brel(argv[0]))
222 		return;
223 	if (argc < 2) {
224 		printf(usage, argv[0]);
225 		return;
226 	}
227 	if (parse_block(argv[0], "block", argv[1], &blk))
228 		return;
229 	retval = ext2fs_brel_get(brel, blk, &ent);
230 	if (retval) {
231 		com_err(argv[0], retval, "while calling ext2fs_brel_get");
232 		return;
233 	}
234 	display_brel_entry(blk, &ent);
235 	return;
236 }
237 
238 void do_brel_start_iter(int argc, char **argv)
239 {
240 	errcode_t retval;
241 
242 	if (check_brel(argv[0]))
243 		return;
244 
245 	retval = ext2fs_brel_start_iter(brel);
246 	if (retval) {
247 		com_err(argv[0], retval, "while calling ext2fs_brel_start_iter");
248 		return;
249 	}
250 	return;
251 }
252 
253 void do_brel_next(int argc, char **argv)
254 {
255 	errcode_t retval;
256 	struct ext2_block_relocate_entry ent;
257 	blk_t	blk;
258 
259 	if (check_brel(argv[0]))
260 		return;
261 
262 	retval = ext2fs_brel_next(brel, &blk, &ent);
263 	if (retval) {
264 		com_err(argv[0], retval, "while calling ext2fs_brel_next");
265 		return;
266 	}
267 	if (blk == 0) {
268 		printf("No more entries!\n");
269 		return;
270 	}
271 	display_brel_entry(blk, &ent);
272 	return;
273 }
274 
275 void do_brel_dump(int argc, char **argv)
276 {
277 	errcode_t retval;
278 	struct ext2_block_relocate_entry ent;
279 	blk_t	blk;
280 
281 	if (check_brel(argv[0]))
282 		return;
283 
284 	retval = ext2fs_brel_start_iter(brel);
285 	if (retval) {
286 		com_err(argv[0], retval, "while calling ext2fs_brel_start_iter");
287 		return;
288 	}
289 
290 	while (1) {
291 		retval = ext2fs_brel_next(brel, &blk, &ent);
292 		if (retval) {
293 			com_err(argv[0], retval, "while calling ext2fs_brel_next");
294 			return;
295 		}
296 		if (blk == 0)
297 			break;
298 
299 		display_brel_entry(blk, &ent);
300 	}
301 	return;
302 }
303 
304 void do_brel_move(int argc, char **argv)
305 {
306 	const char *usage = "%s old_block new_block";
307 	errcode_t retval;
308 	blk_t	old, new;
309 
310 	if (check_brel(argv[0]))
311 		return;
312 	if (argc < 2) {
313 		printf(usage, argv[0]);
314 		return;
315 	}
316 	if (parse_block(argv[0], "old block", argv[1], &old))
317 		return;
318 	if (parse_block(argv[0], "new block", argv[2], &new))
319 		return;
320 
321 	retval = ext2fs_brel_move(brel, old, new);
322 	if (retval) {
323 		com_err(argv[0], retval, "while calling ext2fs_brel_move");
324 		return;
325 	}
326 	return;
327 }
328 
329 void do_brel_delete(int argc, char **argv)
330 {
331 	const char *usage = "%s block";
332 	errcode_t retval;
333 	blk_t	blk;
334 
335 	if (check_brel(argv[0]))
336 		return;
337 	if (argc < 2) {
338 		printf(usage, argv[0]);
339 		return;
340 	}
341 	if (parse_block(argv[0], "block", argv[1], &blk))
342 		return;
343 
344 	retval = ext2fs_brel_delete(brel, blk);
345 	if (retval) {
346 		com_err(argv[0], retval, "while calling ext2fs_brel_delete");
347 		return;
348 	}
349 }
350 
351 void do_irel_ma_create(int argc, char **argv)
352 {
353 	const char	*usage = "Usage: %s name max_inode\n";
354 	errcode_t	retval;
355 	ext2_ino_t	max_ino;
356 
357 	if (argc < 3) {
358 		printf(usage, argv[0]);
359 		return;
360 	}
361 	if (parse_inode(argv[0], "max_inodes", argv[2], &max_ino))
362 		return;
363 	retval = ext2fs_irel_memarray_create(argv[1], max_ino, &irel);
364 	if (retval) {
365 		com_err(argv[0], retval, "while opening memarray irel");
366 		return;
367 	}
368 	return;
369 }
370 
371 void do_irel_free(int argc, char **argv)
372 {
373 	if (check_irel(argv[0]))
374 		return;
375 
376 	ext2fs_irel_free(irel);
377 	irel = NULL;
378 	return;
379 }
380 
381 void do_irel_put(int argc, char **argv)
382 {
383 	const char	*usage = "%s old new max_refs";
384 	errcode_t	retval;
385 	ext2_ino_t	old, new, max_refs;
386 	struct ext2_inode_relocate_entry ent;
387 
388 	if (check_irel(argv[0]))
389 		return;
390 
391 	if (argc < 4) {
392 		printf(usage, argv[0]);
393 		return;
394 	}
395 	if (parse_inode(argv[0], "old inode", argv[1], &old))
396 		return;
397 	if (parse_inode(argv[0], "new inode", argv[2], &new))
398 		return;
399 	if (parse_inode(argv[0], "max_refs", argv[3], &max_refs))
400 		return;
401 	if (max_refs > 65535) {
402 		printf("max_refs too big\n");
403 		return;
404 	}
405 	ent.new = new;
406 	ent.max_refs = (__u16) max_refs;
407 	ent.flags = 0;
408 
409 	retval = ext2fs_irel_put(irel, old, &ent);
410 	if (retval) {
411 		com_err(argv[0], retval, "while calling ext2fs_irel_put");
412 		return;
413 	}
414 	return;
415 }
416 
417 void do_irel_get(int argc, char **argv)
418 {
419 	const char	*usage = "%s inode";
420 	errcode_t	retval;
421 	ext2_ino_t	old;
422 	struct ext2_inode_relocate_entry ent;
423 
424 	if (check_irel(argv[0]))
425 		return;
426 
427 	if (argc < 2) {
428 		printf(usage, argv[0]);
429 		return;
430 	}
431 	if (parse_inode(argv[0], "inode", argv[1], &old))
432 		return;
433 
434 	retval = ext2fs_irel_get(irel, old, &ent);
435 	if (retval) {
436 		com_err(argv[0], retval, "while calling ext2fs_irel_get");
437 		return;
438 	}
439 	display_irel_entry(old, &ent, 1);
440 	return;
441 }
442 
443 void do_irel_get_by_orig(int argc, char **argv)
444 {
445 	const char	*usage = "%s orig_inode";
446 	errcode_t	retval;
447 	ext2_ino_t	orig, old;
448 	struct ext2_inode_relocate_entry ent;
449 
450 	if (check_irel(argv[0]))
451 		return;
452 
453 	if (argc < 2) {
454 		printf(usage, argv[0]);
455 		return;
456 	}
457 	if (parse_inode(argv[0], "original inode", argv[1], &orig))
458 		return;
459 
460 	retval = ext2fs_irel_get_by_orig(irel, orig, &old, &ent);
461 	if (retval) {
462 		com_err(argv[0], retval, "while calling ext2fs_irel_get_by_orig");
463 		return;
464 	}
465 	display_irel_entry(old, &ent, 1);
466 	return;
467 }
468 
469 void do_irel_start_iter(int argc, char **argv)
470 {
471 	errcode_t retval;
472 
473 	if (check_irel(argv[0]))
474 		return;
475 
476 	retval = ext2fs_irel_start_iter(irel);
477 	if (retval) {
478 		com_err(argv[0], retval, "while calling ext2fs_irel_start_iter");
479 		return;
480 	}
481 	return;
482 }
483 
484 void do_irel_next(int argc, char **argv)
485 {
486 	errcode_t	retval;
487 	ext2_ino_t	old;
488 	struct ext2_inode_relocate_entry ent;
489 
490 	if (check_irel(argv[0]))
491 		return;
492 
493 	retval = ext2fs_irel_next(irel, &old, &ent);
494 	if (retval) {
495 		com_err(argv[0], retval, "while calling ext2fs_irel_next");
496 		return;
497 	}
498 	if (old == 0) {
499 		printf("No more entries!\n");
500 		return;
501 	}
502 	display_irel_entry(old, &ent, 1);
503 	return;
504 }
505 
506 void do_irel_dump(int argc, char **argv)
507 {
508 	errcode_t	retval;
509 	ext2_ino_t	ino;
510 	struct ext2_inode_relocate_entry ent;
511 
512 	if (check_irel(argv[0]))
513 		return;
514 
515 	retval = ext2fs_irel_start_iter(irel);
516 	if (retval) {
517 		com_err(argv[0], retval, "while calling ext2fs_irel_start_iter");
518 		return;
519 	}
520 
521 	while (1) {
522 		retval = ext2fs_irel_next(irel, &ino, &ent);
523 		if (retval) {
524 			com_err(argv[0], retval, "while calling ext2fs_irel_next");
525 			return;
526 		}
527 		if (ino == 0)
528 			break;
529 
530 		display_irel_entry(ino, &ent, 1);
531 	}
532 	return;
533 }
534 
535 void do_irel_add_ref(int argc, char **argv)
536 {
537 	const char	*usage = "%s inode block offset";
538 	errcode_t	retval;
539 	blk_t		block, offset;
540 	ext2_ino_t	ino;
541 	struct ext2_inode_reference ref;
542 
543 
544 	if (check_irel(argv[0]))
545 		return;
546 
547 	if (argc < 4) {
548 		printf(usage, argv[0]);
549 		return;
550 	}
551 	if (parse_inode(argv[0], "inode", argv[1], &ino))
552 		return;
553 	if (parse_block(argv[0], "block", argv[2], &block))
554 		return;
555 	if (parse_block(argv[0], "offset", argv[3], &offset))
556 		return;
557 	if (offset > 65535) {
558 		printf("Offset too big.\n");
559 		return;
560 	}
561 	ref.block = block;
562 	ref.offset = offset;
563 
564 	retval = ext2fs_irel_add_ref(irel, ino, &ref);
565 	if (retval) {
566 		com_err(argv[0], retval, "while calling ext2fs_irel_add_ref");
567 		return;
568 	}
569 	return;
570 }
571 
572 void do_irel_start_iter_ref(int argc, char **argv)
573 {
574 	const char	*usage = "%s inode";
575 	errcode_t	retval;
576 	ext2_ino_t	ino;
577 
578 	if (check_irel(argv[0]))
579 		return;
580 
581 	if (argc < 2) {
582 		printf(usage, argv[0]);
583 		return;
584 	}
585 
586 	if (parse_inode(argv[0], "inode", argv[1], &ino))
587 		return;
588 	retval = ext2fs_irel_start_iter_ref(irel, ino);
589 	if (retval) {
590 		com_err(argv[0], retval, "while calling ext2fs_irel_start_iter_ref");
591 		return;
592 	}
593 	return;
594 }
595 
596 void do_irel_next_ref(int argc, char **argv)
597 {
598 	struct ext2_inode_reference ref;
599 	errcode_t retval;
600 
601 	if (check_irel(argv[0]))
602 		return;
603 
604 	retval = ext2fs_irel_next_ref(irel, &ref);
605 	if (retval) {
606 		com_err(argv[0], retval, "while calling ext2fs_irel_next_ref");
607 		return;
608 	}
609 	printf("Inode reference: %u:%u\n", ref.block, ref.offset);
610 	return;
611 }
612 
613 void do_irel_move(int argc, char **argv)
614 {
615 	const char	*usage = "%s old new";
616 	errcode_t	retval;
617 	ext2_ino_t	old, new;
618 
619 	if (check_irel(argv[0]))
620 		return;
621 
622 	if (argc < 3) {
623 		printf(usage, argv[0]);
624 		return;
625 	}
626 	if (parse_inode(argv[0], "old inode", argv[1], &old))
627 		return;
628 	if (parse_inode(argv[0], "new inode", argv[2], &new))
629 		return;
630 
631 	retval = ext2fs_irel_move(irel, old, new);
632 	if (retval) {
633 		com_err(argv[0], retval, "while calling ext2fs_irel_move");
634 		return;
635 	}
636 	return;
637 }
638 
639 void do_irel_delete(int argc, char **argv)
640 {
641 	const char	*usage = "%s inode";
642 	errcode_t	retval;
643 	ext2_ino_t	ino;
644 
645 	if (check_irel(argv[0]))
646 		return;
647 
648 	if (argc < 2) {
649 		printf(usage, argv[0]);
650 		return;
651 	}
652 	if (parse_inode(argv[0], "inode", argv[1], &ino))
653 		return;
654 
655 	retval = ext2fs_irel_delete(irel, ino);
656 	if (retval) {
657 		com_err(argv[0], retval, "while calling ext2fs_irel_delete");
658 		return;
659 	}
660 	return;
661 }
662 
663 static int source_file(const char *cmd_file, int sci_idx)
664 {
665 	FILE		*f;
666 	char		buf[256];
667 	char		*cp;
668 	int		exit_status = 0;
669 	int		retval;
670 	int 		noecho;
671 
672 	if (strcmp(cmd_file, "-") == 0)
673 		f = stdin;
674 	else {
675 		f = fopen(cmd_file, "r");
676 		if (!f) {
677 			perror(cmd_file);
678 			exit(1);
679 		}
680 	}
681 	fflush(stdout);
682 	fflush(stderr);
683 	setbuf(stdout, NULL);
684 	setbuf(stderr, NULL);
685 	while (!feof(f)) {
686 		if (fgets(buf, sizeof(buf), f) == NULL)
687 			break;
688 		if (buf[0] == '#')
689 			continue;
690 		noecho = 0;
691 		if (buf[0] == '-') {
692 			noecho = 1;
693 			buf[0] = ' ';
694 		}
695 		cp = strchr(buf, '\n');
696 		if (cp)
697 			*cp = 0;
698 		cp = strchr(buf, '\r');
699 		if (cp)
700 			*cp = 0;
701 		if (!noecho)
702 			printf("test_rel: %s\n", buf);
703 		retval = ss_execute_line(sci_idx, buf);
704 		if (retval) {
705 			ss_perror(sci_idx, retval, buf);
706 			exit_status++;
707 		}
708 	}
709 	return exit_status;
710 }
711 
712 void main(int argc, char **argv)
713 {
714 	int		retval;
715 	int		sci_idx;
716 	const char	*usage = "Usage: test_rel [-R request] [-f cmd_file]";
717 	int		c;
718 	char		*request = 0;
719 	int		exit_status = 0;
720 	char		*cmd_file = 0;
721 
722 	initialize_ext2_error_table();
723 
724 	while ((c = getopt (argc, argv, "wR:f:")) != EOF) {
725 		switch (c) {
726 		case 'R':
727 			request = optarg;
728 			break;
729 		case 'f':
730 			cmd_file = optarg;
731 			break;
732 		default:
733 			com_err(argv[0], 0, usage);
734 			return;
735 		}
736 	}
737 	sci_idx = ss_create_invocation("test_rel", "0.0", (char *) NULL,
738 				       &test_cmds, &retval);
739 	if (retval) {
740 		ss_perror(sci_idx, retval, "creating invocation");
741 		exit(1);
742 	}
743 
744 	(void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
745 	if (retval) {
746 		ss_perror(sci_idx, retval, "adding standard requests");
747 		exit (1);
748 	}
749 	if (request) {
750 		retval = 0;
751 		retval = ss_execute_line(sci_idx, request);
752 		if (retval) {
753 			ss_perror(sci_idx, retval, request);
754 			exit_status++;
755 		}
756 	} else if (cmd_file) {
757 		exit_status = source_file(cmd_file, sci_idx);
758 	} else {
759 		ss_listen(sci_idx);
760 	}
761 
762 	exit(exit_status);
763 }
764 
765