1/*-
2 * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * Copyright (c) 1982, 1986, 1989, 1993
27 *	The Regents of the University of California.  All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 *    notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 *    notice, this list of conditions and the following disclaimer in the
36 *    documentation and/or other materials provided with the distribution.
37 * 4. Neither the name of the University nor the names of its contributors
38 *    may be used to endorse or promote products derived from this software
39 *    without specific prior written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 */
53
54#include <sys/cdefs.h>
55__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/gjournal.c 359754 2020-04-09 20:38:36Z kevans $");
56
57#include <sys/param.h>
58#include <sys/disklabel.h>
59#include <sys/mount.h>
60#include <sys/stat.h>
61
62#include <ufs/ufs/ufsmount.h>
63#include <ufs/ufs/dinode.h>
64#include <ufs/ffs/fs.h>
65
66#include <stdio.h>
67#include <stdlib.h>
68#include <stdint.h>
69#include <libufs.h>
70#include <strings.h>
71#include <err.h>
72#include <assert.h>
73
74#include "fsck.h"
75
76struct cgchain {
77	union {
78		struct cg cgcu_cg;
79		char cgcu_buf[MAXBSIZE];
80	} cgc_union;
81	int	cgc_busy;
82	int	cgc_dirty;
83	LIST_ENTRY(cgchain) cgc_next;
84};
85#define cgc_cg	cgc_union.cgcu_cg
86
87#define	MAX_CACHED_CGS	1024
88static unsigned ncgs = 0;
89static LIST_HEAD(, cgchain) cglist = LIST_HEAD_INITIALIZER(cglist);
90
91static const char *devnam;
92static struct uufsd *disk = NULL;
93static struct fs *fs = NULL;
94
95static void putcgs(void);
96
97/*
98 * Return cylinder group from the cache or load it if it is not in the
99 * cache yet.
100 * Don't cache more than MAX_CACHED_CGS cylinder groups.
101 */
102static struct cgchain *
103getcg(int cg)
104{
105	struct cgchain *cgc;
106
107	assert(disk != NULL && fs != NULL);
108	LIST_FOREACH(cgc, &cglist, cgc_next) {
109		if (cgc->cgc_cg.cg_cgx == cg) {
110			//printf("%s: Found cg=%d\n", __func__, cg);
111			return (cgc);
112		}
113	}
114	/*
115	 * Our cache is full? Let's clean it up.
116	 */
117	if (ncgs >= MAX_CACHED_CGS) {
118		//printf("%s: Flushing CGs.\n", __func__);
119		putcgs();
120	}
121	cgc = malloc(sizeof(*cgc));
122	if (cgc == NULL) {
123		/*
124		 * Cannot allocate memory?
125		 * Let's put all currently loaded and not busy cylinder groups
126		 * on disk and try again.
127		 */
128		//printf("%s: No memory, flushing CGs.\n", __func__);
129		putcgs();
130		cgc = malloc(sizeof(*cgc));
131		if (cgc == NULL)
132			err(1, "malloc(%zu)", sizeof(*cgc));
133	}
134	if (cgread1(disk, cg) == -1)
135		err(1, "cgread1(%d)", cg);
136	bcopy(&disk->d_cg, &cgc->cgc_cg, sizeof(cgc->cgc_union));
137	cgc->cgc_busy = 0;
138	cgc->cgc_dirty = 0;
139	LIST_INSERT_HEAD(&cglist, cgc, cgc_next);
140	ncgs++;
141	//printf("%s: Read cg=%d\n", __func__, cg);
142	return (cgc);
143}
144
145/*
146 * Mark cylinder group as dirty - it will be written back on putcgs().
147 */
148static void
149dirtycg(struct cgchain *cgc)
150{
151
152	cgc->cgc_dirty = 1;
153}
154
155/*
156 * Mark cylinder group as busy - it will not be freed on putcgs().
157 */
158static void
159busycg(struct cgchain *cgc)
160{
161
162	cgc->cgc_busy = 1;
163}
164
165/*
166 * Unmark the given cylinder group as busy.
167 */
168static void
169unbusycg(struct cgchain *cgc)
170{
171
172	cgc->cgc_busy = 0;
173}
174
175/*
176 * Write back all dirty cylinder groups.
177 * Free all non-busy cylinder groups.
178 */
179static void
180putcgs(void)
181{
182	struct cgchain *cgc, *cgc2;
183
184	assert(disk != NULL && fs != NULL);
185	LIST_FOREACH_SAFE(cgc, &cglist, cgc_next, cgc2) {
186		if (cgc->cgc_busy)
187			continue;
188		LIST_REMOVE(cgc, cgc_next);
189		ncgs--;
190		if (cgc->cgc_dirty) {
191			bcopy(&cgc->cgc_cg, &disk->d_cg,
192			    sizeof(cgc->cgc_union));
193			if (cgwrite1(disk, cgc->cgc_cg.cg_cgx) == -1)
194				err(1, "cgwrite1(%d)", cgc->cgc_cg.cg_cgx);
195			//printf("%s: Wrote cg=%d\n", __func__,
196			//    cgc->cgc_cg.cg_cgx);
197		}
198		free(cgc);
199	}
200}
201
202#if 0
203/*
204 * Free all non-busy cylinder groups without storing the dirty ones.
205 */
206static void
207cancelcgs(void)
208{
209	struct cgchain *cgc;
210
211	assert(disk != NULL && fs != NULL);
212	while ((cgc = LIST_FIRST(&cglist)) != NULL) {
213		if (cgc->cgc_busy)
214			continue;
215		LIST_REMOVE(cgc, cgc_next);
216		//printf("%s: Canceled cg=%d\n", __func__, cgc->cgc_cg.cg_cgx);
217		free(cgc);
218	}
219}
220#endif
221
222/*
223 * Open the given provider, load superblock.
224 */
225static void
226opendisk(void)
227{
228	if (disk != NULL)
229		return;
230	disk = malloc(sizeof(*disk));
231	if (disk == NULL)
232		err(1, "malloc(%zu)", sizeof(*disk));
233	if (ufs_disk_fillout(disk, devnam) == -1) {
234		err(1, "ufs_disk_fillout(%s) failed: %s", devnam,
235		    disk->d_error);
236	}
237	fs = &disk->d_fs;
238}
239
240/*
241 * Mark file system as clean, write the super-block back, close the disk.
242 */
243static void
244closedisk(void)
245{
246
247	fs->fs_clean = 1;
248	if (sbwrite(disk, 0) == -1)
249		err(1, "sbwrite(%s)", devnam);
250	if (ufs_disk_close(disk) == -1)
251		err(1, "ufs_disk_close(%s)", devnam);
252	free(disk);
253	disk = NULL;
254	fs = NULL;
255}
256
257static void
258blkfree(ufs2_daddr_t bno, long size)
259{
260	struct cgchain *cgc;
261	struct cg *cgp;
262	ufs1_daddr_t fragno, cgbno;
263	int i, cg, blk, frags, bbase;
264	u_int8_t *blksfree;
265
266	cg = dtog(fs, bno);
267	cgc = getcg(cg);
268	dirtycg(cgc);
269	cgp = &cgc->cgc_cg;
270	cgbno = dtogd(fs, bno);
271	blksfree = cg_blksfree(cgp);
272	if (size == fs->fs_bsize) {
273		fragno = fragstoblks(fs, cgbno);
274		if (!ffs_isfreeblock(fs, blksfree, fragno))
275			assert(!"blkfree: freeing free block");
276		ffs_setblock(fs, blksfree, fragno);
277		ffs_clusteracct(fs, cgp, fragno, 1);
278		cgp->cg_cs.cs_nbfree++;
279		fs->fs_cstotal.cs_nbfree++;
280		fs->fs_cs(fs, cg).cs_nbfree++;
281	} else {
282		bbase = cgbno - fragnum(fs, cgbno);
283		/*
284		 * decrement the counts associated with the old frags
285		 */
286		blk = blkmap(fs, blksfree, bbase);
287		ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
288		/*
289		 * deallocate the fragment
290		 */
291		frags = numfrags(fs, size);
292		for (i = 0; i < frags; i++) {
293			if (isset(blksfree, cgbno + i))
294				assert(!"blkfree: freeing free frag");
295			setbit(blksfree, cgbno + i);
296		}
297		cgp->cg_cs.cs_nffree += i;
298		fs->fs_cstotal.cs_nffree += i;
299		fs->fs_cs(fs, cg).cs_nffree += i;
300		/*
301		 * add back in counts associated with the new frags
302		 */
303		blk = blkmap(fs, blksfree, bbase);
304		ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
305		/*
306		 * if a complete block has been reassembled, account for it
307		 */
308		fragno = fragstoblks(fs, bbase);
309		if (ffs_isblock(fs, blksfree, fragno)) {
310			cgp->cg_cs.cs_nffree -= fs->fs_frag;
311			fs->fs_cstotal.cs_nffree -= fs->fs_frag;
312			fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
313			ffs_clusteracct(fs, cgp, fragno, 1);
314			cgp->cg_cs.cs_nbfree++;
315			fs->fs_cstotal.cs_nbfree++;
316			fs->fs_cs(fs, cg).cs_nbfree++;
317		}
318	}
319}
320
321/*
322 * Recursively free all indirect blocks.
323 */
324static void
325freeindir(ufs2_daddr_t blk, int level)
326{
327	char sblks[MAXBSIZE];
328	ufs2_daddr_t *blks;
329	int i;
330
331	if (bread(disk, fsbtodb(fs, blk), (void *)&sblks, (size_t)fs->fs_bsize) == -1)
332		err(1, "bread: %s", disk->d_error);
333	blks = (ufs2_daddr_t *)&sblks;
334	for (i = 0; i < NINDIR(fs); i++) {
335		if (blks[i] == 0)
336			break;
337		if (level == 0)
338			blkfree(blks[i], fs->fs_bsize);
339		else
340			freeindir(blks[i], level - 1);
341	}
342	blkfree(blk, fs->fs_bsize);
343}
344
345#define	dblksize(fs, dino, lbn) \
346	((dino)->di_size >= smalllblktosize(fs, (lbn) + 1) \
347	    ? (fs)->fs_bsize \
348	    : fragroundup(fs, blkoff(fs, (dino)->di_size)))
349
350/*
351 * Free all blocks associated with the given inode.
352 */
353static void
354clear_inode(struct ufs2_dinode *dino)
355{
356	ufs2_daddr_t bn;
357	int extblocks, i, level;
358	off_t osize;
359	long bsize;
360
361	extblocks = 0;
362	if (fs->fs_magic == FS_UFS2_MAGIC && dino->di_extsize > 0)
363		extblocks = btodb(fragroundup(fs, dino->di_extsize));
364	/* deallocate external attributes blocks */
365	if (extblocks > 0) {
366		osize = dino->di_extsize;
367		dino->di_blocks -= extblocks;
368		dino->di_extsize = 0;
369		for (i = 0; i < NXADDR; i++) {
370			if (dino->di_extb[i] == 0)
371				continue;
372			blkfree(dino->di_extb[i], sblksize(fs, osize, i));
373		}
374	}
375#define	SINGLE	0	/* index of single indirect block */
376#define	DOUBLE	1	/* index of double indirect block */
377#define	TRIPLE	2	/* index of triple indirect block */
378	/* deallocate indirect blocks */
379	for (level = SINGLE; level <= TRIPLE; level++) {
380		if (dino->di_ib[level] == 0)
381			break;
382		freeindir(dino->di_ib[level], level);
383	}
384	/* deallocate direct blocks and fragments */
385	for (i = 0; i < NDADDR; i++) {
386		bn = dino->di_db[i];
387		if (bn == 0)
388			continue;
389		bsize = dblksize(fs, dino, i);
390		blkfree(bn, bsize);
391	}
392}
393
394void
395gjournal_check(const char *filesys)
396{
397	struct ufs2_dinode *dino;
398	void *p;
399	struct cgchain *cgc;
400	struct cg *cgp;
401	uint8_t *inosused;
402	ino_t cino, ino;
403	int cg, mode;
404
405	devnam = filesys;
406	opendisk();
407	/* Are there any unreferenced inodes in this file system? */
408	if (fs->fs_unrefs == 0) {
409		//printf("No unreferenced inodes.\n");
410		closedisk();
411		return;
412	}
413
414	for (cg = 0; cg < fs->fs_ncg; cg++) {
415		/* Show progress if requested. */
416		if (got_siginfo) {
417			printf("%s: phase j: cyl group %d of %d (%d%%)\n",
418			    cdevname, cg, fs->fs_ncg, cg * 100 / fs->fs_ncg);
419			got_siginfo = 0;
420		}
421		if (got_sigalarm) {
422			setproctitle("%s pj %d%%", cdevname,
423			     cg * 100 / fs->fs_ncg);
424			got_sigalarm = 0;
425		}
426		cgc = getcg(cg);
427		cgp = &cgc->cgc_cg;
428		/* Are there any unreferenced inodes in this cylinder group? */
429		if (cgp->cg_unrefs == 0)
430			continue;
431		//printf("Analizing cylinder group %d (count=%d)\n", cg, cgp->cg_unrefs);
432		/*
433		 * We are going to modify this cylinder group, so we want it to
434		 * be written back.
435		 */
436		dirtycg(cgc);
437		/* We don't want it to be freed in the meantime. */
438		busycg(cgc);
439		inosused = cg_inosused(cgp);
440		/*
441		 * Now go through the list of all inodes in this cylinder group
442		 * to find unreferenced ones.
443		 */
444		for (cino = 0; cino < fs->fs_ipg; cino++) {
445			ino = fs->fs_ipg * cg + cino;
446			/* Unallocated? Skip it. */
447			if (isclr(inosused, cino))
448				continue;
449			if (getino(disk, &p, ino, &mode) == -1)
450				err(1, "getino(cg=%d ino=%ju)",
451				    cg, (uintmax_t)ino);
452			dino = p;
453			/* Not a regular file nor directory? Skip it. */
454			if (!S_ISREG(dino->di_mode) && !S_ISDIR(dino->di_mode))
455				continue;
456			/* Has reference(s)? Skip it. */
457			if (dino->di_nlink > 0)
458				continue;
459			//printf("Clearing inode=%d (size=%jd)\n", ino, (intmax_t)dino->di_size);
460			/* Free inode's blocks. */
461			clear_inode(dino);
462			/* Deallocate it. */
463			clrbit(inosused, cino);
464			/* Update position of last used inode. */
465			if (ino < cgp->cg_irotor)
466				cgp->cg_irotor = ino;
467			/* Update statistics. */
468			cgp->cg_cs.cs_nifree++;
469			fs->fs_cs(fs, cg).cs_nifree++;
470			fs->fs_cstotal.cs_nifree++;
471			cgp->cg_unrefs--;
472			fs->fs_unrefs--;
473			/* If this is directory, update related statistics. */
474			if (S_ISDIR(dino->di_mode)) {
475				cgp->cg_cs.cs_ndir--;
476				fs->fs_cs(fs, cg).cs_ndir--;
477				fs->fs_cstotal.cs_ndir--;
478			}
479			/* Zero-fill the inode. */
480			*dino = ufs2_zino;
481			/* Write the inode back. */
482			if (putino(disk) == -1)
483				err(1, "putino(cg=%d ino=%ju)",
484				    cg, (uintmax_t)ino);
485			if (cgp->cg_unrefs == 0) {
486				//printf("No more unreferenced inodes in cg=%d.\n", cg);
487				break;
488			}
489		}
490		/*
491		 * We don't need this cylinder group anymore, so feel free to
492		 * free it if needed.
493		 */
494		unbusycg(cgc);
495		/*
496		 * If there are no more unreferenced inodes, there is no need to
497		 * check other cylinder groups.
498		 */
499		if (fs->fs_unrefs == 0) {
500			//printf("No more unreferenced inodes (cg=%d/%d).\n", cg,
501			//    fs->fs_ncg);
502			break;
503		}
504	}
505	/* Write back modified cylinder groups. */
506	putcgs();
507	/* Write back updated statistics and super-block. */
508	closedisk();
509}
510