1 /* $OpenBSD: fsirand.c,v 1.44 2024/05/09 08:35:40 florian Exp $ */
2
3 /*
4 * Copyright (c) 1997 Todd C. Miller <millert@openbsd.org>
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 #include <sys/param.h> /* DEV_BSIZE */
20 #include <sys/disklabel.h>
21 #include <sys/ioctl.h>
22 #include <sys/dkio.h>
23 #include <sys/resource.h>
24 #include <sys/time.h>
25
26 #include <ufs/ffs/fs.h>
27 #include <ufs/ufs/dinode.h>
28
29 #include <err.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <util.h>
37
38 void usage(int);
39 int fsirand(char *);
40
41 extern char *__progname;
42
43 int printonly = 0, force = 0, ignorelabel = 0;
44
45 /*
46 * Possible locations for the superblock.
47 */
48 static const int sbtry[] = SBLOCKSEARCH;
49
50 int
main(int argc,char * argv[])51 main(int argc, char *argv[])
52 {
53 int n, ex = 0;
54 struct rlimit rl;
55
56 while ((n = getopt(argc, argv, "bfp")) != -1) {
57 switch (n) {
58 case 'b':
59 ignorelabel = 1;
60 break;
61 case 'p':
62 printonly = 1;
63 break;
64 case 'f':
65 force = 1;
66 break;
67 default:
68 usage(1);
69 }
70 }
71 if (argc - optind < 1)
72 usage(1);
73
74 /* Increase our data size to the max */
75 if (getrlimit(RLIMIT_DATA, &rl) == 0) {
76 rl.rlim_cur = rl.rlim_max;
77 if (setrlimit(RLIMIT_DATA, &rl) == -1)
78 warn("Can't set resource limit to max data size");
79 } else
80 warn("Can't get resource limit for data size");
81
82 for (n = optind; n < argc; n++) {
83 if (argc - optind != 1)
84 (void)puts(argv[n]);
85 ex += fsirand(argv[n]);
86 if (n < argc - 1)
87 putchar('\n');
88 }
89
90 exit(ex);
91 }
92
93 int
fsirand(char * device)94 fsirand(char *device)
95 {
96 struct ufs1_dinode *dp1 = NULL;
97 struct ufs2_dinode *dp2 = NULL;
98 static char *inodebuf;
99 size_t ibufsize, isize;
100 struct fs *sblock, *tmpsblock;
101 ino_t inumber;
102 daddr_t sblockloc, dblk;
103 char sbuf[SBSIZE], sbuftmp[SBSIZE];
104 int devfd, n, i;
105 u_int cg;
106 char *devpath, *ib;
107 u_int32_t bsize = DEV_BSIZE;
108 struct disklabel label;
109
110 if ((devfd = opendev(device, printonly ? O_RDONLY : O_RDWR,
111 0, &devpath)) == -1) {
112 warn("Can't open %s", devpath);
113 return (1);
114 }
115
116 /* Get block size (usually 512) from disklabel if possible */
117 if (!ignorelabel) {
118 if (ioctl(devfd, DIOCGDINFO, &label) == -1)
119 warn("Can't read disklabel, using sector size of %d",
120 bsize);
121 else
122 bsize = label.d_secsize;
123 }
124
125 if (pledge("stdio", NULL) == -1)
126 err(1, "pledge");
127
128 /* Read in master superblock */
129 (void)memset(&sbuf, 0, sizeof(sbuf));
130 sblock = (struct fs *)&sbuf;
131
132 for (i = 0; sbtry[i] != -1; i++) {
133 sblockloc = sbtry[i];
134
135 if (lseek(devfd, sblockloc, SEEK_SET) == -1) {
136 warn("Can't seek to superblock (%lld) on %s",
137 (long long)sblockloc, devpath);
138 return (1);
139 }
140
141 if ((n = read(devfd, sblock, SBSIZE)) != SBSIZE) {
142 warnx("Can't read superblock on %s: %s", devpath,
143 (n < SBSIZE) ? "short read" : strerror(errno));
144 return (1);
145 }
146
147 /* Find a suitable superblock */
148 if (sblock->fs_magic != FS_UFS1_MAGIC &&
149 sblock->fs_magic != FS_UFS2_MAGIC)
150 continue; /* Not a superblock */
151
152 /*
153 * Do not look for an FFS1 file system at SBLOCK_UFS2.
154 * Doing so will find the wrong super-block for file
155 * systems with 64k block size.
156 */
157 if (sblock->fs_magic == FS_UFS1_MAGIC &&
158 sbtry[i] == SBLOCK_UFS2)
159 continue;
160
161 if (sblock->fs_magic == FS_UFS2_MAGIC &&
162 sblock->fs_sblockloc != sbtry[i])
163 continue; /* Not a superblock */
164
165 break;
166 }
167
168 if (sbtry[i] == -1) {
169 warnx("Cannot find file system superblock");
170 return (1);
171 }
172
173 /* Simple sanity checks on the superblock */
174 if (sblock->fs_sbsize > SBSIZE) {
175 warnx("Superblock size is preposterous");
176 return (1);
177 }
178
179 if (sblock->fs_postblformat == FS_42POSTBLFMT) {
180 warnx("Filesystem format is too old, sorry");
181 return (1);
182 }
183
184 if (!force && !printonly && sblock->fs_clean != FS_ISCLEAN) {
185 warnx("Filesystem is not clean, fsck %s first.", devpath);
186 return (1);
187 }
188
189 /* Make sure backup superblocks are sane. */
190 tmpsblock = (struct fs *)&sbuftmp;
191 for (cg = 0; cg < sblock->fs_ncg; cg++) {
192 dblk = fsbtodb(sblock, cgsblock(sblock, cg));
193 if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) == -1) {
194 warn("Can't seek to %lld", (long long)dblk * bsize);
195 return (1);
196 } else if ((n = read(devfd, tmpsblock, SBSIZE)) != SBSIZE) {
197 warn("Can't read backup superblock %d on %s: %s",
198 cg + 1, devpath, (n < SBSIZE) ? "short read"
199 : strerror(errno));
200 return (1);
201 }
202 if (tmpsblock->fs_magic != FS_UFS1_MAGIC &&
203 tmpsblock->fs_magic != FS_UFS2_MAGIC) {
204 warnx("Bad magic number in backup superblock %d on %s",
205 cg + 1, devpath);
206 return (1);
207 }
208 if (tmpsblock->fs_sbsize > SBSIZE) {
209 warnx("Size of backup superblock %d on %s is preposterous",
210 cg + 1, devpath);
211 return (1);
212 }
213 }
214
215 /* XXX - should really cap buffer at 512kb or so */
216 if (sblock->fs_magic == FS_UFS1_MAGIC)
217 isize = sizeof(struct ufs1_dinode);
218 else
219 isize = sizeof(struct ufs2_dinode);
220
221 if ((ib = reallocarray(inodebuf, sblock->fs_ipg, isize)) == NULL)
222 errx(1, "Can't allocate memory for inode buffer");
223 inodebuf = ib;
224 ibufsize = sblock->fs_ipg * isize;
225
226 if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) {
227 if (sblock->fs_inodefmt >= FS_44INODEFMT && sblock->fs_id[0]) {
228 time_t t = sblock->fs_id[0]; /* XXX 2038 */
229 char *ct = ctime(&t);
230 if (ct)
231 (void)printf("%s was randomized on %s", devpath,
232 ct);
233 else
234 (void)printf("%s was randomized on %lld\n",
235 devpath, t);
236 }
237 (void)printf("fsid: %x %x\n", sblock->fs_id[0],
238 sblock->fs_id[1]);
239 }
240
241 /* Randomize fs_id unless old 4.2BSD filesystem */
242 if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
243 /* Randomize fs_id and write out new sblock and backups */
244 sblock->fs_id[0] = (u_int32_t)time(NULL);
245 sblock->fs_id[1] = arc4random();
246
247 if (lseek(devfd, SBOFF, SEEK_SET) == -1) {
248 warn("Can't seek to superblock (%lld) on %s",
249 (long long)SBOFF, devpath);
250 return (1);
251 }
252 if ((n = write(devfd, sblock, SBSIZE)) != SBSIZE) {
253 warn("Can't write superblock on %s: %s", devpath,
254 (n < SBSIZE) ? "short write" : strerror(errno));
255 return (1);
256 }
257 }
258
259 /* For each cylinder group, randomize inodes and update backup sblock */
260 for (cg = 0, inumber = 0; cg < sblock->fs_ncg; cg++) {
261 /* Update superblock if appropriate */
262 if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
263 dblk = fsbtodb(sblock, cgsblock(sblock, cg));
264 if (lseek(devfd, (off_t)dblk * bsize,
265 SEEK_SET) == -1) {
266 warn("Can't seek to %lld",
267 (long long)dblk * bsize);
268 return (1);
269 } else if ((n = write(devfd, sblock, SBSIZE)) !=
270 SBSIZE) {
271 warn("Can't read backup superblock %d on %s: %s",
272 cg + 1, devpath, (n < SBSIZE) ? "short write"
273 : strerror(errno));
274 return (1);
275 }
276 }
277
278 /* Read in inodes, then print or randomize generation nums */
279 dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber));
280 if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) == -1) {
281 warn("Can't seek to %lld", (long long)dblk * bsize);
282 return (1);
283 } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) {
284 warnx("Can't read inodes: %s",
285 (n < ibufsize) ? "short read" : strerror(errno));
286 return (1);
287 }
288
289 for (n = 0; n < sblock->fs_ipg; n++, inumber++) {
290 if (sblock->fs_magic == FS_UFS1_MAGIC)
291 dp1 = &((struct ufs1_dinode *)inodebuf)[n];
292 else
293 dp2 = &((struct ufs2_dinode *)inodebuf)[n];
294 if (inumber >= ROOTINO) {
295 if (printonly)
296 (void)printf("ino %llu gen %x\n",
297 (unsigned long long)inumber,
298 sblock->fs_magic == FS_UFS1_MAGIC ?
299 dp1->di_gen : dp2->di_gen);
300 else if (sblock->fs_magic == FS_UFS1_MAGIC)
301 dp1->di_gen = arc4random();
302 else
303 dp2->di_gen = arc4random();
304 }
305 }
306
307 /* Write out modified inodes */
308 if (!printonly) {
309 if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) == -1) {
310 warn("Can't seek to %lld",
311 (long long)dblk * bsize);
312 return (1);
313 } else if ((n = write(devfd, inodebuf, ibufsize)) !=
314 ibufsize) {
315 warnx("Can't write inodes: %s",
316 (n != ibufsize) ? "short write" :
317 strerror(errno));
318 return (1);
319 }
320 }
321 }
322 (void)close(devfd);
323
324 return(0);
325 }
326
327 void
usage(int ex)328 usage(int ex)
329 {
330 (void)fprintf(stderr, "usage: %s [-bfp] special ...\n",
331 __progname);
332 exit(ex);
333 }
334