xref: /openbsd/sbin/fsirand/fsirand.c (revision db3296cf)
1 /*	$OpenBSD: fsirand.c,v 1.18 2003/06/17 21:56:24 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef lint
20 static char rcsid[] = "$OpenBSD: fsirand.c,v 1.18 2003/06/17 21:56:24 millert Exp $";
21 #endif /* not lint */
22 
23 #include <sys/types.h>
24 #include <sys/disklabel.h>
25 #include <sys/ioctl.h>
26 #include <sys/param.h>
27 #include <sys/resource.h>
28 #include <sys/time.h>
29 
30 #include <ufs/ffs/fs.h>
31 #include <ufs/ufs/dinode.h>
32 
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <util.h>
41 
42 void usage(int);
43 int fsirand(char *);
44 
45 extern char *__progname;
46 
47 int printonly = 0, force = 0, ignorelabel = 0;
48 
49 int
50 main(int argc, char *argv[])
51 {
52 	int n, ex = 0;
53 	struct rlimit rl;
54 
55 	while ((n = getopt(argc, argv, "bfp")) != -1) {
56 		switch (n) {
57 		case 'b':
58 			ignorelabel++;
59 			break;
60 		case 'p':
61 			printonly++;
62 			break;
63 		case 'f':
64 			force++;
65 			break;
66 		default:
67 			usage(1);
68 		}
69 	}
70 	if (argc - optind < 1)
71 		usage(1);
72 
73 	/* Increase our data size to the max */
74 	if (getrlimit(RLIMIT_DATA, &rl) == 0) {
75 		rl.rlim_cur = rl.rlim_max;
76 		if (setrlimit(RLIMIT_DATA, &rl) < 0)
77 			warn("Can't get resource limit to max data size");
78 	} else
79 		warn("Can't get resource limit for data size");
80 
81 	for (n = optind; n < argc; n++) {
82 		if (argc - optind != 1)
83 			(void)puts(argv[n]);
84 		ex += fsirand(argv[n]);
85 		if (n < argc - 1)
86 			putchar('\n');
87 	}
88 
89 	exit(ex);
90 }
91 
92 int
93 fsirand(char *device)
94 {
95 	static struct dinode *inodebuf;
96 	static size_t oldibufsize;
97 	size_t ibufsize;
98 	struct fs *sblock, *tmpsblock;
99 	ino_t inumber, maxino;
100 	daddr_t dblk;
101 	char sbuf[SBSIZE], sbuftmp[SBSIZE];
102 	int devfd, n, cg;
103 	char *devpath;
104 	u_int32_t bsize = DEV_BSIZE;
105 	struct disklabel label;
106 
107 	if ((devfd = opendev(device, printonly ? O_RDONLY : O_RDWR,
108 	    OPENDEV_PART, &devpath)) < 0) {
109 		warn("Can't open %s", devpath);
110 		return (1);
111 	}
112 
113 	/* Get block size (usually 512) from disklabel if possible */
114 	if (!ignorelabel) {
115 		if (ioctl(devfd, DIOCGDINFO, &label) < 0)
116 			warn("Can't read disklabel, using sector size of %d",
117 			    bsize);
118 		else
119 			bsize = label.d_secsize;
120 	}
121 
122 	/* Read in master superblock */
123 	(void)memset(&sbuf, 0, sizeof(sbuf));
124 	sblock = (struct fs *)&sbuf;
125 	if (lseek(devfd, (off_t)SBOFF, SEEK_SET) == -1) {
126 		warn("Can't seek to superblock (%qd) on %s", SBOFF, devpath);
127 		return (1);
128 	}
129 	if ((n = read(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
130 		warnx("Can't read superblock on %s: %s", devpath,
131 		    (n < SBSIZE) ? "short read" : strerror(errno));
132 		return (1);
133 	}
134 	maxino = sblock->fs_ncg * sblock->fs_ipg;
135 
136 	/* Simple sanity checks on the superblock */
137 	if (sblock->fs_magic != FS_MAGIC) {
138 		warnx("Bad magic number in superblock");
139 		return (1);
140 	}
141 	if (sblock->fs_sbsize > SBSIZE) {
142 		warnx("Superblock size is preposterous");
143 		return (1);
144 	}
145 	if (sblock->fs_postblformat == FS_42POSTBLFMT) {
146 		warnx("Filesystem format is too old, sorry");
147 		return (1);
148 	}
149 	if (!force && !printonly && sblock->fs_clean != FS_ISCLEAN) {
150 		warnx("Filesystem is not clean, fsck %s first.", devpath);
151 		return (1);
152 	}
153 
154 	/* Make sure backup superblocks are sane. */
155 	tmpsblock = (struct fs *)&sbuftmp;
156 	for (cg = 0; cg < sblock->fs_ncg; cg++) {
157 		dblk = fsbtodb(sblock, cgsblock(sblock, cg));
158 		if (lseek(devfd, (off_t)dblk * (off_t)bsize, SEEK_SET) < 0) {
159 			warn("Can't seek to %qd", (off_t)dblk * bsize);
160 			return (1);
161 		} else if ((n = read(devfd, (void *)tmpsblock, SBSIZE)) != SBSIZE) {
162 			warn("Can't read backup superblock %d on %s: %s",
163 			    cg + 1, devpath, (n < SBSIZE) ? "short read"
164 			    : strerror(errno));
165 			return (1);
166 		}
167 		if (tmpsblock->fs_magic != FS_MAGIC) {
168 			warnx("Bad magic number in backup superblock %d on %s",
169 			    cg + 1, devpath);
170 			return (1);
171 		}
172 		if (tmpsblock->fs_sbsize > SBSIZE) {
173 			warnx("Size of backup superblock %d on %s is preposterous",
174 			    cg + 1, devpath);
175 			return (1);
176 		}
177 	}
178 
179 	/* XXX - should really cap buffer at 512kb or so */
180 	ibufsize = sizeof(struct dinode) * sblock->fs_ipg;
181 	if (oldibufsize < ibufsize) {
182 		if ((inodebuf = realloc(inodebuf, ibufsize)) == NULL)
183 			errx(1, "Can't allocate memory for inode buffer");
184 		oldibufsize = ibufsize;
185 	}
186 
187 	if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) {
188 		if (sblock->fs_inodefmt >= FS_44INODEFMT && sblock->fs_id[0])
189 			(void)printf("%s was randomized on %s", devpath,
190 			    ctime((const time_t *)&(sblock->fs_id[0])));
191 		(void)printf("fsid: %x %x\n", sblock->fs_id[0],
192 		    sblock->fs_id[1]);
193 	}
194 
195 	/* Randomize fs_id unless old 4.2BSD filesystem */
196 	if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
197 		/* Randomize fs_id and write out new sblock and backups */
198 		sblock->fs_id[0] = (u_int32_t)time(NULL);
199 		sblock->fs_id[1] = arc4random();
200 
201 		if (lseek(devfd, (off_t)SBOFF, SEEK_SET) == -1) {
202 			warn("Can't seek to superblock (%qd) on %s", SBOFF,
203 			    devpath);
204 			return (1);
205 		}
206 		if ((n = write(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
207 			warn("Can't write superblock on %s: %s", devpath,
208 			    (n < SBSIZE) ? "short write" : strerror(errno));
209 			return (1);
210 		}
211 	}
212 
213 	/* For each cylinder group, randomize inodes and update backup sblock */
214 	for (cg = 0, inumber = 0; cg < sblock->fs_ncg; cg++) {
215 		/* Update superblock if appropriate */
216 		if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
217 			dblk = fsbtodb(sblock, cgsblock(sblock, cg));
218 			if (lseek(devfd, (off_t)dblk * (off_t)bsize,
219 			    SEEK_SET) < 0) {
220 				warn("Can't seek to %qd", (off_t)dblk * bsize);
221 				return (1);
222 			} else if ((n = write(devfd, (void *)sblock, SBSIZE)) !=
223 			    SBSIZE) {
224 				warn("Can't read backup superblock %d on %s: %s",
225 				    cg + 1, devpath, (n < SBSIZE) ? "short write"
226 				    : strerror(errno));
227 				return (1);
228 			}
229 		}
230 
231 		/* Read in inodes, then print or randomize generation nums */
232 		dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber));
233 		if (lseek(devfd, (off_t)dblk * (off_t)bsize, SEEK_SET) < 0) {
234 			warn("Can't seek to %qd", (off_t)dblk * bsize);
235 			return (1);
236 		} else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) {
237 			warnx("Can't read inodes: %s",
238 			    (n < ibufsize) ? "short read" : strerror(errno));
239 			return (1);
240 		}
241 
242 		for (n = 0; n < sblock->fs_ipg; n++, inumber++) {
243 			if (inumber >= ROOTINO) {
244 				if (printonly)
245 					(void)printf("ino %d gen %x\n", inumber,
246 					    inodebuf[n].di_gen);
247 				else
248 					inodebuf[n].di_gen = arc4random();
249 			}
250 		}
251 
252 		/* Write out modified inodes */
253 		if (!printonly) {
254 			if (lseek(devfd, (off_t)dblk * (off_t)bsize, SEEK_SET) < 0) {
255 				warn("Can't seek to %qd",
256 				    (off_t)dblk * bsize);
257 				return (1);
258 			} else if ((n = write(devfd, inodebuf, ibufsize)) !=
259 				 ibufsize) {
260 				warnx("Can't write inodes: %s",
261 				    (n != ibufsize) ? "short write" :
262 				    strerror(errno));
263 				return (1);
264 			}
265 		}
266 	}
267 	(void)close(devfd);
268 
269 	return(0);
270 }
271 
272 void
273 usage(int ex)
274 {
275 	(void)fprintf(stderr, "Usage: %s [ -b ] [ -f ] [ -p ] special [special ...]\n",
276 	    __progname);
277 	exit(ex);
278 }
279