1 /* $OpenBSD: ffs.c,v 1.39 2024/01/09 03:16:00 guenther Exp $ */
2 /* $NetBSD: ffs.c,v 1.66 2015/12/21 00:58:08 christos Exp $ */
3
4 /*
5 * Copyright (c) 2001 Wasabi Systems, Inc.
6 * All rights reserved.
7 *
8 * Written by Luke Mewburn for Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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 the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed for the NetBSD Project by
21 * Wasabi Systems, Inc.
22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
24 * written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 /*
39 * Copyright (c) 1982, 1986, 1989, 1993
40 * The Regents of the University of California. All rights reserved.
41 *
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
44 * are met:
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution.
50 * 3. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
67 */
68
69 #include <sys/param.h> /* roundup DEV_BSIZE */
70 #include <sys/types.h>
71 #include <sys/disklabel.h>
72
73 #include <assert.h>
74 #include <errno.h>
75 #include <fcntl.h>
76 #include <stdarg.h>
77 #include <stdio.h>
78 #include <stdint.h>
79 #include <limits.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <unistd.h>
83
84 #include <ufs/ufs/dinode.h>
85 #include <ufs/ufs/dir.h>
86 #include <ufs/ffs/fs.h>
87
88 #include "ffs/ufs_inode.h"
89 #include "ffs/ffs_extern.h"
90
91 #include "makefs.h"
92 #include "ffs.h"
93 #include "ffs/newfs_extern.h"
94
95 #undef DIP
96 #define DIP(dp, field) \
97 ((ffs_opts->version == 1) ? \
98 (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
99
100 /*
101 * Various file system defaults (cribbed from newfs(8)).
102 */
103 #define DFL_FRAGSIZE 2048 /* fragment size */
104 #define DFL_BLKSIZE 16384 /* block size */
105 #define DFL_SECSIZE 512 /* sector size */
106
107
108 typedef struct {
109 u_char *buf; /* buf for directory */
110 doff_t size; /* full size of buf */
111 doff_t cur; /* offset of current entry */
112 } dirbuf_t;
113
114
115 static int ffs_create_image(const char *, fsinfo_t *);
116 static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *);
117 static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
118 static void ffs_size_dir(fsnode *, fsinfo_t *);
119 static void ffs_validate(const char *, fsnode *, fsinfo_t *);
120 static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
121 static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
122 static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
123 fsnode *, fsinfo_t *);
124 static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
125 fsnode *, fsinfo_t *);
126
127 struct disklabel *ffs_makerdroot(const fsinfo_t *);
128
129
130 /* publicly visible functions */
131 void
ffs_prep_opts(fsinfo_t * fsopts)132 ffs_prep_opts(fsinfo_t *fsopts)
133 {
134 ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts));
135
136 const option_t ffs_options[] = {
137 { "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32, 1, INT_MAX },
138 { "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32, 1, INT_MAX },
139 { "bsize", &ffs_opts->bsize, OPT_INT32, 1, INT_MAX },
140 { "density", &ffs_opts->density, OPT_INT32, 1, INT_MAX },
141 { "disklabel", NULL, OPT_STRBUF, 0, 0 },
142 { "extent", &ffs_opts->maxbsize, OPT_INT32, 1, INT_MAX },
143 { "fsize", &ffs_opts->fsize, OPT_INT32, 1, INT_MAX },
144 { "label", ffs_opts->label, OPT_STRARRAY, 1, MAXVOLLEN },
145 { "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32, 1, INT_MAX },
146 { "maxbpg", &ffs_opts->maxbpg, OPT_INT32, 1, INT_MAX },
147 { "minfree", &ffs_opts->minfree, OPT_INT32, 0, 99 },
148 { "optimization", NULL, OPT_STRBUF, 0, 0 },
149 { "rdroot", &ffs_opts->rdroot, OPT_INT32, 0, 1 },
150 { "version", &ffs_opts->version, OPT_INT32, 1, 2 },
151 { .name = NULL }
152 };
153
154 ffs_opts->bsize = -1;
155 ffs_opts->fsize = -1;
156 ffs_opts->density = -1;
157 ffs_opts->minfree = MINFREE;
158 ffs_opts->optimization = FS_OPTSPACE;
159 ffs_opts->maxbpg = -1;
160 ffs_opts->avgfilesize = AVFILESIZ;
161 ffs_opts->avgfpdir = AFPDIR;
162 ffs_opts->version = 1;
163 ffs_opts->rdroot = 0;
164 ffs_opts->lp = NULL;
165 ffs_opts->pp = NULL;
166
167 fsopts->fs_specific = ffs_opts;
168 fsopts->fs_options = copy_opts(ffs_options);
169 }
170
171 void
ffs_cleanup_opts(fsinfo_t * fsopts)172 ffs_cleanup_opts(fsinfo_t *fsopts)
173 {
174 free(fsopts->fs_specific);
175 free(fsopts->fs_options);
176 }
177
178 int
ffs_parse_opts(const char * option,fsinfo_t * fsopts)179 ffs_parse_opts(const char *option, fsinfo_t *fsopts)
180 {
181 ffs_opt_t *ffs_opts = fsopts->fs_specific;
182 option_t *ffs_options = fsopts->fs_options;
183 char buf[1024];
184
185 int rv;
186
187 assert(option != NULL);
188 assert(fsopts != NULL);
189 assert(ffs_opts != NULL);
190
191 rv = set_option(ffs_options, option, buf, sizeof(buf));
192 if (rv == -1)
193 return 0;
194
195 if (ffs_options[rv].name == NULL)
196 abort();
197
198 if (strcmp(ffs_options[rv].name, "disklabel") == 0) {
199 struct disklabel *dp;
200
201 dp = getdiskbyname(buf);
202 if (dp == NULL)
203 errx(1, "unknown disk type: %s", buf);
204
205 ffs_opts->lp = emalloc(sizeof(struct disklabel));
206 *ffs_opts->lp = *dp;
207 } else if (strcmp(ffs_options[rv].name, "optimization") == 0) {
208 if (strcmp(buf, "time") == 0) {
209 ffs_opts->optimization = FS_OPTTIME;
210 } else if (strcmp(buf, "space") == 0) {
211 ffs_opts->optimization = FS_OPTSPACE;
212 } else {
213 warnx("Invalid optimization `%s'", buf);
214 return 0;
215 }
216 }
217 return 1;
218 }
219
220 struct disklabel *
ffs_makerdroot(const fsinfo_t * fsopts)221 ffs_makerdroot(const fsinfo_t *fsopts)
222 {
223 const ffs_opt_t *ffs_opts = fsopts->fs_specific;
224 struct disklabel *lp;
225 struct partition *pp;
226 uint32_t rdsize, poffset;
227 const uint32_t sectorsize = fsopts->sectorsize;
228 const uint32_t fsize = ffs_opts->fsize;
229 const uint32_t bsize = ffs_opts->bsize;
230
231 rdsize = (fsopts->size + sectorsize - 1) / sectorsize;
232 poffset = (fsopts->offset + sectorsize - 1) / sectorsize;
233
234 lp = ecalloc(1, sizeof(struct disklabel));
235
236 lp->d_version = 1;
237 lp->d_type = DTYPE_RDROOT;
238 strlcpy(lp->d_typename, "rdroot", sizeof(lp->d_typename));
239 lp->d_npartitions = RAW_PART + 1;
240 lp->d_secsize = sectorsize;
241 lp->d_ntracks = lp->d_ncylinders = 1;
242 lp->d_secpercyl = rdsize;
243 DL_SETDSIZE(lp, rdsize);
244
245 pp = &lp->d_partitions[0]; /* a.k.a. 'a' */
246 pp->p_fstype = FS_BSDFFS;
247 pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, bsize / fsize);
248 DL_SETPOFFSET(pp, poffset);
249 DL_SETPSIZE(pp, rdsize - poffset);
250
251 pp = &lp->d_partitions[RAW_PART]; /* a.k.a. 'c' */
252 DL_SETPOFFSET(pp, 0);
253 DL_SETPSIZE(pp, DL_GETDSIZE(lp));
254
255 return lp;
256 }
257
258 void
ffs_makefs(const char * image,const char * dir,fsnode * root,fsinfo_t * fsopts)259 ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
260 {
261 struct fs *superblock;
262 ffs_opt_t *ffs_opts = fsopts->fs_specific;
263
264 assert(image != NULL);
265 assert(dir != NULL);
266 assert(root != NULL);
267 assert(fsopts != NULL);
268
269 /* validate tree and options */
270 ffs_validate(dir, root, fsopts);
271
272 printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
273 image, (long long)fsopts->size, (long long)fsopts->inodes);
274
275 /* create image */
276 if (ffs_create_image(image, fsopts) == -1)
277 errx(1, "Image file `%s' not created.", image);
278
279 fsopts->curinode = ROOTINO;
280
281 /* populate image */
282 printf("Populating `%s'\n", image);
283 if (! ffs_populate_dir(dir, root, fsopts))
284 errx(1, "Image file `%s' not populated.", image);
285
286 bcleanup();
287
288 /* update various superblock parameters */
289 superblock = fsopts->superblock;
290 superblock->fs_fmod = 0;
291 superblock->fs_ffs1_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir;
292 superblock->fs_ffs1_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
293 superblock->fs_ffs1_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
294 superblock->fs_ffs1_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
295
296 /* write out superblock; image is now complete */
297 ffs_write_superblock(fsopts->superblock, fsopts);
298
299 if (ffs_opts->rdroot == 1) {
300 ffs_opts->lp = ffs_makerdroot(fsopts);
301 ffs_opts->pp = &ffs_opts->lp->d_partitions[0];
302 }
303
304 if (ffs_opts->lp != NULL) {
305 struct disklabel *lp = ffs_opts->lp;
306 uint16_t *p, *end, sum = 0;
307 ssize_t n;
308 uint32_t bpg;
309
310 bpg = superblock->fs_fpg / superblock->fs_frag;
311 while (bpg > UINT16_MAX)
312 bpg >>= 1;
313 ffs_opts->pp->p_cpg = bpg;
314
315 lp->d_magic = DISKMAGIC;
316 lp->d_magic2 = DISKMAGIC;
317 arc4random_buf(lp->d_uid, sizeof(lp->d_uid));
318 lp->d_checksum = 0;
319
320 p = (uint16_t *)lp;
321 end = (uint16_t *)&lp->d_partitions[lp->d_npartitions];
322 while (p < end)
323 sum ^= *p++;
324 lp->d_checksum = sum;
325
326 n = pwrite(fsopts->fd, lp, sizeof(struct disklabel),
327 fsopts->offset + LABELSECTOR * DEV_BSIZE + LABELOFFSET);
328 if (n == -1)
329 err(1, "failed to write disklabel");
330 else if (n < sizeof(struct disklabel))
331 errx(1, "failed to write disklabel: short write");
332 }
333
334 if (close(fsopts->fd) == -1)
335 err(1, "Closing `%s'", image);
336 fsopts->fd = -1;
337 printf("Image `%s' complete\n", image);
338 }
339
340 /* end of public functions */
341
342
343 static void
ffs_validate(const char * dir,fsnode * root,fsinfo_t * fsopts)344 ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
345 {
346 ffs_opt_t *ffs_opts = fsopts->fs_specific;
347 struct disklabel *lp = ffs_opts->lp;
348 struct partition *pp = NULL;
349 int32_t ncg = 1;
350 int i;
351
352 assert(dir != NULL);
353 assert(root != NULL);
354 assert(fsopts != NULL);
355 assert(ffs_opts != NULL);
356
357 if (lp != NULL && ffs_opts->rdroot == 1)
358 errx(1, "rdroot and disklabel are mutually exclusive");
359
360 if (lp != NULL) {
361 for (i = 0; i < lp->d_npartitions; i++) {
362 pp = &lp->d_partitions[i];
363 if (pp->p_fstype == FS_BSDFFS &&
364 pp->p_offset * lp->d_secsize == fsopts->offset) {
365 break;
366 }
367 }
368 if (i == lp->d_npartitions)
369 errx(1, "no matching partition found in the disklabel");
370 ffs_opts->pp = pp;
371
372 if (pp->p_fragblock == 0)
373 errx(1, "fragment size missing in disktab");
374 if (fsopts->freeblocks != 0 || fsopts->freeblockpc != 0 ||
375 fsopts->freefiles != 0 || fsopts->freefilepc != 0 ||
376 fsopts->minsize != 0 || fsopts->maxsize != 0 ||
377 fsopts->sectorsize != -1 || fsopts->size != 0)
378 errx(1, "-bfMmSs and disklabel are mutually exclusive");
379 if (ffs_opts->fsize != -1 || ffs_opts->bsize != -1)
380 errx(1, "b/fsize and disklabel are mutually exclusive");
381
382 fsopts->sectorsize = lp->d_secsize;
383 fsopts->minsize = fsopts->maxsize =
384 DL_GETPSIZE(pp) * lp->d_secsize;
385 ffs_opts->fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
386 ffs_opts->bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock);
387 } else if (ffs_opts->rdroot == 1) {
388 if (fsopts->freeblocks != 0 || fsopts->freeblockpc != 0 ||
389 fsopts->freefiles != 0 || fsopts->freefilepc != 0 ||
390 fsopts->offset != 0 || fsopts->sectorsize != -1 ||
391 fsopts->minsize != fsopts->maxsize)
392 errx(1, "rdroot and -bfMmOS are mutually exclusive");
393 if (fsopts->minsize == 0 || fsopts->maxsize == 0)
394 errx(1, "rdroot requires -s");
395 if (ffs_opts->minfree != 0)
396 errx(1, "rdroot requires minfree=0");
397 if (ffs_opts->fsize == -1 || ffs_opts->bsize == -1 ||
398 ffs_opts->density == -1)
399 errx(1, "rdroot requires bsize, fsize and density");
400 fsopts->sectorsize = DEV_BSIZE;
401 }
402
403 /* set FFS defaults */
404 if (fsopts->sectorsize == -1)
405 fsopts->sectorsize = DFL_SECSIZE;
406 if (ffs_opts->fsize == -1)
407 ffs_opts->fsize = MAXIMUM(DFL_FRAGSIZE, fsopts->sectorsize);
408 if (ffs_opts->bsize == -1)
409 ffs_opts->bsize = MINIMUM(DFL_BLKSIZE, 8 * ffs_opts->fsize);
410 /* fsopts->density is set below */
411 /* XXX ondisk32 */
412 if (ffs_opts->maxbpg == -1)
413 ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t);
414
415 /* calculate size of tree */
416 ffs_size_dir(root, fsopts);
417 fsopts->inodes += ROOTINO; /* include first two inodes */
418
419 /* add requested slop */
420 fsopts->size += fsopts->freeblocks;
421 fsopts->inodes += fsopts->freefiles;
422 if (fsopts->freefilepc > 0)
423 fsopts->inodes =
424 fsopts->inodes * (100 + fsopts->freefilepc) / 100;
425 if (fsopts->freeblockpc > 0)
426 fsopts->size =
427 fsopts->size * (100 + fsopts->freeblockpc) / 100;
428
429 /* add space needed for superblocks */
430 /*
431 * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
432 * typically used for small filesystems where space matters.
433 * XXX make this an option.
434 */
435 fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
436 /* add space needed to store inodes, x3 for blockmaps, etc */
437 if (ffs_opts->version == 1)
438 fsopts->size += ncg * sizeof(struct ufs1_dinode) *
439 roundup(fsopts->inodes / ncg,
440 ffs_opts->bsize / sizeof(struct ufs1_dinode));
441 else
442 fsopts->size += ncg * sizeof(struct ufs2_dinode) *
443 roundup(fsopts->inodes / ncg,
444 ffs_opts->bsize / sizeof(struct ufs2_dinode));
445
446 /* add minfree */
447 if (ffs_opts->minfree > 0)
448 fsopts->size =
449 fsopts->size * (100 + ffs_opts->minfree) / 100;
450 /*
451 * XXX any other fs slop to add, such as csum's, bitmaps, etc ??
452 */
453
454 if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */
455 fsopts->size = fsopts->minsize;
456
457 /* round up to the next block */
458 fsopts->size = roundup(fsopts->size, ffs_opts->bsize);
459
460 /* calculate density if necessary */
461 if (ffs_opts->density == -1)
462 ffs_opts->density = fsopts->size / fsopts->inodes + 1;
463
464 /* now check calculated sizes vs requested sizes */
465 if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
466 errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
467 dir, (long long)fsopts->size, (long long)fsopts->maxsize);
468 }
469 }
470
471
472 static int
ffs_create_image(const char * image,fsinfo_t * fsopts)473 ffs_create_image(const char *image, fsinfo_t *fsopts)
474 {
475 struct fs *fs;
476 char *buf;
477 int i, bufsize;
478 off_t bufrem;
479 time_t tstamp;
480 int oflags = O_RDWR | O_CREAT;
481
482 assert (image != NULL);
483 assert (fsopts != NULL);
484
485 /* create image */
486 if (fsopts->offset == 0)
487 oflags |= O_TRUNC;
488 if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
489 warn("Can't open `%s' for writing", image);
490 return (-1);
491 }
492
493 /* zero image */
494 bufsize = 8192;
495 bufrem = fsopts->size;
496
497 if (fsopts->offset != 0)
498 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
499 warn("can't seek");
500 return -1;
501 }
502
503 if (bufrem > 0)
504 buf = ecalloc(1, bufsize);
505 while (bufrem > 0) {
506 i = write(fsopts->fd, buf, MINIMUM(bufsize, bufrem));
507 if (i == -1) {
508 warn("zeroing image, %lld bytes to go",
509 (long long)bufrem);
510 free(buf);
511 return (-1);
512 }
513 bufrem -= i;
514 }
515 free(buf);
516
517 /* make the file system */
518 if (Tflag) {
519 tstamp = stampts;
520 srandom_deterministic(stampts);
521 } else
522 tstamp = start_time.tv_sec;
523
524 fs = ffs_mkfs(image, fsopts, tstamp);
525 fsopts->superblock = (void *)fs;
526
527 if ((off_t)(fs->fs_cstotal.cs_nifree + ROOTINO) < fsopts->inodes) {
528 warnx(
529 "Image file `%s' has %lld free inodes; %lld are required.",
530 image,
531 (long long)(fs->fs_cstotal.cs_nifree + ROOTINO),
532 (long long)fsopts->inodes);
533 return (-1);
534 }
535 return (fsopts->fd);
536 }
537
538
539 static void
ffs_size_dir(fsnode * root,fsinfo_t * fsopts)540 ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
541 {
542 struct direct tmpdir;
543 fsnode * node;
544 int curdirsize, this;
545 ffs_opt_t *ffs_opts = fsopts->fs_specific;
546
547 /* node may be NULL (empty directory) */
548 assert(fsopts != NULL);
549 assert(ffs_opts != NULL);
550
551 #define ADDDIRENT(e) do { \
552 tmpdir.d_namlen = strlen((e)); \
553 this = DIRSIZ(&tmpdir); \
554 if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \
555 curdirsize = roundup(curdirsize, DIRBLKSIZ); \
556 curdirsize += this; \
557 } while (0);
558
559 /*
560 * XXX this needs to take into account extra space consumed
561 * by indirect blocks, etc.
562 */
563 #define ADDSIZE(x) do { \
564 fsopts->size += roundup((x), ffs_opts->fsize); \
565 } while (0);
566
567 curdirsize = 0;
568 for (node = root; node != NULL; node = node->next) {
569 ADDDIRENT(node->name);
570 if (node == root) { /* we're at "." */
571 assert(strcmp(node->name, ".") == 0);
572 ADDDIRENT("..");
573 } else if ((node->inode->flags & FI_SIZED) == 0) {
574 /* don't count duplicate names */
575 node->inode->flags |= FI_SIZED;
576 fsopts->inodes++;
577 if (node->type == S_IFREG)
578 ADDSIZE(node->inode->st.st_size);
579 if (node->type == S_IFLNK) {
580 size_t slen;
581
582 slen = strlen(node->symlink) + 1;
583 if (slen >= (ffs_opts->version == 1 ?
584 MAXSYMLINKLEN_UFS1 :
585 MAXSYMLINKLEN_UFS2))
586 ADDSIZE(slen);
587 }
588 }
589 if (node->type == S_IFDIR)
590 ffs_size_dir(node->child, fsopts);
591 }
592 ADDSIZE(curdirsize);
593 }
594
595 static void *
ffs_build_dinode1(struct ufs1_dinode * dinp,dirbuf_t * dbufp,fsnode * cur,fsnode * root,fsinfo_t * fsopts)596 ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
597 fsnode *root, fsinfo_t *fsopts)
598 {
599 size_t slen;
600 void *membuf;
601
602 memset(dinp, 0, sizeof(*dinp));
603 dinp->di_mode = cur->inode->st.st_mode;
604 dinp->di_nlink = cur->inode->nlink;
605 dinp->di_size = cur->inode->st.st_size;
606 dinp->di_flags = cur->inode->st.st_flags;
607 dinp->di_gen = cur->inode->st.st_gen;
608 dinp->di_uid = cur->inode->st.st_uid;
609 dinp->di_gid = cur->inode->st.st_gid;
610
611 dinp->di_atime = cur->inode->st.st_atime;
612 dinp->di_mtime = cur->inode->st.st_mtime;
613 dinp->di_ctime = cur->inode->st.st_ctime;
614 dinp->di_atimensec = cur->inode->st.st_atim.tv_nsec;
615 dinp->di_mtimensec = cur->inode->st.st_mtim.tv_nsec;
616 dinp->di_ctimensec = cur->inode->st.st_ctim.tv_nsec;
617 /* not set: di_db, di_ib, di_blocks, di_spare */
618
619 membuf = NULL;
620 if (cur == root) { /* "."; write dirbuf */
621 membuf = dbufp->buf;
622 dinp->di_size = dbufp->size;
623 } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
624 dinp->di_size = 0; /* a device */
625 dinp->di_rdev = cur->inode->st.st_rdev;
626 } else if (S_ISLNK(cur->type)) { /* symlink */
627 slen = strlen(cur->symlink);
628 if (slen < MAXSYMLINKLEN_UFS1) { /* short link */
629 memcpy(dinp->di_db, cur->symlink, slen);
630 } else
631 membuf = cur->symlink;
632 dinp->di_size = slen;
633 }
634 return membuf;
635 }
636
637 static void *
ffs_build_dinode2(struct ufs2_dinode * dinp,dirbuf_t * dbufp,fsnode * cur,fsnode * root,fsinfo_t * fsopts)638 ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
639 fsnode *root, fsinfo_t *fsopts)
640 {
641 size_t slen;
642 void *membuf;
643
644 memset(dinp, 0, sizeof(*dinp));
645 dinp->di_mode = cur->inode->st.st_mode;
646 dinp->di_nlink = cur->inode->nlink;
647 dinp->di_size = cur->inode->st.st_size;
648 dinp->di_flags = cur->inode->st.st_flags;
649 dinp->di_gen = cur->inode->st.st_gen;
650 dinp->di_uid = cur->inode->st.st_uid;
651 dinp->di_gid = cur->inode->st.st_gid;
652
653 dinp->di_atime = cur->inode->st.st_atime;
654 dinp->di_mtime = cur->inode->st.st_mtime;
655 dinp->di_ctime = cur->inode->st.st_ctime;
656 dinp->di_atimensec = cur->inode->st.st_atim.tv_nsec;
657 dinp->di_mtimensec = cur->inode->st.st_mtim.tv_nsec;
658 dinp->di_ctimensec = cur->inode->st.st_ctim.tv_nsec;
659 /* not set: di_db, di_ib, di_blocks, di_spare */
660
661 membuf = NULL;
662 if (cur == root) { /* "."; write dirbuf */
663 membuf = dbufp->buf;
664 dinp->di_size = dbufp->size;
665 } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
666 dinp->di_size = 0; /* a device */
667 dinp->di_rdev = cur->inode->st.st_rdev;
668 } else if (S_ISLNK(cur->type)) { /* symlink */
669 slen = strlen(cur->symlink);
670 if (slen < MAXSYMLINKLEN_UFS2) { /* short link */
671 memcpy(dinp->di_db, cur->symlink, slen);
672 } else
673 membuf = cur->symlink;
674 dinp->di_size = slen;
675 }
676 return membuf;
677 }
678
679 static int
ffs_populate_dir(const char * dir,fsnode * root,fsinfo_t * fsopts)680 ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
681 {
682 fsnode *cur;
683 dirbuf_t dirbuf;
684 union dinode din;
685 void *membuf;
686 char path[PATH_MAX + 1];
687 ffs_opt_t *ffs_opts = fsopts->fs_specific;
688
689 assert(dir != NULL);
690 assert(root != NULL);
691 assert(fsopts != NULL);
692 assert(ffs_opts != NULL);
693
694 (void)memset(&dirbuf, 0, sizeof(dirbuf));
695
696 /*
697 * pass 1: allocate inode numbers, build directory `file'
698 */
699 for (cur = root; cur != NULL; cur = cur->next) {
700 if ((cur->inode->flags & FI_ALLOCATED) == 0) {
701 cur->inode->flags |= FI_ALLOCATED;
702 if (cur == root && cur->parent != NULL)
703 cur->inode->ino = cur->parent->inode->ino;
704 else {
705 cur->inode->ino = fsopts->curinode;
706 fsopts->curinode++;
707 }
708 }
709 ffs_make_dirbuf(&dirbuf, cur->name, cur);
710 if (cur == root) { /* we're at "."; add ".." */
711 ffs_make_dirbuf(&dirbuf, "..",
712 cur->parent == NULL ? cur : cur->parent->first);
713 root->inode->nlink++; /* count my parent's link */
714 } else if (cur->child != NULL)
715 root->inode->nlink++; /* count my child's link */
716
717 /*
718 * XXX possibly write file and long symlinks here,
719 * ensuring that blocks get written before inodes?
720 * otoh, this isn't a real filesystem, so who
721 * cares about ordering? :-)
722 */
723 }
724
725 /*
726 * pass 2: write out dirbuf, then non-directories at this level
727 */
728 for (cur = root; cur != NULL; cur = cur->next) {
729 if (cur->inode->flags & FI_WRITTEN)
730 continue; /* skip hard-linked entries */
731 cur->inode->flags |= FI_WRITTEN;
732
733 if ((size_t)snprintf(path, sizeof(path), "%s/%s/%s", cur->root,
734 cur->path, cur->name) >= sizeof(path))
735 errx(1, "Pathname too long.");
736
737 if (cur->child != NULL)
738 continue; /* child creates own inode */
739
740 /* build on-disk inode */
741 if (ffs_opts->version == 1)
742 membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
743 root, fsopts);
744 else
745 membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur,
746 root, fsopts);
747
748 if (membuf != NULL) {
749 ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
750 } else if (S_ISREG(cur->type)) {
751 ffs_write_file(&din, cur->inode->ino, path, fsopts);
752 } else {
753 assert (! S_ISDIR(cur->type));
754 ffs_write_inode(&din, cur->inode->ino, fsopts);
755 }
756 }
757
758 /*
759 * pass 3: write out sub-directories
760 */
761 for (cur = root; cur != NULL; cur = cur->next) {
762 if (cur->child == NULL)
763 continue;
764 if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir,
765 cur->name) >= sizeof(path))
766 errx(1, "Pathname too long.");
767 if (! ffs_populate_dir(path, cur->child, fsopts))
768 return (0);
769 }
770
771 /* cleanup */
772 free(dirbuf.buf);
773 return (1);
774 }
775
776
777 static void
ffs_write_file(union dinode * din,uint32_t ino,void * buf,fsinfo_t * fsopts)778 ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
779 {
780 int isfile, ffd;
781 char *fbuf, *p;
782 off_t bufleft, chunk, offset;
783 ssize_t nread;
784 struct inode in;
785 struct mkfsbuf * bp;
786 ffs_opt_t *ffs_opts = fsopts->fs_specific;
787 struct mkfsvnode vp = { fsopts, NULL };
788
789 assert (din != NULL);
790 assert (buf != NULL);
791 assert (fsopts != NULL);
792 assert (ffs_opts != NULL);
793
794 isfile = S_ISREG(DIP(din, mode));
795 fbuf = NULL;
796 ffd = -1;
797 p = NULL;
798
799 in.i_fs = (struct fs *)fsopts->superblock;
800 in.i_devvp = &vp;
801
802 in.i_number = ino;
803 in.i_size = DIP(din, size);
804 if (ffs_opts->version == 1)
805 memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
806 sizeof(in.i_din.ffs1_din));
807 else
808 memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
809 sizeof(in.i_din.ffs2_din));
810
811 if (DIP(din, size) == 0)
812 goto write_inode_and_leave; /* mmm, cheating */
813
814 if (isfile) {
815 fbuf = emalloc(ffs_opts->bsize);
816 if ((ffd = open((char *)buf, O_RDONLY)) == -1) {
817 warn("Can't open `%s' for reading", (char *)buf);
818 goto leave_ffs_write_file;
819 }
820 } else {
821 p = buf;
822 }
823
824 chunk = 0;
825 for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
826 chunk = MINIMUM(bufleft, ffs_opts->bsize);
827 if (!isfile)
828 ;
829 else if ((nread = read(ffd, fbuf, chunk)) == -1)
830 err(1, "Reading `%s', %lld bytes to go", (char *)buf,
831 (long long)bufleft);
832 else if (nread != chunk)
833 errx(1, "Reading `%s', %lld bytes to go, "
834 "read %zd bytes, expected %ju bytes, does "
835 "metalog size= attribute mismatch source size?",
836 (char *)buf, (long long)bufleft, nread,
837 (uintmax_t)chunk);
838 else
839 p = fbuf;
840 offset = DIP(din, size) - bufleft;
841 /*
842 * XXX if holey support is desired, do the check here
843 *
844 * XXX might need to write out last bit in fragroundup
845 * sized chunk. however, ffs_balloc() handles this for us
846 */
847 errno = ffs_balloc(&in, offset, chunk, &bp);
848 bad_ffs_write_file:
849 if (errno != 0)
850 err(1,
851 "Writing inode %d (%s), bytes %lld + %lld",
852 ino,
853 isfile ? (char *)buf :
854 inode_type(DIP(din, mode) & S_IFMT),
855 (long long)offset, (long long)chunk);
856 memcpy(bp->b_data, p, chunk);
857 errno = bwrite(bp);
858 if (errno != 0)
859 goto bad_ffs_write_file;
860 if (!isfile)
861 p += chunk;
862 }
863
864 write_inode_and_leave:
865 ffs_write_inode(&in.i_din, in.i_number, fsopts);
866
867 leave_ffs_write_file:
868 free(fbuf);
869 if (ffd != -1)
870 close(ffd);
871 }
872
873
874 static void
ffs_make_dirbuf(dirbuf_t * dbuf,const char * name,fsnode * node)875 ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node)
876 {
877 struct direct de, *dp;
878 uint16_t llen;
879 u_char *newbuf;
880
881 assert (dbuf != NULL);
882 assert (name != NULL);
883 assert (node != NULL);
884 /* create direct entry */
885 (void)memset(&de, 0, sizeof(de));
886 de.d_ino = node->inode->ino;
887 de.d_type = IFTODT(node->type);
888 de.d_namlen = (uint8_t)strlen(name);
889 strlcpy(de.d_name, name, sizeof de.d_name);
890 de.d_reclen = DIRSIZ(&de);
891
892 dp = (struct direct *)(dbuf->buf + dbuf->cur);
893 llen = 0;
894 if (dp != NULL)
895 llen = DIRSIZ(dp);
896
897 if (de.d_reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) {
898 newbuf = erealloc(dbuf->buf, dbuf->size + DIRBLKSIZ);
899 dbuf->buf = newbuf;
900 dbuf->size += DIRBLKSIZ;
901 memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ);
902 dbuf->cur = dbuf->size - DIRBLKSIZ;
903 } else if (dp) { /* shrink end of previous */
904 dp->d_reclen = llen;
905 dbuf->cur += llen;
906 }
907 dp = (struct direct *)(dbuf->buf + dbuf->cur);
908 memcpy(dp, &de, de.d_reclen);
909 dp->d_reclen = dbuf->size - dbuf->cur;
910 }
911
912 /*
913 * cribbed from sys/ufs/ffs/ffs_alloc.c
914 */
915 static void
ffs_write_inode(union dinode * dp,uint32_t ino,const fsinfo_t * fsopts)916 ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
917 {
918 char *buf;
919 struct ufs1_dinode *dp1;
920 struct ufs2_dinode *dp2, *dip;
921 struct cg *cgp;
922 struct fs *fs;
923 int cg, cgino, i;
924 daddr_t d;
925 char sbbuf[FFS_MAXBSIZE];
926 uint32_t initediblk;
927 ffs_opt_t *ffs_opts = fsopts->fs_specific;
928
929 assert (dp != NULL);
930 assert (ino > 0);
931 assert (fsopts != NULL);
932 assert (ffs_opts != NULL);
933
934 fs = (struct fs *)fsopts->superblock;
935 cg = ino_to_cg(fs, ino);
936 cgino = ino % fs->fs_ipg;
937
938 ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
939 fsopts);
940 cgp = (struct cg *)sbbuf;
941 if (!cg_chkmagic(cgp))
942 errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
943
944 assert (isclr(cg_inosused(cgp), cgino));
945
946 buf = emalloc(fs->fs_bsize);
947 dp1 = (struct ufs1_dinode *)buf;
948 dp2 = (struct ufs2_dinode *)buf;
949
950 if (fs->fs_cstotal.cs_nifree == 0)
951 errx(1, "ffs_write_inode: fs out of inodes for ino %u",
952 ino);
953 if (fs->fs_cs(fs, cg).cs_nifree == 0)
954 errx(1,
955 "ffs_write_inode: cg %d out of inodes for ino %u",
956 cg, ino);
957 setbit(cg_inosused(cgp), cgino);
958 cgp->cg_cs.cs_nifree -= 1;
959 fs->fs_cstotal.cs_nifree--;
960 fs->fs_cs(fs, cg).cs_nifree--;
961 if (S_ISDIR(DIP(dp, mode))) {
962 cgp->cg_cs.cs_ndir += 1;
963 fs->fs_cstotal.cs_ndir++;
964 fs->fs_cs(fs, cg).cs_ndir++;
965 }
966
967 /*
968 * Initialize inode blocks on the fly for UFS2.
969 */
970 initediblk = cgp->cg_initediblk;
971 if (ffs_opts->version == 2 &&
972 (uint32_t)(cgino + INOPB(fs)) > initediblk &&
973 initediblk < cgp->cg_ffs2_niblk) {
974 memset(buf, 0, fs->fs_bsize);
975 dip = (struct ufs2_dinode *)buf;
976 for (i = 0; i < INOPB(fs); i++) {
977 dip->di_gen = random() / 2 + 1;
978 dip++;
979 }
980 ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs,
981 cg * fs->fs_ipg + initediblk)),
982 fs->fs_bsize, buf, fsopts);
983 initediblk += INOPB(fs);
984 cgp->cg_initediblk = initediblk;
985 }
986
987
988 ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
989 fsopts);
990
991 /* now write inode */
992 d = fsbtodb(fs, ino_to_fsba(fs, ino));
993 ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
994 if (ffs_opts->version == 1)
995 dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
996 else
997 dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din;
998 ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
999 free(buf);
1000 }
1001