xref: /dragonfly/sbin/fsck_hammer2/destroy.c (revision 2dac8a3e)
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
38 // ../../sys/libkern/icrc32.c ../hammer2/subs.c ./destroy.c -o destroy
39 
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <limits.h>
49 #include <errno.h>
50 #include <assert.h>
51 
52 #include <vfs/hammer2/hammer2_disk.h>
53 
54 #include "hammer2_subs.h"
55 
56 static int modify_blockref(int, const hammer2_volume_data_t *, int,
57     hammer2_blockref_t *, hammer2_blockref_t *);
58 static int modify_inode(int, const hammer2_blockref_t *,
59     hammer2_media_data_t *, size_t);
60 static int modify_dirent_embedded(int, int, hammer2_blockref_t *);
61 static int modify_dirent(int, int, hammer2_blockref_t *,
62     const hammer2_blockref_t *, hammer2_media_data_t *, size_t);
63 
64 static hammer2_tid_t src_inode = 0;
65 static hammer2_tid_t dst_inode = 0;
66 static const char *src_dirent = NULL;
67 static const char *dst_dirent = NULL;
68 static bool ForceOpt = false;
69 
70 static int
71 destroy_blockref(int fd, uint8_t type)
72 {
73 	bool failed = false;
74 	int i;
75 
76 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
77 		hammer2_volume_data_t voldata;
78 		hammer2_blockref_t broot;
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 		if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX,
85 		    SEEK_SET) == -1) {
86 			perror("lseek");
87 			return -1;
88 		}
89 
90 		ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
91 		if (ret == HAMMER2_PBUFSIZE) {
92 			fprintf(stdout, "zone.%d %016jx\n",
93 			    i, (uintmax_t)broot.data_off);
94 			if (modify_blockref(fd, &voldata, -1, &broot, NULL)
95 			    == -1)
96 				failed = true;
97 		} else if (ret == -1) {
98 			perror("read");
99 			return -1;
100 		} else {
101 			fprintf(stderr, "Failed to read volume header\n");
102 			return -1;
103 		}
104 	}
105 
106 	return failed ? -1 : 0;
107 }
108 
109 static int
110 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
111     size_t *media_bytes)
112 {
113 	hammer2_off_t io_off, io_base;
114 	size_t bytes, io_bytes, boff;
115 	ssize_t ret;
116 
117 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
118 	if (bytes)
119 		bytes = (size_t)1 << bytes;
120 	if (media_bytes)
121 		*media_bytes = bytes;
122 
123 	if (!bytes)
124 		return 0;
125 
126 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
127 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
128 	boff = io_off - io_base;
129 
130 	io_bytes = HAMMER2_MINIOSIZE;
131 	while (io_bytes + boff < bytes)
132 		io_bytes <<= 1;
133 
134 	if (io_bytes > sizeof(*media)) {
135 		fprintf(stderr, "Bad I/O bytes\n");
136 		return -1;
137 	}
138 	if (lseek(fd, io_base, SEEK_SET) == -1) {
139 		perror("lseek");
140 		return -1;
141 	}
142 	ret = read(fd, media, io_bytes);
143 	if (ret == -1) {
144 		perror("read");
145 		return -1;
146 	} else if (ret != (ssize_t)io_bytes) {
147 		fprintf(stderr, "Failed to read media\n");
148 		return -1;
149 	}
150 	if (boff)
151 		memmove(media, (char *)media + boff, bytes);
152 
153 	return 0;
154 }
155 
156 static int
157 write_media(int fd, const hammer2_blockref_t *bref,
158     const hammer2_media_data_t *media, size_t media_bytes)
159 {
160 	hammer2_off_t io_off, io_base;
161 	char buf[HAMMER2_PBUFSIZE];
162 	size_t bytes, io_bytes, boff;
163 	ssize_t ret;
164 
165 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
166 	if (bytes)
167 		bytes = (size_t)1 << bytes;
168 	assert(bytes != 0);
169 	assert(bytes == media_bytes);
170 
171 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
172 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
173 	boff = io_off - io_base;
174 
175 	io_bytes = HAMMER2_MINIOSIZE;
176 	while (io_bytes + boff < bytes)
177 		io_bytes <<= 1;
178 
179 	if (io_bytes > sizeof(buf)) {
180 		fprintf(stderr, "Bad I/O bytes\n");
181 		return -1;
182 	}
183 	if (lseek(fd, io_base, SEEK_SET) == -1) {
184 		perror("lseek");
185 		return -1;
186 	}
187 	if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) {
188 		perror("read");
189 		return -1;
190 	}
191 
192 	memcpy(buf + boff, media, media_bytes);
193 	if (lseek(fd, io_base, SEEK_SET) == -1) {
194 		perror("lseek");
195 		return -1;
196 	}
197 	ret = write(fd, buf, io_bytes);
198 	if (ret == -1) {
199 		perror("write");
200 		return -1;
201 	} else if (ret != (ssize_t)io_bytes) {
202 		fprintf(stderr, "Failed to write media\n");
203 		return -1;
204 	}
205 	if (fsync(fd) == -1) {
206 		perror("fsync");
207 		return -1;
208 	}
209 
210 	return 0;
211 }
212 
213 static int
214 modify_blockref(int fd, const hammer2_volume_data_t *voldata, int bi,
215     hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref)
216 {
217 	hammer2_media_data_t media;
218 	hammer2_blockref_t *bscan;
219 	int i, bcount, namlen;
220 	size_t bytes;
221 
222 	if (read_media(fd, bref, &media, &bytes) == -1)
223 		return -1;
224 
225 	switch (bref->type) {
226 	case HAMMER2_BREF_TYPE_INODE:
227 		if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
228 			bscan = &media.ipdata.u.blockset.blockref[0];
229 			bcount = HAMMER2_SET_COUNT;
230 		} else {
231 			bscan = NULL;
232 			bcount = 0;
233 		}
234 		if (src_inode && media.ipdata.meta.inum == src_inode)
235 			if (modify_inode(fd, bref, &media, bytes) == -1)
236 				return -1;
237 		break;
238 	case HAMMER2_BREF_TYPE_INDIRECT:
239 		bscan = &media.npdata[0];
240 		bcount = bytes / sizeof(hammer2_blockref_t);
241 		break;
242 	case HAMMER2_BREF_TYPE_DIRENT:
243 		bscan = NULL;
244 		bcount = 0;
245 		namlen = bref->embed.dirent.namlen;
246 		if (src_dirent && namlen == strlen(src_dirent)) {
247 			if (namlen <= sizeof(bref->check.buf) &&
248 			    !memcmp(bref->check.buf, src_dirent, namlen)) {
249 				if (modify_dirent_embedded(fd, bi, prev_bref)
250 				    == -1)
251 					return -1;
252 			} else if (!memcmp(media.buf, src_dirent, namlen)) {
253 				if (modify_dirent(fd, bi, prev_bref, bref,
254 				    &media, bytes) == -1)
255 					return -1;
256 			}
257 		}
258 		break;
259 	case HAMMER2_BREF_TYPE_VOLUME:
260 		bscan = &media.voldata.sroot_blockset.blockref[0];
261 		bcount = HAMMER2_SET_COUNT;
262 		break;
263 	default:
264 		bscan = NULL;
265 		bcount = 0;
266 		break;
267 	}
268 
269 	for (i = 0; i < bcount; ++i)
270 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
271 			if (modify_blockref(fd, voldata, i, &bscan[i], bref)
272 			    == -1)
273 				return -1;
274 	return 0;
275 }
276 
277 static int
278 modify_inode(int fd, const hammer2_blockref_t *bref,
279     hammer2_media_data_t *media, size_t media_bytes)
280 {
281 	assert(src_inode == media->ipdata.meta.inum);
282 
283 	if (ForceOpt) {
284 		media->ipdata.meta.inum = dst_inode;
285 		if (write_media(fd, bref, media, media_bytes) == -1)
286 			return -1;
287 	}
288 
289 	printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "",
290 	    src_inode, dst_inode);
291 
292 	return 0;
293 }
294 
295 static int
296 modify_dirent_embedded(int fd, int bi, hammer2_blockref_t *prev_bref)
297 {
298 	hammer2_media_data_t bscan_media;
299 	hammer2_blockref_t *bscan;
300 	size_t bytes;
301 
302 	if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1)
303 		return -1;
304 	assert(bytes);
305 
306 	switch (prev_bref->type) {
307 	case HAMMER2_BREF_TYPE_INODE:
308 		bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
309 		break;
310 	case HAMMER2_BREF_TYPE_INDIRECT:
311 		bscan = &bscan_media.npdata[bi];
312 		break;
313 	default:
314 		assert(0);
315 		break;
316 	}
317 	assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent)));
318 
319 	if (strlen(dst_dirent) > sizeof(bscan->check.buf)) {
320 		fprintf(stderr, "embedded dirent %s (%d bytes) can't exceed "
321 		    "%lu bytes\n", dst_dirent, (int)strlen(dst_dirent),
322 		    sizeof(bscan->check.buf));
323 		return -1;
324 	}
325 
326 	if (ForceOpt) {
327 		memset(bscan->check.buf, 0, sizeof(bscan->check.buf));
328 		memcpy(bscan->check.buf, dst_dirent, strlen(dst_dirent));
329 		bscan->embed.dirent.namlen = strlen(dst_dirent);
330 		bscan->key = dirhash((const unsigned char*)dst_dirent,
331 		    strlen(dst_dirent));
332 		if (write_media(fd, prev_bref, &bscan_media, bytes) == -1)
333 			return -1;
334 	}
335 
336 	printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n",
337 	    ForceOpt ? "Modified " : "",
338 	    src_dirent, (int)strlen(src_dirent),
339 	    dst_dirent, (int)strlen(dst_dirent));
340 
341 	return 0;
342 }
343 
344 static int
345 modify_dirent(int fd, int bi, hammer2_blockref_t *prev_bref,
346     const hammer2_blockref_t *bref, hammer2_media_data_t *media,
347     size_t media_bytes)
348 {
349 	hammer2_media_data_t bscan_media;
350 	hammer2_blockref_t *bscan;
351 	size_t bytes;
352 
353 	assert(!memcmp(src_dirent, media->buf, strlen(src_dirent)));
354 	if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1)
355 		return -1;
356 	assert(bytes);
357 
358 	switch (prev_bref->type) {
359 	case HAMMER2_BREF_TYPE_INODE:
360 		bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
361 		break;
362 	case HAMMER2_BREF_TYPE_INDIRECT:
363 		bscan = &bscan_media.npdata[bi];
364 		break;
365 	default:
366 		assert(0);
367 		break;
368 	}
369 
370 	if (memcmp(bref, bscan, sizeof(*bref))) {
371 		fprintf(stderr, "Blockref contents mismatch\n");
372 		return -1;
373 	}
374 	if (strlen(dst_dirent) > sizeof(media->buf)) {
375 		fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n",
376 		    dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf));
377 		return -1;
378 	}
379 	if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) {
380 		fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n",
381 		    dst_dirent, (int)strlen(dst_dirent),
382 		    sizeof(bscan->check.buf));
383 		return -1;
384 	}
385 
386 	if (ForceOpt) {
387 		memset(media->buf, 0, sizeof(media->buf));
388 		memcpy(media->buf, dst_dirent, strlen(dst_dirent));
389 		bscan->embed.dirent.namlen = strlen(dst_dirent);
390 		bscan->key = dirhash((const unsigned char*)dst_dirent,
391 		    strlen(dst_dirent));
392 		if (write_media(fd, bref, media, media_bytes) == -1)
393 			return -1;
394 		if (write_media(fd, prev_bref, &bscan_media, bytes) == -1) {
395 			memset(media->buf, 0, sizeof(media->buf));
396 			memcpy(media->buf, src_dirent, strlen(src_dirent));
397 			if (write_media(fd, bref, media, media_bytes) == -1)
398 				return -1;
399 			return -1;
400 		}
401 	}
402 
403 	printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n",
404 	    ForceOpt ? "Modified " : "",
405 	    src_dirent, (int)strlen(src_dirent),
406 	    dst_dirent, (int)strlen(dst_dirent));
407 
408 	return 0;
409 }
410 
411 static int
412 init_args(int argc, char **argv, const char **devpathp)
413 {
414 	const char *devpath, *type;
415 
416 	*devpathp = devpath = argv[0];
417 	type = argv[1];
418 
419 	if (!strcmp(type, "inode")) {
420 		errno = 0;
421 		src_inode = strtoull(argv[2], NULL, 16);
422 		if (errno == ERANGE && src_inode == ULLONG_MAX) {
423 			perror("strtoull");
424 			return -1;
425 		}
426 		if (src_inode == 0) {
427 			fprintf(stderr, "Invalid src inode# %ju\n",
428 			    (uintmax_t)src_inode);
429 			return -1;
430 		}
431 		errno = 0;
432 		dst_inode = strtoull(argv[3], NULL, 16);
433 		if (errno == ERANGE && dst_inode == ULLONG_MAX) {
434 			perror("strtoull");
435 			return -1;
436 		}
437 		if (dst_inode == 0) {
438 			fprintf(stderr, "Invalid dst inode# %ju\n",
439 			    (uintmax_t)dst_inode);
440 			return -1;
441 		}
442 		if (src_inode == dst_inode) {
443 			fprintf(stderr, "src equals dst\n");
444 			return -1;
445 		}
446 		printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode,
447 		    (uintmax_t)dst_inode);
448 	} else if (!strcmp(type, "dirent")) {
449 		src_dirent = argv[2];
450 		if (strlen(src_dirent) > HAMMER2_PBUFSIZE) {
451 			fprintf(stderr, "src dirent too long\n");
452 			return -1;
453 		}
454 		dst_dirent = argv[3];
455 		if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) {
456 			fprintf(stderr, "dst dirent too long\n");
457 			return -1;
458 		}
459 		if (!strcmp(src_dirent, dst_dirent)) {
460 			fprintf(stderr, "src equals dst\n");
461 			return -1;
462 		}
463 		printf("%s %s %s\n", devpath, src_dirent, dst_dirent);
464 	} else {
465 		fprintf(stderr, "Invalid blockref type %s\n", type);
466 		return -1;
467 	}
468 
469 	return 0;
470 }
471 
472 int
473 main(int argc, char **argv)
474 {
475 	struct stat st;
476 	int ch, fd;
477 	const char *binpath = argv[0];
478 	const char *devpath;
479 
480 	while ((ch = getopt(argc, argv, "f")) != -1) {
481 		switch(ch) {
482 		case 'f':
483 			ForceOpt = true;
484 			break;
485 		default:
486 			break;
487 		}
488 	}
489 	argc -= optind;
490 	argv += optind;
491 
492 	if (argc < 4) {
493 		fprintf(stderr, "%s [-f] special type src dst\n", binpath);
494 		exit(1);
495 	}
496 
497 	if (init_args(argc, argv, &devpath) == -1)
498 		exit(1);
499 
500 	fd = open(devpath, O_RDWR);
501 	if (fd == -1) {
502 		perror("open");
503 		exit(1);
504 	}
505 
506 	if (fstat(fd, &st) == -1) {
507 		perror("fstat");
508 		exit(1);
509 	}
510 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
511 		fprintf(stderr, "Unsupported file type\n");
512 		exit(1);
513 	}
514 
515 	if (destroy_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1)
516 		exit(1);
517 
518 	close(fd);
519 
520 	return 0;
521 }
522