summaryrefslogtreecommitdiffstats
path: root/libexec/lfs_cleanerd
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:39:25 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:39:25 +0000
commit7d07d2de2f52d4e2eba169e5563165309a795128 (patch)
treec3590f60f61233b4a571cfe3bfc08f6ab6591c88 /libexec/lfs_cleanerd
parentf9ab90d9d6d02989a075d0f0074496d5b1045e4b (diff)
downloadFreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.zip
FreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.tar.gz
BSD 4.4 Lite Libexec Sources
Diffstat (limited to 'libexec/lfs_cleanerd')
-rw-r--r--libexec/lfs_cleanerd/Makefile10
-rw-r--r--libexec/lfs_cleanerd/clean.h168
-rw-r--r--libexec/lfs_cleanerd/cleanerd.c498
-rw-r--r--libexec/lfs_cleanerd/lfs_cleanerd.877
-rw-r--r--libexec/lfs_cleanerd/library.c671
-rw-r--r--libexec/lfs_cleanerd/misc.c94
-rw-r--r--libexec/lfs_cleanerd/print.c218
7 files changed, 1736 insertions, 0 deletions
diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile
new file mode 100644
index 0000000..6993cf7
--- /dev/null
+++ b/libexec/lfs_cleanerd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= lfs_cleanerd
+CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} -DDIAGNOSTIC
+MAN8= lfs_cleanerd.0
+SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c
+
+.PATH: /sys/ufs/lfs
+
+.include <bsd.prog.mk>
diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h
new file mode 100644
index 0000000..0dd8775
--- /dev/null
+++ b/libexec/lfs_cleanerd/clean.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clean.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * The LFS user-level library will be used when writing cleaners and
+ * checkers for LFS file systems. It will have facilities for finding
+ * and parsing LFS segments.
+ */
+
+#define DUMP_SUM_HEADER 0x0001
+#define DUMP_INODE_ADDRS 0x0002
+#define DUMP_FINFOS 0x0004
+#define DUMP_ALL 0xFFFF
+
+#define IFILE_NAME "ifile"
+
+/*
+ * Cleaner parameters
+ * BUSY_LIM: lower bound of the number of segments currently available
+ * as a percentage of the total number of free segments possibly
+ * available.
+ * IDLE_LIM: Same as BUSY_LIM but used when the system is idle.
+ * MIN_SEGS: Minimum number of segments you should always have.
+ * I have no idea what this should be, but it should probably
+ * be a function of lfsp.
+ * NUM_TO_CLEAN: Number of segments to clean at once. Again, this
+ * should probably be based on the file system size and how
+ * full or empty the segments being cleaned are.
+ */
+
+#define BUSY_LIM 0.50
+#define IDLE_LIM 0.90
+
+#define MIN_SEGS(lfsp) (3)
+#define NUM_TO_CLEAN(fsp) (1)
+
+#define MAXLOADS 3
+#define ONE_MIN 0
+#define FIVE_MIN 1
+#define FIFTEEN_MIN 2
+
+typedef struct fs_info {
+ struct statfs *fi_statfsp; /* fsstat info from getfsstat */
+ struct lfs fi_lfs; /* superblock */
+ CLEANERINFO *fi_cip; /* Cleaner info from ifile */
+ SEGUSE *fi_segusep; /* segment usage table (from ifile) */
+ IFILE *fi_ifilep; /* ifile table (from ifile) */
+ u_long fi_daddr_shift; /* shift to get byte offset of daddr */
+ u_long fi_ifile_count; /* # entries in the ifile table */
+ off_t fi_ifile_length; /* length of the ifile */
+} FS_INFO;
+
+/*
+ * XXX: size (in bytes) of a segment
+ * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize?
+ */
+#define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift)
+
+/* daddr -> byte offset */
+#define datobyte(fs, da) ((da) << (fs)->fi_daddr_shift)
+#define bytetoda(fs, byte) ((byte) >> (fs)->fi_daddr_shift)
+
+#define CLEANSIZE(fsp) (fsp->fi_lfs.lfs_cleansz << fsp->fi_lfs.lfs_bshift)
+#define SEGTABSIZE(fsp) (fsp->fi_lfs.lfs_segtabsz << fsp->fi_lfs.lfs_bshift)
+
+#define IFILE_ENTRY(fs, if, i) \
+ ((IFILE *)((caddr_t)(if) + ((i) / (fs)->lfs_ifpb << (fs)->lfs_bshift)) \
+ + (i) % (fs)->lfs_ifpb)
+
+#define SEGUSE_ENTRY(fs, su, i) \
+ ((SEGUSE *)((caddr_t)(su) + (fs)->lfs_bsize * ((i) / (fs)->lfs_sepb)) +\
+ (i) % (fs)->lfs_sepb)
+
+__BEGIN_DECLS
+int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **));
+void err __P((const int, const char *, ...));
+int fs_getmntinfo __P((struct statfs **, char *, int));
+int get __P((int, off_t, void *, size_t));
+FS_INFO *get_fs_info __P((struct statfs *, int));
+int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *));
+int mmap_segment __P((FS_INFO *, int, caddr_t *, int));
+void munmap_segment __P((FS_INFO *, caddr_t, int));
+void reread_fs_info __P((FS_INFO *, int));
+void toss __P((void *, int *, size_t,
+ int (*)(const void *, const void *, const void *), void *));
+
+/*
+ * USEFUL DEBUGGING FUNCTIONS:
+ */
+#ifdef VERBOSE
+#define PRINT_FINFO(fp, ip) { \
+ (void)printf(" %s %s%d version %d nblocks %d\n", \
+ (ip)->if_version > (fp)->fi_version ? "TOSSING" : "KEEPING", \
+ "FINFO for inode: ", (fp)->fi_ino, \
+ (fp)->fi_version, (fp)->fi_nblocks); \
+ fflush(stdout); \
+}
+
+#define PRINT_INODE(b, bip) { \
+ (void) printf("\t%s inode: %d daddr: 0x%lx create: %s\n", \
+ b ? "KEEPING" : "TOSSING", (bip)->bi_inode, (bip)->bi_daddr, \
+ ctime((time_t *)&(bip)->bi_segcreate)); \
+ fflush(stdout); \
+}
+
+#define PRINT_BINFO(bip) { \
+ (void)printf("\tinode: %d lbn: %d daddr: 0x%lx create: %s\n", \
+ (bip)->bi_inode, (bip)->bi_lbn, (bip)->bi_daddr, \
+ ctime((time_t *)&(bip)->bi_segcreate)); \
+ fflush(stdout); \
+}
+
+#define PRINT_SEGUSE(sup, n) { \
+ (void)printf("Segment %d nbytes=%lu\tflags=%c%c%c ninos=%d nsums=%d lastmod: %s\n", \
+ n, (sup)->su_nbytes, \
+ (sup)->su_flags & SEGUSE_DIRTY ? 'D' : 'C', \
+ (sup)->su_flags & SEGUSE_ACTIVE ? 'A' : ' ', \
+ (sup)->su_flags & SEGUSE_SUPERBLOCK ? 'S' : ' ', \
+ (sup)->su_ninos, (sup)->su_nsums, \
+ ctime((time_t *)&(sup)->su_lastmod)); \
+ fflush(stdout); \
+}
+
+void dump_super __P((struct lfs *));
+void dump_cleaner_info __P((void *));
+void print_SEGSUM __P(( struct lfs *, SEGSUM *));
+void print_CLEANERINFO __P((CLEANERINFO *));
+#else
+#define PRINT_FINFO(fp, ip)
+#define PRINT_INODE(b, bip)
+#define PRINT_BINFO(bip)
+#define PRINT_SEGUSE(sup, n)
+#define dump_cleaner_info(cip)
+#define dump_super(lfsp)
+#endif
+__END_DECLS
diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c
new file mode 100644
index 0000000..515d2d5
--- /dev/null
+++ b/libexec/lfs_cleanerd/cleanerd.c
@@ -0,0 +1,498 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "clean.h"
+char *special = "cleanerd";
+int do_small = 0;
+int do_mmap = 0;
+struct cleaner_stats {
+ int blocks_read;
+ int blocks_written;
+ int segs_cleaned;
+ int segs_empty;
+ int segs_error;
+} cleaner_stats;
+
+struct seglist {
+ int sl_id; /* segment number */
+ int sl_cost; /* cleaning cost */
+ char sl_empty; /* is segment empty */
+};
+
+struct tossstruct {
+ struct lfs *lfs;
+ int seg;
+};
+
+/* function prototypes for system calls; not sure where they should go */
+int lfs_segwait __P((fsid_t *, struct timeval *));
+int lfs_segclean __P((fsid_t *, u_long));
+int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int));
+int lfs_markv __P((fsid_t *, BLOCK_INFO *, int));
+
+/* function prototypes */
+int bi_tossold __P((const void *, const void *, const void *));
+int choose_segments __P((FS_INFO *, struct seglist *,
+ int (*)(FS_INFO *, SEGUSE *)));
+void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *)));
+int clean_loop __P((FS_INFO *));
+int clean_segment __P((FS_INFO *, int));
+int cost_benefit __P((FS_INFO *, SEGUSE *));
+int cost_compare __P((const void *, const void *));
+void sig_report __P((int));
+
+/*
+ * Cleaning Cost Functions:
+ *
+ * These return the cost of cleaning a segment. The higher the cost value
+ * the better it is to clean the segment, so empty segments have the highest
+ * cost. (It is probably better to think of this as a priority value
+ * instead).
+ *
+ * This is the cost-benefit policy simulated and described in Rosenblum's
+ * 1991 SOSP paper.
+ */
+
+int
+cost_benefit(fsp, su)
+ FS_INFO *fsp; /* file system information */
+ SEGUSE *su;
+{
+ struct lfs *lfsp;
+ struct timeval t;
+ int age;
+ int live;
+
+ gettimeofday(&t, NULL);
+
+ live = su->su_nbytes;
+ age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod;
+
+ lfsp = &fsp->fi_lfs;
+ if (live == 0)
+ return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
+ else {
+ /*
+ * from lfsSegUsage.c (Mendel's code).
+ * priority calculation is done using INTEGER arithmetic.
+ * sizes are in BLOCKS (that is why we use lblkno below).
+ * age is in seconds.
+ *
+ * priority = ((seg_size - live) * age) / (seg_size + live)
+ */
+#ifdef VERBOSE
+ if (live < 0 || live > seg_size(lfsp)) {
+ err(0, "Bad segusage count: %d", live);
+ live = 0;
+ }
+#endif
+ return (lblkno(lfsp, seg_size(lfsp) - live) * age)
+ / lblkno(lfsp, seg_size(lfsp) + live);
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FS_INFO *fsp;
+ struct statfs *lstatfsp; /* file system stats */
+ struct timeval timeout; /* sleep timeout */
+ fsid_t fsid;
+ int i, nodaemon;
+ int opt, cmd_err;
+ char *fs_name; /* name of filesystem to clean */
+ extern int optind;
+
+ cmd_err = nodaemon = 0;
+ while ((opt = getopt(argc, argv, "smd")) != EOF) {
+ switch (opt) {
+ case 's': /* small writes */
+ do_small = 1;
+ break;
+ case 'm':
+ do_mmap = 1;
+ break;
+ case 'd':
+ nodaemon = 1;
+ break;
+ default:
+ ++cmd_err;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (cmd_err || (argc != 1))
+ err(1, "usage: lfs_cleanerd [-smd] fs_name");
+
+ fs_name = argv[0];
+
+ signal(SIGINT, sig_report);
+ signal(SIGUSR1, sig_report);
+ signal(SIGUSR2, sig_report);
+ if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) {
+ /* didn't find the filesystem */
+ err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name);
+ }
+
+ if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */
+ if (daemon(0, 0) == -1)
+ err(1, "lfs_cleanerd: couldn't become a daemon!");
+
+ timeout.tv_sec = 5*60; /* five minutes */
+ timeout.tv_usec = 0;
+ fsid.val[0] = 0;
+ fsid.val[1] = 0;
+
+ for (fsp = get_fs_info(lstatfsp, do_mmap); ;
+ reread_fs_info(fsp, do_mmap)) {
+ /*
+ * clean the filesystem, and, if it needed cleaning
+ * (i.e. it returned nonzero) try it again
+ * to make sure that some nasty process hasn't just
+ * filled the disk system up.
+ */
+ if (clean_loop(fsp))
+ continue;
+
+#ifdef VERBOSE
+ (void)printf("Cleaner going to sleep.\n");
+#endif
+ if (lfs_segwait(&fsid, &timeout) < 0)
+ err(0, "lfs_segwait: returned error\n");
+#ifdef VERBOSE
+ (void)printf("Cleaner waking up.\n");
+#endif
+ }
+}
+
+/* return the number of segments cleaned */
+int
+clean_loop(fsp)
+ FS_INFO *fsp; /* file system information */
+{
+ double loadavg[MAXLOADS];
+ time_t now;
+ u_long max_free_segs;
+
+ /*
+ * Compute the maximum possible number of free segments, given the
+ * number of free blocks.
+ */
+ max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
+
+ /*
+ * We will clean if there are not enough free blocks or total clean
+ * space is less than BUSY_LIM % of possible clean space.
+ */
+ now = time((time_t *)NULL);
+ if (fsp->fi_cip->clean < max_free_segs &&
+ (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
+ fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) {
+ printf("Cleaner Running at %s (%d of %d segments available)\n",
+ ctime(&now), fsp->fi_cip->clean, max_free_segs);
+ clean_fs(fsp, cost_benefit);
+ return (1);
+ } else {
+ /*
+ * We will also clean if the system is reasonably idle and
+ * the total clean space is less then IDLE_LIM % of possible
+ * clean space.
+ */
+ if (getloadavg(loadavg, MAXLOADS) == -1) {
+ perror("getloadavg: failed\n");
+ return (-1);
+ }
+ if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
+ fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
+ clean_fs(fsp, cost_benefit);
+ printf("Cleaner Running at %s (system idle)\n",
+ ctime(&now));
+ return (1);
+ }
+ }
+ printf("Cleaner Not Running at %s\n", ctime(&now));
+ return (0);
+}
+
+
+void
+clean_fs(fsp, cost_func)
+ FS_INFO *fsp; /* file system information */
+ int (*cost_func) __P((FS_INFO *, SEGUSE *));
+{
+ struct seglist *segs, *sp;
+ int i;
+
+ if ((segs =
+ malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) {
+ err(0, "malloc failed");
+ return;
+ }
+ i = choose_segments(fsp, segs, cost_func);
+#ifdef VERBOSE
+ printf("clean_fs: found %d segments to clean in file system %s\n",
+ i, fsp->fi_statfsp->f_mntonname);
+ fflush(stdout);
+#endif
+ if (i)
+ for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) {
+ if (clean_segment(fsp, sp->sl_id) < 0)
+ perror("clean_segment failed");
+ else if (lfs_segclean(&fsp->fi_statfsp->f_fsid,
+ sp->sl_id) < 0)
+ perror("lfs_segclean failed");
+ printf("Completed cleaning segment %d\n", sp->sl_id);
+ }
+ free(segs);
+}
+
+/*
+ * Segment with the highest priority get sorted to the beginning of the
+ * list. This sort assumes that empty segments always have a higher
+ * cost/benefit than any utilized segment.
+ */
+int
+cost_compare(a, b)
+ const void *a;
+ const void *b;
+{
+ return (((struct seglist *)b)->sl_cost -
+ ((struct seglist *)a)->sl_cost);
+}
+
+
+/*
+ * Returns the number of segments to be cleaned with the elements of seglist
+ * filled in.
+ */
+int
+choose_segments(fsp, seglist, cost_func)
+ FS_INFO *fsp;
+ struct seglist *seglist;
+ int (*cost_func) __P((FS_INFO *, SEGUSE *));
+{
+ struct lfs *lfsp;
+ struct seglist *sp;
+ SEGUSE *sup;
+ int i, nsegs;
+
+ lfsp = &fsp->fi_lfs;
+
+#ifdef VERBOSE
+ (void)printf("Entering choose_segments\n");
+#endif
+ dump_super(lfsp);
+ dump_cleaner_info(fsp->fi_cip);
+
+ for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
+ sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
+ PRINT_SEGUSE(sup, i);
+ if (!(sup->su_flags & SEGUSE_DIRTY) ||
+ sup->su_flags & SEGUSE_ACTIVE)
+ continue;
+#ifdef VERBOSE
+ (void)printf("\tchoosing segment %d\n", i);
+#endif
+ sp->sl_cost = (*cost_func)(fsp, sup);
+ sp->sl_id = i;
+ sp->sl_empty = sup->su_nbytes ? 0 : 1;
+ ++sp;
+ }
+ nsegs = sp - seglist;
+ qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
+#ifdef VERBOSE
+ (void)printf("Returning %d segments\n", nsegs);
+#endif
+ return (nsegs);
+}
+
+
+int
+clean_segment(fsp, id)
+ FS_INFO *fsp; /* file system information */
+ int id; /* segment number */
+{
+ BLOCK_INFO *block_array, *bp;
+ SEGUSE *sp;
+ struct lfs *lfsp;
+ struct tossstruct t;
+ caddr_t seg_buf;
+ int num_blocks, maxblocks, clean_blocks;
+
+ lfsp = &fsp->fi_lfs;
+ sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
+
+#ifdef VERBOSE
+ (void)printf("cleaning segment %d: contains %lu bytes\n", id,
+ sp->su_nbytes);
+ fflush(stdout);
+#endif
+ /* XXX could add debugging to verify that segment is really empty */
+ if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) {
+ ++cleaner_stats.segs_empty;
+ return (0);
+ }
+
+ /* map the segment into a buffer */
+ if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) {
+ err(0, "mmap_segment failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ /* get a list of blocks that are contained by the segment */
+ if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
+ err(0, "clean_segment: lfs_segmapv failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize;
+
+#ifdef VERBOSE
+ (void)printf("lfs_segmapv returned %d blocks\n", num_blocks);
+ fflush(stdout);
+#endif
+
+ /* get the current disk address of blocks contained by the segment */
+ if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
+ perror("clean_segment: lfs_bmapv failed\n");
+ ++cleaner_stats.segs_error;
+ return -1;
+ }
+
+ /* Now toss any blocks not in the current segment */
+ t.lfs = lfsp;
+ t.seg = id;
+ toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
+
+ /* Check if last element should be tossed */
+ if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
+ --num_blocks;
+
+#ifdef VERBOSE
+ {
+ BLOCK_INFO *_bip;
+ u_long *lp;
+ int i;
+
+ (void)printf("after bmapv still have %d blocks\n", num_blocks);
+ fflush(stdout);
+ if (num_blocks)
+ printf("BLOCK INFOS\n");
+ for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
+ PRINT_BINFO(_bip);
+ lp = (u_long *)_bip->bi_bp;
+ }
+ }
+#endif
+ cleaner_stats.blocks_written += num_blocks;
+ if (do_small)
+ maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1;
+ else
+ maxblocks = num_blocks;
+
+ for (bp = block_array; num_blocks > 0; bp += clean_blocks) {
+ clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks;
+ if (lfs_markv(&fsp->fi_statfsp->f_fsid,
+ bp, clean_blocks) < 0) {
+ err(0, "clean_segment: lfs_markv failed");
+ ++cleaner_stats.segs_error;
+ return (-1);
+ }
+ num_blocks -= clean_blocks;
+ }
+
+ free(block_array);
+ munmap_segment(fsp, seg_buf, do_mmap);
+ ++cleaner_stats.segs_cleaned;
+ return (0);
+}
+
+
+int
+bi_tossold(client, a, b)
+ const void *client;
+ const void *a;
+ const void *b;
+{
+ const struct tossstruct *t;
+
+ t = (struct tossstruct *)client;
+
+ return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
+ datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
+}
+
+void
+sig_report(sig)
+ int sig;
+{
+ printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n",
+ "blocks_read ", cleaner_stats.blocks_read,
+ "blocks_written ", cleaner_stats.blocks_written,
+ "segs_cleaned ", cleaner_stats.segs_cleaned,
+ "segs_empty ", cleaner_stats.segs_empty,
+ "seg_error ", cleaner_stats.segs_error);
+ if (sig == SIGUSR2) {
+ cleaner_stats.blocks_read = 0;
+ cleaner_stats.blocks_written = 0;
+ cleaner_stats.segs_cleaned = 0;
+ cleaner_stats.segs_empty = 0;
+ cleaner_stats.segs_error = 0;
+ }
+ if (sig == SIGINT)
+ exit(0);
+}
diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8
new file mode 100644
index 0000000..3d134db
--- /dev/null
+++ b/libexec/lfs_cleanerd/lfs_cleanerd.8
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd "December 11, 1993"
+.Dt LFS_CLEANERD 8
+.Os BSD 4.4
+.Sh NAME
+.Nm lfs_cleanerd
+.Nd garbage collect a log-structured file system
+.Sh SYNOPSIS
+.Nm lfs_cleanerd
+.Op Fl ds
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm lfs_cleanerd
+command starts a daemon process which garbage-collects
+the log-structured file system residing at the point named by
+.Ar node
+in the global file system namespace.
+This command is normally executed by
+.Xr mount_lfs 8
+when the log-structured file system is mounted.
+The daemon will exit within a few minutes
+of when the file system it was cleaning is unmounted.
+.Pp
+Garbage collection on a log-structured file system is done by scanning
+the file system's segments for active, i.e. referenced, data and copying
+it to new segments.
+When all of the active data in a given segment has been copied to a new
+segment that segment can be marked as empty, thus reclaiming the space
+taken by the inactive data which was in it.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Run in debug mode.
+Do not become a daemon process, and print debugging information.
+.It Fl s
+When cleaning the file system, read data in small chunks.
+.El
+.Sh SEE ALSO
+.Xr mount_lfs 8
+.Sh HISTORY
+The
+.Nm lfs_cleanerd
+utility first appeared in 4.4BSD.
diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c
new file mode 100644
index 0000000..b825a02
--- /dev/null
+++ b/libexec/lfs_cleanerd/library.c
@@ -0,0 +1,671 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "clean.h"
+
+void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
+ daddr_t, daddr_t));
+void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
+ daddr_t));
+int bi_compare __P((const void *, const void *));
+int bi_toss __P((const void *, const void *, const void *));
+void get_ifile __P((FS_INFO *, int));
+int get_superblock __P((FS_INFO *, struct lfs *));
+int pseg_valid __P((FS_INFO *, SEGSUM *));
+
+/*
+ * This function will get information on a a filesystem which matches
+ * the name and type given. If a "name" is in a filesystem of the given
+ * type, then buf is filled with that filesystem's info, and the
+ * a non-zero value is returned.
+ */
+int
+fs_getmntinfo(buf, name, type)
+ struct statfs **buf;
+ char *name;
+ int type;
+{
+ /* allocate space for the filesystem info */
+ *buf = (struct statfs *)malloc(sizeof(struct statfs));
+ if (*buf == NULL)
+ return 0;
+
+ /* grab the filesystem info */
+ if (statfs(name, *buf) < 0) {
+ free(*buf);
+ return 0;
+ }
+
+ /* check to see if it's the one we want */
+ if (((*buf)->f_type != type) ||
+ strncmp(name, (*buf)->f_mntonname, MNAMELEN)) {
+ /* "this is not the filesystem you're looking for */
+ free(*buf);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Get all the information available on an LFS file system.
+ * Returns an pointer to an FS_INFO structure, NULL on error.
+ */
+FS_INFO *
+get_fs_info (lstatfsp, use_mmap)
+ struct statfs *lstatfsp; /* IN: pointer to statfs struct */
+ int use_mmap; /* IN: mmap or read */
+{
+ FS_INFO *fsp;
+ int i;
+
+ fsp = (FS_INFO *)malloc(sizeof(FS_INFO));
+ if (fsp == NULL)
+ return NULL;
+ bzero(fsp, sizeof(FS_INFO));
+
+ fsp->fi_statfsp = lstatfsp;
+ if (get_superblock (fsp, &fsp->fi_lfs))
+ err(1, "get_fs_info: get_superblock failed");
+ fsp->fi_daddr_shift =
+ fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb;
+ get_ifile (fsp, use_mmap);
+ return (fsp);
+}
+
+/*
+ * If we are reading the ifile then we need to refresh it. Even if
+ * we are mmapping it, it might have grown. Finally, we need to
+ * refresh the file system information (statfs) info.
+ */
+void
+reread_fs_info(fsp, use_mmap)
+ FS_INFO *fsp; /* IN: prointer fs_infos to reread */
+ int use_mmap;
+{
+ int i;
+
+ if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp))
+ err(1, "reread_fs_info: statfs failed");
+ get_ifile (fsp, use_mmap);
+}
+
+/*
+ * Gets the superblock from disk (possibly in face of errors)
+ */
+int
+get_superblock (fsp, sbp)
+ FS_INFO *fsp; /* local file system info structure */
+ struct lfs *sbp;
+{
+ char mntfromname[MNAMELEN+1];
+ int fid;
+
+ strcpy(mntfromname, "/dev/r");
+ strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
+
+ if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
+ err(0, "get_superblock: bad open");
+ return (-1);
+ }
+
+ get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs));
+ close (fid);
+
+ return (0);
+}
+
+/*
+ * This function will map the ifile into memory. It causes a
+ * fatal error on failure.
+ */
+void
+get_ifile (fsp, use_mmap)
+ FS_INFO *fsp;
+ int use_mmap;
+
+{
+ struct stat file_stat;
+ caddr_t ifp;
+ char *ifile_name;
+ int count, fid;
+
+ ifp = NULL;
+ ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) +
+ strlen(IFILE_NAME)+2);
+ strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"),
+ IFILE_NAME);
+
+ if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0)
+ err(1, "get_ifile: bad open");
+
+ if (fstat (fid, &file_stat))
+ err(1, "get_ifile: fstat failed");
+
+ if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) {
+ (void) close(fid);
+ return;
+ }
+
+ /* get the ifile */
+ if (use_mmap) {
+ if (fsp->fi_cip)
+ munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length);
+ ifp = mmap ((caddr_t)0, file_stat.st_size,
+ PROT_READ|PROT_WRITE, 0, fid, (off_t)0);
+ if (ifp == (caddr_t)(-1))
+ err(1, "get_ifile: mmap failed");
+ } else {
+ if (fsp->fi_cip)
+ free(fsp->fi_cip);
+ if (!(ifp = malloc (file_stat.st_size)))
+ err (1, "get_ifile: malloc failed");
+redo_read:
+ count = read (fid, ifp, (size_t) file_stat.st_size);
+
+ if (count < 0)
+ err(1, "get_ifile: bad ifile read");
+ else if (count < file_stat.st_size) {
+ err(0, "get_ifile");
+ if (lseek(fid, 0, SEEK_SET) < 0)
+ err(1, "get_ifile: bad ifile lseek");
+ goto redo_read;
+ }
+ }
+ fsp->fi_ifile_length = file_stat.st_size;
+ close (fid);
+
+ fsp->fi_cip = (CLEANERINFO *)ifp;
+ fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp));
+ fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp));
+
+ /*
+ * The number of ifile entries is equal to the number of blocks
+ * blocks in the ifile minus the ones allocated to cleaner info
+ * and segment usage table multiplied by the number of ifile
+ * entries per page.
+ */
+ fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift -
+ fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) *
+ fsp->fi_lfs.lfs_ifpb;
+
+ free (ifile_name);
+}
+
+/*
+ * This function will scan a segment and return a list of
+ * <inode, blocknum> pairs which indicate which blocks were
+ * contained as live data within the segment when the segment
+ * summary was read (it may have "died" since then). Any given
+ * pair will be listed at most once.
+ */
+int
+lfs_segmapv(fsp, seg, seg_buf, blocks, bcount)
+ FS_INFO *fsp; /* pointer to local file system information */
+ int seg; /* the segment number */
+ caddr_t seg_buf; /* the buffer containing the segment's data */
+ BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */
+ int *bcount; /* OUT: number of active blocks in segment */
+{
+ BLOCK_INFO *bip;
+ SEGSUM *sp;
+ SEGUSE *sup;
+ FINFO *fip;
+ struct lfs *lfsp;
+ caddr_t s, segend;
+ daddr_t pseg_addr, seg_addr;
+ int i, nelem, nblocks, sumsize;
+ time_t timestamp;
+
+ lfsp = &fsp->fi_lfs;
+ nelem = 2 * lfsp->lfs_ssize;
+ if (!(bip = malloc(nelem * sizeof(BLOCK_INFO))))
+ goto err0;
+
+ sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg);
+ s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0);
+ seg_addr = sntoda(lfsp, seg);
+ pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0);
+#ifdef VERBOSE
+ printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr);
+#endif /* VERBOSE */
+
+ *bcount = 0;
+ for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) {
+ sp = (SEGSUM *)s;
+
+#ifdef VERBOSE
+ printf("\tpartial at: 0x%x\n", pseg_addr);
+ print_SEGSUM(lfsp, sp);
+ fflush(stdout);
+#endif /* VERBOSE */
+
+ nblocks = pseg_valid(fsp, sp);
+ if (nblocks <= 0)
+ break;
+
+ /* Check if we have hit old data */
+ if (timestamp > ((SEGSUM*)s)->ss_create)
+ break;
+ timestamp = ((SEGSUM*)s)->ss_create;
+
+#ifdef DIAGNOSTIC
+ /* Verfiy size of summary block */
+ sumsize = sizeof(SEGSUM) +
+ (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
+ for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) {
+ sumsize += sizeof(FINFO) +
+ (fip->fi_nblocks - 1) * sizeof(daddr_t);
+ fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]);
+ }
+ if (sumsize > LFS_SUMMARY_SIZE) {
+ fprintf(stderr,
+ "Segment %d summary block too big: %d\n",
+ seg, sumsize);
+ exit(1);
+ }
+#endif
+
+ if (*bcount + nblocks + sp->ss_ninos > nelem) {
+ nelem = *bcount + nblocks + sp->ss_ninos;
+ bip = realloc (bip, nelem * sizeof(BLOCK_INFO));
+ if (!bip)
+ goto err0;
+ }
+ add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr);
+ add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr);
+ pseg_addr += fsbtodb(lfsp, nblocks) +
+ bytetoda(fsp, LFS_SUMMARY_SIZE);
+ s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE;
+ }
+ qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare);
+ toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL);
+#ifdef VERBOSE
+ {
+ BLOCK_INFO *_bip;
+ int i;
+
+ printf("BLOCK INFOS\n");
+ for (_bip = bip, i=0; i < *bcount; ++_bip, ++i)
+ PRINT_BINFO(_bip);
+ }
+#endif
+ *blocks = bip;
+ return (0);
+
+err0: *bcount = 0;
+ return (-1);
+
+}
+
+/*
+ * This will parse a partial segment and fill in BLOCK_INFO structures
+ * for each block described in the segment summary. It will not include
+ * blocks or inodes from files with new version numbers.
+ */
+void
+add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr)
+ FS_INFO *fsp; /* pointer to super block */
+ BLOCK_INFO *bip; /* Block info array */
+ int *countp; /* IN/OUT: number of blocks in array */
+ SEGSUM *sp; /* segment summmary pointer */
+ caddr_t seg_buf; /* buffer containing segment */
+ daddr_t segaddr; /* address of this segment */
+ daddr_t psegaddr; /* address of this partial segment */
+{
+ IFILE *ifp;
+ FINFO *fip;
+ caddr_t bp;
+ daddr_t *dp, *iaddrp;
+ int db_per_block, i, j;
+ u_long page_size;
+
+#ifdef VERBOSE
+ printf("FILE INFOS\n");
+#endif
+ db_per_block = fsbtodb(&fsp->fi_lfs, 1);
+ page_size = fsp->fi_lfs.lfs_bsize;
+ bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE;
+ bip += *countp;
+ psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE);
+ iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ --iaddrp;
+ for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo;
+ ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) {
+
+ ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino);
+ PRINT_FINFO(fip, ifp);
+ if (ifp->if_version > fip->fi_version)
+ continue;
+ dp = &(fip->fi_blocks[0]);
+ for (j = 0; j < fip->fi_nblocks; j++, dp++) {
+ while (psegaddr == *iaddrp) {
+ psegaddr += db_per_block;
+ bp += page_size;
+ --iaddrp;
+ }
+ bip->bi_inode = fip->fi_ino;
+ bip->bi_lbn = *dp;
+ bip->bi_daddr = psegaddr;
+ bip->bi_segcreate = (time_t)(sp->ss_create);
+ bip->bi_bp = bp;
+ bip->bi_version = ifp->if_version;
+ psegaddr += db_per_block;
+ bp += page_size;
+ ++bip;
+ ++(*countp);
+ }
+ }
+}
+
+/*
+ * For a particular segment summary, reads the inode blocks and adds
+ * INODE_INFO structures to the array. Returns the number of inodes
+ * actually added.
+ */
+void
+add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr)
+ FS_INFO *fsp; /* pointer to super block */
+ BLOCK_INFO *bip; /* block info array */
+ int *countp; /* pointer to current number of inodes */
+ SEGSUM *sp; /* segsum pointer */
+ caddr_t seg_buf; /* the buffer containing the segment's data */
+ daddr_t seg_addr; /* disk address of seg_buf */
+{
+ struct dinode *di;
+ struct lfs *lfsp;
+ IFILE *ifp;
+ BLOCK_INFO *bp;
+ daddr_t *daddrp;
+ ino_t inum;
+ int i;
+
+ if (sp->ss_ninos <= 0)
+ return;
+
+ bp = bip + *countp;
+ lfsp = &fsp->fi_lfs;
+#ifdef VERBOSE
+ (void) printf("INODES:\n");
+#endif
+ daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ for (i = 0; i < sp->ss_ninos; ++i) {
+ if (i % INOPB(lfsp) == 0) {
+ --daddrp;
+ di = (struct dinode *)(seg_buf +
+ ((*daddrp - seg_addr) << fsp->fi_daddr_shift));
+ } else
+ ++di;
+
+ inum = di->di_inumber;
+ bp->bi_lbn = LFS_UNUSED_LBN;
+ bp->bi_inode = inum;
+ bp->bi_daddr = *daddrp;
+ bp->bi_bp = di;
+ bp->bi_segcreate = sp->ss_create;
+
+ if (inum == LFS_IFILE_INUM) {
+ bp->bi_version = 1; /* Ifile version should be 1 */
+ bp++;
+ ++(*countp);
+ PRINT_INODE(1, bp);
+ } else {
+ ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum);
+ PRINT_INODE(ifp->if_daddr == *daddrp, bp);
+ bp->bi_version = ifp->if_version;
+ if (ifp->if_daddr == *daddrp) {
+ bp++;
+ ++(*countp);
+ }
+ }
+ }
+}
+
+/*
+ * Checks the summary checksum and the data checksum to determine if the
+ * segment is valid or not. Returns the size of the partial segment if it
+ * is valid, * and 0 otherwise. Use dump_summary to figure out size of the
+ * the partial as well as whether or not the checksum is valid.
+ */
+int
+pseg_valid (fsp, ssp)
+ FS_INFO *fsp; /* pointer to file system info */
+ SEGSUM *ssp; /* pointer to segment summary block */
+{
+ caddr_t p;
+ int i, nblocks;
+ u_long *datap;
+
+ if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 ||
+ nblocks > fsp->fi_lfs.lfs_ssize - 1)
+ return(0);
+
+ /* check data/inode block(s) checksum too */
+ datap = (u_long *)malloc(nblocks * sizeof(u_long));
+ p = (caddr_t)ssp + LFS_SUMMARY_SIZE;
+ for (i = 0; i < nblocks; ++i) {
+ datap[i] = *((u_long *)p);
+ p += fsp->fi_lfs.lfs_bsize;
+ }
+ if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum)
+ return (0);
+
+ return (nblocks);
+}
+
+
+/* #define MMAP_SEGMENT */
+/*
+ * read a segment into a memory buffer
+ */
+int
+mmap_segment (fsp, segment, segbuf, use_mmap)
+ FS_INFO *fsp; /* file system information */
+ int segment; /* segment number */
+ caddr_t *segbuf; /* pointer to buffer area */
+ int use_mmap; /* mmap instead of read */
+{
+ struct lfs *lfsp;
+ int fid; /* fildes for file system device */
+ daddr_t seg_daddr; /* base disk address of segment */
+ off_t seg_byte;
+ size_t ssize;
+ char mntfromname[MNAMELEN+2];
+
+ lfsp = &fsp->fi_lfs;
+
+ /* get the disk address of the beginning of the segment */
+ seg_daddr = sntoda(lfsp, segment);
+ seg_byte = datobyte(fsp, seg_daddr);
+ ssize = seg_size(lfsp);
+
+ strcpy(mntfromname, "/dev/r");
+ strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
+
+ if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
+ err(0, "mmap_segment: bad open");
+ return (-1);
+ }
+
+ if (use_mmap) {
+ *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ,
+ 0, fid, seg_byte);
+ if (*(long *)segbuf < 0) {
+ err(0, "mmap_segment: mmap failed");
+ return (NULL);
+ }
+ } else {
+#ifdef VERBOSE
+ printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n",
+ seg_daddr, ssize, seg_byte);
+#endif
+ /* malloc the space for the buffer */
+ *segbuf = malloc(ssize);
+ if (!*segbuf) {
+ err(0, "mmap_segment: malloc failed");
+ return(NULL);
+ }
+
+ /* read the segment data into the buffer */
+ if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) {
+ err (0, "mmap_segment: bad lseek");
+ free(*segbuf);
+ return (-1);
+ }
+
+ if (read (fid, *segbuf, ssize) != ssize) {
+ err (0, "mmap_segment: bad read");
+ free(*segbuf);
+ return (-1);
+ }
+ }
+ close (fid);
+
+ return (0);
+}
+
+void
+munmap_segment (fsp, seg_buf, use_mmap)
+ FS_INFO *fsp; /* file system information */
+ caddr_t seg_buf; /* pointer to buffer area */
+ int use_mmap; /* mmap instead of read/write */
+{
+ if (use_mmap)
+ munmap (seg_buf, seg_size(&fsp->fi_lfs));
+ else
+ free (seg_buf);
+}
+
+
+/*
+ * USEFUL DEBUGGING TOOLS:
+ */
+void
+print_SEGSUM (lfsp, p)
+ struct lfs *lfsp;
+ SEGSUM *p;
+{
+ if (p)
+ (void) dump_summary(lfsp, p, DUMP_ALL, NULL);
+ else printf("0x0");
+ fflush(stdout);
+}
+
+int
+bi_compare(a, b)
+ const void *a;
+ const void *b;
+{
+ const BLOCK_INFO *ba, *bb;
+ int diff;
+
+ ba = a;
+ bb = b;
+
+ if (diff = (int)(ba->bi_inode - bb->bi_inode))
+ return (diff);
+ if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) {
+ if (ba->bi_lbn == LFS_UNUSED_LBN)
+ return(-1);
+ else if (bb->bi_lbn == LFS_UNUSED_LBN)
+ return(1);
+ else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0)
+ return(1);
+ else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0)
+ return(-1);
+ else
+ return (diff);
+ }
+ if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate))
+ return (diff);
+ diff = (int)(ba->bi_daddr - bb->bi_daddr);
+ return (diff);
+}
+
+int
+bi_toss(dummy, a, b)
+ const void *dummy;
+ const void *a;
+ const void *b;
+{
+ const BLOCK_INFO *ba, *bb;
+
+ ba = a;
+ bb = b;
+
+ return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn);
+}
+
+void
+toss(p, nump, size, dotoss, client)
+ void *p;
+ int *nump;
+ size_t size;
+ int (*dotoss) __P((const void *, const void *, const void *));
+ void *client;
+{
+ int i;
+ void *p1;
+
+ if (*nump == 0)
+ return;
+
+ for (i = *nump; --i > 0;) {
+ p1 = p + size;
+ if (dotoss(client, p, p1)) {
+ memmove(p, p1, i * size);
+ --(*nump);
+ } else
+ p += size;
+ }
+}
diff --git a/libexec/lfs_cleanerd/misc.c b/libexec/lfs_cleanerd/misc.c
new file mode 100644
index 0000000..ad6e11a
--- /dev/null
+++ b/libexec/lfs_cleanerd/misc.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+extern char *special;
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const int fatal, const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "%s: ", special);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (errno)
+ (void)fprintf(stderr, " %s", strerror(errno));
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+}
+
+void
+get(fd, off, p, len)
+ int fd;
+ off_t off;
+ void *p;
+ size_t len;
+{
+ int rbytes;
+
+ if (lseek(fd, off, SEEK_SET) < 0)
+ err(1, "%s: %s", special, strerror(errno));
+ if ((rbytes = read(fd, p, len)) < 0)
+ err(1, "%s: %s", special, strerror(errno));
+ if (rbytes != len)
+ err(1, "%s: short read (%d, not %d)", special, rbytes, len);
+}
diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c
new file mode 100644
index 0000000..5c3863a
--- /dev/null
+++ b/libexec/lfs_cleanerd/print.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "clean.h"
+
+/*
+ * Print out a summary block; return number of blocks in segment; 0
+ * for empty segment or corrupt segment.
+ * Returns a pointer to the array of inode addresses.
+ */
+int
+dump_summary(lfsp, sp, flags, iaddrp)
+ struct lfs *lfsp;
+ SEGSUM *sp;
+ u_long flags;
+ daddr_t **iaddrp;
+{
+ int i, j, numblocks;
+ daddr_t *dp;
+
+ FINFO *fp;
+ int ck;
+
+ if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum,
+ LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum))))
+ return(-1);
+
+ if (flags & DUMP_SUM_HEADER) {
+ (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X",
+ "next ", sp->ss_next,
+ "nfinfo ", sp->ss_nfinfo,
+ "ninos ", sp->ss_ninos,
+ "sumsum ", sp->ss_sumsum,
+ "datasum ", sp->ss_datasum );
+ (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create));
+ }
+
+ numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
+
+ /* Dump out inode disk addresses */
+ if (flags & DUMP_INODE_ADDRS)
+ printf(" Inode addresses:");
+
+ dp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
+ for (--dp, i = 0; i < sp->ss_ninos; --dp)
+ if (flags & DUMP_INODE_ADDRS) {
+ (void)printf("\t0x%lx", *dp);
+ if (++i % 7 == 0)
+ (void)printf("\n");
+ } else
+ ++i;
+ if (iaddrp)
+ *iaddrp = ++dp;
+ if (flags & DUMP_INODE_ADDRS)
+ printf("\n");
+
+ for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; ++i) {
+ numblocks += fp->fi_nblocks;
+ if (flags & DUMP_FINFOS) {
+ (void)printf(" %s%d version %d nblocks %d\n",
+ "FINFO for inode: ", fp->fi_ino,
+ fp->fi_version, fp->fi_nblocks);
+ dp = &(fp->fi_blocks[0]);
+ for (j = 0; j < fp->fi_nblocks; j++, dp++) {
+ (void)printf("\t%d", *dp);
+ if ((j % 8) == 7)
+ (void)printf("\n");
+ }
+ if ((j % 8) != 0)
+ (void)printf("\n");
+ fp = (FINFO *)dp;
+ } else {
+ fp = (FINFO *)(&fp->fi_blocks[fp->fi_nblocks]);
+ }
+ }
+ return (numblocks);
+}
+
+#ifdef VERBOSE
+void
+dump_cleaner_info(ipage)
+ void *ipage;
+{
+ CLEANERINFO *cip;
+
+ cip = (CLEANERINFO *)ipage;
+ (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n",
+ cip->clean, cip->dirty);
+}
+
+void
+dump_super(lfsp)
+ struct lfs *lfsp;
+{
+ int i;
+
+ (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n",
+ "magic ", lfsp->lfs_magic,
+ "version ", lfsp->lfs_version,
+ "size ", lfsp->lfs_size,
+ "ssize ", lfsp->lfs_ssize);
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "dsize ", lfsp->lfs_dsize,
+ "bsize ", lfsp->lfs_bsize,
+ "fsize ", lfsp->lfs_fsize,
+ "frag ", lfsp->lfs_frag);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "minfree ", lfsp->lfs_minfree,
+ "inopb ", lfsp->lfs_inopb,
+ "ifpb ", lfsp->lfs_ifpb,
+ "nindir ", lfsp->lfs_nindir);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "nseg ", lfsp->lfs_nseg,
+ "nspf ", lfsp->lfs_nspf,
+ "cleansz ", lfsp->lfs_cleansz,
+ "segtabsz ", lfsp->lfs_segtabsz);
+
+ (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n",
+ "ffmask ", lfsp->lfs_ffmask,
+ "ffshift ", lfsp->lfs_ffshift,
+ "fbmask ", lfsp->lfs_fbmask,
+ "fbshift ", lfsp->lfs_fbshift);
+
+ (void)printf("%s%d\t\t%s0x%X\t%s0x%qx\n",
+ "fsbtodb ", lfsp->lfs_fsbtodb,
+ "cksum ", lfsp->lfs_cksum,
+ "maxfilesize ", lfsp->lfs_maxfilesize);
+
+ (void)printf("Superblock disk addresses:\t");
+ for (i = 0; i < LFS_MAXNUMSB; i++) {
+ (void)printf(" 0x%X", lfsp->lfs_sboffs[i]);
+ if ( i == (LFS_MAXNUMSB >> 1))
+ (void)printf("\n\t\t\t\t");
+ }
+ (void)printf("\n");
+
+ (void)printf("Checkpoint Info\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d\n",
+ "free ", lfsp->lfs_free,
+ "idaddr ", lfsp->lfs_idaddr,
+ "ifile ", lfsp->lfs_ifile);
+ (void)printf("%s%d\t%s%d\t%s%d\n",
+ "bfree ", lfsp->lfs_bfree,
+ "avail ", lfsp->lfs_avail,
+ "uinodes ", lfsp->lfs_uinodes);
+ (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t",
+ "nfiles ", lfsp->lfs_nfiles,
+ "lastseg ", lfsp->lfs_lastseg,
+ "nextseg ", lfsp->lfs_nextseg,
+ "curseg ", lfsp->lfs_curseg,
+ "offset ", lfsp->lfs_offset);
+ (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp));
+ (void)printf("\nIn-Memory Information\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d\t%s%d\t%s%d\n",
+ "seglock ", lfsp->lfs_seglock,
+ "iocount ", lfsp->lfs_iocount,
+ "writer ", lfsp->lfs_writer,
+ "dirops ", lfsp->lfs_dirops,
+ "doifile ", lfsp->lfs_doifile );
+ (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n",
+ "nactive ", lfsp->lfs_nactive,
+ "fmod ", lfsp->lfs_fmod,
+ "clean ", lfsp->lfs_clean,
+ "ronly ", lfsp->lfs_ronly);
+}
+#endif /* VERBOSE */
OpenPOWER on IntegriCloud