xref: /dragonfly/sbin/fsck_hammer2/destroy.c (revision f0e61bb7)
1 /*
2  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2019 The DragonFly Project
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The DragonFly Project
7  * by Matthew Dillon <dillon@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 // # gcc -Wall -g -I../../sys -I../hammer2 ../../sys/libkern/icrc32.c ../hammer2/subs.c ../hammer2/ondisk.c ./destroy.c -o destroy
38 
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdbool.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <errno.h>
49 #include <assert.h>
50 
51 #include <vfs/hammer2/hammer2_disk.h>
52 
53 #include "hammer2_subs.h"
54 
55 static int modify_blockref(const hammer2_volume_data_t *, int,
56     hammer2_blockref_t *, hammer2_blockref_t *);
57 static int modify_inode(const hammer2_blockref_t *,
58     hammer2_media_data_t *, size_t);
59 static int modify_dirent_embedded(int, hammer2_blockref_t *);
60 static int modify_dirent(int, hammer2_blockref_t *, const hammer2_blockref_t *,
61     hammer2_media_data_t *, size_t);
62 
63 static hammer2_tid_t src_inode = 0;
64 static hammer2_tid_t dst_inode = 0;
65 static const char *src_dirent = NULL;
66 static const char *dst_dirent = NULL;
67 static bool ForceOpt = false;
68 
69 static int
70 destroy_blockref(uint8_t type)
71 {
72 	bool failed = false;
73 	int i;
74 
75 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
76 		hammer2_volume_data_t voldata;
77 		hammer2_blockref_t broot;
78 		hammer2_off_t off;
79 		ssize_t ret;
80 
81 		memset(&broot, 0, sizeof(broot));
82 		broot.type = type;
83 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
84 		off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX;
85 		if (lseek(hammer2_get_root_volume_fd(),
86 		    off - hammer2_get_root_volume_offset(), SEEK_SET) == -1) {
87 			perror("lseek");
88 			return -1;
89 		}
90 
91 		ret = read(hammer2_get_root_volume_fd(), &voldata,
92 		    HAMMER2_VOLUME_BYTES);
93 		if (ret == HAMMER2_VOLUME_BYTES) {
94 			fprintf(stdout, "zone.%d %016jx\n",
95 			    i, (uintmax_t)broot.data_off);
96 			if (modify_blockref(&voldata, -1, &broot, NULL) == -1)
97 				failed = true;
98 		} else if (ret == -1) {
99 			perror("read");
100 			return -1;
101 		} else {
102 			fprintf(stderr, "Failed to read volume header\n");
103 			return -1;
104 		}
105 	}
106 
107 	return failed ? -1 : 0;
108 }
109 
110 static int
111 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
112     size_t *media_bytes)
113 {
114 	hammer2_off_t io_off, io_base;
115 	size_t bytes, io_bytes, boff;
116 	ssize_t ret;
117 	int fd;
118 
119 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
120 	if (bytes)
121 		bytes = (size_t)1 << bytes;
122 	if (media_bytes)
123 		*media_bytes = bytes;
124 
125 	if (!bytes)
126 		return 0;
127 
128 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
129 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
130 	boff = io_off - io_base;
131 
132 	io_bytes = HAMMER2_LBUFSIZE;
133 	while (io_bytes + boff < bytes)
134 		io_bytes <<= 1;
135 
136 	if (io_bytes > sizeof(*media)) {
137 		fprintf(stderr, "Bad I/O bytes\n");
138 		return -1;
139 	}
140 	fd = hammer2_get_volume_fd(io_off);
141 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
142 	    == -1) {
143 		perror("lseek");
144 		return -1;
145 	}
146 	ret = read(fd, media, io_bytes);
147 	if (ret == -1) {
148 		perror("read");
149 		return -1;
150 	} else if (ret != (ssize_t)io_bytes) {
151 		fprintf(stderr, "Failed to read media\n");
152 		return -1;
153 	}
154 	if (boff)
155 		memmove(media, (char *)media + boff, bytes);
156 
157 	return 0;
158 }
159 
160 static int
161 write_media(const hammer2_blockref_t *bref, const hammer2_media_data_t *media,
162     size_t media_bytes)
163 {
164 	hammer2_off_t io_off, io_base;
165 	char buf[HAMMER2_PBUFSIZE];
166 	size_t bytes, io_bytes, boff;
167 	ssize_t ret;
168 	int fd;
169 
170 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
171 	if (bytes)
172 		bytes = (size_t)1 << bytes;
173 	assert(bytes != 0);
174 	assert(bytes == media_bytes);
175 
176 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
177 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
178 	boff = io_off - io_base;
179 
180 	io_bytes = HAMMER2_LBUFSIZE;
181 	while (io_bytes + boff < bytes)
182 		io_bytes <<= 1;
183 
184 	if (io_bytes > sizeof(buf)) {
185 		fprintf(stderr, "Bad I/O bytes\n");
186 		return -1;
187 	}
188 	fd = hammer2_get_volume_fd(io_off);
189 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
190 	    == -1) {
191 		perror("lseek");
192 		return -1;
193 	}
194 	if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) {
195 		perror("read");
196 		return -1;
197 	}
198 
199 	memcpy(buf + boff, media, media_bytes);
200 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
201 	    == -1) {
202 		perror("lseek");
203 		return -1;
204 	}
205 	ret = write(fd, buf, io_bytes);
206 	if (ret == -1) {
207 		perror("write");
208 		return -1;
209 	} else if (ret != (ssize_t)io_bytes) {
210 		fprintf(stderr, "Failed to write media\n");
211 		return -1;
212 	}
213 	if (fsync(fd) == -1) {
214 		perror("fsync");
215 		return -1;
216 	}
217 
218 	return 0;
219 }
220 
221 static int
222 modify_blockref(const hammer2_volume_data_t *voldata, int bi,
223     hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref)
224 {
225 	hammer2_media_data_t media;
226 	hammer2_blockref_t *bscan;
227 	int i, bcount, namlen;
228 	size_t bytes;
229 
230 	if (read_media(bref, &media, &bytes) == -1)
231 		return -1;
232 
233 	switch (bref->type) {
234 	case HAMMER2_BREF_TYPE_INODE:
235 		if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
236 			bscan = &media.ipdata.u.blockset.blockref[0];
237 			bcount = HAMMER2_SET_COUNT;
238 		} else {
239 			bscan = NULL;
240 			bcount = 0;
241 		}
242 		if (src_inode && media.ipdata.meta.inum == src_inode)
243 			if (modify_inode(bref, &media, bytes) == -1)
244 				return -1;
245 		break;
246 	case HAMMER2_BREF_TYPE_INDIRECT:
247 		bscan = &media.npdata[0];
248 		bcount = bytes / sizeof(hammer2_blockref_t);
249 		break;
250 	case HAMMER2_BREF_TYPE_DIRENT:
251 		bscan = NULL;
252 		bcount = 0;
253 		namlen = bref->embed.dirent.namlen;
254 		if (src_dirent && namlen == strlen(src_dirent)) {
255 			if (namlen <= sizeof(bref->check.buf) &&
256 			    !memcmp(bref->check.buf, src_dirent, namlen)) {
257 				if (modify_dirent_embedded(bi, prev_bref) == -1)
258 					return -1;
259 			} else if (!memcmp(media.buf, src_dirent, namlen)) {
260 				if (modify_dirent(bi, prev_bref, bref, &media,
261 				    bytes) == -1)
262 					return -1;
263 			}
264 		}
265 		break;
266 	case HAMMER2_BREF_TYPE_VOLUME:
267 		bscan = &media.voldata.sroot_blockset.blockref[0];
268 		bcount = HAMMER2_SET_COUNT;
269 		break;
270 	default:
271 		bscan = NULL;
272 		bcount = 0;
273 		break;
274 	}
275 
276 	for (i = 0; i < bcount; ++i)
277 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
278 			if (modify_blockref(voldata, i, &bscan[i], bref) == -1)
279 				return -1;
280 	return 0;
281 }
282 
283 static int
284 modify_inode(const hammer2_blockref_t *bref,
285     hammer2_media_data_t *media, size_t media_bytes)
286 {
287 	assert(src_inode == media->ipdata.meta.inum);
288 
289 	if (ForceOpt) {
290 		media->ipdata.meta.inum = dst_inode;
291 		if (write_media(bref, media, media_bytes) == -1)
292 			return -1;
293 	}
294 
295 	printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "",
296 	    src_inode, dst_inode);
297 
298 	return 0;
299 }
300 
301 static int
302 modify_dirent_embedded(int bi, hammer2_blockref_t *prev_bref)
303 {
304 	hammer2_media_data_t bscan_media;
305 	hammer2_blockref_t *bscan;
306 	size_t bytes;
307 
308 	if (read_media(prev_bref, &bscan_media, &bytes) == -1)
309 		return -1;
310 	assert(bytes);
311 
312 	switch (prev_bref->type) {
313 	case HAMMER2_BREF_TYPE_INODE:
314 		bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
315 		break;
316 	case HAMMER2_BREF_TYPE_INDIRECT:
317 		bscan = &bscan_media.npdata[bi];
318 		break;
319 	default:
320 		assert(0);
321 		break;
322 	}
323 	assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent)));
324 
325 	if (strlen(dst_dirent) > sizeof(bscan->check.buf)) {
326 		fprintf(stderr, "embedded dirent %s (%d bytes) can't exceed "
327 		    "%lu bytes\n", dst_dirent, (int)strlen(dst_dirent),
328 		    sizeof(bscan->check.buf));
329 		return -1;
330 	}
331 
332 	if (ForceOpt) {
333 		memset(bscan->check.buf, 0, sizeof(bscan->check.buf));
334 		memcpy(bscan->check.buf, dst_dirent, strlen(dst_dirent));
335 		bscan->embed.dirent.namlen = strlen(dst_dirent);
336 		bscan->key = dirhash(dst_dirent, strlen(dst_dirent));
337 		if (write_media(prev_bref, &bscan_media, bytes) == -1)
338 			return -1;
339 	}
340 
341 	printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n",
342 	    ForceOpt ? "Modified " : "",
343 	    src_dirent, (int)strlen(src_dirent),
344 	    dst_dirent, (int)strlen(dst_dirent));
345 
346 	return 0;
347 }
348 
349 static int
350 modify_dirent(int bi, hammer2_blockref_t *prev_bref,
351     const hammer2_blockref_t *bref, hammer2_media_data_t *media,
352     size_t media_bytes)
353 {
354 	hammer2_media_data_t bscan_media;
355 	hammer2_blockref_t *bscan;
356 	size_t bytes;
357 
358 	assert(!memcmp(src_dirent, media->buf, strlen(src_dirent)));
359 	if (read_media(prev_bref, &bscan_media, &bytes) == -1)
360 		return -1;
361 	assert(bytes);
362 
363 	switch (prev_bref->type) {
364 	case HAMMER2_BREF_TYPE_INODE:
365 		bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
366 		break;
367 	case HAMMER2_BREF_TYPE_INDIRECT:
368 		bscan = &bscan_media.npdata[bi];
369 		break;
370 	default:
371 		assert(0);
372 		break;
373 	}
374 
375 	if (memcmp(bref, bscan, sizeof(*bref))) {
376 		fprintf(stderr, "Blockref contents mismatch\n");
377 		return -1;
378 	}
379 	if (strlen(dst_dirent) > sizeof(media->buf)) {
380 		fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n",
381 		    dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf));
382 		return -1;
383 	}
384 	if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) {
385 		fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n",
386 		    dst_dirent, (int)strlen(dst_dirent),
387 		    sizeof(bscan->check.buf));
388 		return -1;
389 	}
390 
391 	if (ForceOpt) {
392 		memset(media->buf, 0, sizeof(media->buf));
393 		memcpy(media->buf, dst_dirent, strlen(dst_dirent));
394 		bscan->embed.dirent.namlen = strlen(dst_dirent);
395 		bscan->key = dirhash(dst_dirent, strlen(dst_dirent));
396 		if (write_media(bref, media, media_bytes) == -1)
397 			return -1;
398 		if (write_media(prev_bref, &bscan_media, bytes) == -1) {
399 			memset(media->buf, 0, sizeof(media->buf));
400 			memcpy(media->buf, src_dirent, strlen(src_dirent));
401 			if (write_media(bref, media, media_bytes) == -1)
402 				return -1;
403 			return -1;
404 		}
405 	}
406 
407 	printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n",
408 	    ForceOpt ? "Modified " : "",
409 	    src_dirent, (int)strlen(src_dirent),
410 	    dst_dirent, (int)strlen(dst_dirent));
411 
412 	return 0;
413 }
414 
415 static int
416 init_args(int argc, char **argv, const char **devpathp)
417 {
418 	const char *devpath, *type;
419 
420 	*devpathp = devpath = argv[0];
421 	type = argv[1];
422 
423 	if (!strcmp(type, "inode")) {
424 		errno = 0;
425 		src_inode = strtoull(argv[2], NULL, 16);
426 		if (errno == ERANGE && src_inode == ULLONG_MAX) {
427 			perror("strtoull");
428 			return -1;
429 		}
430 		if (src_inode == 0) {
431 			fprintf(stderr, "Invalid src inode# %ju\n",
432 			    (uintmax_t)src_inode);
433 			return -1;
434 		}
435 		errno = 0;
436 		dst_inode = strtoull(argv[3], NULL, 16);
437 		if (errno == ERANGE && dst_inode == ULLONG_MAX) {
438 			perror("strtoull");
439 			return -1;
440 		}
441 		if (dst_inode == 0) {
442 			fprintf(stderr, "Invalid dst inode# %ju\n",
443 			    (uintmax_t)dst_inode);
444 			return -1;
445 		}
446 		if (src_inode == dst_inode) {
447 			fprintf(stderr, "src equals dst\n");
448 			return -1;
449 		}
450 		printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode,
451 		    (uintmax_t)dst_inode);
452 	} else if (!strcmp(type, "dirent")) {
453 		src_dirent = argv[2];
454 		if (strlen(src_dirent) > HAMMER2_PBUFSIZE) {
455 			fprintf(stderr, "src dirent too long\n");
456 			return -1;
457 		}
458 		dst_dirent = argv[3];
459 		if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) {
460 			fprintf(stderr, "dst dirent too long\n");
461 			return -1;
462 		}
463 		if (!strcmp(src_dirent, dst_dirent)) {
464 			fprintf(stderr, "src equals dst\n");
465 			return -1;
466 		}
467 		printf("%s %s %s\n", devpath, src_dirent, dst_dirent);
468 	} else {
469 		fprintf(stderr, "Invalid blockref type %s\n", type);
470 		return -1;
471 	}
472 
473 	return 0;
474 }
475 
476 int
477 main(int argc, char **argv)
478 {
479 	int ch;
480 	const char *binpath = argv[0];
481 	const char *devpath;
482 
483 	while ((ch = getopt(argc, argv, "f")) != -1) {
484 		switch(ch) {
485 		case 'f':
486 			ForceOpt = true;
487 			break;
488 		default:
489 			break;
490 		}
491 	}
492 	argc -= optind;
493 	argv += optind;
494 
495 	if (argc < 4) {
496 		fprintf(stderr, "%s [-f] special type src dst\n", binpath);
497 		exit(1);
498 	}
499 
500 	if (init_args(argc, argv, &devpath) == -1)
501 		exit(1);
502 
503 	hammer2_init_volumes(devpath, 0);
504 
505 	if (destroy_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1)
506 		exit(1);
507 
508 	hammer2_cleanup_volumes();
509 
510 	return 0;
511 }
512