xref: /haiku/src/bin/bfs_tools/bfswhich.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * Copyright (c) 2002-2009 pinc Software. All Rights Reserved.
3  * Released under the terms of the MIT license.
4  */
5 
6 //!	Finds out to which file(s) a particular block belongs
7 
8 
9 #include "Bitmap.h"
10 #include "Disk.h"
11 #include "Inode.h"
12 #include "Hashtable.h"
13 #include "BPlusTree.h"
14 #include "dump.h"
15 
16 #include <fs_info.h>
17 
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <ctype.h>
23 #include <errno.h>
24 
25 
26 void scanNodes(Disk& disk, Directory* directory, const char* name,
27 	const block_run& checkForRun);
28 
29 
30 char gEscape[3] = {0x1b, '[', 0};
31 off_t gCount = 1;
32 
33 
34 bool
35 checkForBlockRunIntersection(const block_run& check, const block_run& against)
36 {
37 	if (check.allocation_group == against.allocation_group
38 		&& check.start + check.length > against.start
39 		&& check.start < against.start + against.length)
40 		return true;
41 
42 	return false;
43 }
44 
45 
46 bool
47 checkNode(Disk &disk, Inode *inode, block_run checkForRun)
48 {
49 	// check the inode space itself
50 	if (checkForBlockRunIntersection(inode->BlockRun(), checkForRun))
51 		return true;
52 
53 	// the data stream of symlinks is no real data stream
54 	if (inode->IsSymlink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0)
55 		return false;
56 
57 	// direct blocks
58 
59 	const data_stream* data = &inode->InodeBuffer()->data;
60 
61 	if (data->max_direct_range == 0)
62 		return false;
63 
64 	for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
65 		if (data->direct[i].IsZero())
66 			break;
67 
68 		if (checkForBlockRunIntersection(data->direct[i], checkForRun))
69 			return true;
70 	}
71 
72 	// indirect blocks
73 
74 	if (data->max_indirect_range == 0 || data->indirect.IsZero())
75 		return false;
76 
77 	if (checkForBlockRunIntersection(data->indirect, checkForRun))
78 		return true;
79 
80 	DataStream *stream = dynamic_cast<DataStream *>(inode);
81 	if (stream == NULL)
82 		return false;
83 
84 	// load indirect blocks
85 	int32 bytes = data->indirect.length << disk.BlockShift();
86 	block_run* indirect = (block_run*)malloc(bytes);
87 	if (indirect == NULL)
88 		return false;
89 	if (disk.ReadAt(disk.ToOffset(data->indirect), indirect, bytes) <= 0)
90 		return false;
91 
92 	int32 runs = bytes / sizeof(block_run);
93 	for (int32 i = 0; i < runs; i++) {
94 		if (indirect[i].IsZero())
95 			break;
96 
97 		if (checkForBlockRunIntersection(indirect[i], checkForRun))
98 			return true;
99 	}
100 	free(indirect);
101 
102 	// double indirect blocks
103 
104 	if (data->max_double_indirect_range == 0 || data->double_indirect.IsZero())
105 		return false;
106 
107 	if (checkForBlockRunIntersection(data->double_indirect, checkForRun))
108 		return true;
109 
110 	// TODO: to be implemented...
111 
112 	return false;
113 }
114 
115 
116 void
117 scanNode(Disk& disk, Inode* inode, const char* name,
118 	const block_run& checkForRun)
119 {
120 	if (checkNode(disk, inode, checkForRun)) {
121 		printf("Inode at (%ld, %u, %u) \"%s\" intersects\n",
122 			inode->BlockRun().allocation_group, inode->BlockRun().start,
123 			inode->BlockRun().length, name);
124 	}
125 
126 	if (!inode->Attributes().IsZero()) {
127 		Inode *attributeDirectory = Inode::Factory(&disk,
128 			inode->Attributes());
129 		if (attributeDirectory != NULL) {
130 			scanNodes(disk,
131 				dynamic_cast<Directory *>(attributeDirectory), "(attr-dir)",
132 				checkForRun);
133 		}
134 
135 		delete attributeDirectory;
136 	}
137 }
138 
139 
140 void
141 scanNodes(Disk& disk, Directory* directory, const char* directoryName,
142 	const block_run& checkForRun)
143 {
144 	if (directory == NULL)
145 		return;
146 
147 	scanNode(disk, directory, directoryName, checkForRun);
148 
149 	directory->Rewind();
150 	char name[B_FILE_NAME_LENGTH];
151 	block_run run;
152 	while (directory->GetNextEntry(name, &run) == B_OK) {
153 		if (!strcmp(name, ".") || !strcmp(name, ".."))
154 			continue;
155 
156 		if (++gCount % 50 == 0)
157 			printf("  %7Ld%s1A\n", gCount, gEscape);
158 
159 		Inode *inode = Inode::Factory(&disk, run);
160 		if (inode != NULL) {
161 
162 			if (inode->IsDirectory()) {
163 				scanNodes(disk, static_cast<Directory *>(inode), name,
164 					checkForRun);
165 			} else
166 				scanNode(disk, inode, name, checkForRun);
167 
168 			delete inode;
169 		} else {
170 			printf("  Directory \"%s\" (%ld, %d) points to corrupt inode \"%s\" "
171 				"(%ld, %d)\n", directory->Name(),
172 				directory->BlockRun().allocation_group,directory
173 					->BlockRun().start,
174 				name, run.allocation_group, run.start);
175 		}
176 	}
177 }
178 
179 
180 void
181 scanNodes(Disk& disk, const block_run& checkForRun, bool scanAll)
182 {
183 	Directory* root = (Directory*)Inode::Factory(&disk, disk.Root());
184 
185 	puts("Scanning nodes (this will take some time)...");
186 
187 	if (root == NULL || root->InitCheck() != B_OK) {
188 		fprintf(stderr,"  Could not open root directory!\n");
189 		return;
190 	}
191 
192 	scanNodes(disk, root, "(root)", checkForRun);
193 
194 	delete root;
195 
196 	if (scanAll) {
197 		Directory* indices = (Directory*)Inode::Factory(&disk, disk.Indices());
198 
199 		puts("Scanning index nodes (this will take some time)...");
200 
201 		scanNodes(disk, indices, "(indices)", checkForRun);
202 
203 		delete indices;
204 	}
205 
206 	printf("  %7Ld nodes found.\n", gCount);
207 }
208 
209 
210 void
211 testBitmap(Disk& disk, const block_run& run)
212 {
213 	Bitmap bitmap;
214 	status_t status = bitmap.SetTo(&disk);
215 	if (status != B_OK) {
216 		fprintf(stderr, "Bitmap initialization failed: %s\n", strerror(status));
217 		return;
218 	}
219 
220 	printf("Block bitmap sees block %lld as %s.\n", disk.ToBlock(run),
221 		bitmap.UsedAt(disk.ToBlock(run)) ? "used" : "free");
222 }
223 
224 
225 block_run
226 parseBlockRun(Disk& disk, char* first, char* last)
227 {
228 	char* comma;
229 
230 	if (last != NULL)
231 		return block_run::Run(atol(first), atol(last), 1);
232 
233 	if ((comma = strchr(first, ',')) != NULL) {
234 		*comma++ = '\0';
235 		return block_run::Run(atol(first), atol(comma));
236 	}
237 
238 	return disk.ToBlockRun(atoll(first));
239 }
240 
241 
242 void
243 printUsage(char* tool)
244 {
245 	char* filename = strrchr(tool, '/');
246 	fprintf(stderr, "usage: %s <device> <allocation_group> <start>\n",
247 		filename ? filename + 1 : tool);
248 }
249 
250 
251 int
252 main(int argc, char** argv)
253 {
254 	puts("Copyright (c) 2001-2009 pinc Software.");
255 
256 	char* toolName = argv[0];
257 	if (argc < 2 || !strcmp(argv[1], "--help")) {
258 		printUsage(toolName);
259 		return -1;
260 	}
261 
262 	bool scanAll = false;
263 
264 	while (*++argv) {
265 		char *arg = *argv;
266 		if (*arg == '-') {
267 			while (*++arg && isalpha(*arg)) {
268 				switch (*arg) {
269 					case 'a':
270 						scanAll = true;
271 						break;
272 					default:
273 						printUsage(toolName);
274 						return -1;
275 				}
276 			}
277 		} else
278 			break;
279 	}
280 
281 	Disk disk(argv[0]);
282 	status_t status = disk.InitCheck();
283 	if (status < B_OK) {
284 		fprintf(stderr, "Could not open device or file \"%s\": %s\n",
285 			argv[0], strerror(status));
286 		return -1;
287 	}
288 
289 	if (disk.ValidateSuperBlock() != B_OK) {
290 		fprintf(stderr, "The disk's superblock is corrupt!\n");
291 		return -1;
292 	}
293 
294 	// Set the block_run to the right value (as specified on the command line)
295 	if (!argv[1]) {
296 		fprintf(stderr, "Need the allocation group and starting offset (or the "
297 			"block number) of the block to search for!\n");
298 		return -1;
299 	}
300 	block_run run = parseBlockRun(disk, argv[1], argv[2]);
301 	off_t block = disk.ToBlock(run);
302 
303 	putchar('\n');
304 	testBitmap(disk, run);
305 
306 	if (checkForBlockRunIntersection(disk.Log(), run)) {
307 		printf("Log area (%lu.%u.%u) intersects\n",
308 			disk.Log().allocation_group, disk.Log().start,
309 			disk.Log().length);
310 	} else if (block < 1) {
311 		printf("Superblock intersects\n");
312 	} else if (block < 1 + disk.BitmapSize()) {
313 		printf("Block bitmap intersects (start %d, end %lu)\n", 1,
314 			disk.BitmapSize());
315 	} else
316 		scanNodes(disk, run, scanAll);
317 
318 	return 0;
319 }
320 
321