summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorMartin Natano <natano@cvs.openbsd.org>2016-10-16 17:08:54 +0000
committerMartin Natano <natano@cvs.openbsd.org>2016-10-16 17:08:54 +0000
commit27dd9826b6ad14ee3599a81db61506458f2b8438 (patch)
treefc159319113b6ca6798aa78f6c9648e1f507dc55 /usr.sbin
parent4289d4423235fe954dcd700a8d0dec601ce2ec7f (diff)
Import makefs - a tool to create filesystem images from a directory.
This is a rough port of the NetBSD tool with some features removed we don't need. It compiles, but I don't promise anything more. Importing now, so we can hack on it in tree. The supported filesystem types are: cd9660, ffs and msdosfs. ok deraadt
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/makefs/Makefile39
-rw-r--r--usr.sbin/makefs/README124
-rw-r--r--usr.sbin/makefs/cd9660.c2144
-rw-r--r--usr.sbin/makefs/cd9660.h353
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_archimedes.c121
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_archimedes.h48
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_conversion.c201
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_debug.c482
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_eltorito.c694
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_eltorito.h162
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_strings.c118
-rw-r--r--usr.sbin/makefs/cd9660/cd9660_write.c504
-rw-r--r--usr.sbin/makefs/cd9660/iso9660_rrip.c834
-rw-r--r--usr.sbin/makefs/cd9660/iso9660_rrip.h290
-rw-r--r--usr.sbin/makefs/ffs.c1152
-rw-r--r--usr.sbin/makefs/ffs.h68
-rw-r--r--usr.sbin/makefs/ffs/buf.c211
-rw-r--r--usr.sbin/makefs/ffs/buf.h115
-rw-r--r--usr.sbin/makefs/ffs/ffs_alloc.c589
-rw-r--r--usr.sbin/makefs/ffs/ffs_balloc.c574
-rw-r--r--usr.sbin/makefs/ffs/ffs_extern.h76
-rw-r--r--usr.sbin/makefs/ffs/mkfs.c824
-rw-r--r--usr.sbin/makefs/ffs/newfs_extern.h34
-rw-r--r--usr.sbin/makefs/ffs/ufs_bmap.c136
-rw-r--r--usr.sbin/makefs/ffs/ufs_inode.h112
-rw-r--r--usr.sbin/makefs/fs/cd9660/cd9660_rrip.h143
-rw-r--r--usr.sbin/makefs/fs/cd9660/iso.h275
-rw-r--r--usr.sbin/makefs/fs/cd9660/iso_rrip.h85
-rw-r--r--usr.sbin/makefs/fs/msdosfs/bootsect.h96
-rw-r--r--usr.sbin/makefs/fs/msdosfs/bpb.h190
-rw-r--r--usr.sbin/makefs/fs/msdosfs/clock_subr.c193
-rw-r--r--usr.sbin/makefs/fs/msdosfs/clock_subr.h111
-rw-r--r--usr.sbin/makefs/fs/msdosfs/denode.h326
-rw-r--r--usr.sbin/makefs/fs/msdosfs/direntry.h145
-rw-r--r--usr.sbin/makefs/fs/msdosfs/fat.h117
-rw-r--r--usr.sbin/makefs/fs/msdosfs/msdosfs_conv.c1023
-rw-r--r--usr.sbin/makefs/fs/msdosfs/msdosfs_fat.c1098
-rw-r--r--usr.sbin/makefs/fs/msdosfs/msdosfs_lookup.c654
-rw-r--r--usr.sbin/makefs/fs/msdosfs/msdosfs_unicode.c960
-rw-r--r--usr.sbin/makefs/fs/msdosfs/msdosfsmount.h229
-rw-r--r--usr.sbin/makefs/makefs.8341
-rw-r--r--usr.sbin/makefs/makefs.c438
-rw-r--r--usr.sbin/makefs/makefs.h248
-rw-r--r--usr.sbin/makefs/msdos.c258
-rw-r--r--usr.sbin/makefs/msdos.h39
-rw-r--r--usr.sbin/makefs/msdos/msdosfs_denode.c358
-rw-r--r--usr.sbin/makefs/msdos/msdosfs_vfsops.c424
-rw-r--r--usr.sbin/makefs/msdos/msdosfs_vnops.c629
-rw-r--r--usr.sbin/makefs/newfs_msdos/mkfs_msdos.c902
-rw-r--r--usr.sbin/makefs/newfs_msdos/mkfs_msdos.h68
-rw-r--r--usr.sbin/makefs/sys/bootblock.h1449
-rw-r--r--usr.sbin/makefs/sys/clock.h89
-rw-r--r--usr.sbin/makefs/sys/quota.h81
-rw-r--r--usr.sbin/makefs/ufs/ffs/ffs_bswap.c266
-rw-r--r--usr.sbin/makefs/ufs/ffs/ffs_extern.h103
-rw-r--r--usr.sbin/makefs/ufs/ffs/ffs_subr.c278
-rw-r--r--usr.sbin/makefs/ufs/ffs/ffs_tables.c136
-rw-r--r--usr.sbin/makefs/ufs/ffs/fs.h743
-rw-r--r--usr.sbin/makefs/ufs/ufs/dinode.h176
-rw-r--r--usr.sbin/makefs/ufs/ufs/dir.h204
-rw-r--r--usr.sbin/makefs/ufs/ufs/quota.h92
-rw-r--r--usr.sbin/makefs/ufs/ufs/ufs_bswap.h64
-rw-r--r--usr.sbin/makefs/walk.c447
-rw-r--r--usr.sbin/makefs/xmalloc.c43
64 files changed, 23526 insertions, 0 deletions
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644
index 00000000000..23e46fb3500
--- /dev/null
+++ b/usr.sbin/makefs/Makefile
@@ -0,0 +1,39 @@
+# $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $
+#
+
+.include <bsd.own.mk>
+
+PROG= makefs
+
+SRCS= cd9660.c ffs.c msdos.c \
+ makefs.c \
+ walk.c \
+ xmalloc.c
+
+# cd9660
+SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c
+SRCS+= cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c
+
+# ffs
+SRCS+= ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c ffs_tables.c ufs_bmap.c
+SRCS+= buf.c mkfs.c
+
+# msdos
+SRCS+= mkfs_msdos.c msdosfs_fat.c msdosfs_conv.c msdosfs_vfsops.c
+SRCS+= msdosfs_lookup.c msdosfs_denode.c msdosfs_vnops.c msdosfs_unicode.c
+SRCS+= clock_subr.c
+
+MAN= makefs.8
+
+CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/fs/cd9660
+CPPFLAGS+= -DMSDOS_EI -I${.CURDIR}/fs/msdosfs -I${.CURDIR}/newfs_msdos
+#CPPFLAGS+= -DMSDOSFS_DEBUG
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.PATH: ${.CURDIR}/cd9660 ${.CURDIR}/fs/cd9660 \
+ ${.CURDIR}/ffs ${.CURDIR}/ufs/ffs \
+ ${.CURDIR}/msdos ${.CURDIR}/fs/msdosfs ${.CURDIR}/newfs_msdos
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/makefs/README b/usr.sbin/makefs/README
new file mode 100644
index 00000000000..db5ef461dfd
--- /dev/null
+++ b/usr.sbin/makefs/README
@@ -0,0 +1,124 @@
+$NetBSD: README,v 1.7 2015/01/12 19:50:47 christos Exp $
+
+makefs - build a file system image from a directory tree
+
+NOTES:
+
+ * This tool uses modified local copies of source found in other
+ parts of the tree. This is intentional.
+
+ * makefs is a work in progress, and subject to change.
+
+
+user overview:
+--------------
+
+makefs creates a file system image from a given directory tree.
+the following file system types can be built:
+
+ cd9660 ISO 9660 file system
+ ffs BSD fast file system
+ msdos MS-DOS `FAT' file system (FAT12, FAT16, FAT32)
+
+Various file system independent parameters and contraints can be
+specified, such as:
+
+ - minimum file system size (in KB)
+ - maximum file system size (in KB)
+ - free inodes
+ - free blocks (in KB)
+ - file containing list of files to specifically exclude or include
+ - fnmatch(3) pattern of filenames to exclude or include
+ - endianness of target file system
+
+File system specific parameters can be given as well, with a command
+line option such as "-o fsspeccific-options,comma-separated".
+For example, ffs would allow tuning of:
+
+ - block & fragment size
+ - cylinder groups
+ - number of blocks per inode
+ - minimum free space
+
+Other file systems might have controls on how to "munge" file names to
+fit within the constraints of the target file system.
+
+Exit codes:
+ 0 all ok
+ 1 fatal error
+ 2 some files couldn't be added during image creation
+ (bad perms, missing file, etc). image will continue
+ to be made
+
+
+Implementation overview:
+------------------------
+
+The implementation must allow for easy addition of extra file systems
+with minimal changes to the file system independent sections.
+
+The main program will:
+ - parse the options, including calling fs-specific routines to
+ validate fs-specific options
+ - walk the tree, building up a data structure which represents
+ the tree to stuff into the image. The structure will
+ probably be a similar tree to what mtree(8) uses internally;
+ a linked list of entries per directory with a child pointer
+ to children of directories. ".." won't be stored in the list;
+ the fs-specific tree walker should add this if required by the fs.
+ this builder have the smarts to handle hard links correctly.
+ - Call an fs-specific routine to build the image based on the
+ data structures.
+
+Each fs-specific module should have the following external interfaces:
+
+ prepare_options optional file system specific defaults that need to be
+ setup before parsing fs-specific options.
+
+ parse_options parse the string for fs-specific options, feeding
+ errors back to the user as appropriate
+
+ cleanup_options optional file system specific data that need to be
+ cleaned up when done with this filesystem.
+
+ make_fs take the data structures representing the
+ directory tree and fs parameters,
+ validate that the parameters are valid
+ (e.g, the requested image will be large enough),
+ create the image, and
+ populate the image
+
+prepare_options and cleanup_options are optional and can be NULL.
+
+NOTE: All file system specific options are referenced via the fs_specific
+pointer from the fsinfo_t strucutre. It is up to the filesystem to allocate
+and free any data needed for this via the prepare and cleanup callbacks.
+
+Each fs-specific module will need to add its routines to the dispatch array
+in makefs.c and add prototypes for these to makefs.h
+
+All other implementation details should not need to change any of the
+generic code.
+
+ffs implementation
+------------------
+
+In the ffs case, we can leverage off sbin/newfs/mkfs.c to actually build
+the image. When building and populating the image, the implementation
+can be greatly simplified if some assumptions are made:
+ - the total required size (in blocks and inodes) is determined
+ as part of the validation phase
+ - a "file" (including a directory) has a known size, so
+ support for growing a file is not necessary
+
+Two underlying primitives are provided:
+ make_inode create an inode, returning the inode number
+
+ write_file write file (from memory if DIR, file descriptor
+ if FILE or SYMLINK), referencing given inode.
+ it is smart enough to know if a short symlink
+ can be stuffed into the inode, etc.
+
+When creating a directory, the directory entries in the previously
+built tree data structure is scanned and built in memory so it can
+be written entirely as a single write_file() operation.
diff --git a/usr.sbin/makefs/cd9660.c b/usr.sbin/makefs/cd9660.c
new file mode 100644
index 00000000000..313db045653
--- /dev/null
+++ b/usr.sbin/makefs/cd9660.c
@@ -0,0 +1,2144 @@
+/* $NetBSD: cd9660.c,v 1.52 2015/12/24 15:52:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989, 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. 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.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <util.h>
+#include <inttypes.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660/iso9660_rrip.h"
+#include "cd9660/cd9660_archimedes.h"
+
+/*
+ * Global variables
+ */
+
+static void cd9660_finalize_PVD(iso9660_disk *);
+static cd9660node *cd9660_allocate_cd9660node(void);
+static void cd9660_set_defaults(iso9660_disk *);
+static int cd9660_arguments_set_string(const char *, const char *, size_t,
+ char, char *);
+static void cd9660_populate_iso_dir_record(
+ struct _iso_directory_record_cd9660 *, u_char, u_char, u_char,
+ const char *);
+static void cd9660_setup_root_node(iso9660_disk *);
+static int cd9660_setup_volume_descriptors(iso9660_disk *);
+#if 0
+static int cd9660_fill_extended_attribute_record(cd9660node *);
+#endif
+static void cd9660_sort_nodes(cd9660node *);
+static int cd9660_translate_node_common(iso9660_disk *, cd9660node *);
+static int cd9660_translate_node(iso9660_disk *, fsnode *, cd9660node *);
+static int cd9660_compare_filename(const char *, const char *);
+static void cd9660_sorted_child_insert(cd9660node *, cd9660node *);
+static int cd9660_handle_collisions(iso9660_disk *, cd9660node *, int);
+static cd9660node *cd9660_rename_filename(iso9660_disk *, cd9660node *, int,
+ int);
+static void cd9660_copy_filenames(iso9660_disk *, cd9660node *);
+static void cd9660_sorting_nodes(cd9660node *);
+static int cd9660_count_collisions(cd9660node *);
+static cd9660node *cd9660_rrip_move_directory(iso9660_disk *, cd9660node *);
+static int cd9660_add_dot_records(iso9660_disk *, cd9660node *);
+
+static void cd9660_convert_structure(iso9660_disk *, fsnode *, cd9660node *, int,
+ int *, int *);
+static void cd9660_free_structure(cd9660node *);
+static int cd9660_generate_path_table(iso9660_disk *);
+static int cd9660_level1_convert_filename(iso9660_disk *, const char *, char *,
+ int);
+static int cd9660_level2_convert_filename(iso9660_disk *, const char *, char *,
+ int);
+#if 0
+static int cd9660_joliet_convert_filename(iso9660_disk *, const char *, char *,
+ int);
+#endif
+static int cd9660_convert_filename(iso9660_disk *, const char *, char *, int);
+static void cd9660_populate_dot_records(iso9660_disk *, cd9660node *);
+static int64_t cd9660_compute_offsets(iso9660_disk *, cd9660node *, int64_t);
+#if 0
+static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int);
+#endif
+static cd9660node *cd9660_create_virtual_entry(iso9660_disk *, const char *,
+ cd9660node *, int, int);
+static cd9660node *cd9660_create_file(iso9660_disk *, const char *,
+ cd9660node *, cd9660node *);
+static cd9660node *cd9660_create_directory(iso9660_disk *, const char *,
+ cd9660node *, cd9660node *);
+static cd9660node *cd9660_create_special_directory(iso9660_disk *, u_char,
+ cd9660node *);
+static int cd9660_add_generic_bootimage(iso9660_disk *, const char *);
+
+
+/*
+ * Allocate and initalize a cd9660node
+ * @returns struct cd9660node * Pointer to new node, or NULL on error
+ */
+static cd9660node *
+cd9660_allocate_cd9660node(void)
+{
+ cd9660node *temp = ecalloc(1, sizeof(*temp));
+ TAILQ_INIT(&temp->cn_children);
+ temp->parent = temp->dot_record = temp->dot_dot_record = NULL;
+ temp->ptnext = temp->ptprev = temp->ptlast = NULL;
+ temp->node = NULL;
+ temp->isoDirRecord = NULL;
+ temp->isoExtAttributes = NULL;
+ temp->rr_real_parent = temp->rr_relocated = NULL;
+ temp->su_tail_data = NULL;
+ return temp;
+}
+
+int cd9660_defaults_set = 0;
+
+/**
+* Set default values for cd9660 extension to makefs
+*/
+static void
+cd9660_set_defaults(iso9660_disk *diskStructure)
+{
+ /*Fix the sector size for now, though the spec allows for other sizes*/
+ diskStructure->sectorSize = 2048;
+
+ /* Set up defaults in our own structure */
+ diskStructure->verbose_level = 0;
+ diskStructure->keep_bad_images = 0;
+ diskStructure->follow_sym_links = 0;
+ diskStructure->isoLevel = 2;
+
+ diskStructure->rock_ridge_enabled = 0;
+ diskStructure->rock_ridge_renamed_dir_name = 0;
+ diskStructure->rock_ridge_move_count = 0;
+ diskStructure->rr_moved_dir = 0;
+
+ diskStructure->archimedes_enabled = 0;
+ diskStructure->chrp_boot = 0;
+
+ diskStructure->include_padding_areas = 1;
+
+ /* Spec breaking functionality */
+ diskStructure->allow_deep_trees =
+ diskStructure->allow_start_dot =
+ diskStructure->allow_max_name =
+ diskStructure->allow_illegal_chars =
+ diskStructure->allow_lowercase =
+ diskStructure->allow_multidot =
+ diskStructure->omit_trailing_period = 0;
+
+ /* Make sure the PVD is clear */
+ memset(&diskStructure->primaryDescriptor, 0, 2048);
+
+ memset(diskStructure->primaryDescriptor.publisher_id, 0x20,128);
+ memset(diskStructure->primaryDescriptor.preparer_id, 0x20,128);
+ memset(diskStructure->primaryDescriptor.application_id, 0x20,128);
+ memset(diskStructure->primaryDescriptor.copyright_file_id, 0x20,37);
+ memset(diskStructure->primaryDescriptor.abstract_file_id, 0x20,37);
+ memset(diskStructure->primaryDescriptor.bibliographic_file_id, 0x20,37);
+
+ strcpy(diskStructure->primaryDescriptor.system_id,"NetBSD");
+
+ cd9660_defaults_set = 1;
+
+ /* Boot support: Initially disabled */
+ diskStructure->has_generic_bootimage = 0;
+ diskStructure->generic_bootimage = NULL;
+
+ diskStructure->boot_image_directory = 0;
+ /*memset(diskStructure->boot_descriptor, 0, 2048);*/
+
+ diskStructure->is_bootable = 0;
+ TAILQ_INIT(&diskStructure->boot_images);
+ LIST_INIT(&diskStructure->boot_entries);
+}
+
+void
+cd9660_prep_opts(fsinfo_t *fsopts)
+{
+ iso9660_disk *diskStructure = ecalloc(1, sizeof(*diskStructure));
+
+#define OPT_STR(letter, name, desc) \
+ { letter, name, NULL, OPT_STRBUF, 0, 0, desc }
+
+#define OPT_NUM(letter, name, field, min, max, desc) \
+ { letter, name, &diskStructure->field, \
+ sizeof(diskStructure->field) == 8 ? OPT_INT64 : \
+ (sizeof(diskStructure->field) == 4 ? OPT_INT32 : \
+ (sizeof(diskStructure->field) == 2 ? OPT_INT16 : OPT_INT8)), \
+ min, max, desc }
+
+#define OPT_BOOL(letter, name, field, desc) \
+ OPT_NUM(letter, name, field, 0, 1, desc)
+
+ const option_t cd9660_options[] = {
+ OPT_NUM('l', "isolevel", isoLevel,
+ 1, 3, "ISO Level"),
+ OPT_NUM('v', "verbose", verbose_level,
+ 0, 2, "Turns on verbose output"),
+
+ OPT_BOOL('h', "help", displayHelp,
+ "Show help message"),
+ OPT_BOOL('S', "follow-symlinks", follow_sym_links,
+ "Resolve symlinks in pathnames"),
+ OPT_BOOL('R', "rockridge", rock_ridge_enabled,
+ "Enable Rock-Ridge extensions"),
+ OPT_BOOL('C', "chrp-boot", chrp_boot,
+ "Enable CHRP boot"),
+ OPT_BOOL('K', "keep-bad-images", keep_bad_images,
+ "Keep bad images"),
+ OPT_BOOL('D', "allow-deep-trees", allow_deep_trees,
+ "Allow trees more than 8 levels"),
+ OPT_BOOL('a', "allow-max-name", allow_max_name,
+ "Allow 37 char filenames (unimplemented)"),
+ OPT_BOOL('i', "allow-illegal-chars", allow_illegal_chars,
+ "Allow illegal characters in filenames"),
+ OPT_BOOL('D', "allow-multidot", allow_multidot,
+ "Allow multiple periods in filenames"),
+ OPT_BOOL('o', "omit-trailing-period", omit_trailing_period,
+ "Omit trailing periods in filenames"),
+ OPT_BOOL('\0', "allow-lowercase", allow_lowercase,
+ "Allow lowercase characters in filenames"),
+ OPT_BOOL('\0', "archimedes", archimedes_enabled,
+ "Enable Archimedes structure"),
+ OPT_BOOL('\0', "no-trailing-padding", include_padding_areas,
+ "Include padding areas"),
+
+ OPT_STR('A', "applicationid", "Application Identifier"),
+ OPT_STR('P', "publisher", "Publisher Identifier"),
+ OPT_STR('p', "preparer", "Preparer Identifier"),
+ OPT_STR('L', "label", "Disk Label"),
+ OPT_STR('V', "volumeid", "Volume Set Identifier"),
+ OPT_STR('B', "bootimage", "Boot image parameter"),
+ OPT_STR('G', "generic-bootimage", "Generic boot image param"),
+ OPT_STR('\0', "bootimagedir", "Boot image directory"),
+ OPT_STR('\0', "no-emul-boot", "No boot emulation"),
+ OPT_STR('\0', "no-boot", "No boot support"),
+ OPT_STR('\0', "hard-disk-boot", "Boot from hard disk"),
+ OPT_STR('\0', "boot-load-segment", "Boot load segment"),
+
+ { .name = NULL }
+ };
+
+ fsopts->fs_specific = diskStructure;
+ fsopts->fs_options = copy_opts(cd9660_options);
+
+ cd9660_set_defaults(diskStructure);
+}
+
+void
+cd9660_cleanup_opts(fsinfo_t *fsopts)
+{
+ free(fsopts->fs_specific);
+ free(fsopts->fs_options);
+}
+
+static int
+cd9660_arguments_set_string(const char *val, const char *fieldtitle,
+ size_t length, char testmode, char * dest)
+{
+ size_t len;
+ int test;
+
+ if (val == NULL)
+ warnx("error: The %s requires a string argument", fieldtitle);
+ else if ((len = strlen(val)) <= length) {
+ if (testmode == 'd')
+ test = cd9660_valid_d_chars(val);
+ else
+ test = cd9660_valid_a_chars(val);
+ if (test) {
+ memcpy(dest, val, len);
+ if (test == 2)
+ cd9660_uppercase_characters(dest, len);
+ return 1;
+ } else
+ warnx("error: The %s must be composed of "
+ "%c-characters", fieldtitle, testmode);
+ } else
+ warnx("error: The %s must be at most 32 characters long",
+ fieldtitle);
+ return 0;
+}
+
+/*
+ * Command-line parsing function
+ */
+
+int
+cd9660_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ int rv, i;
+ iso9660_disk *diskStructure = fsopts->fs_specific;
+ option_t *cd9660_options = fsopts->fs_options;
+ char buf[1024];
+ const char *name, *desc;
+
+ assert(option != NULL);
+
+ if (debug & DEBUG_FS_PARSE_OPTS)
+ printf("%s: got `%s'\n", __func__, option);
+
+ i = set_option(cd9660_options, option, buf, sizeof(buf));
+ if (i == -1)
+ return 0;
+
+ if (cd9660_options[i].name == NULL)
+ abort();
+
+
+ name = cd9660_options[i].name;
+ desc = cd9660_options[i].desc;
+ switch (cd9660_options[i].letter) {
+ case 'h':
+ case 'S':
+ rv = 0; /* this is not handled yet */
+ break;
+ case 'L':
+ rv = cd9660_arguments_set_string(buf, desc, 32, 'd',
+ diskStructure->primaryDescriptor.volume_id);
+ break;
+ case 'A':
+ rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+ diskStructure->primaryDescriptor.application_id);
+ break;
+ case 'P':
+ rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+ diskStructure->primaryDescriptor.publisher_id);
+ break;
+ case 'p':
+ rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+ diskStructure->primaryDescriptor.preparer_id);
+ break;
+ case 'V':
+ rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
+ diskStructure->primaryDescriptor.volume_set_id);
+ break;
+ /* Boot options */
+ case 'B':
+ if (buf[0] == '\0') {
+ warnx("The Boot Image parameter requires a valid boot"
+ " information string");
+ rv = 0;
+ } else
+ rv = cd9660_add_boot_disk(diskStructure, buf);
+ break;
+ case 'G':
+ if (buf[0] == '\0') {
+ warnx("The Generic Boot Image parameter requires a"
+ " valid boot information string");
+ rv = 0;
+ } else
+ rv = cd9660_add_generic_bootimage(diskStructure, buf);
+ break;
+ default:
+ if (strcmp(name, "bootimagedir") == 0) {
+ /*
+ * XXXfvdl this is unused.
+ */
+ if (buf[0] == '\0') {
+ warnx("The Boot Image Directory parameter"
+ " requires a directory name");
+ rv = 0;
+ } else {
+ diskStructure->boot_image_directory =
+ emalloc(strlen(buf) + 1);
+ /* BIG TODO: Add the max length function here */
+ rv = cd9660_arguments_set_string(buf, desc, 12,
+ 'd', diskStructure->boot_image_directory);
+ }
+ } else if (strcmp(name, "no-emul-boot") == 0 ||
+ strcmp(name, "no-boot") == 0 ||
+ strcmp(name, "hard-disk-boot") == 0) {
+ /* RRIP */
+ cd9660_eltorito_add_boot_option(diskStructure, name, 0);
+ rv = 1;
+ } else if (strcmp(name, "boot-load-segment") == 0) {
+ if (buf[0] == '\0') {
+ warnx("Option `%s' doesn't contain a value",
+ name);
+ rv = 0;
+ } else {
+ cd9660_eltorito_add_boot_option(diskStructure,
+ name, buf);
+ rv = 1;
+ }
+ } else
+ rv = 1;
+ }
+ return rv;
+}
+
+/*
+ * Main function for cd9660_makefs
+ * Builds the ISO image file
+ * @param const char *image The image filename to create
+ * @param const char *dir The directory that is being read
+ * @param struct fsnode *root The root node of the filesystem tree
+ * @param struct fsinfo_t *fsopts Any options
+ */
+void
+cd9660_makefs(const char *image, const char *dir, fsnode *root,
+ fsinfo_t *fsopts)
+{
+ int64_t startoffset;
+ int numDirectories;
+ uint64_t pathTableSectors;
+ int64_t firstAvailableSector;
+ int64_t totalSpace;
+ int error;
+ cd9660node *real_root;
+ iso9660_disk *diskStructure = fsopts->fs_specific;
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s: ISO level is %i\n", __func__,
+ diskStructure->isoLevel);
+ if (diskStructure->isoLevel < 2 &&
+ diskStructure->allow_multidot)
+ errx(EXIT_FAILURE, "allow-multidot requires iso level of 2");
+
+ assert(image != NULL);
+ assert(dir != NULL);
+ assert(root != NULL);
+
+ if (diskStructure->displayHelp) {
+ /*
+ * Display help here - probably want to put it in
+ * a separate function
+ */
+ return;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s: image %s directory %s root %p\n", __func__,
+ image, dir, root);
+
+ /* Set up some constants. Later, these will be defined with options */
+
+ /* Counter needed for path tables */
+ numDirectories = 0;
+
+ /* Convert tree to our own format */
+ /* Actually, we now need to add the REAL root node, at level 0 */
+
+ real_root = cd9660_allocate_cd9660node();
+ real_root->isoDirRecord = emalloc(sizeof(*real_root->isoDirRecord));
+ /* Leave filename blank for root */
+ memset(real_root->isoDirRecord->name, 0,
+ ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+ real_root->level = 0;
+ diskStructure->rootNode = real_root;
+ real_root->type = CD9660_TYPE_DIR;
+ error = 0;
+ real_root->node = root;
+ cd9660_convert_structure(diskStructure, root, real_root, 1,
+ &numDirectories, &error);
+
+ if (TAILQ_EMPTY(&real_root->cn_children)) {
+ errx(EXIT_FAILURE, "%s: converted directory is empty. "
+ "Tree conversion failed", __func__);
+ } else if (error != 0) {
+ errx(EXIT_FAILURE, "%s: tree conversion failed", __func__);
+ } else {
+ if (diskStructure->verbose_level > 0)
+ printf("%s: tree converted\n", __func__);
+ }
+
+ /* Add the dot and dot dot records */
+ cd9660_add_dot_records(diskStructure, real_root);
+
+ cd9660_setup_root_node(diskStructure);
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s: done converting tree\n", __func__);
+
+ /* non-SUSP extensions */
+ if (diskStructure->archimedes_enabled)
+ archimedes_convert_tree(diskStructure->rootNode);
+
+ /* Rock ridge / SUSP init pass */
+ if (diskStructure->rock_ridge_enabled) {
+ cd9660_susp_initialize(diskStructure, diskStructure->rootNode,
+ diskStructure->rootNode, NULL);
+ }
+
+ /* Build path table structure */
+ diskStructure->pathTableLength = cd9660_generate_path_table(
+ diskStructure);
+
+ pathTableSectors = CD9660_BLOCKS(diskStructure->sectorSize,
+ diskStructure->pathTableLength);
+
+ firstAvailableSector = cd9660_setup_volume_descriptors(diskStructure);
+ if (diskStructure->is_bootable) {
+ firstAvailableSector = cd9660_setup_boot(diskStructure,
+ firstAvailableSector);
+ if (firstAvailableSector < 0)
+ errx(EXIT_FAILURE, "setup_boot failed");
+ }
+ /* LE first, then BE */
+ diskStructure->primaryLittleEndianTableSector = firstAvailableSector;
+ diskStructure->primaryBigEndianTableSector =
+ diskStructure->primaryLittleEndianTableSector + pathTableSectors;
+
+ /* Set the secondary ones to -1, not going to use them for now */
+ diskStructure->secondaryBigEndianTableSector = -1;
+ diskStructure->secondaryLittleEndianTableSector = -1;
+
+ diskStructure->dataFirstSector =
+ diskStructure->primaryBigEndianTableSector + pathTableSectors;
+ if (diskStructure->verbose_level > 0)
+ printf("%s: Path table conversion complete. "
+ "Each table is %i bytes, or %" PRIu64 " sectors.\n",
+ __func__,
+ diskStructure->pathTableLength, pathTableSectors);
+
+ startoffset = diskStructure->sectorSize*diskStructure->dataFirstSector;
+
+ totalSpace = cd9660_compute_offsets(diskStructure, real_root, startoffset);
+
+ diskStructure->totalSectors = diskStructure->dataFirstSector +
+ CD9660_BLOCKS(diskStructure->sectorSize, totalSpace);
+
+ /* Disabled until pass 1 is done */
+ if (diskStructure->rock_ridge_enabled) {
+ diskStructure->susp_continuation_area_start_sector =
+ diskStructure->totalSectors;
+ diskStructure->totalSectors +=
+ CD9660_BLOCKS(diskStructure->sectorSize,
+ diskStructure->susp_continuation_area_size);
+ cd9660_susp_finalize(diskStructure, diskStructure->rootNode);
+ }
+
+
+ cd9660_finalize_PVD(diskStructure);
+
+ /* Add padding sectors, just for testing purposes right now */
+ /* diskStructure->totalSectors+=150; */
+
+ /* Debugging output */
+ if (diskStructure->verbose_level > 0) {
+ printf("%s: Sectors 0-15 reserved\n", __func__);
+ printf("%s: Primary path tables starts in sector %"
+ PRId64 "\n", __func__,
+ diskStructure->primaryLittleEndianTableSector);
+ printf("%s: File data starts in sector %"
+ PRId64 "\n", __func__, diskStructure->dataFirstSector);
+ printf("%s: Total sectors: %"
+ PRId64 "\n", __func__, diskStructure->totalSectors);
+ }
+
+ /*
+ * Add padding sectors at the end
+ * TODO: Clean this up and separate padding
+ */
+ if (diskStructure->include_padding_areas)
+ diskStructure->totalSectors += 150;
+
+ cd9660_write_image(diskStructure, image);
+
+ if (diskStructure->verbose_level > 1) {
+ debug_print_volume_descriptor_information(diskStructure);
+ debug_print_tree(diskStructure, real_root, 0);
+ debug_print_path_tree(real_root);
+ }
+
+ /* Clean up data structures */
+ cd9660_free_structure(real_root);
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s: done\n", __func__);
+}
+
+/* Generic function pointer - implement later */
+typedef int (*cd9660node_func)(cd9660node *);
+
+static void
+cd9660_finalize_PVD(iso9660_disk *diskStructure)
+{
+ time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
+
+ /* root should be a fixed size of 34 bytes since it has no name */
+ memcpy(diskStructure->primaryDescriptor.root_directory_record,
+ diskStructure->rootNode->dot_record->isoDirRecord, 34);
+
+ /* In RRIP, this might be longer than 34 */
+ diskStructure->primaryDescriptor.root_directory_record[0] = 34;
+
+ /* Set up all the important numbers in the PVD */
+ cd9660_bothendian_dword(diskStructure->totalSectors,
+ (unsigned char *)diskStructure->primaryDescriptor.volume_space_size);
+ cd9660_bothendian_word(1,
+ (unsigned char *)diskStructure->primaryDescriptor.volume_set_size);
+ cd9660_bothendian_word(1,
+ (unsigned char *)
+ diskStructure->primaryDescriptor.volume_sequence_number);
+ cd9660_bothendian_word(diskStructure->sectorSize,
+ (unsigned char *)
+ diskStructure->primaryDescriptor.logical_block_size);
+ cd9660_bothendian_dword(diskStructure->pathTableLength,
+ (unsigned char *)diskStructure->primaryDescriptor.path_table_size);
+
+ cd9660_731(diskStructure->primaryLittleEndianTableSector,
+ (u_char *)diskStructure->primaryDescriptor.type_l_path_table);
+ cd9660_732(diskStructure->primaryBigEndianTableSector,
+ (u_char *)diskStructure->primaryDescriptor.type_m_path_table);
+
+ diskStructure->primaryDescriptor.file_structure_version[0] = 1;
+
+ /* Pad all strings with spaces instead of nulls */
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_id, 32);
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.system_id, 32);
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_set_id,
+ 128);
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.publisher_id,
+ 128);
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.preparer_id,
+ 128);
+ cd9660_pad_string_spaces(diskStructure->primaryDescriptor.application_id,
+ 128);
+ cd9660_pad_string_spaces(
+ diskStructure->primaryDescriptor.copyright_file_id, 37);
+ cd9660_pad_string_spaces(
+ diskStructure->primaryDescriptor.abstract_file_id, 37);
+ cd9660_pad_string_spaces(
+ diskStructure->primaryDescriptor.bibliographic_file_id, 37);
+
+ /* Setup dates */
+ cd9660_time_8426(
+ (unsigned char *)diskStructure->primaryDescriptor.creation_date,
+ tstamp);
+ cd9660_time_8426(
+ (unsigned char *)diskStructure->primaryDescriptor.modification_date,
+ tstamp);
+
+#if 0
+ cd9660_set_date(diskStructure->primaryDescriptor.expiration_date,
+ tstamp);
+#endif
+ memset(diskStructure->primaryDescriptor.expiration_date, '0' ,16);
+ diskStructure->primaryDescriptor.expiration_date[16] = 0;
+
+ cd9660_time_8426(
+ (unsigned char *)diskStructure->primaryDescriptor.effective_date,
+ tstamp);
+}
+
+static void
+cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record,
+ u_char ext_attr_length, u_char flags,
+ u_char name_len, const char * name)
+{
+ record->ext_attr_length[0] = ext_attr_length;
+ record->flags[0] = ISO_FLAG_CLEAR | flags;
+ record->file_unit_size[0] = 0;
+ record->interleave[0] = 0;
+ cd9660_bothendian_word(1, record->volume_sequence_number);
+ record->name_len[0] = name_len;
+ memset(record->name, '\0', sizeof (record->name));
+ memcpy(record->name, name, name_len);
+ record->length[0] = 33 + name_len;
+
+ /* Todo : better rounding */
+ record->length[0] += (record->length[0] & 1) ? 1 : 0;
+}
+
+static void
+cd9660_setup_root_node(iso9660_disk *diskStructure)
+{
+ cd9660_populate_iso_dir_record(diskStructure->rootNode->isoDirRecord,
+ 0, ISO_FLAG_DIRECTORY, 1, "\0");
+
+}
+
+/*********** SUPPORT FUNCTIONS ***********/
+static int
+cd9660_setup_volume_descriptors(iso9660_disk *diskStructure)
+{
+ /* Boot volume descriptor should come second */
+ int sector = 16;
+ /* For now, a fixed 2 : PVD and terminator */
+ volume_descriptor *temp, *t;
+
+ /* Set up the PVD */
+ temp = emalloc(sizeof(*temp));
+ temp->volumeDescriptorData =
+ (unsigned char *)&diskStructure->primaryDescriptor;
+ temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD;
+ temp->volumeDescriptorData[6] = 1;
+ temp->sector = sector;
+ memcpy(temp->volumeDescriptorData + 1,
+ ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+ diskStructure->firstVolumeDescriptor = temp;
+
+ sector++;
+ /* Set up boot support if enabled. BVD must reside in sector 17 */
+ if (diskStructure->is_bootable) {
+ t = emalloc(sizeof(*t));
+ t->volumeDescriptorData = ecalloc(1, 2048);
+ temp->next = t;
+ temp = t;
+ t->sector = 17;
+ if (diskStructure->verbose_level > 0)
+ printf("Setting up boot volume descriptor\n");
+ cd9660_setup_boot_volume_descriptor(diskStructure, t);
+ sector++;
+ }
+
+ /* Set up the terminator */
+ t = emalloc(sizeof(*t));
+ t->volumeDescriptorData = ecalloc(1, 2048);
+ temp->next = t;
+ t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR;
+ t->next = 0;
+ t->volumeDescriptorData[6] = 1;
+ t->sector = sector;
+ memcpy(t->volumeDescriptorData + 1,
+ ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+
+ sector++;
+ return sector;
+}
+
+#if 0
+/*
+ * Populate EAR at some point. Not required, but is used by NetBSD's
+ * cd9660 support
+ */
+static int
+cd9660_fill_extended_attribute_record(cd9660node *node)
+{
+ node->isoExtAttributes = emalloc(sizeof(*node->isoExtAttributes));
+ return 1;
+}
+#endif
+
+static int
+cd9660_translate_node_common(iso9660_disk *diskStructure, cd9660node *newnode)
+{
+ time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
+ u_char flag;
+ char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+ /* Now populate the isoDirRecord structure */
+ memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+ (void)cd9660_convert_filename(diskStructure, newnode->node->name,
+ temp, !(S_ISDIR(newnode->node->type)));
+
+ flag = ISO_FLAG_CLEAR;
+ if (S_ISDIR(newnode->node->type))
+ flag |= ISO_FLAG_DIRECTORY;
+
+ cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0,
+ flag, strlen(temp), temp);
+
+ /* Set the various dates */
+
+ /* If we want to use the current date and time */
+
+ cd9660_time_915(newnode->isoDirRecord->date, tstamp);
+
+ cd9660_bothendian_dword(newnode->fileDataLength,
+ newnode->isoDirRecord->size);
+ /* If the file is a link, we want to set the size to 0 */
+ if (S_ISLNK(newnode->node->type))
+ newnode->fileDataLength = 0;
+
+ return 1;
+}
+
+/*
+ * Translate fsnode to cd9660node
+ * Translate filenames and other metadata, including dates, sizes,
+ * permissions, etc
+ * @param struct fsnode * The node generated by makefs
+ * @param struct cd9660node * The intermediate node to be written to
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_translate_node(iso9660_disk *diskStructure, fsnode *node,
+ cd9660node *newnode)
+{
+ if (node == NULL) {
+ if (diskStructure->verbose_level > 0)
+ printf("%s: NULL node passed, returning\n", __func__);
+ return 0;
+ }
+ newnode->isoDirRecord = emalloc(sizeof(*newnode->isoDirRecord));
+ /* Set the node pointer */
+ newnode->node = node;
+
+ /* Set the size */
+ if (!(S_ISDIR(node->type)))
+ newnode->fileDataLength = node->inode->st.st_size;
+
+ if (cd9660_translate_node_common(diskStructure, newnode) == 0)
+ return 0;
+
+ /* Finally, overwrite some of the values that are set by default */
+ cd9660_time_915(newnode->isoDirRecord->date,
+ stampst.st_ino ? stampst.st_mtime : node->inode->st.st_mtime);
+
+ return 1;
+}
+
+/*
+ * Compares two ISO filenames
+ * @param const char * The first file name
+ * @param const char * The second file name
+ * @returns : -1 if first is less than second, 0 if they are the same, 1 if
+ * the second is greater than the first
+ */
+static int
+cd9660_compare_filename(const char *first, const char *second)
+{
+ /*
+ * This can be made more optimal once it has been tested
+ * (the extra character, for example, is for testing)
+ */
+
+ int p1 = 0;
+ int p2 = 0;
+ char c1, c2;
+ /* First, on the filename */
+
+ while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1
+ && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) {
+ c1 = first[p1];
+ c2 = second[p2];
+ if (c1 == '.' && c2 =='.')
+ break;
+ else if (c1 == '.') {
+ p2++;
+ c1 = ' ';
+ } else if (c2 == '.') {
+ p1++;
+ c2 = ' ';
+ } else {
+ p1++;
+ p2++;
+ }
+
+ if (c1 < c2)
+ return -1;
+ else if (c1 > c2) {
+ return 1;
+ }
+ }
+
+ if (first[p1] == '.' && second[p2] == '.') {
+ p1++;
+ p2++;
+ while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1
+ && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) {
+ c1 = first[p1];
+ c2 = second[p2];
+ if (c1 == ';' && c2 == ';')
+ break;
+ else if (c1 == ';') {
+ p2++;
+ c1 = ' ';
+ } else if (c2 == ';') {
+ p1++;
+ c2 = ' ';
+ } else {
+ p1++;
+ p2++;
+ }
+
+ if (c1 < c2)
+ return -1;
+ else if (c1 > c2)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Insert a node into list with ISO sorting rules
+ * @param cd9660node * The head node of the list
+ * @param cd9660node * The node to be inserted
+ */
+static void
+cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new)
+{
+ int compare;
+ cd9660node *cn;
+ struct cd9660_children_head *head = &parent->cn_children;
+
+ /* TODO: Optimize? */
+ cn_new->parent = parent;
+
+ /*
+ * first will either be 0, the . or the ..
+ * if . or .., this means no other entry may be written before first
+ * if 0, the new node may be inserted at the head
+ */
+
+ TAILQ_FOREACH(cn, head, cn_next_child) {
+ /*
+ * Dont insert a node twice -
+ * that would cause an infinite loop
+ */
+ if (cn_new == cn)
+ return;
+
+ compare = cd9660_compare_filename(cn_new->isoDirRecord->name,
+ cn->isoDirRecord->name);
+
+ if (compare == 0)
+ compare = cd9660_compare_filename(cn_new->node->name,
+ cn->node->name);
+
+ if (compare < 0)
+ break;
+ }
+ if (cn == NULL)
+ TAILQ_INSERT_TAIL(head, cn_new, cn_next_child);
+ else
+ TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child);
+}
+
+/*
+ * Called After cd9660_sorted_child_insert
+ * handles file collisions by suffixing each filname with ~n
+ * where n represents the files respective place in the ordering
+ */
+static int
+cd9660_handle_collisions(iso9660_disk *diskStructure, cd9660node *colliding,
+ int past)
+{
+ cd9660node *iter, *next, *prev;
+ int skip;
+ int delete_chars = 0;
+ int temp_past = past;
+ int temp_skip;
+ int flag = 0;
+ cd9660node *end_of_range;
+
+ for (iter = TAILQ_FIRST(&colliding->cn_children);
+ iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) {
+ if (strcmp(iter->isoDirRecord->name,
+ next->isoDirRecord->name) != 0) {
+ iter = TAILQ_NEXT(iter, cn_next_child);
+ continue;
+ }
+ flag = 1;
+ temp_skip = skip = cd9660_count_collisions(iter);
+ end_of_range = iter;
+ while (temp_skip > 0) {
+ temp_skip--;
+ end_of_range = TAILQ_NEXT(end_of_range, cn_next_child);
+ }
+ temp_past = past;
+ while (temp_past > 0) {
+ if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL)
+ end_of_range = next;
+ else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL)
+ iter = prev;
+ else
+ delete_chars++;
+ temp_past--;
+ }
+ skip += past;
+ iter = cd9660_rename_filename(diskStructure, iter, skip,
+ delete_chars);
+ }
+ return flag;
+}
+
+
+static cd9660node *
+cd9660_rename_filename(iso9660_disk *diskStructure, cd9660node *iter, int num,
+ int delete_chars)
+{
+ int i = 0;
+ int numbts, dot, semi, digit, digits, temp, powers, multiplier, count;
+ char *naming;
+ int maxlength;
+ char *tmp;
+
+ if (diskStructure->verbose_level > 0)
+ printf("Rename_filename called\n");
+
+ /* TODO : A LOT of chanes regarding 8.3 filenames */
+ if (diskStructure->isoLevel == 1)
+ maxlength = 8;
+ else if (diskStructure->isoLevel == 2)
+ maxlength = 31;
+ else
+ maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION;
+
+ tmp = emalloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+ while (i < num) {
+ powers = 1;
+ count = 0;
+ digits = 1;
+ multiplier = 1;
+ while (((int)(i / powers) ) >= 10) {
+ digits++;
+ powers = powers * 10;
+ }
+
+ naming = iter->o_name;
+
+ /*
+ while ((*naming != '.') && (*naming != ';')) {
+ naming++;
+ count++;
+ }
+ */
+
+ dot = -1;
+ semi = -1;
+ while (count < maxlength) {
+ if (*naming == '.')
+ dot = count;
+ else if (*naming == ';') {
+ semi = count;
+ break;
+ }
+ naming++;
+ count++;
+ }
+
+ if ((count + digits) < maxlength)
+ numbts = count;
+ else
+ numbts = maxlength - (digits);
+ numbts -= delete_chars;
+
+ /* 8.3 rules - keep the extension, add before the dot */
+
+ /*
+ * This code makes a bunch of assumptions.
+ * See if you can spot them all :)
+ */
+
+#if 0
+ if (diskStructure->isoLevel == 1) {
+ numbts = 8 - digits - delete_chars;
+ if (dot < 0) {
+
+ } else {
+ if (dot < 8) {
+ memmove(&tmp[numbts],&tmp[dot],4);
+ }
+ }
+ }
+#else
+ (void)dot;
+ (void)semi;
+ (void)multiplier;
+#endif
+
+ /* (copying just the filename before the '.' */
+ memcpy(tmp, (iter->o_name), numbts);
+
+ /* adding the appropriate number following the name */
+ temp = i;
+ while (digits > 0) {
+ digit = (int)(temp / powers);
+ temp = temp - digit * powers;
+ sprintf(&tmp[numbts] , "%d", digit);
+ digits--;
+ numbts++;
+ powers = powers / 10;
+ }
+
+ while ((*naming != ';') && (numbts < maxlength)) {
+ tmp[numbts] = (*naming);
+ naming++;
+ numbts++;
+ }
+
+ tmp[numbts] = ';';
+ tmp[numbts+1] = '1';
+ tmp[numbts+2] = '\0';
+
+ /*
+ * now tmp has exactly the identifier
+ * we want so we'll copy it back to record
+ */
+ memcpy((iter->isoDirRecord->name), tmp, numbts + 3);
+
+ iter = TAILQ_NEXT(iter, cn_next_child);
+ i++;
+ }
+
+ free(tmp);
+ return iter;
+}
+
+/* Todo: Figure out why these functions are nec. */
+static void
+cd9660_copy_filenames(iso9660_disk *diskStructure, cd9660node *node)
+{
+ cd9660node *cn;
+
+ if (TAILQ_EMPTY(&node->cn_children))
+ return;
+
+ if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) {
+ debug_print_tree(diskStructure, diskStructure->rootNode, 0);
+ exit(1);
+ }
+
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+ cd9660_copy_filenames(diskStructure, cn);
+ memcpy(cn->o_name, cn->isoDirRecord->name,
+ ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+ }
+}
+
+static void
+cd9660_sorting_nodes(cd9660node *node)
+{
+ cd9660node *cn;
+
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+ cd9660_sorting_nodes(cn);
+ cd9660_sort_nodes(node);
+}
+
+/* XXX Bubble sort. */
+static void
+cd9660_sort_nodes(cd9660node *node)
+{
+ cd9660node *cn, *next;
+
+ do {
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+ if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL)
+ return;
+ else if (strcmp(next->isoDirRecord->name,
+ cn->isoDirRecord->name) >= 0)
+ continue;
+ TAILQ_REMOVE(&node->cn_children, next, cn_next_child);
+ TAILQ_INSERT_BEFORE(cn, next, cn_next_child);
+ break;
+ }
+ } while (cn != NULL);
+}
+
+static int
+cd9660_count_collisions(cd9660node *copy)
+{
+ int count = 0;
+ cd9660node *iter, *next;
+
+ for (iter = copy;
+ (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;
+ iter = next) {
+ if (cd9660_compare_filename(iter->isoDirRecord->name,
+ next->isoDirRecord->name) == 0)
+ count++;
+ else
+ return count;
+ }
+#if 0
+ if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) {
+ printf("%s: count is %i \n", __func__, count);
+ compare = cd9660_compare_filename(iter->isoDirRecord->name,
+ next->isoDirRecord->name);
+ if (compare == 0) {
+ count++;
+ return cd9660_recurse_on_collision(next, count);
+ } else
+ return count;
+ }
+#endif
+ return count;
+}
+
+static cd9660node *
+cd9660_rrip_move_directory(iso9660_disk *diskStructure, cd9660node *dir)
+{
+ char newname[9];
+ cd9660node *tfile;
+
+ /*
+ * This function needs to:
+ * 1) Create an empty virtual file in place of the old directory
+ * 2) Point the virtual file to the new directory
+ * 3) Point the relocated directory to its old parent
+ * 4) Move the directory specified by dir into rr_moved_dir,
+ * and rename it to "diskStructure->rock_ridge_move_count" (as a string)
+ */
+
+ /* First see if the moved directory even exists */
+ if (diskStructure->rr_moved_dir == NULL) {
+ diskStructure->rr_moved_dir = cd9660_create_directory(
+ diskStructure, ISO_RRIP_DEFAULT_MOVE_DIR_NAME,
+ diskStructure->rootNode, dir);
+ if (diskStructure->rr_moved_dir == NULL)
+ return 0;
+ cd9660_time_915(diskStructure->rr_moved_dir->isoDirRecord->date,
+ stampst.st_ino ? stampst.st_mtime : start_time.tv_sec);
+ }
+
+ /* Create a file with the same ORIGINAL name */
+ tfile = cd9660_create_file(diskStructure, dir->node->name, dir->parent,
+ dir);
+ if (tfile == NULL)
+ return NULL;
+
+ diskStructure->rock_ridge_move_count++;
+ snprintf(newname, sizeof(newname), "%08i",
+ diskStructure->rock_ridge_move_count);
+
+ /* Point to old parent */
+ dir->rr_real_parent = dir->parent;
+
+ /* Place the placeholder file */
+ if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) {
+ TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile,
+ cn_next_child);
+ } else {
+ cd9660_sorted_child_insert(dir->rr_real_parent, tfile);
+ }
+
+ /* Point to new parent */
+ dir->parent = diskStructure->rr_moved_dir;
+
+ /* Point the file to the moved directory */
+ tfile->rr_relocated = dir;
+
+ /* Actually move the directory */
+ cd9660_sorted_child_insert(diskStructure->rr_moved_dir, dir);
+
+ /* TODO: Inherit permissions / ownership (basically the entire inode) */
+
+ /* Set the new name */
+ memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+ strncpy(dir->isoDirRecord->name, newname, 8);
+ dir->isoDirRecord->length[0] = 34 + 8;
+ dir->isoDirRecord->name_len[0] = 8;
+
+ return dir;
+}
+
+static int
+cd9660_add_dot_records(iso9660_disk *diskStructure, cd9660node *root)
+{
+ struct cd9660_children_head *head = &root->cn_children;
+ cd9660node *cn;
+
+ TAILQ_FOREACH(cn, head, cn_next_child) {
+ if ((cn->type & CD9660_TYPE_DIR) == 0)
+ continue;
+ /* Recursion first */
+ cd9660_add_dot_records(diskStructure, cn);
+ }
+ cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOT, root);
+ cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOTDOT,
+ root);
+ return 1;
+}
+
+/*
+ * Convert node to cd9660 structure
+ * This function is designed to be called recursively on the root node of
+ * the filesystem
+ * Lots of recursion going on here, want to make sure it is efficient
+ * @param struct fsnode * The root node to be converted
+ * @param struct cd9660* The parent node (should not be NULL)
+ * @param int Current directory depth
+ * @param int* Running count of the number of directories that are being created
+ */
+static void
+cd9660_convert_structure(iso9660_disk *diskStructure, fsnode *root,
+ cd9660node *parent_node, int level, int *numDirectories, int *error)
+{
+ fsnode *iterator = root;
+ cd9660node *this_node;
+ int working_level;
+ int add;
+ int flag = 0;
+ int counter = 0;
+
+ /*
+ * Newer, more efficient method, reduces recursion depth
+ */
+ if (root == NULL) {
+ warnx("%s: root is null", __func__);
+ return;
+ }
+
+ /* Test for an empty directory - makefs still gives us the . record */
+ if ((S_ISDIR(root->type)) && (root->name[0] == '.')
+ && (root->name[1] == '\0')) {
+ root = root->next;
+ if (root == NULL)
+ return;
+ }
+ if ((this_node = cd9660_allocate_cd9660node()) == NULL) {
+ CD9660_MEM_ALLOC_ERROR(__func__);
+ }
+
+ /*
+ * To reduce the number of recursive calls, we will iterate over
+ * the next pointers to the right.
+ */
+ while (iterator != NULL) {
+ add = 1;
+ /*
+ * Increment the directory count if this is a directory
+ * Ignore "." entries. We will generate them later
+ */
+ if (!S_ISDIR(iterator->type) ||
+ strcmp(iterator->name, ".") != 0) {
+
+ /* Translate the node, including its filename */
+ this_node->parent = parent_node;
+ cd9660_translate_node(diskStructure, iterator,
+ this_node);
+ this_node->level = level;
+
+ if (S_ISDIR(iterator->type)) {
+ (*numDirectories)++;
+ this_node->type = CD9660_TYPE_DIR;
+ working_level = level + 1;
+
+ /*
+ * If at level 8, directory would be at 8
+ * and have children at 9 which is not
+ * allowed as per ISO spec
+ */
+ if (level == 8) {
+ if ((!diskStructure->allow_deep_trees) &&
+ (!diskStructure->rock_ridge_enabled)) {
+ warnx("error: found entry "
+ "with depth greater "
+ "than 8.");
+ (*error) = 1;
+ return;
+ } else if (diskStructure->
+ rock_ridge_enabled) {
+ working_level = 3;
+ /*
+ * Moved directory is actually
+ * at level 2.
+ */
+ this_node->level =
+ working_level - 1;
+ if (cd9660_rrip_move_directory(
+ diskStructure,
+ this_node) == 0) {
+ warnx("Failure in "
+ "cd9660_rrip_"
+ "move_directory"
+ );
+ (*error) = 1;
+ return;
+ }
+ add = 0;
+ }
+ }
+
+ /* Do the recursive call on the children */
+ if (iterator->child != 0) {
+ cd9660_convert_structure(diskStructure,
+ iterator->child, this_node,
+ working_level,
+ numDirectories, error);
+
+ if ((*error) == 1) {
+ warnx("%s: Error on recursive "
+ "call", __func__);
+ return;
+ }
+ }
+
+ } else {
+ /* Only directories should have children */
+ assert(iterator->child == NULL);
+
+ this_node->type = CD9660_TYPE_FILE;
+ }
+
+ /*
+ * Finally, do a sorted insert
+ */
+ if (add) {
+ cd9660_sorted_child_insert(
+ parent_node, this_node);
+ }
+
+ /*Allocate new temp_node */
+ if (iterator->next != 0) {
+ this_node = cd9660_allocate_cd9660node();
+ if (this_node == NULL)
+ CD9660_MEM_ALLOC_ERROR(__func__);
+ }
+ }
+ iterator = iterator->next;
+ }
+
+ /* cd9660_handle_collisions(first_node); */
+
+ /* TODO: need cleanup */
+ cd9660_copy_filenames(diskStructure, parent_node);
+
+ do {
+ flag = cd9660_handle_collisions(diskStructure, parent_node,
+ counter);
+ counter++;
+ cd9660_sorting_nodes(parent_node);
+ } while ((flag == 1) && (counter < 100));
+}
+
+/*
+ * Clean up the cd9660node tree
+ * This is designed to be called recursively on the root node
+ * @param struct cd9660node *root The node to free
+ * @returns void
+ */
+static void
+cd9660_free_structure(cd9660node *root)
+{
+ cd9660node *cn;
+
+ while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) {
+ TAILQ_REMOVE(&root->cn_children, cn, cn_next_child);
+ cd9660_free_structure(cn);
+ }
+ free(root);
+}
+
+/*
+ * Be a little more memory conservative:
+ * instead of having the TAILQ_ENTRY as part of the cd9660node,
+ * just create a temporary structure
+ */
+struct ptq_entry
+{
+ TAILQ_ENTRY(ptq_entry) ptq;
+ cd9660node *node;
+} *n;
+
+#define PTQUEUE_NEW(n,s,r,t){\
+ n = emalloc(sizeof(struct s)); \
+ if (n == NULL) \
+ return r; \
+ n->node = t;\
+}
+
+/*
+ * Generate the path tables
+ * The specific implementation of this function is left as an exercise to the
+ * programmer. It could be done recursively. Make sure you read how the path
+ * table has to be laid out, it has levels.
+ * @param struct iso9660_disk *disk The disk image
+ * @returns int The number of built path tables (between 1 and 4), 0 on failure
+ */
+static int
+cd9660_generate_path_table(iso9660_disk *diskStructure)
+{
+ cd9660node *cn, *dirNode = diskStructure->rootNode;
+ cd9660node *last = dirNode;
+ int pathTableSize = 0; /* computed as we go */
+ int counter = 1; /* root gets a count of 0 */
+
+ TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head;
+ TAILQ_INIT(&pt_head);
+
+ PTQUEUE_NEW(n, ptq_entry, -1, diskStructure->rootNode);
+
+ /* Push the root node */
+ TAILQ_INSERT_HEAD(&pt_head, n, ptq);
+
+ /* Breadth-first traversal of file structure */
+ while (pt_head.tqh_first != 0) {
+ n = pt_head.tqh_first;
+ dirNode = n->node;
+ TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq);
+ free(n);
+
+ /* Update the size */
+ pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE
+ + dirNode->isoDirRecord->name_len[0]+
+ (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1);
+ /* includes the padding bit */
+
+ dirNode->ptnumber=counter;
+ if (dirNode != last) {
+ last->ptnext = dirNode;
+ dirNode->ptprev = last;
+ }
+ last = dirNode;
+
+ /* Push children onto queue */
+ TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) {
+ /*
+ * Dont add the DOT and DOTDOT types to the path
+ * table.
+ */
+ if ((cn->type != CD9660_TYPE_DOT)
+ && (cn->type != CD9660_TYPE_DOTDOT)) {
+
+ if (S_ISDIR(cn->node->type)) {
+ PTQUEUE_NEW(n, ptq_entry, -1, cn);
+ TAILQ_INSERT_TAIL(&pt_head, n, ptq);
+ }
+ }
+ }
+ counter++;
+ }
+ return pathTableSize;
+}
+
+void
+cd9660_compute_full_filename(cd9660node *node, char *buf)
+{
+ int len;
+
+ len = CD9660MAXPATH + 1;
+ len = snprintf(buf, len, "%s/%s/%s", node->node->root,
+ node->node->path, node->node->name);
+ if (len > CD9660MAXPATH)
+ errx(EXIT_FAILURE, "Pathname too long.");
+}
+
+/* NEW filename conversion method */
+typedef int(*cd9660_filename_conversion_functor)(iso9660_disk *, const char *,
+ char *, int);
+
+
+/*
+ * TODO: These two functions are almost identical.
+ * Some code cleanup is possible here
+ *
+ * XXX bounds checking!
+ */
+static int
+cd9660_level1_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+ char *newname, int is_file)
+{
+ /*
+ * ISO 9660 : 10.1
+ * File Name shall not contain more than 8 d or d1 characters
+ * File Name Extension shall not contain more than 3 d or d1 characters
+ * Directory Identifier shall not contain more than 8 d or d1 characters
+ */
+ int namelen = 0;
+ int extlen = 0;
+ int found_ext = 0;
+
+ while (*oldname != '\0' && extlen < 3) {
+ /* Handle period first, as it is special */
+ if (*oldname == '.') {
+ if (found_ext) {
+ *newname++ = '_';
+ extlen ++;
+ }
+ else {
+ *newname++ = '.';
+ found_ext = 1;
+ }
+ } else {
+ /* cut RISC OS file type off ISO name */
+ if (diskStructure->archimedes_enabled &&
+ *oldname == ',' && strlen(oldname) == 4)
+ break;
+
+ /* Enforce 12.3 / 8 */
+ if (namelen == 8 && !found_ext)
+ break;
+
+ if (islower((unsigned char)*oldname))
+ *newname++ = toupper((unsigned char)*oldname);
+ else if (isupper((unsigned char)*oldname)
+ || isdigit((unsigned char)*oldname))
+ *newname++ = *oldname;
+ else
+ *newname++ = '_';
+
+ if (found_ext)
+ extlen++;
+ else
+ namelen++;
+ }
+ oldname++;
+ }
+ if (is_file) {
+ if (!found_ext && !diskStructure->omit_trailing_period)
+ *newname++ = '.';
+ /* Add version */
+ sprintf(newname, ";%i", 1);
+ }
+ return namelen + extlen + found_ext;
+}
+
+/* XXX bounds checking! */
+static int
+cd9660_level2_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+ char *newname, int is_file)
+{
+ /*
+ * ISO 9660 : 7.5.1
+ * File name : 0+ d or d1 characters
+ * separator 1 (.)
+ * File name extension : 0+ d or d1 characters
+ * separator 2 (;)
+ * File version number (5 characters, 1-32767)
+ * 1 <= Sum of File name and File name extension <= 30
+ */
+ int namelen = 0;
+ int extlen = 0;
+ int found_ext = 0;
+
+ while (*oldname != '\0' && namelen + extlen < 30) {
+ /* Handle period first, as it is special */
+ if (*oldname == '.') {
+ if (found_ext) {
+ if (diskStructure->allow_multidot) {
+ *newname++ = '.';
+ } else {
+ *newname++ = '_';
+ }
+ extlen ++;
+ }
+ else {
+ *newname++ = '.';
+ found_ext = 1;
+ }
+ } else {
+ /* cut RISC OS file type off ISO name */
+ if (diskStructure->archimedes_enabled &&
+ *oldname == ',' && strlen(oldname) == 4)
+ break;
+
+ if (islower((unsigned char)*oldname))
+ *newname++ = toupper((unsigned char)*oldname);
+ else if (isupper((unsigned char)*oldname) ||
+ isdigit((unsigned char)*oldname))
+ *newname++ = *oldname;
+ else if (diskStructure->allow_multidot &&
+ *oldname == '.') {
+ *newname++ = '.';
+ } else {
+ *newname++ = '_';
+ }
+
+ if (found_ext)
+ extlen++;
+ else
+ namelen++;
+ }
+ oldname ++;
+ }
+ if (is_file) {
+ if (!found_ext && !diskStructure->omit_trailing_period)
+ *newname++ = '.';
+ /* Add version */
+ sprintf(newname, ";%i", 1);
+ }
+ return namelen + extlen + found_ext;
+}
+
+#if 0
+static int
+cd9660_joliet_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+ char *newname, int is_file)
+{
+ /* TODO: implement later, move to cd9660_joliet.c ?? */
+}
+#endif
+
+
+/*
+ * Convert a file name to ISO compliant file name
+ * @param char * oldname The original filename
+ * @param char ** newname The new file name, in the appropriate character
+ * set and of appropriate length
+ * @param int 1 if file, 0 if directory
+ * @returns int The length of the new string
+ */
+static int
+cd9660_convert_filename(iso9660_disk *diskStructure, const char *oldname,
+ char *newname, int is_file)
+{
+ /* NEW */
+ cd9660_filename_conversion_functor conversion_function = 0;
+ if (diskStructure->isoLevel == 1)
+ conversion_function = &cd9660_level1_convert_filename;
+ else if (diskStructure->isoLevel == 2)
+ conversion_function = &cd9660_level2_convert_filename;
+ return (*conversion_function)(diskStructure, oldname, newname, is_file);
+}
+
+int
+cd9660_compute_record_size(iso9660_disk *diskStructure, cd9660node *node)
+{
+ int size = node->isoDirRecord->length[0];
+
+ if (diskStructure->rock_ridge_enabled)
+ size += node->susp_entry_size;
+ size += node->su_tail_size;
+ size += size & 1; /* Ensure length of record is even. */
+ assert(size <= 254);
+ return size;
+}
+
+static void
+cd9660_populate_dot_records(iso9660_disk *diskStructure, cd9660node *node)
+{
+ node->dot_record->fileDataSector = node->fileDataSector;
+ memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34);
+ node->dot_record->isoDirRecord->name_len[0] = 1;
+ node->dot_record->isoDirRecord->name[0] = 0;
+ node->dot_record->isoDirRecord->name[1] = 0;
+ node->dot_record->isoDirRecord->length[0] = 34;
+ node->dot_record->fileRecordSize =
+ cd9660_compute_record_size(diskStructure, node->dot_record);
+
+ if (node == diskStructure->rootNode) {
+ node->dot_dot_record->fileDataSector = node->fileDataSector;
+ memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord,
+ 34);
+ } else {
+ node->dot_dot_record->fileDataSector =
+ node->parent->fileDataSector;
+ memcpy(node->dot_dot_record->isoDirRecord,
+ node->parent->isoDirRecord,34);
+ }
+ node->dot_dot_record->isoDirRecord->name_len[0] = 1;
+ node->dot_dot_record->isoDirRecord->name[0] = 1;
+ node->dot_dot_record->isoDirRecord->name[1] = 0;
+ node->dot_dot_record->isoDirRecord->length[0] = 34;
+ node->dot_dot_record->fileRecordSize =
+ cd9660_compute_record_size(diskStructure, node->dot_dot_record);
+}
+
+/*
+ * @param struct cd9660node *node The node
+ * @param int The offset (in bytes) - SHOULD align to the beginning of a sector
+ * @returns int The total size of files and directory entries (should be
+ * a multiple of sector size)
+*/
+static int64_t
+cd9660_compute_offsets(iso9660_disk *diskStructure, cd9660node *node,
+ int64_t startOffset)
+{
+ /*
+ * This function needs to compute the size of directory records and
+ * runs, file lengths, and set the appropriate variables both in
+ * cd9660node and isoDirEntry
+ */
+ int64_t used_bytes = 0;
+ int64_t current_sector_usage = 0;
+ cd9660node *child;
+ fsinode *inode;
+ int64_t r;
+
+ assert(node != NULL);
+
+
+ /*
+ * NOTE : There needs to be some special case detection for
+ * the "real root" node, since for it, node->node is undefined
+ */
+
+ node->fileDataSector = -1;
+
+ if (node->type & CD9660_TYPE_DIR) {
+ node->fileRecordSize = cd9660_compute_record_size(
+ diskStructure, node);
+ /*Set what sector this directory starts in*/
+ node->fileDataSector =
+ CD9660_BLOCKS(diskStructure->sectorSize,startOffset);
+
+ cd9660_bothendian_dword(node->fileDataSector,
+ node->isoDirRecord->extent);
+
+ /*
+ * First loop over children, need to know the size of
+ * their directory records
+ */
+ node->fileSectorsUsed = 1;
+ TAILQ_FOREACH(child, &node->cn_children, cn_next_child) {
+ node->fileDataLength +=
+ cd9660_compute_record_size(diskStructure, child);
+ if ((cd9660_compute_record_size(diskStructure, child) +
+ current_sector_usage) >=
+ diskStructure->sectorSize) {
+ current_sector_usage = 0;
+ node->fileSectorsUsed++;
+ }
+
+ current_sector_usage +=
+ cd9660_compute_record_size(diskStructure, child);
+ }
+
+ cd9660_bothendian_dword(node->fileSectorsUsed *
+ diskStructure->sectorSize,node->isoDirRecord->size);
+
+ /*
+ * This should point to the sector after the directory
+ * record (or, the first byte in that sector)
+ */
+ used_bytes += node->fileSectorsUsed * diskStructure->sectorSize;
+
+ for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+ child != NULL; child = TAILQ_NEXT(child, cn_next_child)) {
+ /* Directories need recursive call */
+ if (S_ISDIR(child->node->type)) {
+ r = cd9660_compute_offsets(diskStructure, child,
+ used_bytes + startOffset);
+
+ if (r != -1)
+ used_bytes += r;
+ else
+ return -1;
+ }
+ }
+
+ /* Explicitly set the . and .. records */
+ cd9660_populate_dot_records(diskStructure, node);
+
+ /* Finally, do another iteration to write the file data*/
+ for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+ child != NULL;
+ child = TAILQ_NEXT(child, cn_next_child)) {
+ /* Files need extent set */
+ if (S_ISDIR(child->node->type))
+ continue;
+ child->fileRecordSize =
+ cd9660_compute_record_size(diskStructure, child);
+
+ child->fileSectorsUsed =
+ CD9660_BLOCKS(diskStructure->sectorSize,
+ child->fileDataLength);
+
+ inode = child->node->inode;
+ if ((inode->flags & FI_ALLOCATED) == 0) {
+ inode->ino =
+ CD9660_BLOCKS(diskStructure->sectorSize,
+ used_bytes + startOffset);
+ inode->flags |= FI_ALLOCATED;
+ used_bytes += child->fileSectorsUsed *
+ diskStructure->sectorSize;
+ } else {
+ INODE_WARNX(("%s: already allocated inode %d "
+ "data sectors at %" PRIu32, __func__,
+ (int)inode->st.st_ino, inode->ino));
+ }
+ child->fileDataSector = inode->ino;
+ cd9660_bothendian_dword(child->fileDataSector,
+ child->isoDirRecord->extent);
+ }
+ }
+
+ return used_bytes;
+}
+
+#if 0
+/* Might get rid of this func */
+static int
+cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file)
+{
+ to->node->inode->st.st_dev = 0;
+ to->node->inode->st.st_ino = 0;
+ to->node->inode->st.st_size = 0;
+ to->node->inode->st.st_blksize = from->node->inode->st.st_blksize;
+ to->node->inode->st.st_atime = from->node->inode->st.st_atime;
+ to->node->inode->st.st_mtime = from->node->inode->st.st_mtime;
+ to->node->inode->st.st_ctime = from->node->inode->st.st_ctime;
+ to->node->inode->st.st_uid = from->node->inode->st.st_uid;
+ to->node->inode->st.st_gid = from->node->inode->st.st_gid;
+ to->node->inode->st.st_mode = from->node->inode->st.st_mode;
+ /* Clear out type */
+ to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT);
+ if (file)
+ to->node->inode->st.st_mode |= S_IFREG;
+ else
+ to->node->inode->st.st_mode |= S_IFDIR;
+ return 1;
+}
+#endif
+
+static cd9660node *
+cd9660_create_virtual_entry(iso9660_disk *diskStructure, const char *name,
+ cd9660node *parent, int file, int insert)
+{
+ cd9660node *temp;
+ fsnode * tfsnode;
+
+ assert(parent != NULL);
+
+ temp = cd9660_allocate_cd9660node();
+ if (temp == NULL)
+ return NULL;
+
+ tfsnode = emalloc(sizeof(*tfsnode));
+ tfsnode->name = estrdup(name);
+ temp->isoDirRecord = emalloc(sizeof(*temp->isoDirRecord));
+
+ cd9660_convert_filename(diskStructure, tfsnode->name,
+ temp->isoDirRecord->name, file);
+
+ temp->node = tfsnode;
+ temp->parent = parent;
+
+ if (insert) {
+ if (temp->parent != NULL) {
+ temp->level = temp->parent->level + 1;
+ if (!TAILQ_EMPTY(&temp->parent->cn_children))
+ cd9660_sorted_child_insert(temp->parent, temp);
+ else
+ TAILQ_INSERT_HEAD(&temp->parent->cn_children,
+ temp, cn_next_child);
+ }
+ }
+
+ if (parent->node != NULL) {
+ tfsnode->type = parent->node->type;
+ }
+
+ /* Clear out file type bits */
+ tfsnode->type &= ~(S_IFMT);
+ if (file)
+ tfsnode->type |= S_IFREG;
+ else
+ tfsnode->type |= S_IFDIR;
+
+ /* Indicate that there is no spec entry (inode) */
+ tfsnode->flags &= ~(FSNODE_F_HASSPEC);
+#if 0
+ cd9660_copy_stat_info(parent, temp, file);
+#endif
+ return temp;
+}
+
+static cd9660node *
+cd9660_create_file(iso9660_disk *diskStructure, const char *name,
+ cd9660node *parent, cd9660node *me)
+{
+ cd9660node *temp;
+
+ temp = cd9660_create_virtual_entry(diskStructure, name, parent, 1, 1);
+ if (temp == NULL)
+ return NULL;
+
+ temp->fileDataLength = 0;
+
+ temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL;
+
+ temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
+ *temp->node->inode = *me->node->inode;
+
+ if (cd9660_translate_node_common(diskStructure, temp) == 0)
+ return NULL;
+ return temp;
+}
+
+/*
+ * Create a new directory which does not exist on disk
+ * @param const char * name The name to assign to the directory
+ * @param const char * parent Pointer to the parent directory
+ * @returns cd9660node * Pointer to the new directory
+ */
+static cd9660node *
+cd9660_create_directory(iso9660_disk *diskStructure, const char *name,
+ cd9660node *parent, cd9660node *me)
+{
+ cd9660node *temp;
+
+ temp = cd9660_create_virtual_entry(diskStructure, name, parent, 0, 1);
+ if (temp == NULL)
+ return NULL;
+ temp->node->type |= S_IFDIR;
+
+ temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL;
+
+ temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
+ *temp->node->inode = *me->node->inode;
+
+ if (cd9660_translate_node_common(diskStructure, temp) == 0)
+ return NULL;
+ return temp;
+}
+
+static cd9660node *
+cd9660_create_special_directory(iso9660_disk *diskStructure, u_char type,
+ cd9660node *parent)
+{
+ cd9660node *temp, *first;
+ char na[2];
+
+ assert(parent != NULL);
+
+ if (type == CD9660_TYPE_DOT)
+ na[0] = 0;
+ else if (type == CD9660_TYPE_DOTDOT)
+ na[0] = 1;
+ else
+ return 0;
+
+ na[1] = 0;
+ if ((temp = cd9660_create_virtual_entry(diskStructure, na, parent,
+ 0, 0)) == NULL)
+ return NULL;
+
+ temp->parent = parent;
+ temp->type = type;
+ temp->isoDirRecord->length[0] = 34;
+ /* Dot record is always first */
+ if (type == CD9660_TYPE_DOT) {
+ parent->dot_record = temp;
+ TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child);
+ /* DotDot should be second */
+ } else if (type == CD9660_TYPE_DOTDOT) {
+ parent->dot_dot_record = temp;
+ /*
+ * If the first child is the dot record, insert
+ * this second. Otherwise, insert it at the head.
+ */
+ if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL ||
+ (first->type & CD9660_TYPE_DOT) == 0) {
+ TAILQ_INSERT_HEAD(&parent->cn_children, temp,
+ cn_next_child);
+ } else {
+ TAILQ_INSERT_AFTER(&parent->cn_children, first, temp,
+ cn_next_child);
+ }
+ }
+
+ return temp;
+}
+
+static int
+cd9660_add_generic_bootimage(iso9660_disk *diskStructure, const char *bootimage)
+{
+ struct stat stbuf;
+
+ assert(bootimage != NULL);
+
+ if (*bootimage == '\0') {
+ warnx("Error: Boot image must be a filename");
+ return 0;
+ }
+
+ diskStructure->generic_bootimage = estrdup(bootimage);
+
+ /* Get information about the file */
+ if (lstat(diskStructure->generic_bootimage, &stbuf) == -1)
+ err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+ diskStructure->generic_bootimage);
+
+ if (stbuf.st_size > 32768) {
+ warnx("Error: Boot image must be no greater than 32768 bytes");
+ return 0;
+ }
+
+ if (diskStructure->verbose_level > 0) {
+ printf("Generic boot image image has size %lld\n",
+ (long long)stbuf.st_size);
+ }
+
+ diskStructure->has_generic_bootimage = 1;
+
+ return 1;
+}
diff --git a/usr.sbin/makefs/cd9660.h b/usr.sbin/makefs/cd9660.h
new file mode 100644
index 00000000000..5f803f24f80
--- /dev/null
+++ b/usr.sbin/makefs/cd9660.h
@@ -0,0 +1,353 @@
+/* $NetBSD: cd9660.h,v 1.21 2015/12/24 15:52:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 _MAKEFS_CD9660_H
+#define _MAKEFS_CD9660_H
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include "makefs.h"
+#include "iso.h"
+#include "iso_rrip.h"
+#include "cd9660/cd9660_eltorito.h"
+
+#ifdef DEBUG
+#define INODE_WARNX(__x) warnx __x
+#else /* DEBUG */
+#define INODE_WARNX(__x)
+#endif /* DEBUG */
+
+#define CD9660MAXPATH 4096
+
+#define ISO_STRING_FILTER_NONE = 0x00
+#define ISO_STRING_FILTER_DCHARS = 0x01
+#define ISO_STRING_FILTER_ACHARS = 0x02
+
+/*
+Extended preferences type, in the spirit of what makefs gives us (only ints)
+*/
+typedef struct {
+ const char *shortName; /* Short option */
+ const char *name; /* option name */
+ char *value; /* where to stuff the value */
+ int minLength; /* minimum for value */
+ int maxLength; /* maximum for value */
+ const char *desc; /* option description */
+ int filterFlags;
+} string_option_t;
+
+/******** STRUCTURES **********/
+
+/*Defaults*/
+#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE"
+#define ISO_DEFAULT_APPID "MAKEFS"
+#define ISO_DEFAULT_PUBLISHER "MAKEFS"
+#define ISO_DEFAULT_PREPARER "MAKEFS"
+
+#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001"
+#define ISO_VOLUME_DESCRIPTOR_BOOT 0
+#define ISO_VOLUME_DESCRIPTOR_PVD 1
+#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255
+
+/*30 for name and extension, as well as version number and padding bit*/
+#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30
+#define ISO_FILENAME_MAXLENGTH 36
+#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37
+
+#define ISO_FLAG_CLEAR 0x00
+#define ISO_FLAG_HIDDEN 0x01
+#define ISO_FLAG_DIRECTORY 0x02
+#define ISO_FLAG_ASSOCIATED 0x04
+#define ISO_FLAG_PERMISSIONS 0x08
+#define ISO_FLAG_RESERVED5 0x10
+#define ISO_FLAG_RESERVED6 0x20
+#define ISO_FLAG_FINAL_RECORD 0x40
+
+#define ISO_PATHTABLE_ENTRY_BASESIZE 8
+
+#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED"
+#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved"
+
+#define CD9660_BLOCKS(__sector_size, __bytes) \
+ howmany((__bytes), (__sector_size))
+
+#define CD9660_MEM_ALLOC_ERROR(_F) \
+ err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__)
+
+#define CD9660_TYPE_FILE 0x01
+#define CD9660_TYPE_DIR 0x02
+#define CD9660_TYPE_DOT 0x04
+#define CD9660_TYPE_DOTDOT 0x08
+#define CD9660_TYPE_VIRTUAL 0x80
+
+#define CD9660_INODE_HASH_SIZE 1024
+#define CD9660_SECTOR_SIZE 2048
+
+#define CD9660_END_PADDING 150
+
+/* Slight modification of the ISO structure in iso.h */
+typedef struct _iso_directory_record_cd9660 {
+ u_char length [ISODCL (1, 1)]; /* 711 */
+ u_char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ u_char extent [ISODCL (3, 10)]; /* 733 */
+ u_char size [ISODCL (11, 18)]; /* 733 */
+ u_char date [ISODCL (19, 25)]; /* 7 by 711 */
+ u_char flags [ISODCL (26, 26)];
+ u_char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ u_char interleave [ISODCL (28, 28)]; /* 711 */
+ u_char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ u_char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} iso_directory_record_cd9660;
+
+/* TODO: Lots of optimization of this structure */
+typedef struct _cd9660node {
+ u_char type;/* Used internally */
+ /* Tree structure */
+ struct _cd9660node *parent; /* parent (NULL if root) */
+ TAILQ_HEAD(cd9660_children_head, _cd9660node) cn_children;
+ TAILQ_ENTRY(_cd9660node) cn_next_child;
+
+ struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */
+ struct _cd9660node *dot_dot_record;
+
+ fsnode *node; /* pointer to fsnode */
+ struct _iso_directory_record_cd9660 *isoDirRecord;
+ struct iso_extended_attributes *isoExtAttributes;
+
+ /***** SIZE CALCULATION *****/
+ /*already stored in isoDirRecord, but this is an int version, and will be
+ copied to isoDirRecord on writing*/
+ uint32_t fileDataSector;
+
+ /*
+ * same thing, though some notes:
+ * If a file, this is the file size
+ * If a directory, this is the size of all its children's
+ * directory records
+ * plus necessary padding
+ */
+ int64_t fileDataLength;
+
+ int64_t fileSectorsUsed;
+ int fileRecordSize;/*copy of a variable, int for quicker calculations*/
+
+ /* Old name, used for renaming - needs to be optimized but low priority */
+ char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+ /***** SPACE RESERVED FOR EXTENSIONS *****/
+ /* For memory efficiency's sake - we should move this to a separate struct
+ and point to null if not needed */
+ /* For Rock Ridge */
+ struct _cd9660node *rr_real_parent, *rr_relocated;
+
+ int64_t susp_entry_size;
+ int64_t susp_dot_entry_size;
+ int64_t susp_dot_dot_entry_size;
+
+ /* Continuation area stuff */
+ int64_t susp_entry_ce_start;
+ int64_t susp_dot_ce_start;
+ int64_t susp_dot_dot_ce_start;
+
+ int64_t susp_entry_ce_length;
+ int64_t susp_dot_ce_length;
+ int64_t susp_dot_dot_ce_length;
+
+ /* Data to put at the end of the System Use field */
+ int64_t su_tail_size;
+ char *su_tail_data;
+
+ /*** PATH TABLE STUFF ***/
+ int level; /*depth*/
+ int ptnumber;
+ struct _cd9660node *ptnext, *ptprev, *ptlast;
+
+ /* SUSP entries */
+ TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head;
+} cd9660node;
+
+typedef struct _path_table_entry
+{
+ u_char length[ISODCL (1, 1)];
+ u_char extended_attribute_length[ISODCL (2, 2)];
+ u_char first_sector[ISODCL (3, 6)];
+ u_char parent_number[ISODCL (7, 8)];
+ u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} path_table_entry;
+
+typedef struct _volume_descriptor
+{
+ u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/
+ int64_t sector;
+ struct _volume_descriptor *next;
+} volume_descriptor;
+
+typedef struct _iso9660_disk {
+ int sectorSize;
+ struct iso_primary_descriptor primaryDescriptor;
+ struct iso_supplementary_descriptor supplementaryDescriptor;
+
+ volume_descriptor *firstVolumeDescriptor;
+
+ cd9660node *rootNode;
+
+ /* Important sector numbers here */
+ /* primaryDescriptor.type_l_path_table*/
+ int64_t primaryBigEndianTableSector;
+
+ /* primaryDescriptor.type_m_path_table*/
+ int64_t primaryLittleEndianTableSector;
+
+ /* primaryDescriptor.opt_type_l_path_table*/
+ int64_t secondaryBigEndianTableSector;
+
+ /* primaryDescriptor.opt_type_m_path_table*/
+ int64_t secondaryLittleEndianTableSector;
+
+ /* primaryDescriptor.path_table_size*/
+ int pathTableLength;
+ int64_t dataFirstSector;
+
+ int64_t totalSectors;
+ /* OPTIONS GO HERE */
+ int isoLevel;
+
+ int include_padding_areas;
+
+ int follow_sym_links;
+ int verbose_level;
+ int displayHelp;
+ int keep_bad_images;
+
+ /* SUSP options and variables */
+ int64_t susp_continuation_area_start_sector;
+ int64_t susp_continuation_area_size;
+ int64_t susp_continuation_area_current_free;
+
+ int rock_ridge_enabled;
+ /* Other Rock Ridge Variables */
+ char *rock_ridge_renamed_dir_name;
+ int rock_ridge_move_count;
+ cd9660node *rr_moved_dir;
+
+ int archimedes_enabled;
+ int chrp_boot;
+
+ /* Spec breaking options */
+ u_char allow_deep_trees;
+ u_char allow_start_dot;
+ u_char allow_max_name; /* Allow 37 char filenames*/
+ u_char allow_illegal_chars; /* ~, !, # */
+ u_char allow_lowercase;
+ u_char allow_multidot;
+ u_char omit_trailing_period;
+
+ /* BOOT INFORMATION HERE */
+ int has_generic_bootimage; /* Default to 0 */
+ char *generic_bootimage;
+
+ int is_bootable;/* Default to 0 */
+ int64_t boot_catalog_sector;
+ boot_volume_descriptor *boot_descriptor;
+ char * boot_image_directory;
+
+ TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images;
+ int image_serialno;
+ LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries;
+
+} iso9660_disk;
+
+/************ FUNCTIONS **************/
+int cd9660_valid_a_chars(const char *);
+int cd9660_valid_d_chars(const char *);
+void cd9660_uppercase_characters(char *, size_t);
+
+/* ISO Data Types */
+void cd9660_721(uint16_t, unsigned char *);
+void cd9660_731(uint32_t, unsigned char *);
+void cd9660_722(uint16_t, unsigned char *);
+void cd9660_732(uint32_t, unsigned char *);
+void cd9660_bothendian_dword(uint32_t dw, unsigned char *);
+void cd9660_bothendian_word(uint16_t dw, unsigned char *);
+void cd9660_set_date(char *, time_t);
+void cd9660_time_8426(unsigned char *, time_t);
+void cd9660_time_915(unsigned char *, time_t);
+
+/*** Boot Functions ***/
+int cd9660_write_generic_bootimage(FILE *);
+int cd9660_write_boot(iso9660_disk *, FILE *);
+int cd9660_add_boot_disk(iso9660_disk *, const char *);
+int cd9660_eltorito_add_boot_option(iso9660_disk *, const char *,
+ const char *);
+int cd9660_setup_boot(iso9660_disk *, int);
+int cd9660_setup_boot_volume_descriptor(iso9660_disk *,
+ volume_descriptor *);
+
+
+/*** Write Functions ***/
+int cd9660_write_image(iso9660_disk *, const char *image);
+int cd9660_copy_file(iso9660_disk *, FILE *, off_t, const char *);
+
+void cd9660_compute_full_filename(cd9660node *, char *);
+int cd9660_compute_record_size(iso9660_disk *, cd9660node *);
+
+/* Debugging functions */
+void debug_print_tree(iso9660_disk *, cd9660node *,int);
+void debug_print_path_tree(cd9660node *);
+void debug_print_volume_descriptor_information(iso9660_disk *);
+void debug_dump_to_xml_ptentry(path_table_entry *,int, int);
+void debug_dump_to_xml_path_table(FILE *, off_t, int, int);
+void debug_dump_to_xml(FILE *);
+int debug_get_encoded_number(unsigned char *, int);
+void debug_dump_integer(const char *, char *,int);
+void debug_dump_string(const char *,unsigned char *,int);
+void debug_dump_directory_record_9_1(unsigned char *);
+void debug_dump_to_xml_volume_descriptor(unsigned char *,int);
+
+void cd9660_pad_string_spaces(char *, int);
+
+#endif
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.c b/usr.sbin/makefs/cd9660/cd9660_archimedes.c
new file mode 100644
index 00000000000..79f558c58e2
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.c
@@ -0,0 +1,121 @@
+/* $NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ *
+ * RISC OS CDFS looks for a special block at the end of the System Use
+ * Field for each file. If present, this contains the RISC OS load
+ * and exec address (used to hold the file timestamp and type), the
+ * file attributes, and a flag indicating whether the first character
+ * of the filename should be replaced with '!' (since many special
+ * RISC OS filenames do).
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660_archimedes.h"
+
+/*
+ * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC
+ * OS time (non-leap(?) centiseconds since 1900-01-01(?)).
+ */
+
+static u_int64_t
+riscos_date(time_t unixtime)
+{
+ u_int64_t base;
+
+ base = 31536000ULL * 70 + 86400 * 17;
+ return (((u_int64_t)unixtime) + base)*100;
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to a node if that seems appropriate.
+ *
+ * We touch regular files with names matching /,[0-9a-f]{3}$/ and
+ * directories matching /^!/.
+ */
+static void
+archimedes_convert_node(cd9660node *node)
+{
+ struct ISO_ARCHIMEDES *arc;
+ size_t len;
+ int type = -1;
+ uint64_t stamp;
+
+ if (node->su_tail_data != NULL)
+ /* Something else already has the tail. */
+ return;
+
+ len = strlen(node->node->name);
+ if (len < 1) return;
+
+ if (len >= 4 && node->node->name[len-4] == ',')
+ /* XXX should support ,xxx and ,lxa */
+ type = strtoul(node->node->name + len - 3, NULL, 16);
+ if (type == -1 && node->node->name[0] != '!')
+ return;
+ if (type == -1) type = 0;
+
+ assert(sizeof(*arc) == 32);
+ arc = ecalloc(1, sizeof(*arc));
+
+ stamp = riscos_date(node->node->inode->st.st_mtime);
+
+ memcpy(arc->magic, "ARCHIMEDES", 10);
+ cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr);
+ cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr);
+ arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR;
+ arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0;
+ node->su_tail_data = (void *)arc;
+ node->su_tail_size = sizeof(*arc);
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to an entire tree recursively.
+ */
+void
+archimedes_convert_tree(cd9660node *node)
+{
+ cd9660node *cn;
+
+ assert(node != NULL);
+
+ archimedes_convert_node(node);
+
+ /* Recurse on children. */
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+ archimedes_convert_tree(cn);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.h b/usr.sbin/makefs/cd9660/cd9660_archimedes.h
new file mode 100644
index 00000000000..61338d269ff
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.h
@@ -0,0 +1,48 @@
+/* $NetBSD: cd9660_archimedes.h,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ */
+
+struct ISO_ARCHIMEDES {
+ char magic[10]; /* "ARCHIMEDES" */
+ unsigned char loadaddr[4]; /* Load address, little-endian */
+ unsigned char execaddr[4]; /* Exec address, little-endian */
+ unsigned char ro_attr; /* RISC OS attributes */
+#define RO_ACCESS_UR 0x01 /* Owner read */
+#define RO_ACCESS_UW 0x02 /* Owner write */
+#define RO_ACCESS_L 0x04 /* Locked */
+#define RO_ACCESS_OR 0x10 /* Public read */
+#define RO_ACCESS_OW 0x20 /* Public write */
+ unsigned char cdfs_attr; /* Extra attributes for CDFS */
+#define CDFS_PLING 0x01 /* Filename begins with '!' */
+ char reserved[12];
+};
+
+extern void archimedes_convert_tree(cd9660node *);
diff --git a/usr.sbin/makefs/cd9660/cd9660_conversion.c b/usr.sbin/makefs/cd9660/cd9660_conversion.c
new file mode 100644
index 00000000000..af21211c828
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_conversion.c
@@ -0,0 +1,201 @@
+/* $NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+#include "cd9660.h"
+
+#define bswap16 swap16
+#define bswap32 swap32
+
+
+static char cd9660_compute_gm_offset(time_t);
+
+#if 0
+static inline int
+cd9660_pad_even(length)
+int length;
+{
+ return length + (length & 0x01);
+}
+#endif
+
+/*
+* These can probably be implemented using a macro
+*/
+
+/* Little endian */
+void
+cd9660_721(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ w = bswap16(w);
+#endif
+ memcpy(twochar,&w,2);
+}
+
+void
+cd9660_731(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ w = bswap32(w);
+#endif
+ memcpy(fourchar,&w,4);
+}
+
+/* Big endian */
+void
+cd9660_722(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ w = bswap16(w);
+#endif
+ memcpy(twochar,&w,2);
+}
+
+void
+cd9660_732(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ w = bswap32(w);
+#endif
+ memcpy(fourchar,&w,4);
+}
+
+/**
+* Convert a dword into a double endian string of eight characters
+* @param int The double word to convert
+* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least
+* eight characters long
+*/
+void
+cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar)
+{
+ uint32_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ le = dw;
+ be = bswap32(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ be = dw;
+ le = bswap32(dw);
+#endif
+ memcpy(eightchar, &le, 4);
+ memcpy((eightchar+4), &be, 4);
+}
+
+/**
+* Convert a word into a double endian string of four characters
+* @param int The word to convert
+* @param char* The string to write the both endian word to - It is assumed this is allocated and at least
+* four characters long
+*/
+void
+cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar)
+{
+ uint16_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ le = dw;
+ be = bswap16(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ be = dw;
+ le = bswap16(dw);
+#endif
+ memcpy(fourchar, &le, 2);
+ memcpy((fourchar+2), &be, 2);
+}
+
+void
+cd9660_pad_string_spaces(char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i ++) {
+ if (str[i] == '\0')
+ str[i] = 0x20;
+ }
+}
+
+static char
+cd9660_compute_gm_offset(time_t tim)
+{
+ struct tm t, gm;
+
+ (void)localtime_r(&tim, &t);
+ (void)gmtime_r(&tim, &gm);
+ gm.tm_year -= t.tm_year;
+ gm.tm_yday -= t.tm_yday;
+ gm.tm_hour -= t.tm_hour;
+ gm.tm_min -= t.tm_min;
+ if (gm.tm_year < 0)
+ gm.tm_yday = -1;
+ else if (gm.tm_year > 0)
+ gm.tm_yday = 1;
+
+ return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15);
+}
+
+/* Long dates: 17 characters */
+void
+cd9660_time_8426(unsigned char *buf, time_t tim)
+{
+ struct tm t;
+ char temp[18];
+
+ (void)localtime_r(&tim, &t);
+ (void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i",
+ 1900+(int)t.tm_year,
+ (int)t.tm_mon+1,
+ (int)t.tm_mday,
+ (int)t.tm_hour,
+ (int)t.tm_min,
+ (int)t.tm_sec,
+ 0);
+ (void)memcpy(buf, temp, 16);
+ buf[16] = cd9660_compute_gm_offset(tim);
+}
+
+/* Short dates: 7 characters */
+void
+cd9660_time_915(unsigned char *buf, time_t tim)
+{
+ struct tm t;
+
+ (void)localtime_r(&tim, &t);
+ buf[0] = t.tm_year;
+ buf[1] = t.tm_mon+1;
+ buf[2] = t.tm_mday;
+ buf[3] = t.tm_hour;
+ buf[4] = t.tm_min;
+ buf[5] = t.tm_sec;
+ buf[6] = cd9660_compute_gm_offset(tim);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_debug.c b/usr.sbin/makefs/cd9660/cd9660_debug.c
new file mode 100644
index 00000000000..64fef22a512
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_debug.c
@@ -0,0 +1,482 @@
+/* $NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <inttypes.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+static void debug_print_susp_attrs(cd9660node *, int);
+static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *,
+ int);
+
+static inline void
+print_n_tabs(int n)
+{
+ int i;
+
+ for (i = 1; i <= n; i ++)
+ printf("\t");
+}
+
+#if 0
+void
+debug_print_rrip_info(n)
+cd9660node *n;
+{
+ struct ISO_SUSP_ATTRIBUTES *t;
+ TAILQ_FOREACH(t, &node->head, rr_ll) {
+
+ }
+}
+#endif
+
+static void
+debug_print_susp_attrs(cd9660node *n, int indent)
+{
+ struct ISO_SUSP_ATTRIBUTES *t;
+
+ TAILQ_FOREACH(t, &n->head, rr_ll) {
+ print_n_tabs(indent);
+ printf("-");
+ printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0],
+ t->attr.su_entry.SP.h.type[1],
+ (int)t->attr.su_entry.SP.h.length[0]);
+ printf("\n");
+ }
+}
+
+void
+debug_print_tree(iso9660_disk *diskStructure, cd9660node *node, int level)
+{
+ cd9660node *cn;
+
+ print_n_tabs(level);
+ if (node->type & CD9660_TYPE_DOT) {
+ printf(". (%i)\n",
+ isonum_733(node->isoDirRecord->extent));
+ } else if (node->type & CD9660_TYPE_DOTDOT) {
+ printf("..(%i)\n",
+ isonum_733(node->isoDirRecord->extent));
+ } else if (node->isoDirRecord->name[0]=='\0') {
+ printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n",
+ node->fileDataSector,
+ node->fileDataSector +
+ node->fileSectorsUsed - 1);
+ } else {
+ printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n",
+ node->isoDirRecord->name,
+ (node->isoDirRecord->flags[0]
+ & ISO_FLAG_DIRECTORY) ? "DIR" : "FILE",
+ node->fileDataSector,
+ (node->fileSectorsUsed == 0) ?
+ node->fileDataSector :
+ node->fileDataSector
+ + node->fileSectorsUsed - 1);
+ }
+ if (diskStructure->rock_ridge_enabled)
+ debug_print_susp_attrs(node, level + 1);
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+ debug_print_tree(diskStructure, cn, level + 1);
+}
+
+void
+debug_print_path_tree(cd9660node *n)
+{
+ cd9660node *iterator = n;
+
+ /* Only display this message when called with the root node */
+ if (n->parent == NULL)
+ printf("debug_print_path_table: Dumping path table contents\n");
+
+ while (iterator != NULL) {
+ if (iterator->isoDirRecord->name[0] == '\0')
+ printf("0) (ROOT)\n");
+ else
+ printf("%i) %s\n", iterator->level,
+ iterator->isoDirRecord->name);
+
+ iterator = iterator->ptnext;
+ }
+}
+
+void
+debug_print_volume_descriptor_information(iso9660_disk *diskStructure)
+{
+ volume_descriptor *tmp = diskStructure->firstVolumeDescriptor;
+ char temp[CD9660_SECTOR_SIZE];
+
+ printf("==Listing Volume Descriptors==\n");
+
+ while (tmp != NULL) {
+ memset(temp, 0, CD9660_SECTOR_SIZE);
+ memcpy(temp, tmp->volumeDescriptorData + 1, 5);
+ printf("Volume descriptor in sector %" PRId64
+ ": type %i, ID %s\n",
+ tmp->sector, tmp->volumeDescriptorData[0], temp);
+ switch(tmp->volumeDescriptorData[0]) {
+ case 0:/*boot record*/
+ break;
+
+ case 1: /* PVD */
+ break;
+
+ case 2: /* SVD */
+ break;
+
+ case 3: /* Volume Partition Descriptor */
+ break;
+
+ case 255: /* terminator */
+ break;
+ }
+ tmp = tmp->next;
+ }
+
+ printf("==Done Listing Volume Descriptors==\n");
+}
+
+void
+debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode)
+{
+ printf("<ptentry num=\"%i\">\n" ,num);
+ printf("<length>%i</length>\n", pttemp->length[0]);
+ printf("<extended_attribute_length>%i</extended_attribute_length>\n",
+ pttemp->extended_attribute_length[0]);
+ printf("<parent_number>%i</parent_number>\n",
+ debug_get_encoded_number(pttemp->parent_number,mode));
+ debug_dump_to_xml_padded_hex_output("name",
+ pttemp->name, pttemp->length[0]);
+ printf("</ptentry>\n");
+}
+
+void
+debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode)
+{
+ path_table_entry pttemp;
+ int t = 0;
+ int n = 0;
+
+ if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ while (t < size) {
+ /* Read fixed data first */
+ fread(&pttemp, 1, 8, fd);
+ t += 8;
+ /* Read variable */
+ fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd);
+ t += pttemp.length[0];
+ debug_dump_to_xml_ptentry(&pttemp, n, mode);
+ n++;
+ }
+
+}
+
+/*
+ * XML Debug output functions
+ * Dump hierarchy of CD, as well as volume info, to XML
+ * Can be used later to diff against a standard,
+ * or just provide easy to read detailed debug output
+ */
+void
+debug_dump_to_xml(FILE *fd)
+{
+ unsigned char buf[CD9660_SECTOR_SIZE];
+ off_t sector;
+ int t, t2;
+ struct iso_primary_descriptor primaryVD;
+ struct _boot_volume_descriptor bootVD;
+
+ memset(&primaryVD, 0, sizeof(primaryVD));
+ printf("<cd9660dump>\n");
+
+ /* Display Volume Descriptors */
+ sector = 16;
+ do {
+ if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+ err(1, "fseeko");
+ fread(buf, 1, CD9660_SECTOR_SIZE, fd);
+ t = (int)((unsigned char)buf[0]);
+ switch (t) {
+ case 0:
+ memcpy(&bootVD, buf, CD9660_SECTOR_SIZE);
+ break;
+ case 1:
+ memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE);
+ break;
+ }
+ debug_dump_to_xml_volume_descriptor(buf, sector);
+ sector++;
+ } while (t != 255);
+
+ t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table,
+ 731);
+ t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733);
+ printf("Path table 1 located at sector %i and is %i bytes long\n",
+ t,t2);
+ debug_dump_to_xml_path_table(fd, t, t2, 721);
+
+ t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table,
+ 731);
+ debug_dump_to_xml_path_table(fd, t, t2, 722);
+
+ printf("</cd9660dump>\n");
+}
+
+static void
+debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf,
+ int len)
+{
+ int i;
+ int t;
+
+ printf("<%s>",element);
+ for (i = 0; i < len; i++) {
+ t = (unsigned char)buf[i];
+ if (t >= 32 && t < 127)
+ printf("%c",t);
+ }
+ printf("</%s>\n",element);
+
+ printf("<%s:hex>",element);
+ for (i = 0; i < len; i++) {
+ t = (unsigned char)buf[i];
+ printf(" %x",t);
+ }
+ printf("</%s:hex>\n",element);
+}
+
+int
+debug_get_encoded_number(unsigned char* buf, int mode)
+{
+ switch (mode) {
+ /* 711: Single bite */
+ case 711:
+ return isonum_711(buf);
+
+ /* 712: Single signed byte */
+ case 712:
+ return isonum_712((signed char *)buf);
+
+ /* 721: 16 bit LE */
+ case 721:
+ return isonum_721(buf);
+
+ /* 731: 32 bit LE */
+ case 731:
+ return isonum_731(buf);
+
+ /* 722: 16 bit BE */
+ case 722:
+ return isonum_722(buf);
+
+ /* 732: 32 bit BE */
+ case 732:
+ return isonum_732(buf);
+
+ /* 723: 16 bit bothE */
+ case 723:
+ return isonum_723(buf);
+
+ /* 733: 32 bit bothE */
+ case 733:
+ return isonum_733(buf);
+ }
+ return 0;
+}
+
+void
+debug_dump_integer(const char *element, char* buf, int mode)
+{
+ printf("<%s>%i</%s>\n", element,
+ debug_get_encoded_number((unsigned char *)buf, mode), element);
+}
+
+void
+debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused)
+{
+
+}
+
+void
+debug_dump_directory_record_9_1(unsigned char* buf)
+{
+ printf("<directoryrecord>\n");
+ debug_dump_integer("length",
+ ((struct iso_directory_record*) buf)->length, 711);
+ debug_dump_integer("ext_attr_length",
+ ((struct iso_directory_record*) buf)->ext_attr_length,711);
+ debug_dump_integer("extent",
+ (char *)((struct iso_directory_record*) buf)->extent, 733);
+ debug_dump_integer("size",
+ (char *)((struct iso_directory_record*) buf)->size, 733);
+ debug_dump_integer("flags",
+ ((struct iso_directory_record*) buf)->flags, 711);
+ debug_dump_integer("file_unit_size",
+ ((struct iso_directory_record*) buf)->file_unit_size,711);
+ debug_dump_integer("interleave",
+ ((struct iso_directory_record*) buf)->interleave, 711);
+ debug_dump_integer("volume_sequence_number",
+ ((struct iso_directory_record*) buf)->volume_sequence_number,
+ 723);
+ debug_dump_integer("name_len",
+ ((struct iso_directory_record*) buf)->name_len, 711);
+ debug_dump_to_xml_padded_hex_output("name",
+ (u_char *)((struct iso_directory_record*) buf)->name,
+ debug_get_encoded_number((u_char *)
+ ((struct iso_directory_record*) buf)->length, 711));
+ printf("</directoryrecord>\n");
+}
+
+
+void
+debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector)
+{
+ printf("<volumedescriptor sector=\"%i\">\n", sector);
+ printf("<vdtype>");
+ switch(buf[0]) {
+ case 0:
+ printf("boot");
+ break;
+
+ case 1:
+ printf("primary");
+ break;
+
+ case 2:
+ printf("supplementary");
+ break;
+
+ case 3:
+ printf("volume partition descriptor");
+ break;
+
+ case 255:
+ printf("terminator");
+ break;
+ }
+
+ printf("</vdtype>\n");
+ switch(buf[0]) {
+ case 1:
+ debug_dump_integer("type",
+ ((struct iso_primary_descriptor*)buf)->type, 711);
+ debug_dump_to_xml_padded_hex_output("id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->id,
+ ISODCL ( 2, 6));
+ debug_dump_integer("version",
+ ((struct iso_primary_descriptor*)buf)->version,
+ 711);
+ debug_dump_to_xml_padded_hex_output("system_id",
+ (u_char *)((struct iso_primary_descriptor*)buf)->system_id,
+ ISODCL(9,40));
+ debug_dump_to_xml_padded_hex_output("volume_id",
+ (u_char *)((struct iso_primary_descriptor*)buf)->volume_id,
+ ISODCL(41,72));
+ debug_dump_integer("volume_space_size",
+ ((struct iso_primary_descriptor*)buf)->volume_space_size,
+ 733);
+ debug_dump_integer("volume_set_size",
+ ((struct iso_primary_descriptor*)buf)->volume_set_size,
+ 733);
+ debug_dump_integer("volume_sequence_number",
+ ((struct iso_primary_descriptor*)buf)->volume_sequence_number,
+ 723);
+ debug_dump_integer("logical_block_size",
+ ((struct iso_primary_descriptor*)buf)->logical_block_size,
+ 723);
+ debug_dump_integer("path_table_size",
+ ((struct iso_primary_descriptor*)buf)->path_table_size,
+ 733);
+ debug_dump_integer("type_l_path_table",
+ ((struct iso_primary_descriptor*)buf)->type_l_path_table,
+ 731);
+ debug_dump_integer("opt_type_l_path_table",
+ ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table,
+ 731);
+ debug_dump_integer("type_m_path_table",
+ ((struct iso_primary_descriptor*)buf)->type_m_path_table,
+ 732);
+ debug_dump_integer("opt_type_m_path_table",
+ ((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732);
+ debug_dump_directory_record_9_1(
+ (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record);
+ debug_dump_to_xml_padded_hex_output("volume_set_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id,
+ ISODCL (191, 318));
+ debug_dump_to_xml_padded_hex_output("publisher_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id,
+ ISODCL (319, 446));
+ debug_dump_to_xml_padded_hex_output("preparer_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id,
+ ISODCL (447, 574));
+ debug_dump_to_xml_padded_hex_output("application_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->application_id,
+ ISODCL (575, 702));
+ debug_dump_to_xml_padded_hex_output("copyright_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id,
+ ISODCL (703, 739));
+ debug_dump_to_xml_padded_hex_output("abstract_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id,
+ ISODCL (740, 776));
+ debug_dump_to_xml_padded_hex_output("bibliographic_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id,
+ ISODCL (777, 813));
+
+ debug_dump_to_xml_padded_hex_output("creation_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->creation_date,
+ ISODCL (814, 830));
+ debug_dump_to_xml_padded_hex_output("modification_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->modification_date,
+ ISODCL (831, 847));
+ debug_dump_to_xml_padded_hex_output("expiration_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date,
+ ISODCL (848, 864));
+ debug_dump_to_xml_padded_hex_output("effective_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->effective_date,
+ ISODCL (865, 881));
+
+ debug_dump_to_xml_padded_hex_output("file_structure_version",
+ (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version,
+ ISODCL(882,882));
+ break;
+ }
+ printf("</volumedescriptor>\n");
+}
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c
new file mode 100644
index 00000000000..e4f9b157dfd
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.c
@@ -0,0 +1,694 @@
+/* $NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+
+#include "cd9660.h"
+#include "cd9660_eltorito.h"
+#include <sys/bootblock.h>
+#include <util.h>
+#include <inttypes.h>
+
+#ifdef DEBUG
+#define ELTORITO_DPRINTF(__x) printf __x
+#else
+#define ELTORITO_DPRINTF(__x)
+#endif
+
+#include <util.h>
+
+static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
+ struct cd9660_boot_image *);
+static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+#if 0
+static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
+#endif
+
+int
+cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info)
+{
+ struct stat stbuf;
+ const char *mode_msg;
+ char *temp;
+ char *sysname;
+ char *filename;
+ struct cd9660_boot_image *new_image, *tmp_image;
+
+ assert(boot_info != NULL);
+
+ if (*boot_info == '\0') {
+ warnx("Error: Boot disk information must be in the "
+ "format 'system;filename'");
+ return 0;
+ }
+
+ /* First decode the boot information */
+ temp = estrdup(boot_info);
+
+ sysname = temp;
+ filename = strchr(sysname, ';');
+ if (filename == NULL) {
+ warnx("supply boot disk information in the format "
+ "'system;filename'");
+ free(temp);
+ return 0;
+ }
+
+ *filename++ = '\0';
+
+ if (diskStructure->verbose_level > 0) {
+ printf("Found bootdisk with system %s, and filename %s\n",
+ sysname, filename);
+ }
+ new_image = ecalloc(1, sizeof(*new_image));
+ new_image->loadSegment = 0; /* default for now */
+
+ /* Decode System */
+ if (strcmp(sysname, "i386") == 0)
+ new_image->system = ET_SYS_X86;
+ else if (strcmp(sysname, "powerpc") == 0)
+ new_image->system = ET_SYS_PPC;
+ else if (strcmp(sysname, "macppc") == 0 ||
+ strcmp(sysname, "mac68k") == 0)
+ new_image->system = ET_SYS_MAC;
+ else {
+ warnx("boot disk system must be "
+ "i386, powerpc, macppc, or mac68k");
+ free(temp);
+ free(new_image);
+ return 0;
+ }
+
+
+ new_image->filename = estrdup(filename);
+
+ free(temp);
+
+ /* Get information about the file */
+ if (lstat(new_image->filename, &stbuf) == -1)
+ err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+ new_image->filename);
+
+ switch (stbuf.st_size) {
+ case 1440 * 1024:
+ new_image->targetMode = ET_MEDIA_144FDD;
+ mode_msg = "Assigned boot image to 1.44 emulation mode";
+ break;
+ case 1200 * 1024:
+ new_image->targetMode = ET_MEDIA_12FDD;
+ mode_msg = "Assigned boot image to 1.2 emulation mode";
+ break;
+ case 2880 * 1024:
+ new_image->targetMode = ET_MEDIA_288FDD;
+ mode_msg = "Assigned boot image to 2.88 emulation mode";
+ break;
+ default:
+ new_image->targetMode = ET_MEDIA_NOEM;
+ mode_msg = "Assigned boot image to no emulation mode";
+ break;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s\n", mode_msg);
+
+ new_image->size = stbuf.st_size;
+ new_image->num_sectors =
+ howmany(new_image->size, diskStructure->sectorSize) *
+ howmany(diskStructure->sectorSize, 512);
+ if (diskStructure->verbose_level > 0) {
+ printf("New image has size %d, uses %d 512-byte sectors\n",
+ new_image->size, new_image->num_sectors);
+ }
+ new_image->sector = -1;
+ /* Bootable by default */
+ new_image->bootable = ET_BOOTABLE;
+ /* Add boot disk */
+
+ /* Group images for the same platform together. */
+ TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) {
+ if (tmp_image->system != new_image->system)
+ break;
+ }
+
+ if (tmp_image == NULL) {
+ TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image,
+ image_list);
+ } else
+ TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
+
+ new_image->serialno = diskStructure->image_serialno++;
+
+ /* TODO : Need to do anything about the boot image in the tree? */
+ diskStructure->is_bootable = 1;
+
+ return 1;
+}
+
+int
+cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure,
+ const char *option_string, const char *value)
+{
+ char *eptr;
+ struct cd9660_boot_image *image;
+
+ assert(option_string != NULL);
+
+ /* Find the last image added */
+ TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) {
+ if (image->serialno + 1 == diskStructure->image_serialno)
+ break;
+ }
+ if (image == NULL)
+ errx(EXIT_FAILURE, "Attempted to add boot option, "
+ "but no boot images have been specified");
+
+ if (strcmp(option_string, "no-emul-boot") == 0) {
+ image->targetMode = ET_MEDIA_NOEM;
+ } else if (strcmp(option_string, "no-boot") == 0) {
+ image->bootable = ET_NOT_BOOTABLE;
+ } else if (strcmp(option_string, "hard-disk-boot") == 0) {
+ image->targetMode = ET_MEDIA_HDD;
+ } else if (strcmp(option_string, "boot-load-segment") == 0) {
+ image->loadSegment = strtoul(value, &eptr, 16);
+ if (eptr == value || *eptr != '\0' || errno != ERANGE) {
+ warn("%s: strtoul", __func__);
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static struct boot_catalog_entry *
+cd9660_init_boot_catalog_entry(void)
+{
+ return ecalloc(1, sizeof(struct boot_catalog_entry));
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_validation_entry(char sys)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_validation_entry *ve;
+ int16_t checksum;
+ unsigned char *csptr;
+ size_t i;
+ entry = cd9660_init_boot_catalog_entry();
+
+ ve = &entry->entry_data.VE;
+
+ ve->header_id[0] = 1;
+ ve->platform_id[0] = sys;
+ ve->key[0] = 0x55;
+ ve->key[1] = 0xAA;
+
+ /* Calculate checksum */
+ checksum = 0;
+ cd9660_721(0, ve->checksum);
+ csptr = (unsigned char*)ve;
+ for (i = 0; i < sizeof(*ve); i += 2) {
+ checksum += (int16_t)csptr[i];
+ checksum += 256 * (int16_t)csptr[i + 1];
+ }
+ checksum = -checksum;
+ cd9660_721(checksum, ve->checksum);
+
+ ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
+ "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
+ ve->key[0], ve->key[1], checksum));
+ return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
+{
+ struct boot_catalog_entry *default_entry;
+ boot_catalog_initial_entry *ie;
+
+ default_entry = cd9660_init_boot_catalog_entry();
+ if (default_entry == NULL)
+ return NULL;
+
+ ie = &default_entry->entry_data.IE;
+
+ ie->boot_indicator[0] = disk->bootable;
+ ie->media_type[0] = disk->targetMode;
+ cd9660_721(disk->loadSegment, ie->load_segment);
+ ie->system_type[0] = disk->system;
+ cd9660_721(disk->num_sectors, ie->sector_count);
+ cd9660_731(disk->sector, ie->load_rba);
+
+ ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
+ "load segment %04x, system type %d, sector count %d, "
+ "load rba %d\n", __func__, ie->boot_indicator[0],
+ ie->media_type[0], disk->loadSegment, ie->system_type[0],
+ disk->num_sectors, disk->sector));
+ return default_entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_head(char platform)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_section_header *sh;
+
+ entry = cd9660_init_boot_catalog_entry();
+ if (entry == NULL)
+ return NULL;
+
+ sh = &entry->entry_data.SH;
+ /* More by default. The last one will manually be set to 0x91 */
+ sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
+ sh->platform_id[0] = platform;
+ sh->num_section_entries[0] = 0;
+ return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_section_entry *se;
+ if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
+ return NULL;
+
+ se = &entry->entry_data.SE;
+
+ se->boot_indicator[0] = ET_BOOTABLE;
+ se->media_type[0] = disk->targetMode;
+ cd9660_721(disk->loadSegment, se->load_segment);
+ cd9660_721(disk->num_sectors, se->sector_count);
+ cd9660_731(disk->sector, se->load_rba);
+ return entry;
+}
+
+#if 0
+static u_char
+cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
+{
+ /*
+ For hard drive booting, we need to examine the MBR to figure
+ out what the partition type is
+ */
+ return 0;
+}
+#endif
+
+/*
+ * Set up the BVD, Boot catalog, and the boot entries, but do no writing
+ */
+int
+cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector)
+{
+ int sector;
+ int used_sectors;
+ int num_entries = 0;
+ int catalog_sectors;
+ struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
+ *valid_entry, *default_entry, *temp, *head, **headp, *next;
+ struct cd9660_boot_image *tmp_disk;
+
+ headp = NULL;
+ x86_head = mac_head = ppc_head = NULL;
+
+ /* If there are no boot disks, don't bother building boot information */
+ if (TAILQ_EMPTY(&diskStructure->boot_images))
+ return 0;
+
+ /* Point to catalog: For now assume it consumes one sector */
+ ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
+ diskStructure->boot_catalog_sector = first_sector;
+ cd9660_bothendian_dword(first_sector,
+ diskStructure->boot_descriptor->boot_catalog_pointer);
+
+ /* Step 1: Generate boot catalog */
+ /* Step 1a: Validation entry */
+ valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
+ if (valid_entry == NULL)
+ return -1;
+
+ /*
+ * Count how many boot images there are,
+ * and how many sectors they consume.
+ */
+ num_entries = 1;
+ used_sectors = 0;
+
+ TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+ used_sectors += tmp_disk->num_sectors;
+
+ /* One default entry per image */
+ num_entries++;
+ }
+ catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize);
+ used_sectors += catalog_sectors;
+
+ if (diskStructure->verbose_level > 0) {
+ printf("%s: there will be %i entries consuming %i sectors. "
+ "Catalog is %i sectors\n", __func__, num_entries,
+ used_sectors, catalog_sectors);
+ }
+
+ /* Populate sector numbers */
+ sector = first_sector + catalog_sectors;
+ TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+ tmp_disk->sector = sector;
+ sector += tmp_disk->num_sectors;
+ }
+
+ LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct);
+
+ /* Step 1b: Initial/default entry */
+ /* TODO : PARAM */
+ tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
+ default_entry = cd9660_boot_setup_default_entry(tmp_disk);
+ if (default_entry == NULL) {
+ warnx("Error: memory allocation failed in cd9660_setup_boot");
+ return -1;
+ }
+
+ LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
+
+ /* Todo: multiple default entries? */
+
+ tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+
+ temp = default_entry;
+
+ /* If multiple boot images are given : */
+ while (tmp_disk != NULL) {
+ /* Step 2: Section header */
+ switch (tmp_disk->system) {
+ case ET_SYS_X86:
+ headp = &x86_head;
+ break;
+ case ET_SYS_PPC:
+ headp = &ppc_head;
+ break;
+ case ET_SYS_MAC:
+ headp = &mac_head;
+ break;
+ default:
+ warnx("%s: internal error: unknown system type",
+ __func__);
+ return -1;
+ }
+
+ if (*headp == NULL) {
+ head =
+ cd9660_boot_setup_section_head(tmp_disk->system);
+ if (head == NULL) {
+ warnx("Error: memory allocation failed in "
+ "cd9660_setup_boot");
+ return -1;
+ }
+ LIST_INSERT_AFTER(default_entry, head, ll_struct);
+ *headp = head;
+ } else
+ head = *headp;
+
+ head->entry_data.SH.num_section_entries[0]++;
+
+ /* Step 2a: Section entry and extensions */
+ temp = cd9660_boot_setup_section_entry(tmp_disk);
+ if (temp == NULL) {
+ warn("%s: cd9660_boot_setup_section_entry", __func__);
+ return -1;
+ }
+
+ while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
+ next->entry_type == ET_ENTRY_SE)
+ head = next;
+
+ LIST_INSERT_AFTER(head, temp, ll_struct);
+ tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+ }
+
+ /* TODO: Remaining boot disks when implemented */
+
+ return first_sector + used_sectors;
+}
+
+int
+cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure,
+ volume_descriptor *bvd)
+{
+ boot_volume_descriptor *bvdData =
+ (boot_volume_descriptor*)bvd->volumeDescriptorData;
+
+ bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
+ memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+ bvdData->version[0] = 1;
+ memcpy(bvdData->boot_system_identifier, ET_ID, 23);
+ memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+ diskStructure->boot_descriptor =
+ (boot_volume_descriptor*) bvd->volumeDescriptorData;
+ return 1;
+}
+
+static int
+cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
+ off_t nsectors, int type)
+{
+ uint8_t val;
+ uint32_t lba;
+
+ if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ val = 0x80; /* Bootable */
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = 0xff; /* CHS begin */
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = type; /* Part type */
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = 0xff; /* CHS end */
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+
+ /* LBA extent */
+ lba = htole32(sector_start);
+ fwrite(&lba, sizeof(lba), 1, fd);
+ lba = htole32(nsectors);
+ fwrite(&lba, sizeof(lba), 1, fd);
+
+ return 0;
+}
+
+static int
+cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
+ off_t sector_start, off_t nsectors, off_t sector_size,
+ const char *part_name, const char *part_type)
+{
+ uint32_t apm32, part_status;
+ uint16_t apm16;
+
+ /* See Apple Tech Note 1189 for the details about the pmPartStatus
+ * flags.
+ * Below the flags which are default:
+ * - IsValid 0x01
+ * - IsAllocated 0x02
+ * - IsReadable 0x10
+ * - IsWritable 0x20
+ */
+ part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE |
+ APPLE_PS_WRITABLE;
+
+ if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ /* Signature */
+ apm16 = htobe16(0x504d);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ apm16 = 0;
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+
+ /* Total number of partitions */
+ apm32 = htobe32(total_partitions);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* Bounds */
+ apm32 = htobe32(sector_start);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ apm32 = htobe32(nsectors);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+
+ fwrite(part_name, strlen(part_name) + 1, 1, fd);
+ fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
+ fwrite(part_type, strlen(part_type) + 1, 1, fd);
+ fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
+
+ apm32 = 0;
+ /* pmLgDataStart */
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* pmDataCnt */
+ apm32 = htobe32(nsectors);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* pmPartStatus */
+ apm32 = htobe32(part_status);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+
+ return 0;
+}
+
+int
+cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd)
+{
+ struct boot_catalog_entry *e;
+ struct cd9660_boot_image *t;
+ int apm_partitions = 0;
+ int mbr_partitions = 0;
+
+ /* write boot catalog */
+ if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector *
+ diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing boot catalog to sector %" PRId64 "\n",
+ diskStructure->boot_catalog_sector);
+ }
+ LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) {
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing catalog entry of type %d\n",
+ e->entry_type);
+ }
+ /*
+ * It doesnt matter which one gets written
+ * since they are the same size
+ */
+ fwrite(&(e->entry_data.VE), 1, 32, fd);
+ }
+ if (diskStructure->verbose_level > 0)
+ printf("Finished writing boot catalog\n");
+
+ /* copy boot images */
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing boot image from %s to sectors %d\n",
+ t->filename, t->sector);
+ }
+ cd9660_copy_file(diskStructure, fd, t->sector, t->filename);
+
+ if (t->system == ET_SYS_MAC)
+ apm_partitions++;
+ if (t->system == ET_SYS_PPC)
+ mbr_partitions++;
+ }
+
+ /* some systems need partition tables as well */
+ if (mbr_partitions > 0 || diskStructure->chrp_boot) {
+ uint16_t sig;
+
+ fseek(fd, 0x1fe, SEEK_SET);
+ sig = htole16(0xaa55);
+ fwrite(&sig, sizeof(sig), 1, fd);
+
+ mbr_partitions = 0;
+
+ /* Write ISO9660 descriptor, enclosing the whole disk */
+ if (diskStructure->chrp_boot)
+ cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+ 0, diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512), 0x96);
+
+ /* Write all partition entries */
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (t->system != ET_SYS_PPC)
+ continue;
+ cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+ t->sector * (diskStructure->sectorSize / 512),
+ t->num_sectors * (diskStructure->sectorSize / 512),
+ 0x41 /* PReP Boot */);
+ }
+ }
+
+ if (apm_partitions > 0) {
+ /* Write DDR and global APM info */
+ uint32_t apm32;
+ uint16_t apm16;
+ int total_parts;
+
+ fseek(fd, 0, SEEK_SET);
+ apm16 = htobe16(0x4552);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ /* Device block size */
+ apm16 = htobe16(512);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ /* Device block count */
+ apm32 = htobe32(diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512));
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* Device type/id */
+ apm16 = htobe16(1);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+
+ /* Count total needed entries */
+ total_parts = 2 + apm_partitions; /* Self + ISO9660 */
+
+ /* Write self-descriptor */
+ cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
+ total_parts, 512, "Apple", "Apple_partition_map");
+
+ /* Write all partition entries */
+ apm_partitions = 0;
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (t->system != ET_SYS_MAC)
+ continue;
+
+ cd9660_write_apm_partition_entry(fd,
+ 1 + apm_partitions++, total_parts,
+ t->sector * (diskStructure->sectorSize / 512),
+ t->num_sectors * (diskStructure->sectorSize / 512),
+ 512, "CD Boot", "Apple_Bootstrap");
+ }
+
+ /* Write ISO9660 descriptor, enclosing the whole disk */
+ cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
+ total_parts, 0, diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512), 512, "ISO9660",
+ "CD_ROM_Mode_1");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.h b/usr.sbin/makefs/cd9660/cd9660_eltorito.h
new file mode 100644
index 00000000000..2661483956a
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.h
@@ -0,0 +1,162 @@
+/* $NetBSD: cd9660_eltorito.h,v 1.5 2009/07/04 14:31:38 ahoka Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 _CD9660_ELTORITO_H_
+#define _CD9660_ELTORITO_H_
+
+/* Boot defines */
+#define ET_ID "EL TORITO SPECIFICATION"
+#define ET_SYS_X86 0
+#define ET_SYS_PPC 1
+#define ET_SYS_MAC 2
+
+#define ET_BOOT_ENTRY_SIZE 0x20
+
+#define ET_BOOTABLE 0x88
+#define ET_NOT_BOOTABLE 0
+
+#define ET_MEDIA_NOEM 0
+#define ET_MEDIA_12FDD 1
+#define ET_MEDIA_144FDD 2
+#define ET_MEDIA_288FDD 3
+#define ET_MEDIA_HDD 4
+
+#define ET_INDICATOR_HEADERMORE 0x90
+#define ET_INDICATOR_HEADERLAST 0x91
+#define ET_INDICATOR_EXTENSION 0x44
+
+/*** Boot Structures ***/
+
+typedef struct _boot_volume_descriptor {
+ u_char boot_record_indicator [ISODCL(0x00,0x00)];
+ u_char identifier [ISODCL(0x01,0x05)];
+ u_char version [ISODCL(0x06,0x06)];
+ u_char boot_system_identifier [ISODCL(0x07,0x26)];
+ u_char unused1 [ISODCL(0x27,0x46)];
+ u_char boot_catalog_pointer [ISODCL(0x47,0x4A)];
+ u_char unused2 [ISODCL(0x4B,0x7FF)];
+} boot_volume_descriptor;
+
+typedef struct _boot_catalog_validation_entry {
+ u_char header_id [ISODCL(0x00,0x00)];
+ u_char platform_id [ISODCL(0x01,0x01)];
+ u_char reserved1 [ISODCL(0x02,0x03)];
+ u_char manufacturer [ISODCL(0x04,0x1B)];
+ u_char checksum [ISODCL(0x1C,0x1D)];
+ u_char key [ISODCL(0x1E,0x1F)];
+} boot_catalog_validation_entry;
+
+typedef struct _boot_catalog_initial_entry {
+ u_char boot_indicator [ISODCL(0x00,0x00)];
+ u_char media_type [ISODCL(0x01,0x01)];
+ u_char load_segment [ISODCL(0x02,0x03)];
+ u_char system_type [ISODCL(0x04,0x04)];
+ u_char unused_1 [ISODCL(0x05,0x05)];
+ u_char sector_count [ISODCL(0x06,0x07)];
+ u_char load_rba [ISODCL(0x08,0x0B)];
+ u_char unused_2 [ISODCL(0x0C,0x1F)];
+} boot_catalog_initial_entry;
+
+#define ET_SECTION_HEADER_MORE 0x90
+#define ET_SECTION_HEADER_LAST 0x91
+
+typedef struct _boot_catalog_section_header {
+ u_char header_indicator [ISODCL(0x00,0x00)];
+ u_char platform_id [ISODCL(0x01,0x01)];
+ u_char num_section_entries [ISODCL(0x02,0x03)];
+ u_char id_string [ISODCL(0x04,0x1F)];
+} boot_catalog_section_header;
+
+typedef struct _boot_catalog_section_entry {
+ u_char boot_indicator [ISODCL(0x00,0x00)];
+ u_char media_type [ISODCL(0x01,0x01)];
+ u_char load_segment [ISODCL(0x02,0x03)];
+ u_char system_type [ISODCL(0x04,0x04)];
+ u_char unused_1 [ISODCL(0x05,0x05)];
+ u_char sector_count [ISODCL(0x06,0x07)];
+ u_char load_rba [ISODCL(0x08,0x0B)];
+ u_char selection_criteria [ISODCL(0x0C,0x0C)];
+ u_char vendor_criteria [ISODCL(0x0D,0x1F)];
+} boot_catalog_section_entry;
+
+typedef struct _boot_catalog_section_entry_extension {
+ u_char extension_indicator [ISODCL(0x00,0x00)];
+ u_char flags [ISODCL(0x01,0x01)];
+ u_char vendor_criteria [ISODCL(0x02,0x1F)];
+} boot_catalog_section_entry_extension;
+
+#define ET_ENTRY_VE 1
+#define ET_ENTRY_IE 2
+#define ET_ENTRY_SH 3
+#define ET_ENTRY_SE 4
+#define ET_ENTRY_EX 5
+
+struct boot_catalog_entry {
+ char entry_type;
+ union {
+ boot_catalog_validation_entry VE;
+ boot_catalog_initial_entry IE;
+ boot_catalog_section_header SH;
+ boot_catalog_section_entry SE;
+ boot_catalog_section_entry_extension EX;
+ } entry_data;
+
+ LIST_ENTRY(boot_catalog_entry) ll_struct;
+};
+
+/* Temporary structure */
+struct cd9660_boot_image {
+ char *filename;
+ int size;
+ int sector; /* copied to LoadRBA */
+ int num_sectors;
+ unsigned int loadSegment;
+ u_char targetMode;
+ u_char system;
+ u_char bootable;
+ /*
+ * If the boot image exists in the filesystem
+ * already, this is a pointer to that node. For the sake
+ * of simplicity in future versions, this pointer is only
+ * to the node in the primary volume. This SHOULD be done
+ * via a hashtable lookup.
+ */
+ struct _cd9660node *boot_image_node;
+ TAILQ_ENTRY(cd9660_boot_image) image_list;
+ int serialno;
+};
+
+
+#endif /* _CD9660_ELTORITO_H_ */
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_strings.c b/usr.sbin/makefs/cd9660/cd9660_strings.c
new file mode 100644
index 00000000000..a50f6e5c283
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_strings.c
@@ -0,0 +1,118 @@
+/* $NetBSD: cd9660_strings.c,v 1.6 2015/12/24 15:52:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <ctype.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+
+
+void
+cd9660_uppercase_characters(char *str, size_t len)
+{
+ size_t p;
+
+ for (p = 0; p < len; p++) {
+ if (islower((unsigned char)str[p]) )
+ str[p] -= 32;
+ }
+}
+
+static inline int
+cd9660_is_d_char(char c)
+{
+ return (isupper((unsigned char)c)
+ || c == '_'
+ || (c >= '0' && c <= '9'));
+}
+
+static inline int
+cd9660_is_a_char(char c)
+{
+ return (isupper((unsigned char)c)
+ || c == '_'
+ || (c >= '%' && c <= '?')
+ || (c >= ' ' && c <= '\"'));
+}
+
+/*
+ * Test a string to see if it is composed of valid a characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ * upper case, 0 otherwise
+ */
+int
+cd9660_valid_a_chars(const char *str)
+{
+ const char *c = str;
+ int upperFound = 0;
+
+ while ((*c) != '\0') {
+ if (!(cd9660_is_a_char(*c))) {
+ if (islower((unsigned char)*c) )
+ upperFound = 1;
+ else
+ return 0;
+ }
+ c++;
+ }
+ return upperFound + 1;
+}
+
+/*
+ * Test a string to see if it is composed of valid d characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ * upper case, 0 otherwise
+ */
+int
+cd9660_valid_d_chars(const char *str)
+{
+ const char *c=str;
+ int upperFound = 0;
+
+ while ((*c) != '\0') {
+ if (!(cd9660_is_d_char(*c))) {
+ if (islower((unsigned char)*c) )
+ upperFound = 1;
+ else
+ return 0;
+ }
+ c++;
+ }
+ return upperFound + 1;
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_write.c b/usr.sbin/makefs/cd9660/cd9660_write.c
new file mode 100644
index 00000000000..8489841976a
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_write.c
@@ -0,0 +1,504 @@
+/* $NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+#include <util.h>
+
+static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *);
+static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int);
+static int cd9660_write_path_tables(iso9660_disk *, FILE *);
+static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *);
+static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t,
+ const unsigned char *, int);
+#if 0
+static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *);
+#endif
+static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t);
+
+/*
+ * Write the image
+ * Writes the entire image
+ * @param const char* The filename for the image
+ * @returns int 1 on success, 0 on failure
+ */
+int
+cd9660_write_image(iso9660_disk *diskStructure, const char* image)
+{
+ FILE *fd;
+ int status;
+ char buf[CD9660_SECTOR_SIZE];
+
+ if ((fd = fopen(image, "w+")) == NULL) {
+ err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
+ image);
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Writing image\n");
+
+ if (diskStructure->has_generic_bootimage) {
+ status = cd9660_copy_file(diskStructure, fd, 0,
+ diskStructure->generic_bootimage);
+ if (status == 0) {
+ warnx("%s: Error writing generic boot image",
+ __func__);
+ goto cleanup_bad_image;
+ }
+ }
+
+ /* Write the volume descriptors */
+ status = cd9660_write_volume_descriptors(diskStructure, fd);
+ if (status == 0) {
+ warnx("%s: Error writing volume descriptors to image",
+ __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Volume descriptors written\n");
+
+ /*
+ * Write the path tables: there are actually four, but right
+ * now we are only concearned with two.
+ */
+ status = cd9660_write_path_tables(diskStructure, fd);
+ if (status == 0) {
+ warnx("%s: Error writing path tables to image", __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Path tables written\n");
+
+ /* Write the directories and files */
+ status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode);
+ if (status == 0) {
+ warnx("%s: Error writing files to image", __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->is_bootable) {
+ cd9660_write_boot(diskStructure, fd);
+ }
+
+ /* Write padding bits. This is temporary */
+ memset(buf, 0, CD9660_SECTOR_SIZE);
+ cd9660_write_filedata(diskStructure, fd,
+ diskStructure->totalSectors - 1, buf, 1);
+
+ if (diskStructure->verbose_level > 0)
+ printf("Files written\n");
+ fclose(fd);
+
+ if (diskStructure->verbose_level > 0)
+ printf("Image closed\n");
+ return 1;
+
+cleanup_bad_image:
+ fclose(fd);
+ if (!diskStructure->keep_bad_images)
+ unlink(image);
+ if (diskStructure->verbose_level > 0)
+ printf("Bad image cleaned up\n");
+ return 0;
+}
+
+static int
+cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd)
+{
+ volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor;
+ while (vd_temp != NULL) {
+ cd9660_write_filedata(diskStructure, fd, vd_temp->sector,
+ vd_temp->volumeDescriptorData, 1);
+ vd_temp = vd_temp->next;
+ }
+ return 1;
+}
+
+/*
+ * Write out an individual path table
+ * Used just to keep redundant code to a minimum
+ * @param FILE *fd Valid file pointer
+ * @param int Sector to start writing path table to
+ * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
+ * @returns int 1 on success, 0 on failure
+ */
+static int
+cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+ int mode)
+{
+ int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize,
+ diskStructure->pathTableLength);
+ unsigned char *buffer;
+ unsigned char *buffer_head;
+ int len;
+ path_table_entry temp_entry;
+ cd9660node *ptcur;
+
+ buffer = ecalloc(path_table_sectors, diskStructure->sectorSize);
+ buffer_head = buffer;
+
+ ptcur = diskStructure->rootNode;
+
+ while (ptcur != NULL) {
+ memset(&temp_entry, 0, sizeof(path_table_entry));
+ temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
+ temp_entry.extended_attribute_length[0] =
+ ptcur->isoDirRecord->ext_attr_length[0];
+ memcpy(temp_entry.name, ptcur->isoDirRecord->name,
+ temp_entry.length[0] + 1);
+
+ /* round up */
+ len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
+
+ /* todo: function pointers instead */
+ if (mode == LITTLE_ENDIAN) {
+ cd9660_731(ptcur->fileDataSector,
+ temp_entry.first_sector);
+ cd9660_721((ptcur->parent == NULL ?
+ 1 : ptcur->parent->ptnumber),
+ temp_entry.parent_number);
+ } else {
+ cd9660_732(ptcur->fileDataSector,
+ temp_entry.first_sector);
+ cd9660_722((ptcur->parent == NULL ?
+ 1 : ptcur->parent->ptnumber),
+ temp_entry.parent_number);
+ }
+
+
+ memcpy(buffer, &temp_entry, len);
+ buffer += len;
+
+ ptcur = ptcur->ptnext;
+ }
+
+ return cd9660_write_filedata(diskStructure, fd, sector, buffer_head,
+ path_table_sectors);
+}
+
+
+/*
+ * Write out the path tables to disk
+ * Each file descriptor should be pointed to by the PVD, so we know which
+ * sector to copy them to. One thing to watch out for: the only path tables
+ * stored are in the endian mode that the application is compiled for. So,
+ * the first thing to do is write out that path table, then to write the one
+ * in the other endian mode requires to convert the endianness of each entry
+ * in the table. The best way to do this would be to create a temporary
+ * path_table_entry structure, then for each path table entry, copy it to
+ * the temporary entry, translate, then copy that to disk.
+ *
+ * @param FILE* Valid file descriptor
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd)
+{
+ if (cd9660_write_path_table(diskStructure, fd,
+ diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
+ return 0;
+
+ if (cd9660_write_path_table(diskStructure, fd,
+ diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0)
+ return 0;
+
+ /* @TODO: handle remaining two path tables */
+ return 1;
+}
+
+/*
+ * Write a file to disk
+ * Writes a file, its directory record, and its data to disk
+ * This file is designed to be called RECURSIVELY, so initially call it
+ * with the root node. All of the records should store what sector the
+ * file goes in, so no computation should be necessary.
+ *
+ * @param int fd Valid file descriptor
+ * @param struct cd9660node* writenode Pointer to the file to be written
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode)
+{
+ char *buf;
+ char *temp_file_name;
+ int ret;
+ off_t working_sector;
+ int cur_sector_offset;
+ iso_directory_record_cd9660 temp_record;
+ cd9660node *temp;
+ int rv = 0;
+
+ /* Todo : clean up variables */
+
+ temp_file_name = ecalloc(CD9660MAXPATH + 1, 1);
+ buf = emalloc(diskStructure->sectorSize);
+ if ((writenode->level != 0) &&
+ !(writenode->node->type & S_IFDIR)) {
+ fsinode *inode = writenode->node->inode;
+ /* Only attempt to write unwritten files that have length. */
+ if ((inode->flags & FI_WRITTEN) != 0) {
+ INODE_WARNX(("%s: skipping written inode %d", __func__,
+ (int)inode->st.st_ino));
+ } else if (writenode->fileDataLength > 0) {
+ INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
+ __func__, (int)inode->st.st_ino, inode->ino));
+ inode->flags |= FI_WRITTEN;
+ cd9660_compute_full_filename(writenode,
+ temp_file_name);
+ ret = cd9660_copy_file(diskStructure, fd,
+ writenode->fileDataSector, temp_file_name);
+ if (ret == 0)
+ goto out;
+ }
+ } else {
+ /*
+ * Here is a new revelation that ECMA didnt explain
+ * (at least not well).
+ * ALL . and .. records store the name "\0" and "\1"
+ * resepctively. So, for each directory, we have to
+ * make a new node.
+ *
+ * This is where it gets kinda messy, since we have to
+ * be careful of sector boundaries
+ */
+ cur_sector_offset = 0;
+ working_sector = writenode->fileDataSector;
+ if (fseeko(fd, working_sector * diskStructure->sectorSize,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ /*
+ * Now loop over children, writing out their directory
+ * records - beware of sector boundaries
+ */
+ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+ /*
+ * Copy the temporary record and adjust its size
+ * if necessary
+ */
+ memcpy(&temp_record, temp->isoDirRecord,
+ sizeof(iso_directory_record_cd9660));
+
+ temp_record.length[0] =
+ cd9660_compute_record_size(diskStructure, temp);
+
+ if (temp_record.length[0] + cur_sector_offset >=
+ diskStructure->sectorSize) {
+ cur_sector_offset = 0;
+ working_sector++;
+
+ /* Seek to the next sector. */
+ if (fseeko(fd, working_sector *
+ diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+ }
+ /* Write out the basic ISO directory record */
+ (void)fwrite(&temp_record, 1,
+ temp->isoDirRecord->length[0], fd);
+ if (diskStructure->rock_ridge_enabled) {
+ cd9660_write_rr(diskStructure, fd, temp,
+ cur_sector_offset, working_sector);
+ }
+ if (fseeko(fd, working_sector *
+ diskStructure->sectorSize + cur_sector_offset +
+ temp_record.length[0] - temp->su_tail_size,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+ if (temp->su_tail_size > 0)
+ fwrite(temp->su_tail_data, 1,
+ temp->su_tail_size, fd);
+ if (ferror(fd)) {
+ warnx("%s: write error", __func__);
+ goto out;
+ }
+ cur_sector_offset += temp_record.length[0];
+
+ }
+
+ /*
+ * Recurse on children.
+ */
+ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+ if ((ret = cd9660_write_file(diskStructure, fd, temp))
+ == 0)
+ goto out;
+ }
+ }
+ rv = 1;
+out:
+ free(temp_file_name);
+ free(buf);
+ return rv;
+}
+
+/*
+ * Wrapper function to write a buffer (one sector) to disk.
+ * Seeks and writes the buffer.
+ * NOTE: You dont NEED to use this function, but it might make your
+ * life easier if you have to write things that align to a sector
+ * (such as volume descriptors).
+ *
+ * @param int fd Valid file descriptor
+ * @param int sector Sector number to write to
+ * @param const unsigned char* Buffer to write. This should be the
+ * size of a sector, and if only a portion
+ * is written, the rest should be set to 0.
+ */
+static int
+cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+ const unsigned char *buf, int numsecs)
+{
+ off_t curpos;
+ size_t success;
+
+ curpos = ftello(fd);
+
+ if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd);
+
+ if (fseeko(fd, curpos, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ if (success == 1)
+ success = diskStructure->sectorSize * numsecs;
+ return success;
+}
+
+#if 0
+static int
+cd9660_write_buffered(FILE *fd, off_t offset, int buff_len,
+ const unsigned char* buffer)
+{
+ static int working_sector = -1;
+ static char buf[CD9660_SECTOR_SIZE];
+
+ return 0;
+}
+#endif
+
+int
+cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector,
+ const char *filename)
+{
+ FILE *rf;
+ int bytes_read;
+ off_t sector = start_sector;
+ int buf_size = diskStructure->sectorSize;
+ char *buf;
+
+ buf = emalloc(buf_size);
+ if ((rf = fopen(filename, "rb")) == NULL) {
+ warn("%s: cannot open %s", __func__, filename);
+ free(buf);
+ return 0;
+ }
+
+ if (diskStructure->verbose_level > 1)
+ printf("Writing file: %s\n",filename);
+
+ if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ while (!feof(rf)) {
+ bytes_read = fread(buf,1,buf_size,rf);
+ if (ferror(rf)) {
+ warn("%s: fread", __func__);
+ free(buf);
+ (void)fclose(rf);
+ return 0;
+ }
+
+ fwrite(buf,1,bytes_read,fd);
+ if (ferror(fd)) {
+ warn("%s: fwrite", __func__);
+ free(buf);
+ (void)fclose(rf);
+ return 0;
+ }
+ sector++;
+ }
+
+ fclose(rf);
+ free(buf);
+ return 1;
+}
+
+static void
+cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode,
+ off_t offset, off_t sector)
+{
+ int in_ca = 0;
+ struct ISO_SUSP_ATTRIBUTES *myattr;
+
+ offset += writenode->isoDirRecord->length[0];
+ if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) ==
+ -1)
+ err(1, "fseeko");
+ /* Offset now points at the end of the record */
+ TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
+ fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
+
+ if (!in_ca) {
+ offset += CD9660_SUSP_ENTRY_SIZE(myattr);
+ if (myattr->last_in_suf) {
+ /*
+ * Point the offset to the start of this
+ * record's CE area
+ */
+ if (fseeko(fd, ((off_t)diskStructure->
+ susp_continuation_area_start_sector *
+ diskStructure->sectorSize)
+ + writenode->susp_entry_ce_start,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+ in_ca = 1;
+ }
+ }
+ }
+
+ /*
+ * If we had to go to the continuation area, head back to
+ * where we should be.
+ */
+ if (in_ca)
+ if (fseeko(fd, sector * diskStructure->sectorSize + offset,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c
new file mode 100644
index 00000000000..15b4422c069
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c
@@ -0,0 +1,834 @@
+/* $NetBSD: iso9660_rrip.c,v 1.14 2014/05/30 13:14:47 martin Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+/* This will hold all the function definitions
+ * defined in iso9660_rrip.h
+ */
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+#include <sys/queue.h>
+#include <stdio.h>
+#include <util.h>
+
+static void cd9660_rrip_initialize_inode(cd9660node *);
+static int cd9660_susp_handle_continuation(iso9660_disk *, cd9660node *);
+static int cd9660_susp_handle_continuation_common(iso9660_disk *, cd9660node *,
+ int);
+
+int
+cd9660_susp_initialize(iso9660_disk *diskStructure, cd9660node *node,
+ cd9660node *parent, cd9660node *grandparent)
+{
+ cd9660node *cn;
+ int r;
+
+ /* Make sure the node is not NULL. If it is, there are major problems */
+ assert(node != NULL);
+
+ if (!(node->type & CD9660_TYPE_DOT) &&
+ !(node->type & CD9660_TYPE_DOTDOT))
+ TAILQ_INIT(&(node->head));
+ if (node->dot_record != 0)
+ TAILQ_INIT(&(node->dot_record->head));
+ if (node->dot_dot_record != 0)
+ TAILQ_INIT(&(node->dot_dot_record->head));
+
+ /* SUSP specific entries here */
+ if ((r = cd9660_susp_initialize_node(diskStructure, node)) < 0)
+ return r;
+
+ /* currently called cd9660node_rrip_init_links */
+ r = cd9660_rrip_initialize_node(diskStructure, node, parent, grandparent);
+ if (r < 0)
+ return r;
+
+ /*
+ * See if we need a CE record, and set all of the
+ * associated counters.
+ *
+ * This should be called after all extensions. After
+ * this is called, no new records should be added.
+ */
+ if ((r = cd9660_susp_handle_continuation(diskStructure, node)) < 0)
+ return r;
+
+ /* Recurse on children. */
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+ if ((r = cd9660_susp_initialize(diskStructure, cn, node, parent)) < 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+cd9660_susp_finalize(iso9660_disk *diskStructure, cd9660node *node)
+{
+ cd9660node *temp;
+ int r;
+
+ assert(node != NULL);
+
+ if (node == diskStructure->rootNode)
+ diskStructure->susp_continuation_area_current_free = 0;
+
+ if ((r = cd9660_susp_finalize_node(diskStructure, node)) < 0)
+ return r;
+ if ((r = cd9660_rrip_finalize_node(diskStructure, node)) < 0)
+ return r;
+
+ TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) {
+ if ((r = cd9660_susp_finalize(diskStructure, temp)) < 0)
+ return r;
+ }
+ return 1;
+}
+
+/*
+ * If we really wanted to speed things up, we could have some sort of
+ * lookup table on the SUSP entry type that calls a functor. Or, we could
+ * combine the functions. These functions are kept separate to allow
+ * easier addition of other extensions.
+
+ * For the sake of simplicity and clarity, we won't be doing that for now.
+ */
+
+/*
+ * SUSP needs to update the following types:
+ * CE (continuation area)
+ */
+int
+cd9660_susp_finalize_node(iso9660_disk *diskStructure, cd9660node *node)
+{
+ struct ISO_SUSP_ATTRIBUTES *t;
+
+ /* Handle CE counters */
+ if (node->susp_entry_ce_length > 0) {
+ node->susp_entry_ce_start =
+ diskStructure->susp_continuation_area_current_free;
+ diskStructure->susp_continuation_area_current_free +=
+ node->susp_entry_ce_length;
+ }
+
+ TAILQ_FOREACH(t, &node->head, rr_ll) {
+ if (t->susp_type != SUSP_TYPE_SUSP ||
+ t->entry_type != SUSP_ENTRY_SUSP_CE)
+ continue;
+ cd9660_bothendian_dword(
+ diskStructure->
+ susp_continuation_area_start_sector,
+ t->attr.su_entry.CE.ca_sector);
+
+ cd9660_bothendian_dword(
+ diskStructure->
+ susp_continuation_area_start_sector,
+ t->attr.su_entry.CE.ca_sector);
+ cd9660_bothendian_dword(node->susp_entry_ce_start,
+ t->attr.su_entry.CE.offset);
+ cd9660_bothendian_dword(node->susp_entry_ce_length,
+ t->attr.su_entry.CE.length);
+ }
+ return 0;
+}
+
+int
+cd9660_rrip_finalize_node(iso9660_disk *diskStructure __unused,
+ cd9660node *node)
+{
+ struct ISO_SUSP_ATTRIBUTES *t;
+
+ TAILQ_FOREACH(t, &node->head, rr_ll) {
+ if (t->susp_type != SUSP_TYPE_RRIP)
+ continue;
+ switch (t->entry_type) {
+ case SUSP_ENTRY_RRIP_CL:
+ /* Look at rr_relocated*/
+ if (node->rr_relocated == NULL)
+ return -1;
+ cd9660_bothendian_dword(
+ node->rr_relocated->fileDataSector,
+ (unsigned char *)
+ t->attr.rr_entry.CL.dir_loc);
+ break;
+ case SUSP_ENTRY_RRIP_PL:
+ /* Look at rr_real_parent */
+ if (node->parent == NULL ||
+ node->parent->rr_real_parent == NULL)
+ return -1;
+ cd9660_bothendian_dword(
+ node->parent->rr_real_parent->fileDataSector,
+ (unsigned char *)
+ t->attr.rr_entry.PL.dir_loc);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
+cd9660_susp_handle_continuation_common(iso9660_disk *diskStructure,
+ cd9660node *node, int space)
+{
+ int ca_used, susp_used, susp_used_pre_ce, working;
+ struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST;
+
+ pre_ce = last = NULL;
+ working = 254 - space;
+ if (node->su_tail_size > 0)
+ /* Allow 4 bytes for "ST" record. */
+ working -= node->su_tail_size + 4;
+ /* printf("There are %i bytes to work with\n",working); */
+
+ susp_used_pre_ce = susp_used = 0;
+ ca_used = 0;
+ TAILQ_FOREACH(temp, &node->head, rr_ll) {
+ if (working < 0)
+ break;
+ /*
+ * printf("SUSP Entry found, length is %i\n",
+ * CD9660_SUSP_ENTRY_SIZE(temp));
+ */
+ working -= CD9660_SUSP_ENTRY_SIZE(temp);
+ if (working >= 0) {
+ last = temp;
+ susp_used += CD9660_SUSP_ENTRY_SIZE(temp);
+ }
+ if (working >= 28) {
+ /*
+ * Remember the last entry after which we
+ * could insert a "CE" entry.
+ */
+ pre_ce = last;
+ susp_used_pre_ce = susp_used;
+ }
+ }
+
+ /* A CE entry is needed */
+ if (working <= 0) {
+ CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+ SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY);
+ cd9660_susp_ce(CE, node);
+ /* This will automatically insert at the appropriate location */
+ if (pre_ce != NULL)
+ TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll);
+ else
+ TAILQ_INSERT_HEAD(&node->head, CE, rr_ll);
+ last = CE;
+ susp_used = susp_used_pre_ce + 28;
+ /* Count how much CA data is necessary */
+ for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL;
+ temp = TAILQ_NEXT(temp, rr_ll)) {
+ ca_used += CD9660_SUSP_ENTRY_SIZE(temp);
+ }
+ }
+
+ /* An ST entry is needed */
+ if (node->su_tail_size > 0) {
+ ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+ SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY);
+ cd9660_susp_st(ST, node);
+ if (last != NULL)
+ TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll);
+ else
+ TAILQ_INSERT_HEAD(&node->head, ST, rr_ll);
+ last = ST;
+ susp_used += 4;
+ }
+ if (last != NULL)
+ last->last_in_suf = 1;
+
+ node->susp_entry_size = susp_used;
+ node->susp_entry_ce_length = ca_used;
+
+ diskStructure->susp_continuation_area_size += ca_used;
+ return 1;
+}
+
+/* See if a continuation entry is needed for each of the different types */
+static int
+cd9660_susp_handle_continuation(iso9660_disk *diskStructure, cd9660node *node)
+{
+ assert (node != NULL);
+
+ /* Entry */
+ if (cd9660_susp_handle_continuation_common(diskStructure,
+ node,(int)(node->isoDirRecord->length[0])) < 0)
+ return 0;
+
+ return 1;
+}
+
+int
+cd9660_susp_initialize_node(iso9660_disk *diskStructure, cd9660node *node)
+{
+ struct ISO_SUSP_ATTRIBUTES *temp;
+
+ /*
+ * Requirements/notes:
+ * CE: is added for us where needed
+ * ST: not sure if it is even required, but if so, should be
+ * handled by the CE code
+ * PD: isnt needed (though might be added for testing)
+ * SP: is stored ONLY on the . record of the root directory
+ * ES: not sure
+ */
+
+ /* Check for root directory, add SP and ER if needed. */
+ if (node->type & CD9660_TYPE_DOT) {
+ if (node->parent == diskStructure->rootNode) {
+ temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+ SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT);
+ cd9660_susp_sp(temp, node);
+
+ /* Should be first entry. */
+ TAILQ_INSERT_HEAD(&node->head, temp, rr_ll);
+ }
+ }
+ return 1;
+}
+
+static void
+cd9660_rrip_initialize_inode(cd9660node *node)
+{
+ struct ISO_SUSP_ATTRIBUTES *attr;
+
+ /*
+ * Inode dependent values - this may change,
+ * but for now virtual files and directories do
+ * not have an inode structure
+ */
+
+ if ((node->node != NULL) && (node->node->inode != NULL)) {
+ /* PX - POSIX attributes */
+ attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+ cd9660node_rrip_px(attr, node->node);
+
+ TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+ /* TF - timestamp */
+ attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY);
+ cd9660node_rrip_tf(attr, node->node);
+ TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+ /* SL - Symbolic link */
+ /* ?????????? Dan - why is this here? */
+ if (TAILQ_EMPTY(&node->cn_children) &&
+ node->node->inode != NULL &&
+ S_ISLNK(node->node->inode->st.st_mode))
+ cd9660_createSL(node);
+
+ /* PN - device number */
+ if (node->node->inode != NULL &&
+ ((S_ISCHR(node->node->inode->st.st_mode) ||
+ S_ISBLK(node->node->inode->st.st_mode)))) {
+ attr =
+ cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_PN, "PN",
+ SUSP_LOC_ENTRY);
+ cd9660node_rrip_pn(attr, node->node);
+ TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+ }
+ }
+}
+
+int
+cd9660_rrip_initialize_node(iso9660_disk *diskStructure, cd9660node *node,
+ cd9660node *parent, cd9660node *grandparent)
+{
+ struct ISO_SUSP_ATTRIBUTES *current = NULL;
+
+ assert(node != NULL);
+
+ if (node->type & CD9660_TYPE_DOT) {
+ /*
+ * Handle ER - should be the only entry to appear on
+ * a "." record
+ */
+ if (node->parent == diskStructure->rootNode) {
+ cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID,
+ SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC);
+ }
+ if (parent != NULL && parent->node != NULL &&
+ parent->node->inode != NULL) {
+ /* PX - POSIX attributes */
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+ cd9660node_rrip_px(current, parent->node);
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ }
+ } else if (node->type & CD9660_TYPE_DOTDOT) {
+ if (grandparent != NULL && grandparent->node != NULL &&
+ grandparent->node->inode != NULL) {
+ /* PX - POSIX attributes */
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+ cd9660node_rrip_px(current, grandparent->node);
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ }
+ /* Handle PL */
+ if (parent != NULL && parent->rr_real_parent != NULL) {
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT);
+ cd9660_rrip_PL(current,node);
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ }
+ } else {
+ cd9660_rrip_initialize_inode(node);
+
+ /*
+ * Not every node needs a NM set - only if the name is
+ * actually different. IE: If a file is TEST -> TEST,
+ * no NM. test -> TEST, need a NM
+ *
+ * The rr_moved_dir needs to be assigned a NM record as well.
+ */
+ if (node == diskStructure->rr_moved_dir) {
+ cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME);
+ }
+ else if ((node->node != NULL) &&
+ ((strlen(node->node->name) !=
+ (uint8_t)node->isoDirRecord->name_len[0]) ||
+ (memcmp(node->node->name,node->isoDirRecord->name,
+ (uint8_t)node->isoDirRecord->name_len[0]) != 0))) {
+ cd9660_rrip_NM(node);
+ }
+
+
+
+ /* Rock ridge directory relocation code here. */
+
+ /* First handle the CL for the placeholder file. */
+ if (node->rr_relocated != NULL) {
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY);
+ cd9660_rrip_CL(current, node);
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ }
+
+ /* Handle RE*/
+ if (node->rr_real_parent != NULL) {
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY);
+ cd9660_rrip_RE(current,node);
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ }
+ }
+ return 1;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id,
+ int write_loc)
+{
+ struct ISO_SUSP_ATTRIBUTES* temp;
+
+ temp = emalloc(sizeof(*temp));
+ temp->susp_type = susp_type;
+ temp->entry_type = entry_type;
+ temp->last_in_suf = 0;
+ /* Phase this out */
+ temp->type_of[0] = type_id[0];
+ temp->type_of[1] = type_id[1];
+ temp->write_location = write_loc;
+
+ /*
+ * Since the first four bytes is common, lets go ahead and
+ * set the type identifier, since we are passing that to this
+ * function anyhow.
+ */
+ temp->attr.su_entry.SP.h.type[0] = type_id[0];
+ temp->attr.su_entry.SP.h.type[1] = type_id[1];
+ return temp;
+}
+
+int
+cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused)
+{
+ p->attr.rr_entry.PL.h.length[0] = 12;
+ p->attr.rr_entry.PL.h.version[0] = 1;
+ return 1;
+}
+
+int
+cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+ p->attr.rr_entry.CL.h.length[0] = 12;
+ p->attr.rr_entry.CL.h.version[0] = 1;
+ return 1;
+}
+
+int
+cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+ p->attr.rr_entry.RE.h.length[0] = 4;
+ p->attr.rr_entry.RE.h.version[0] = 1;
+ return 1;
+}
+
+void
+cd9660_createSL(cd9660node *node)
+{
+ struct ISO_SUSP_ATTRIBUTES* current;
+ int path_count, dir_count, done, i, j, dir_copied;
+ char temp_cr[255];
+ char temp_sl[255]; /* used in copying continuation entry*/
+ char* sl_ptr;
+
+ sl_ptr = node->node->symlink;
+
+ done = 0;
+ path_count = 0;
+ dir_count = 0;
+ dir_copied = 0;
+ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+
+ current->attr.rr_entry.SL.h.version[0] = 1;
+ current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+ if (*sl_ptr == '/') {
+ temp_cr[0] = SL_FLAGS_ROOT;
+ temp_cr[1] = 0;
+ memcpy(current->attr.rr_entry.SL.component + path_count,
+ temp_cr, 2);
+ path_count += 2;
+ sl_ptr++;
+ }
+
+ for (i = 0; i < (dir_count + 2); i++)
+ temp_cr[i] = '\0';
+
+ while (!done) {
+ while ((*sl_ptr != '/') && (*sl_ptr != '\0')) {
+ dir_copied = 1;
+ if (*sl_ptr == '.') {
+ if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1)
+ == '\0')) {
+ temp_cr[0] = SL_FLAGS_CURRENT;
+ sl_ptr++;
+ } else if(*(sl_ptr + 1) == '.') {
+ if ((*(sl_ptr + 2) == '/') ||
+ (*(sl_ptr + 2) == '\0')) {
+ temp_cr[0] = SL_FLAGS_PARENT;
+ sl_ptr += 2;
+ }
+ } else {
+ temp_cr[dir_count+2] = *sl_ptr;
+ sl_ptr++;
+ dir_count++;
+ }
+ } else {
+ temp_cr[dir_count + 2] = *sl_ptr;
+ sl_ptr++;
+ dir_count++;
+ }
+ }
+
+ if ((path_count + dir_count) >= 249) {
+ current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE;
+
+ j = 0;
+
+ if (path_count <= 249) {
+ while(j != (249 - path_count)) {
+ temp_sl[j] = temp_cr[j];
+ j++;
+ }
+ temp_sl[0] = SL_FLAGS_CONTINUE;
+ temp_sl[1] = j - 2;
+ memcpy(
+ current->attr.rr_entry.SL.component +
+ path_count,
+ temp_sl, j);
+ }
+
+ path_count += j;
+ current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ current= cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+ current->attr.rr_entry.SL.h.version[0] = 1;
+ current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+ path_count = 0;
+
+ if (dir_count > 2) {
+ while (j != dir_count + 2) {
+ current->attr.rr_entry.SL.component[
+ path_count + 2] = temp_cr[j];
+ j++;
+ path_count++;
+ }
+ current->attr.rr_entry.SL.component[1]
+ = path_count;
+ path_count+= 2;
+ } else {
+ while(j != dir_count) {
+ current->attr.rr_entry.SL.component[
+ path_count+2] = temp_cr[j];
+ j++;
+ path_count++;
+ }
+ }
+ } else {
+ if (dir_copied == 1) {
+ temp_cr[1] = dir_count;
+ memcpy(current->attr.rr_entry.SL.component +
+ path_count,
+ temp_cr, dir_count + 2);
+ path_count += dir_count + 2;
+ }
+ }
+
+ if (*sl_ptr == '\0') {
+ done = 1;
+ current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+ TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+ } else {
+ sl_ptr++;
+ dir_count = 0;
+ dir_copied = 0;
+ for(i = 0; i < 255; i++) {
+ temp_cr[i] = '\0';
+ }
+ }
+ }
+}
+
+int
+cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo)
+{
+ v->attr.rr_entry.PX.h.length[0] = 36;
+ v->attr.rr_entry.PX.h.version[0] = 1;
+ cd9660_bothendian_dword(pxinfo->inode->st.st_mode,
+ v->attr.rr_entry.PX.mode);
+ cd9660_bothendian_dword(pxinfo->inode->st.st_nlink,
+ v->attr.rr_entry.PX.links);
+ cd9660_bothendian_dword(pxinfo->inode->st.st_uid,
+ v->attr.rr_entry.PX.uid);
+ cd9660_bothendian_dword(pxinfo->inode->st.st_gid,
+ v->attr.rr_entry.PX.gid);
+
+ /* Ignoring the serial number for now */
+ return 1;
+}
+
+int
+cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode)
+{
+ pn_field->attr.rr_entry.PN.h.length[0] = 20;
+ pn_field->attr.rr_entry.PN.h.version[0] = 1;
+
+ if (sizeof (fnode->inode->st.st_rdev) > 4)
+ cd9660_bothendian_dword(
+ (uint64_t)fnode->inode->st.st_rdev >> 32,
+ pn_field->attr.rr_entry.PN.high);
+ else
+ cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high);
+
+ cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff,
+ pn_field->attr.rr_entry.PN.low);
+ return 1;
+}
+
+#if 0
+int
+cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node)
+{
+ int nm_length = strlen(file_node->isoDirRecord->name) + 5;
+ p->attr.rr_entry.NM.h.type[0] = 'N';
+ p->attr.rr_entry.NM.h.type[1] = 'M';
+ sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name);
+ p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length;
+ p->attr.rr_entry.NM.h.version[0] = (unsigned char)1;
+ p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT;
+ return 1;
+}
+#endif
+
+int
+cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node)
+{
+ p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES;
+ p->attr.rr_entry.TF.h.length[0] = 5;
+ p->attr.rr_entry.TF.h.version[0] = 1;
+
+ /*
+ * Need to add creation time, backup time,
+ * expiration time, and effective time.
+ */
+
+ cd9660_time_915(p->attr.rr_entry.TF.timestamp,
+ _node->inode->st.st_atime);
+ p->attr.rr_entry.TF.h.length[0] += 7;
+
+ cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7,
+ _node->inode->st.st_mtime);
+ p->attr.rr_entry.TF.h.length[0] += 7;
+
+ cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14,
+ _node->inode->st.st_ctime);
+ p->attr.rr_entry.TF.h.length[0] += 7;
+ return 1;
+}
+
+int
+cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+ p->attr.su_entry.SP.h.length[0] = 7;
+ p->attr.su_entry.SP.h.version[0] = 1;
+ p->attr.su_entry.SP.check[0] = 0xBE;
+ p->attr.su_entry.SP.check[1] = 0xEF;
+ p->attr.su_entry.SP.len_skp[0] = 0;
+ return 1;
+}
+
+int
+cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused)
+{
+ p->attr.su_entry.ST.h.type[0] = 'S';
+ p->attr.su_entry.ST.h.type[1] = 'T';
+ p->attr.su_entry.ST.h.length[0] = 4;
+ p->attr.su_entry.ST.h.version[0] = 1;
+ return 1;
+}
+
+int
+cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+ p->attr.su_entry.CE.h.length[0] = 28;
+ p->attr.su_entry.CE.h.version[0] = 1;
+ /* Other attributes dont matter right now, will be updated later */
+ return 1;
+}
+
+int
+cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused)
+{
+ return 1;
+}
+
+void
+cd9660_rrip_add_NM(cd9660node *node, const char *name)
+{
+ int working,len;
+ const char *p;
+ struct ISO_SUSP_ATTRIBUTES *r;
+
+ /*
+ * Each NM record has 254 byes to work with. This means that
+ * the name data itself only has 249 bytes to work with. So, a
+ * name with 251 characters would require two nm records.
+ */
+ p = name;
+ working = 1;
+ while (working) {
+ r = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+ SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY);
+ r->attr.rr_entry.NM.h.version[0] = 1;
+ r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE;
+ len = strlen(p);
+
+ if (len > 249) {
+ len = 249;
+ r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE;
+ } else {
+ working = 0;
+ }
+ memcpy(r->attr.rr_entry.NM.altname, p, len);
+ r->attr.rr_entry.NM.h.length[0] = 5 + len;
+
+ TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+
+ p += len;
+ }
+}
+
+void
+cd9660_rrip_NM(cd9660node *node)
+{
+ cd9660_rrip_add_NM(node, node->node->name);
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ER(cd9660node *node,
+ u_char ext_version, const char* ext_id, const char* ext_des,
+ const char* ext_src)
+{
+ int l;
+ struct ISO_SUSP_ATTRIBUTES *r;
+
+ r = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+ SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT);
+
+ /* Fixed data is 8 bytes */
+ r->attr.su_entry.ER.h.length[0] = 8;
+ r->attr.su_entry.ER.h.version[0] = 1;
+
+ r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id);
+ r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des);
+ r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src);
+
+ l = r->attr.su_entry.ER.len_id[0] +
+ r->attr.su_entry.ER.len_src[0] +
+ r->attr.su_entry.ER.len_des[0];
+
+ /* Everything must fit. */
+ assert(l + r->attr.su_entry.ER.h.length[0] <= 254);
+
+ r->attr.su_entry.ER.h.length[0] += (u_char)l;
+
+
+ r->attr.su_entry.ER.ext_ver[0] = ext_version;
+ memcpy(r->attr.su_entry.ER.ext_data, ext_id,
+ (int)r->attr.su_entry.ER.len_id[0]);
+ l = (int) r->attr.su_entry.ER.len_id[0];
+ memcpy(r->attr.su_entry.ER.ext_data + l,ext_des,
+ (int)r->attr.su_entry.ER.len_des[0]);
+
+ l += (int)r->attr.su_entry.ER.len_des[0];
+ memcpy(r->attr.su_entry.ER.ext_data + l,ext_src,
+ (int)r->attr.su_entry.ER.len_src[0]);
+
+ TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+ return r;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused)
+{
+ return NULL;
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.h b/usr.sbin/makefs/cd9660/iso9660_rrip.h
new file mode 100644
index 00000000000..1f54ae84048
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/iso9660_rrip.h
@@ -0,0 +1,290 @@
+/* $NetBSD: iso9660_rrip.h,v 1.6 2013/01/28 21:03:28 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 __ISO9660_RRIP_H__
+#define __ISO9660_RRIP_H__
+
+/*
+ * This will hold all the functions needed to
+ * write an ISO 9660 image with Rock Ridge Extensions
+ */
+
+/* For writing must use ISO_RRIP_EXTREF structure */
+
+#include "makefs.h"
+#include <cd9660_rrip.h>
+#include "cd9660.h"
+#include <sys/queue.h>
+
+#define PX_LENGTH 0x2C
+#define PN_LENGTH 0x14
+#define TF_CREATION 0x00
+#define TF_MODIFY 0x01
+#define TF_ACCESS 0x02
+#define TF_ATTRIBUTES 0x04
+#define TF_BACKUP 0x08
+#define TF_EXPIRATION 0x10
+#define TF_EFFECTIVE 0x20
+#define TF_LONGFORM 0x40
+#define NM_CONTINUE 0x80
+#define NM_CURRENT 0x100
+#define NM_PARENT 0x200
+
+
+#define SUSP_LOC_ENTRY 0x01
+#define SUSP_LOC_DOT 0x02
+#define SUSP_LOC_DOTDOT 0x04
+
+#define SUSP_TYPE_SUSP 1
+#define SUSP_TYPE_RRIP 2
+
+#define SUSP_ENTRY_SUSP_CE 1
+#define SUSP_ENTRY_SUSP_PD 2
+#define SUSP_ENTRY_SUSP_SP 3
+#define SUSP_ENTRY_SUSP_ST 4
+#define SUSP_ENTRY_SUSP_ER 5
+#define SUSP_ENTRY_SUSP_ES 6
+
+#define SUSP_ENTRY_RRIP_PX 1
+#define SUSP_ENTRY_RRIP_PN 2
+#define SUSP_ENTRY_RRIP_SL 3
+#define SUSP_ENTRY_RRIP_NM 4
+#define SUSP_ENTRY_RRIP_CL 5
+#define SUSP_ENTRY_RRIP_PL 6
+#define SUSP_ENTRY_RRIP_RE 7
+#define SUSP_ENTRY_RRIP_TF 8
+#define SUSP_ENTRY_RRIP_SF 9
+
+#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282"
+#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."
+#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."
+
+#define SL_FLAGS_NONE 0
+#define SL_FLAGS_CONTINUE 1
+#define SL_FLAGS_CURRENT 2
+#define SL_FLAGS_PARENT 4
+#define SL_FLAGS_ROOT 8
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char mode [ISODCL(5,12)];
+ u_char links [ISODCL(13,20)];
+ u_char uid [ISODCL(21,28)];
+ u_char gid [ISODCL(29,36)];
+ u_char serial [ISODCL(37,44)];/* Not used */
+} ISO_RRIP_PX;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char high [ISODCL(5,12)];
+ u_char low [ISODCL(13,20)];
+} ISO_RRIP_PN;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char component [ISODCL ( 4, 256)];
+ u_int nBytes;
+} ISO_RRIP_SL;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char timestamp [ISODCL ( 5, 256)];
+} ISO_RRIP_TF;
+
+#define RRIP_NM_FLAGS_NONE 0x00
+#define RRIP_NM_FLAGS_CONTINUE 0x01
+#define RRIP_NM_FLAGS_CURRENT 0x02
+#define RRIP_NM_FLAGS_PARENT 0x04
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char altname [ISODCL ( 4, 256)];
+} ISO_RRIP_NM;
+
+/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char ca_sector [ISODCL ( 5, 12)];
+ u_char offset [ISODCL ( 13, 20)];
+ u_char length [ISODCL ( 21, 28)];
+} ISO_SUSP_CE;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char padding_area [ISODCL ( 4, 256)];
+} ISO_SUSP_PD;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char check [ISODCL ( 4, 5)];
+ u_char len_skp [ISODCL ( 6, 6)];
+} ISO_SUSP_SP;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+} ISO_SUSP_ST;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char len_id [ISODCL ( 4, 4)];
+ u_char len_des [ISODCL ( 5, 5)];
+ u_char len_src [ISODCL ( 6, 6)];
+ u_char ext_ver [ISODCL ( 7, 7)];
+ u_char ext_data [ISODCL (8,256)];
+/* u_char ext_id [ISODCL ( 8, 256)];
+ u_char ext_des [ISODCL ( 257, 513)];
+ u_char ext_src [ISODCL ( 514, 770)];*/
+} ISO_SUSP_ER;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char ext_seq [ISODCL ( 4, 4)];
+} ISO_SUSP_ES;
+
+typedef union {
+ ISO_RRIP_PX PX;
+ ISO_RRIP_PN PN;
+ ISO_RRIP_SL SL;
+ ISO_RRIP_NM NM;
+ ISO_RRIP_CLINK CL;
+ ISO_RRIP_PLINK PL;
+ ISO_RRIP_RELDIR RE;
+ ISO_RRIP_TF TF;
+} rrip_entry;
+
+typedef union {
+ ISO_SUSP_CE CE;
+ ISO_SUSP_PD PD;
+ ISO_SUSP_SP SP;
+ ISO_SUSP_ST ST;
+ ISO_SUSP_ER ER;
+ ISO_SUSP_ES ES;
+} susp_entry;
+
+typedef union {
+ susp_entry su_entry;
+ rrip_entry rr_entry;
+} SUSP_ENTRIES;
+
+struct ISO_SUSP_ATTRIBUTES {
+ SUSP_ENTRIES attr;
+ int type;
+ char type_of[2];
+ char last_in_suf; /* last entry in the System Use Field? */
+ /* Dan's addons - will merge later. This allows use of a switch */
+ char susp_type; /* SUSP or RRIP */
+ char entry_type; /* Record type */
+ char write_location;
+ TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll;
+};
+
+#define CD9660_SUSP_ENTRY_SIZE(entry)\
+ ((int) ((entry)->attr.su_entry.SP.h.length[0]))
+
+/* Recursive function - move later to func pointer code*/
+int cd9660_susp_finalize(iso9660_disk *, cd9660node *);
+
+/* These two operate on single nodes */
+int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *);
+int cd9660_rrip_finalize_node(iso9660_disk *, cd9660node *);
+
+/* POSIX File attribute */
+int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Device number */
+int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Symbolic link */
+int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Alternate Name function */
+void cd9660_rrip_NM(cd9660node *);
+void cd9660_rrip_add_NM(cd9660node *,const char *);
+
+/* Parent and child link function */
+int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+
+
+/*
+ * Relocation directory function. I'm not quite sure what
+ * sort of parameters are needed, but personally I don't think
+ * any parameters are needed except for the memory address where
+ * the information needs to be put in
+ */
+int cd9660node_rrip_re(void *, fsnode *);
+
+/*
+ * Don't know if this function is needed because it apparently is an
+ * optional feature that does not really need to be implemented but I
+ * thought I should add it anyway.
+ */
+int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int);
+int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *,
+ const char *, const char *);
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*,
+ cd9660node *);
+
+
+/* Helper functions */
+
+/* Common SUSP/RRIP functions */
+int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *,
+ cd9660node *);
+int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *,
+ int);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *,
+ struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int);
+
+/* RRIP specific functions */
+int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *,
+ cd9660node *);
+void cd9660_createSL(cd9660node *);
+
+/* Functions that probably can be removed */
+/* int cd9660node_initialize_node(int, char *); */
+
+
+#endif
diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
new file mode 100644
index 00000000000..96ef4ea2944
--- /dev/null
+++ b/usr.sbin/makefs/ffs.c
@@ -0,0 +1,1152 @@
+/* $NetBSD: ffs.c,v 1.66 2015/12/21 00:58:08 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989, 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. 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.
+ *
+ * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+#include <sys/statvfs.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/ufs_bswap.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/newfs_extern.h"
+#include "ffs/ffs_extern.h"
+
+#undef DIP
+#define DIP(dp, field) \
+ ((ffs_opts->version == 1) ? \
+ (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
+
+/*
+ * Various file system defaults (cribbed from newfs(8)).
+ */
+#define DFL_FRAGSIZE 1024 /* fragment size */
+#define DFL_BLKSIZE 8192 /* block size */
+#define DFL_SECSIZE 512 /* sector size */
+#define DFL_CYLSPERGROUP 65536 /* cylinders per group */
+#define DFL_FRAGSPERINODE 4 /* fragments per inode */
+#define DFL_ROTDELAY 0 /* rotational delay */
+#define DFL_NRPOS 1 /* rotational positions */
+#define DFL_RPM 3600 /* rpm of disk */
+#define DFL_NSECTORS 64 /* # of sectors */
+#define DFL_NTRACKS 16 /* # of tracks */
+
+
+typedef struct {
+ u_char *buf; /* buf for directory */
+ doff_t size; /* full size of buf */
+ doff_t cur; /* offset of current entry */
+} dirbuf_t;
+
+
+static int ffs_create_image(const char *, fsinfo_t *);
+static void ffs_dump_fsinfo(fsinfo_t *);
+static void ffs_dump_dirbuf(dirbuf_t *, const char *, int);
+static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
+static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
+static void ffs_size_dir(fsnode *, fsinfo_t *);
+static void ffs_validate(const char *, fsnode *, fsinfo_t *);
+static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
+static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
+static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+
+
+
+ /* publically visible functions */
+void
+ffs_prep_opts(fsinfo_t *fsopts)
+{
+ ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts));
+
+ const option_t ffs_options[] = {
+ { 'b', "bsize", &ffs_opts->bsize, OPT_INT32,
+ 1, INT_MAX, "block size" },
+ { 'f', "fsize", &ffs_opts->fsize, OPT_INT32,
+ 1, INT_MAX, "fragment size" },
+ { 'd', "density", &ffs_opts->density, OPT_INT32,
+ 1, INT_MAX, "bytes per inode" },
+ { 'm', "minfree", &ffs_opts->minfree, OPT_INT32,
+ 0, 99, "minfree" },
+ { 'M', "maxbpg", &ffs_opts->maxbpg, OPT_INT32,
+ 1, INT_MAX, "max blocks per file in a cg" },
+ { 'a', "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32,
+ 1, INT_MAX, "expected average file size" },
+ { 'n', "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32,
+ 1, INT_MAX, "expected # of files per directory" },
+ { 'x', "extent", &ffs_opts->maxbsize, OPT_INT32,
+ 1, INT_MAX, "maximum # extent size" },
+ { 'g', "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32,
+ 1, INT_MAX, "max # of blocks per group" },
+ { 'v', "version", &ffs_opts->version, OPT_INT32,
+ 1, 2, "UFS version" },
+ { 'o', "optimization", NULL, OPT_STRBUF,
+ 0, 0, "Optimization (time|space)" },
+ { 'l', "label", ffs_opts->label, OPT_STRARRAY,
+ 1, sizeof(ffs_opts->label), "UFS label" },
+ { .name = NULL }
+ };
+
+ ffs_opts->bsize= -1;
+ ffs_opts->fsize= -1;
+ ffs_opts->cpg= -1;
+ ffs_opts->density= -1;
+ ffs_opts->minfree= -1;
+ ffs_opts->optimization= -1;
+ ffs_opts->maxcontig= -1;
+ ffs_opts->maxbpg= -1;
+ ffs_opts->avgfilesize= -1;
+ ffs_opts->avgfpdir= -1;
+ ffs_opts->version = 1;
+
+ fsopts->fs_specific = ffs_opts;
+ fsopts->fs_options = copy_opts(ffs_options);
+}
+
+void
+ffs_cleanup_opts(fsinfo_t *fsopts)
+{
+ free(fsopts->fs_specific);
+ free(fsopts->fs_options);
+}
+
+int
+ffs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+ option_t *ffs_options = fsopts->fs_options;
+ char buf[1024];
+
+ int rv;
+
+ assert(option != NULL);
+ assert(fsopts != NULL);
+ assert(ffs_opts != NULL);
+
+ if (debug & DEBUG_FS_PARSE_OPTS)
+ printf("ffs_parse_opts: got `%s'\n", option);
+
+ rv = set_option(ffs_options, option, buf, sizeof(buf));
+ if (rv == -1)
+ return 0;
+
+ if (ffs_options[rv].name == NULL)
+ abort();
+
+ switch (ffs_options[rv].letter) {
+ case 'o':
+ if (strcmp(buf, "time") == 0) {
+ ffs_opts->optimization = FS_OPTTIME;
+ } else if (strcmp(buf, "space") == 0) {
+ ffs_opts->optimization = FS_OPTSPACE;
+ } else {
+ warnx("Invalid optimization `%s'", buf);
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+
+void
+ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ struct fs *superblock;
+ struct timeval start;
+
+ assert(image != NULL);
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ if (debug & DEBUG_FS_MAKEFS)
+ printf("ffs_makefs: image %s directory %s root %p\n",
+ image, dir, root);
+
+ /* validate tree and options */
+ TIMER_START(start);
+ ffs_validate(dir, root, fsopts);
+ TIMER_RESULTS(start, "ffs_validate");
+
+ printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
+ image, (long long)fsopts->size, (long long)fsopts->inodes);
+
+ /* create image */
+ TIMER_START(start);
+ if (ffs_create_image(image, fsopts) == -1)
+ errx(1, "Image file `%s' not created.", image);
+ TIMER_RESULTS(start, "ffs_create_image");
+
+ fsopts->curinode = UFS_ROOTINO;
+
+ if (debug & DEBUG_FS_MAKEFS)
+ putchar('\n');
+
+ /* populate image */
+ printf("Populating `%s'\n", image);
+ TIMER_START(start);
+ if (! ffs_populate_dir(dir, root, fsopts))
+ errx(1, "Image file `%s' not populated.", image);
+ TIMER_RESULTS(start, "ffs_populate_dir");
+
+ /* ensure no outstanding buffers remain */
+ if (debug & DEBUG_FS_MAKEFS)
+ bcleanup();
+
+ /* update various superblock parameters */
+ superblock = fsopts->superblock;
+ superblock->fs_fmod = 0;
+ superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir;
+ superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
+ superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
+ superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
+
+ /* write out superblock; image is now complete */
+ ffs_write_superblock(fsopts->superblock, fsopts);
+ if (close(fsopts->fd) == -1)
+ err(1, "Closing `%s'", image);
+ fsopts->fd = -1;
+ printf("Image `%s' complete\n", image);
+}
+
+ /* end of public functions */
+
+
+static void
+ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ int32_t ncg = 1;
+#if notyet
+ int32_t spc, nspf, ncyl, fssize;
+#endif
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+ assert(ffs_opts != NULL);
+
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("ffs_validate: before defaults set:\n");
+ ffs_dump_fsinfo(fsopts);
+ }
+
+ /* set FFS defaults */
+ if (fsopts->sectorsize == -1)
+ fsopts->sectorsize = DFL_SECSIZE;
+ if (ffs_opts->fsize == -1)
+ ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
+ if (ffs_opts->bsize == -1)
+ ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize);
+ if (ffs_opts->cpg == -1)
+ ffs_opts->cpg = DFL_CYLSPERGROUP;
+ else
+ ffs_opts->cpgflg = 1;
+ /* fsopts->density is set below */
+ if (ffs_opts->nsectors == -1)
+ ffs_opts->nsectors = DFL_NSECTORS;
+ if (ffs_opts->minfree == -1)
+ ffs_opts->minfree = MINFREE;
+ if (ffs_opts->optimization == -1)
+ ffs_opts->optimization = DEFAULTOPT;
+ if (ffs_opts->maxcontig == -1)
+ ffs_opts->maxcontig =
+ MAX(1, MIN(MAXBSIZE, FFS_MAXBSIZE) / ffs_opts->bsize);
+ /* XXX ondisk32 */
+ if (ffs_opts->maxbpg == -1)
+ ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t);
+ if (ffs_opts->avgfilesize == -1)
+ ffs_opts->avgfilesize = AVFILESIZ;
+ if (ffs_opts->avgfpdir == -1)
+ ffs_opts->avgfpdir = AFPDIR;
+
+ /* calculate size of tree */
+ ffs_size_dir(root, fsopts);
+ fsopts->inodes += UFS_ROOTINO; /* include first two inodes */
+
+ if (debug & DEBUG_FS_VALIDATE)
+ printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+
+ /* add requested slop */
+ fsopts->size += fsopts->freeblocks;
+ fsopts->inodes += fsopts->freefiles;
+ if (fsopts->freefilepc > 0)
+ fsopts->inodes =
+ fsopts->inodes * (100 + fsopts->freefilepc) / 100;
+ if (fsopts->freeblockpc > 0)
+ fsopts->size =
+ fsopts->size * (100 + fsopts->freeblockpc) / 100;
+
+ /* add space needed for superblocks */
+ /*
+ * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
+ * typically used for small filesystems where space matters.
+ * XXX make this an option.
+ */
+ fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
+ /* add space needed to store inodes, x3 for blockmaps, etc */
+ if (ffs_opts->version == 1)
+ fsopts->size += ncg * DINODE1_SIZE *
+ roundup(fsopts->inodes / ncg,
+ ffs_opts->bsize / DINODE1_SIZE);
+ else
+ fsopts->size += ncg * DINODE2_SIZE *
+ roundup(fsopts->inodes / ncg,
+ ffs_opts->bsize / DINODE2_SIZE);
+
+ /* add minfree */
+ if (ffs_opts->minfree > 0)
+ fsopts->size =
+ fsopts->size * (100 + ffs_opts->minfree) / 100;
+ /*
+ * XXX any other fs slop to add, such as csum's, bitmaps, etc ??
+ */
+
+ if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */
+ fsopts->size = fsopts->minsize;
+
+ /* round up to the next block */
+ fsopts->size = roundup(fsopts->size, ffs_opts->bsize);
+
+ /* calculate density if necessary */
+ if (ffs_opts->density == -1)
+ ffs_opts->density = fsopts->size / fsopts->inodes + 1;
+
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("ffs_validate: after defaults set:\n");
+ ffs_dump_fsinfo(fsopts);
+ printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
+ dir, (long long)fsopts->size, (long long)fsopts->inodes);
+ }
+ /* now check calculated sizes vs requested sizes */
+ if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
+ errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
+ dir, (long long)fsopts->size, (long long)fsopts->maxsize);
+ }
+}
+
+
+static void
+ffs_dump_fsinfo(fsinfo_t *f)
+{
+
+ ffs_opt_t *fs = f->fs_specific;
+
+ printf("fsopts at %p\n", f);
+
+ printf("\tsize %lld, inodes %lld, curinode %u\n",
+ (long long)f->size, (long long)f->inodes, f->curinode);
+
+ printf("\tminsize %lld, maxsize %lld\n",
+ (long long)f->minsize, (long long)f->maxsize);
+ printf("\tfree files %lld, freefile %% %d\n",
+ (long long)f->freefiles, f->freefilepc);
+ printf("\tfree blocks %lld, freeblock %% %d\n",
+ (long long)f->freeblocks, f->freeblockpc);
+ printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
+
+ printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
+ fs->bsize, fs->fsize, fs->cpg, fs->density);
+ printf("\tnsectors %d, rpm %d, minfree %d\n",
+ fs->nsectors, fs->rpm, fs->minfree);
+ printf("\tmaxcontig %d, maxbpg %d\n",
+ fs->maxcontig, fs->maxbpg);
+ printf("\toptimization %s\n",
+ fs->optimization == FS_OPTSPACE ? "space" : "time");
+}
+
+
+static int
+ffs_create_image(const char *image, fsinfo_t *fsopts)
+{
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+ struct statvfs sfs;
+#endif
+ struct fs *fs;
+ char *buf;
+ int i, bufsize;
+ off_t bufrem;
+ time_t tstamp;
+ int oflags = O_RDWR | O_CREAT;
+
+ assert (image != NULL);
+ assert (fsopts != NULL);
+
+ /* create image */
+ if (fsopts->offset == 0)
+ oflags |= O_TRUNC;
+ if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
+ warn("Can't open `%s' for writing", image);
+ return (-1);
+ }
+
+ /* zero image */
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+ if (fstatvfs(fsopts->fd, &sfs) == -1) {
+#endif
+ bufsize = 8192;
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+ warn("can't fstatvfs `%s', using default %d byte chunk",
+ image, bufsize);
+ } else
+ bufsize = sfs.f_iosize;
+#endif
+ bufrem = fsopts->size;
+
+ if (fsopts->sparse) {
+ if (ftruncate(fsopts->fd, bufrem) == -1) {
+ printf ("ERROR in truncate. Sparse option disabled\n");
+ fsopts->sparse = 0;
+ } else {
+ bufrem = 0; /* File truncated at bufrem. Remaining is 0 */
+ buf = NULL;
+ }
+ }
+
+ if (fsopts->offset != 0)
+ if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
+ warn("can't seek");
+ return -1;
+ }
+
+ if ((debug & DEBUG_FS_CREATE_IMAGE) && fsopts->sparse == 0)
+ printf(
+ "zero-ing image `%s', %lld sectors, using %d byte chunks\n",
+ image, (long long)bufrem, bufsize);
+ if (bufrem > 0)
+ buf = ecalloc(1, bufsize);
+ while (bufrem > 0) {
+ i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
+ if (i == -1) {
+ warn("zeroing image, %lld bytes to go",
+ (long long)bufrem);
+ free(buf);
+ return (-1);
+ }
+ bufrem -= i;
+ }
+ if (buf)
+ free(buf);
+
+ /* make the file system */
+ if (debug & DEBUG_FS_CREATE_IMAGE)
+ printf("calling mkfs(\"%s\", ...)\n", image);
+
+ if (stampst.st_ino == 1)
+ tstamp = stampst.st_ctime;
+ else
+ tstamp = start_time.tv_sec;
+
+ srandom(tstamp);
+
+ fs = ffs_mkfs(image, fsopts, tstamp);
+ fsopts->superblock = (void *)fs;
+ if (debug & DEBUG_FS_CREATE_IMAGE) {
+ time_t t;
+
+ t = (time_t)((struct fs *)fsopts->superblock)->fs_time;
+ printf("mkfs returned %p; fs_time %s",
+ fsopts->superblock, ctime(&t));
+ printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n",
+ (long long)fs->fs_cstotal.cs_nbfree,
+ (long long)fs->fs_cstotal.cs_nffree,
+ (long long)fs->fs_cstotal.cs_nifree,
+ (long long)fs->fs_cstotal.cs_ndir);
+ }
+
+ if ((off_t)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO) < fsopts->inodes) {
+ warnx(
+ "Image file `%s' has %lld free inodes; %lld are required.",
+ image,
+ (long long)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO),
+ (long long)fsopts->inodes);
+ return (-1);
+ }
+ return (fsopts->fd);
+}
+
+
+static void
+ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
+{
+ struct direct tmpdir;
+ fsnode * node;
+ int curdirsize, this;
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+
+ /* node may be NULL (empty directory) */
+ assert(fsopts != NULL);
+ assert(ffs_opts != NULL);
+
+ if (debug & DEBUG_FS_SIZE_DIR)
+ printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+
+#define ADDDIRENT(e) do { \
+ tmpdir.d_namlen = strlen((e)); \
+ this = UFS_DIRSIZ(0, &tmpdir, 0); \
+ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
+ printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \
+ e, tmpdir.d_namlen, this, curdirsize); \
+ if (this + curdirsize > roundup(curdirsize, UFS_DIRBLKSIZ)) \
+ curdirsize = roundup(curdirsize, UFS_DIRBLKSIZ); \
+ curdirsize += this; \
+ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
+ printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \
+ e, tmpdir.d_namlen, this, curdirsize); \
+} while (0);
+
+ /*
+ * XXX this needs to take into account extra space consumed
+ * by indirect blocks, etc.
+ */
+#define ADDSIZE(x) do { \
+ fsopts->size += roundup((x), ffs_opts->fsize); \
+} while (0);
+
+ curdirsize = 0;
+ for (node = root; node != NULL; node = node->next) {
+ ADDDIRENT(node->name);
+ if (node == root) { /* we're at "." */
+ assert(strcmp(node->name, ".") == 0);
+ ADDDIRENT("..");
+ } else if ((node->inode->flags & FI_SIZED) == 0) {
+ /* don't count duplicate names */
+ node->inode->flags |= FI_SIZED;
+ if (debug & DEBUG_FS_SIZE_DIR_NODE)
+ printf("ffs_size_dir: `%s' size %lld\n",
+ node->name,
+ (long long)node->inode->st.st_size);
+ fsopts->inodes++;
+ if (node->type == S_IFREG)
+ ADDSIZE(node->inode->st.st_size);
+ if (node->type == S_IFLNK) {
+ size_t slen;
+
+ slen = strlen(node->symlink) + 1;
+ if (slen >= (ffs_opts->version == 1 ?
+ UFS1_MAXSYMLINKLEN :
+ UFS2_MAXSYMLINKLEN))
+ ADDSIZE(slen);
+ }
+ }
+ if (node->type == S_IFDIR)
+ ffs_size_dir(node->child, fsopts);
+ }
+ ADDSIZE(curdirsize);
+
+ if (debug & DEBUG_FS_SIZE_DIR)
+ printf("ffs_size_dir: exit: size %lld inodes %lld\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+}
+
+static void *
+ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+ fsnode *root, fsinfo_t *fsopts)
+{
+ size_t slen;
+ void *membuf;
+ struct stat *st = stampst.st_ino == 1 ? &stampst : &cur->inode->st;
+
+ memset(dinp, 0, sizeof(*dinp));
+ dinp->di_mode = cur->inode->st.st_mode;
+ dinp->di_nlink = cur->inode->nlink;
+ dinp->di_size = cur->inode->st.st_size;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+
+ dinp->di_atime = st->st_atime;
+ dinp->di_mtime = st->st_mtime;
+ dinp->di_ctime = st->st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = st->st_atimensec;
+ dinp->di_mtimensec = st->st_mtimensec;
+ dinp->di_ctimensec = st->st_ctimensec;
+#endif
+ /* not set: di_db, di_ib, di_blocks, di_spare */
+
+ membuf = NULL;
+ if (cur == root) { /* "."; write dirbuf */
+ membuf = dbufp->buf;
+ dinp->di_size = dbufp->size;
+ } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+ dinp->di_size = 0; /* a device */
+ dinp->di_rdev =
+ ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap);
+ } else if (S_ISLNK(cur->type)) { /* symlink */
+ slen = strlen(cur->symlink);
+ if (slen < UFS1_MAXSYMLINKLEN) { /* short link */
+ memcpy(dinp->di_db, cur->symlink, slen);
+ } else
+ membuf = cur->symlink;
+ dinp->di_size = slen;
+ }
+ return membuf;
+}
+
+static void *
+ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+ fsnode *root, fsinfo_t *fsopts)
+{
+ size_t slen;
+ void *membuf;
+ struct stat *st = stampst.st_ino == 1 ? &stampst : &cur->inode->st;
+
+ memset(dinp, 0, sizeof(*dinp));
+ dinp->di_mode = cur->inode->st.st_mode;
+ dinp->di_nlink = cur->inode->nlink;
+ dinp->di_size = cur->inode->st.st_size;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+
+ dinp->di_atime = st->st_atime;
+ dinp->di_mtime = st->st_mtime;
+ dinp->di_ctime = st->st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = st->st_atimensec;
+ dinp->di_mtimensec = st->st_mtimensec;
+ dinp->di_ctimensec = st->st_ctimensec;
+#endif
+ /* not set: di_db, di_ib, di_blocks, di_spare */
+
+ membuf = NULL;
+ if (cur == root) { /* "."; write dirbuf */
+ membuf = dbufp->buf;
+ dinp->di_size = dbufp->size;
+ } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+ dinp->di_size = 0; /* a device */
+ dinp->di_rdev =
+ ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap);
+ } else if (S_ISLNK(cur->type)) { /* symlink */
+ slen = strlen(cur->symlink);
+ if (slen < UFS2_MAXSYMLINKLEN) { /* short link */
+ memcpy(dinp->di_db, cur->symlink, slen);
+ } else
+ membuf = cur->symlink;
+ dinp->di_size = slen;
+ }
+ return membuf;
+}
+
+static int
+ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ fsnode *cur;
+ dirbuf_t dirbuf;
+ union dinode din;
+ void *membuf;
+ char path[MAXPATHLEN + 1];
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+ assert(ffs_opts != NULL);
+
+ (void)memset(&dirbuf, 0, sizeof(dirbuf));
+
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root);
+
+ /*
+ * pass 1: allocate inode numbers, build directory `file'
+ */
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+ cur->inode->flags |= FI_ALLOCATED;
+ if (cur == root && cur->parent != NULL)
+ cur->inode->ino = cur->parent->inode->ino;
+ else {
+ cur->inode->ino = fsopts->curinode;
+ fsopts->curinode++;
+ }
+ }
+ ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
+ if (cur == root) { /* we're at "."; add ".." */
+ ffs_make_dirbuf(&dirbuf, "..",
+ cur->parent == NULL ? cur : cur->parent->first,
+ fsopts->needswap);
+ root->inode->nlink++; /* count my parent's link */
+ } else if (cur->child != NULL)
+ root->inode->nlink++; /* count my child's link */
+
+ /*
+ * XXX possibly write file and long symlinks here,
+ * ensuring that blocks get written before inodes?
+ * otoh, this isn't a real filesystem, so who
+ * cares about ordering? :-)
+ */
+ }
+ if (debug & DEBUG_FS_POPULATE_DIRBUF)
+ ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
+
+ /*
+ * pass 2: write out dirbuf, then non-directories at this level
+ */
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 2 dir %s\n", dir);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (cur->inode->flags & FI_WRITTEN)
+ continue; /* skip hard-linked entries */
+ cur->inode->flags |= FI_WRITTEN;
+
+ if ((size_t)snprintf(path, sizeof(path), "%s/%s/%s", cur->root,
+ cur->path, cur->name) >= sizeof(path))
+ errx(1, "Pathname too long.");
+
+ if (cur->child != NULL)
+ continue; /* child creates own inode */
+
+ /* build on-disk inode */
+ if (ffs_opts->version == 1)
+ membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
+ root, fsopts);
+ else
+ membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur,
+ root, fsopts);
+
+ if (debug & DEBUG_FS_POPULATE_NODE) {
+ printf("ffs_populate_dir: writing ino %d, %s",
+ cur->inode->ino, inode_type(cur->type));
+ if (cur->inode->nlink > 1)
+ printf(", nlink %d", cur->inode->nlink);
+ putchar('\n');
+ }
+
+ if (membuf != NULL) {
+ ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
+ } else if (S_ISREG(cur->type)) {
+ ffs_write_file(&din, cur->inode->ino, path, fsopts);
+ } else {
+ assert (! S_ISDIR(cur->type));
+ ffs_write_inode(&din, cur->inode->ino, fsopts);
+ }
+ }
+
+ /*
+ * pass 3: write out sub-directories
+ */
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 3 dir %s\n", dir);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (cur->child == NULL)
+ continue;
+ if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir,
+ cur->name) >= sizeof(path))
+ errx(1, "Pathname too long.");
+ if (! ffs_populate_dir(path, cur->child, fsopts))
+ return (0);
+ }
+
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: DONE dir %s\n", dir);
+
+ /* cleanup */
+ if (dirbuf.buf != NULL)
+ free(dirbuf.buf);
+ return (1);
+}
+
+
+static void
+ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
+{
+ int isfile, ffd;
+ char *fbuf, *p;
+ off_t bufleft, chunk, offset;
+ ssize_t nread;
+ struct inode in;
+ struct buf * bp;
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+ struct vnode vp = { fsopts, NULL };
+
+ assert (din != NULL);
+ assert (buf != NULL);
+ assert (fsopts != NULL);
+ assert (ffs_opts != NULL);
+
+ isfile = S_ISREG(DIP(din, mode));
+ fbuf = NULL;
+ ffd = -1;
+ p = NULL;
+
+ in.i_fs = (struct fs *)fsopts->superblock;
+ in.i_devvp = &vp;
+
+ if (debug & DEBUG_FS_WRITE_FILE) {
+ printf(
+ "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
+ ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT),
+ (long long)DIP(din, size));
+ if (isfile)
+ printf(", file '%s'\n", (char *)buf);
+ else
+ printf(", buffer %p\n", buf);
+ }
+
+ in.i_number = ino;
+ in.i_size = DIP(din, size);
+ if (ffs_opts->version == 1)
+ memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
+ sizeof(in.i_din.ffs1_din));
+ else
+ memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
+ sizeof(in.i_din.ffs2_din));
+
+ if (DIP(din, size) == 0)
+ goto write_inode_and_leave; /* mmm, cheating */
+
+ if (isfile) {
+ fbuf = emalloc(ffs_opts->bsize);
+ if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
+ warn("Can't open `%s' for reading", (char *)buf);
+ goto leave_ffs_write_file;
+ }
+ } else {
+ p = buf;
+ }
+
+ chunk = 0;
+ for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
+ chunk = MIN(bufleft, ffs_opts->bsize);
+ if (!isfile)
+ ;
+ else if ((nread = read(ffd, fbuf, chunk)) == -1)
+ err(EXIT_FAILURE, "Reading `%s', %lld bytes to go",
+ (char *)buf, (long long)bufleft);
+ else if (nread != chunk)
+ errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, "
+ "read %zd bytes, expected %ju bytes, does "
+ "metalog size= attribute mismatch source size?",
+ (char *)buf, (long long)bufleft, nread,
+ (uintmax_t)chunk);
+ else
+ p = fbuf;
+ offset = DIP(din, size) - bufleft;
+ if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
+ printf(
+ "ffs_write_file: write %p offset %lld size %lld left %lld\n",
+ p, (long long)offset,
+ (long long)chunk, (long long)bufleft);
+ /*
+ * XXX if holey support is desired, do the check here
+ *
+ * XXX might need to write out last bit in fragroundup
+ * sized chunk. however, ffs_balloc() handles this for us
+ */
+ errno = ffs_balloc(&in, offset, chunk, &bp);
+ bad_ffs_write_file:
+ if (errno != 0)
+ err(1,
+ "Writing inode %d (%s), bytes %lld + %lld",
+ ino,
+ isfile ? (char *)buf :
+ inode_type(DIP(din, mode) & S_IFMT),
+ (long long)offset, (long long)chunk);
+ memcpy(bp->b_data, p, chunk);
+ errno = bwrite(bp);
+ if (errno != 0)
+ goto bad_ffs_write_file;
+ if (!isfile)
+ p += chunk;
+ }
+
+ write_inode_and_leave:
+ ffs_write_inode(&in.i_din, in.i_number, fsopts);
+
+ leave_ffs_write_file:
+ if (fbuf)
+ free(fbuf);
+ if (ffd != -1)
+ close(ffd);
+}
+
+
+static void
+ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
+{
+ doff_t i;
+ struct direct *de;
+ uint16_t reclen;
+
+ assert (dbuf != NULL);
+ assert (dir != NULL);
+ printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
+ dir, dbuf->size, dbuf->cur);
+
+ for (i = 0; i < dbuf->size; ) {
+ de = (struct direct *)(dbuf->buf + i);
+ reclen = ufs_rw16(de->d_reclen, needswap);
+ printf(
+ " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
+ ufs_rw32(de->d_fileno, needswap),
+ inode_type(DTTOIF(de->d_type)), i, reclen,
+ de->d_namlen, de->d_name);
+ i += reclen;
+ assert(reclen > 0);
+ }
+}
+
+static void
+ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
+{
+ struct direct de, *dp;
+ uint16_t llen, reclen;
+ u_char *newbuf;
+
+ assert (dbuf != NULL);
+ assert (name != NULL);
+ assert (node != NULL);
+ /* create direct entry */
+ (void)memset(&de, 0, sizeof(de));
+ de.d_fileno = ufs_rw32(node->inode->ino, needswap);
+ de.d_type = IFTODT(node->type);
+ de.d_namlen = (uint8_t)strlen(name);
+ strcpy(de.d_name, name);
+ reclen = UFS_DIRSIZ(0, &de, needswap);
+ de.d_reclen = ufs_rw16(reclen, needswap);
+
+ dp = (struct direct *)(dbuf->buf + dbuf->cur);
+ llen = 0;
+ if (dp != NULL)
+ llen = UFS_DIRSIZ(0, dp, needswap);
+
+ if (debug & DEBUG_FS_MAKE_DIRBUF)
+ printf(
+ "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
+ " ino %d type %d reclen %d namlen %d name %.30s\n",
+ dbuf->size, dbuf->cur, llen,
+ ufs_rw32(de.d_fileno, needswap), de.d_type, reclen,
+ de.d_namlen, de.d_name);
+
+ if (reclen + dbuf->cur + llen > roundup(dbuf->size, UFS_DIRBLKSIZ)) {
+ if (debug & DEBUG_FS_MAKE_DIRBUF)
+ printf("ffs_make_dirbuf: growing buf to %d\n",
+ dbuf->size + UFS_DIRBLKSIZ);
+ newbuf = erealloc(dbuf->buf, dbuf->size + UFS_DIRBLKSIZ);
+ dbuf->buf = newbuf;
+ dbuf->size += UFS_DIRBLKSIZ;
+ memset(dbuf->buf + dbuf->size - UFS_DIRBLKSIZ, 0, UFS_DIRBLKSIZ);
+ dbuf->cur = dbuf->size - UFS_DIRBLKSIZ;
+ } else if (dp) { /* shrink end of previous */
+ dp->d_reclen = ufs_rw16(llen,needswap);
+ dbuf->cur += llen;
+ }
+ dp = (struct direct *)(dbuf->buf + dbuf->cur);
+ memcpy(dp, &de, reclen);
+ dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
+}
+
+/*
+ * cribbed from sys/ufs/ffs/ffs_alloc.c
+ */
+static void
+ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
+{
+ char *buf;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2, *dip;
+ struct cg *cgp;
+ struct fs *fs;
+ int cg, cgino, i;
+ daddr_t d;
+ char sbbuf[FFS_MAXBSIZE];
+ uint32_t initediblk;
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+
+ assert (dp != NULL);
+ assert (ino > 0);
+ assert (fsopts != NULL);
+ assert (ffs_opts != NULL);
+
+ fs = (struct fs *)fsopts->superblock;
+ cg = ino_to_cg(fs, ino);
+ cgino = ino % fs->fs_ipg;
+ if (debug & DEBUG_FS_WRITE_INODE)
+ printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
+ dp, ino, cg, cgino);
+
+ ffs_rdfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+ fsopts);
+ cgp = (struct cg *)sbbuf;
+ if (!cg_chkmagic(cgp, fsopts->needswap))
+ errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
+
+ assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino));
+
+ buf = emalloc(fs->fs_bsize);
+ dp1 = (struct ufs1_dinode *)buf;
+ dp2 = (struct ufs2_dinode *)buf;
+
+ if (fs->fs_cstotal.cs_nifree == 0)
+ errx(1, "ffs_write_inode: fs out of inodes for ino %u",
+ ino);
+ if (fs->fs_cs(fs, cg).cs_nifree == 0)
+ errx(1,
+ "ffs_write_inode: cg %d out of inodes for ino %u",
+ cg, ino);
+ setbit(cg_inosused(cgp, fsopts->needswap), cgino);
+ ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
+ fs->fs_cstotal.cs_nifree--;
+ fs->fs_cs(fs, cg).cs_nifree--;
+ if (S_ISDIR(DIP(dp, mode))) {
+ ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
+ fs->fs_cstotal.cs_ndir++;
+ fs->fs_cs(fs, cg).cs_ndir++;
+ }
+
+ /*
+ * Initialize inode blocks on the fly for UFS2.
+ */
+ initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap);
+ if (ffs_opts->version == 2 &&
+ (uint32_t)(cgino + FFS_INOPB(fs)) > initediblk &&
+ initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) {
+ memset(buf, 0, fs->fs_bsize);
+ dip = (struct ufs2_dinode *)buf;
+ for (i = 0; i < FFS_INOPB(fs); i++) {
+ dip->di_gen = random() / 2 + 1;
+ dip++;
+ }
+ ffs_wtfs(FFS_FSBTODB(fs, ino_to_fsba(fs,
+ cg * fs->fs_ipg + initediblk)),
+ fs->fs_bsize, buf, fsopts);
+ initediblk += FFS_INOPB(fs);
+ cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap);
+ }
+
+
+ ffs_wtfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+ fsopts);
+
+ /* now write inode */
+ d = FFS_FSBTODB(fs, ino_to_fsba(fs, ino));
+ ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
+ if (fsopts->needswap) {
+ if (ffs_opts->version == 1)
+ ffs_dinode1_swap(&dp->ffs1_din,
+ &dp1[ino_to_fsbo(fs, ino)]);
+ else
+ ffs_dinode2_swap(&dp->ffs2_din,
+ &dp2[ino_to_fsbo(fs, ino)]);
+ } else {
+ if (ffs_opts->version == 1)
+ dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
+ else
+ dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din;
+ }
+ ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
+ free(buf);
+}
+
+void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ exit(1);
+}
diff --git a/usr.sbin/makefs/ffs.h b/usr.sbin/makefs/ffs.h
new file mode 100644
index 00000000000..79223bd54dd
--- /dev/null
+++ b/usr.sbin/makefs/ffs.h
@@ -0,0 +1,68 @@
+/* $NetBSD: ffs.h,v 1.2 2011/10/09 21:33:43 christos Exp $ */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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 _FFS_H
+#define _FFS_H
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+typedef struct {
+ char label[MAXVOLLEN]; /* volume name/label */
+ int bsize; /* block size */
+ int fsize; /* fragment size */
+ int cpg; /* cylinders per group */
+ int cpgflg; /* cpg was specified by user */
+ int density; /* bytes per inode */
+ int ntracks; /* number of tracks */
+ int nsectors; /* number of sectors */
+ int rpm; /* rpm */
+ int minfree; /* free space threshold */
+ int optimization; /* optimization (space or time) */
+ int maxcontig; /* max contiguous blocks to allocate */
+ int rotdelay; /* rotational delay between blocks */
+ int maxbpg; /* maximum blocks per file in a cyl group */
+ int nrpos; /* # of distinguished rotational positions */
+ int avgfilesize; /* expected average file size */
+ int avgfpdir; /* expected # of files per directory */
+ int version; /* filesystem version (1 = FFS, 2 = UFS2) */
+ int maxbsize; /* maximum extent size */
+ int maxblkspercg; /* max # of blocks per cylinder group */
+ /* XXX: support `old' file systems ? */
+} ffs_opt_t;
+
+#endif /* _FFS_H */
diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c
new file mode 100644
index 00000000000..4b432255073
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.c
@@ -0,0 +1,211 @@
+/* $NetBSD: buf.c,v 1.24 2016/06/24 19:24:11 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "buf.h"
+
+TAILQ_HEAD(buftailhead,buf) buftail;
+
+int
+bread(struct vnode *vp, daddr_t blkno, int size, int u2 __unused,
+ struct buf **bpp)
+{
+ off_t offset;
+ ssize_t rv;
+ fsinfo_t *fs = vp->fs;
+
+ assert (bpp != NULL);
+
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: blkno %lld size %d\n", (long long)blkno, size);
+ *bpp = getblk(vp, blkno, size, 0, 0);
+ offset = (*bpp)->b_blkno * fs->sectorsize + fs->offset;
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: blkno %lld offset %lld bcount %ld\n",
+ (long long)(*bpp)->b_blkno, (long long) offset,
+ (*bpp)->b_bcount);
+ if (lseek((*bpp)->b_fs->fd, offset, SEEK_SET) == -1)
+ err(EXIT_FAILURE, "%s: lseek %lld (%lld)", __func__,
+ (long long)(*bpp)->b_blkno, (long long)offset);
+ rv = read((*bpp)->b_fs->fd, (*bpp)->b_data, (size_t)(*bpp)->b_bcount);
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: read %ld (%lld) returned %zd\n",
+ (*bpp)->b_bcount, (long long)offset, rv);
+ if (rv == -1) /* read error */
+ err(EXIT_FAILURE, "%s: read %ld (%lld) returned %zd", __func__,
+ (*bpp)->b_bcount, (long long)offset, rv);
+ else if (rv != (*bpp)->b_bcount) /* short read */
+ errx(EXIT_FAILURE, "%s: read %ld (%lld) returned %zd", __func__,
+ (*bpp)->b_bcount, (long long)offset, rv);
+ else
+ return (0);
+}
+
+void
+brelse(struct buf *bp, int u1 __unused)
+{
+
+ assert (bp != NULL);
+ assert (bp->b_data != NULL);
+
+ if (bp->b_lblkno < 0) {
+ /*
+ * XXX don't remove any buffers with negative logical block
+ * numbers (lblkno), so that we retain the mapping
+ * of negative lblkno -> real blkno that ffs_balloc()
+ * sets up.
+ *
+ * if we instead released these buffers, and implemented
+ * ufs_strategy() (and ufs_bmaparray()) and called those
+ * from bread() and bwrite() to convert the lblkno to
+ * a real blkno, we'd add a lot more code & complexity
+ * and reading off disk, for little gain, because this
+ * simple hack works for our purpose.
+ */
+ bp->b_bcount = 0;
+ return;
+ }
+
+ TAILQ_REMOVE(&buftail, bp, b_tailq);
+ free(bp->b_data);
+ free(bp);
+}
+
+int
+bwrite(struct buf *bp)
+{
+ off_t offset;
+ ssize_t rv;
+ size_t bytes;
+ fsinfo_t *fs = bp->b_fs;
+
+ assert (bp != NULL);
+ offset = bp->b_blkno * fs->sectorsize + fs->offset;
+ bytes = (size_t)bp->b_bcount;
+ if (debug & DEBUG_BUF_BWRITE)
+ printf("bwrite: blkno %lld offset %lld bcount %zu\n",
+ (long long)bp->b_blkno, (long long) offset, bytes);
+ if (lseek(bp->b_fs->fd, offset, SEEK_SET) == -1)
+ return (errno);
+ rv = write(bp->b_fs->fd, bp->b_data, bytes);
+ if (debug & DEBUG_BUF_BWRITE)
+ printf("bwrite: write %ld (offset %lld) returned %lld\n",
+ bp->b_bcount, (long long)offset, (long long)rv);
+ brelse(bp, 0);
+ if (rv == (ssize_t)bytes)
+ return (0);
+ else if (rv == -1) /* write error */
+ return (errno);
+ else /* short write ? */
+ return (EAGAIN);
+}
+
+void
+bcleanup(void)
+{
+ struct buf *bp;
+
+ /*
+ * XXX this really shouldn't be necessary, but i'm curious to
+ * know why there's still some buffers lying around that
+ * aren't brelse()d
+ */
+
+ if (TAILQ_EMPTY(&buftail))
+ return;
+
+ printf("bcleanup: unflushed buffers:\n");
+ TAILQ_FOREACH(bp, &buftail, b_tailq) {
+ printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n",
+ (long long)bp->b_lblkno, (long long)bp->b_blkno,
+ bp->b_bcount, bp->b_bufsize);
+ }
+ printf("bcleanup: done\n");
+}
+
+struct buf *
+getblk(struct vnode *vp, daddr_t blkno, int size, int u1 __unused,
+ int u2 __unused)
+{
+ static int buftailinitted;
+ struct buf *bp;
+ void *n;
+
+ if (debug & DEBUG_BUF_GETBLK)
+ printf("getblk: blkno %lld size %d\n", (long long)blkno, size);
+
+ bp = NULL;
+ if (!buftailinitted) {
+ if (debug & DEBUG_BUF_GETBLK)
+ printf("getblk: initialising tailq\n");
+ TAILQ_INIT(&buftail);
+ buftailinitted = 1;
+ } else {
+ TAILQ_FOREACH(bp, &buftail, b_tailq) {
+ if (bp->b_lblkno != blkno)
+ continue;
+ break;
+ }
+ }
+ if (bp == NULL) {
+ bp = ecalloc(1, sizeof(*bp));
+ bp->b_bufsize = 0;
+ bp->b_blkno = bp->b_lblkno = blkno;
+ bp->b_fs = vp->fs;
+ bp->b_data = NULL;
+ TAILQ_INSERT_HEAD(&buftail, bp, b_tailq);
+ }
+ bp->b_bcount = size;
+ if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) {
+ n = erealloc(bp->b_data, (size_t)size);
+ memset(n, 0, (size_t)size);
+ bp->b_data = n;
+ bp->b_bufsize = size;
+ }
+
+ return (bp);
+}
diff --git a/usr.sbin/makefs/ffs/buf.h b/usr.sbin/makefs/ffs/buf.h
new file mode 100644
index 00000000000..654788b7b3c
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.h
@@ -0,0 +1,115 @@
+/* $NetBSD: buf.h,v 1.10 2015/03/29 05:52:59 agc Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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 _FFS_BUF_H
+#define _FFS_BUF_H
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <err.h>
+
+struct componentname {
+ char *cn_nameptr;
+ size_t cn_namelen;
+};
+
+struct makefs_fsinfo;
+struct vnode {
+ struct makefs_fsinfo *fs;
+ void *v_data;
+};
+
+#define vput(a) ((void)(a))
+
+struct buf {
+ void * b_data;
+ long b_bufsize;
+ long b_bcount;
+ daddr_t b_blkno;
+ daddr_t b_lblkno;
+ struct makefs_fsinfo * b_fs;
+
+ TAILQ_ENTRY(buf) b_tailq;
+};
+
+struct kauth_cred;
+void bcleanup(void);
+int bread(struct vnode *, daddr_t, int, int, struct buf **);
+void brelse(struct buf *, int);
+int bwrite(struct buf *);
+struct buf * getblk(struct vnode *, daddr_t, int, int, int);
+
+#define bdwrite(bp) bwrite(bp)
+#define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount)
+
+#define B_MODIFY 0
+#define BC_AGE 0
+
+#define min(a, b) MIN((a), (b))
+#define microtime(tv) gettimeofday((tv), NULL)
+#define KASSERT(a)
+#define IO_SYNC 1
+
+struct pool {
+ size_t size;
+};
+
+#define pool_init(p, s, a1, a2, a3, a4, a5, a6) (p)->size = (s)
+#define pool_get(p, f) ecalloc(1, (p)->size)
+#define pool_put(p, a) free(a)
+#define pool_destroy(p)
+
+#define MALLOC_DECLARE(a)
+#define malloc_type_attach(a)
+#define malloc_type_detach(a)
+
+#define mutex_enter(m)
+#define mutex_exit(m)
+#define mutex_init(m, t, i)
+#define mutex_destroy(m)
+
+#define NOCRED NULL
+#define DEV_BSHIFT 9
+
+#endif /* _FFS_BUF_H */
diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c
new file mode 100644
index 00000000000..74aa0d1cad3
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_alloc.c
@@ -0,0 +1,589 @@
+/* $NetBSD: ffs_alloc.c,v 1.29 2016/06/24 19:24:11 christos Exp $ */
+/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1986, 1989, 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. 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.
+ *
+ * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+
+static int scanc(u_int, const u_char *, const u_char *, int);
+
+static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
+static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int,
+ daddr_t (*)(struct inode *, int, daddr_t, int));
+static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
+
+/* in ffs_tables.c */
+extern const int inside[], around[];
+extern const u_char * const fragtbl[];
+
+/*
+ * Allocate a block in the file system.
+ *
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate a block in the same cylinder group.
+ * 4) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ * If no block preference is given the following hierarchy is used
+ * to allocate a block:
+ * 1) allocate a block in the cylinder group that contains the
+ * inode for the file.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ */
+int
+ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size,
+ daddr_t *bnp)
+{
+ struct fs *fs = ip->i_fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0) {
+ errx(EXIT_FAILURE, "%s: bad size: bsize %d size %d", __func__,
+ fs->fs_bsize, size);
+ }
+ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+ goto nospace;
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ if (bpref == 0)
+ cg = ino_to_cg(fs, ip->i_number);
+ else
+ cg = dtog(fs, bpref);
+ bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg);
+ if (bno > 0) {
+ DIP_ADD(ip, blocks, size / DEV_BSIZE);
+ *bnp = bno;
+ return (0);
+ }
+nospace:
+ return (ENOSPC);
+}
+
+/*
+ * Select the desired position for the next block in a file. The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ *
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. If no blocks have been allocated in any other section, the
+ * policy is to place the section in a cylinder group with a greater than
+ * average number of free blocks. An appropriate cylinder group is found
+ * by using a rotor that sweeps the cylinder groups. When a new group of
+ * blocks is needed, the sweep begins in the cylinder group following the
+ * cylinder group from which the previous allocation was made. The sweep
+ * continues until a cylinder group with greater than the average number
+ * of free blocks is found. If the allocation is for the first block in an
+ * indirect block, the information on the previous allocation is unavailable;
+ * here a best guess is made based upon the logical block number being
+ * allocated.
+ *
+ * If a section is already partially allocated, the policy is to
+ * contiguously allocate fs_maxcontig blocks. The end of one of these
+ * contiguous blocks and the beginning of the next is physically separated
+ * so that the disk head will be in transit between them for at least
+ * fs_rotdelay milliseconds. This is to allow time for the processor to
+ * schedule another I/O transfer.
+ */
+/* XXX ondisk32 */
+daddr_t
+ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap)
+{
+ struct fs *fs;
+ int cg;
+ int avgbfree, startcg;
+
+ fs = ip->i_fs;
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) {
+ cg = ino_to_cg(fs, ip->i_number);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ /*
+ * Find a cylinder with greater than average number of
+ * unused data blocks.
+ */
+ if (indx == 0 || bap[indx - 1] == 0)
+ startcg =
+ ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+ else
+ startcg = dtog(fs,
+ ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ for (cg = 0; cg <= startcg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ return (0);
+ }
+ /*
+ * We just always try to lay things out contiguously.
+ */
+ return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+daddr_t
+ffs_blkpref_ufs2(struct inode *ip, daddr_t lbn, int indx, int64_t *bap)
+{
+ struct fs *fs;
+ int cg;
+ int avgbfree, startcg;
+
+ fs = ip->i_fs;
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) {
+ cg = ino_to_cg(fs, ip->i_number);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ /*
+ * Find a cylinder with greater than average number of
+ * unused data blocks.
+ */
+ if (indx == 0 || bap[indx - 1] == 0)
+ startcg =
+ ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+ else
+ startcg = dtog(fs,
+ ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ for (cg = 0; cg < startcg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ return (0);
+ }
+ /*
+ * We just always try to lay things out contiguously.
+ */
+ return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ * 1) allocate the block in its requested cylinder group.
+ * 2) quadradically rehash on the cylinder group number.
+ * 3) brute force search for a free block.
+ *
+ * `size': size for data blocks, mode for inodes
+ */
+/*VARARGS5*/
+static daddr_t
+ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size,
+ daddr_t (*allocator)(struct inode *, int, daddr_t, int))
+{
+ struct fs *fs;
+ daddr_t result;
+ int i, icg = cg;
+
+ fs = ip->i_fs;
+ /*
+ * 1: preferred cylinder group
+ */
+ result = (*allocator)(ip, cg, pref, size);
+ if (result)
+ return (result);
+ /*
+ * 2: quadratic rehash
+ */
+ for (i = 1; i < fs->fs_ncg; i *= 2) {
+ cg += i;
+ if (cg >= fs->fs_ncg)
+ cg -= fs->fs_ncg;
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ }
+ /*
+ * 3: brute force search
+ * Note that we start at i == 2, since 0 was checked initially,
+ * and 1 is always checked in the quadratic rehash.
+ */
+ cg = (icg + 2) % fs->fs_ncg;
+ for (i = 2; i < fs->fs_ncg; i++) {
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ cg++;
+ if (cg == fs->fs_ncg)
+ cg = 0;
+ }
+ return (0);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+static daddr_t
+ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size)
+{
+ struct cg *cgp;
+ struct buf *bp;
+ daddr_t bno, blkno;
+ int error, frags, allocsiz, i;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
+ return (0);
+ error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, 0, &bp);
+ if (error) {
+ return (0);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp, needswap) ||
+ (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+ brelse(bp, 0);
+ return (0);
+ }
+ if (size == fs->fs_bsize) {
+ bno = ffs_alloccgblk(ip, bp, bpref);
+ bwrite(bp);
+ return (bno);
+ }
+ /*
+ * check to see if any fragments are already available
+ * allocsiz is the size which will be allocated, hacking
+ * it down to a smaller size if necessary
+ */
+ frags = ffs_numfrags(fs, size);
+ for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
+ if (cgp->cg_frsum[allocsiz] != 0)
+ break;
+ if (allocsiz == fs->fs_frag) {
+ /*
+ * no fragments were available, so a block will be
+ * allocated, and hacked up
+ */
+ if (cgp->cg_cs.cs_nbfree == 0) {
+ brelse(bp, 0);
+ return (0);
+ }
+ bno = ffs_alloccgblk(ip, bp, bpref);
+ bpref = dtogd(fs, bno);
+ for (i = frags; i < fs->fs_frag; i++)
+ setbit(cg_blksfree(cgp, needswap), bpref + i);
+ i = fs->fs_frag - frags;
+ ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ fs->fs_fmod = 1;
+ ufs_add32(cgp->cg_frsum[i], 1, needswap);
+ bdwrite(bp);
+ return (bno);
+ }
+ bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+ for (i = 0; i < frags; i++)
+ clrbit(cg_blksfree(cgp, needswap), bno + i);
+ ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap);
+ fs->fs_cstotal.cs_nffree -= frags;
+ fs->fs_cs(fs, cg).cs_nffree -= frags;
+ fs->fs_fmod = 1;
+ ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap);
+ if (frags != allocsiz)
+ ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap);
+ blkno = cg * fs->fs_fpg + bno;
+ bdwrite(bp);
+ return blkno;
+}
+
+/*
+ * Allocate a block in a cylinder group.
+ *
+ * This algorithm implements the following policy:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate the next available block on the block rotor for the
+ * specified cylinder group.
+ * Note that this routine only allocates fs_bsize blocks; these
+ * blocks may be fragmented by the routine that allocates them.
+ */
+static daddr_t
+ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref)
+{
+ struct cg *cgp;
+ daddr_t blkno;
+ int32_t bno;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+ u_int8_t *blksfree;
+
+ cgp = (struct cg *)bp->b_data;
+ blksfree = cg_blksfree(cgp, needswap);
+ if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) {
+ bpref = ufs_rw32(cgp->cg_rotor, needswap);
+ } else {
+ bpref = ffs_blknum(fs, bpref);
+ bno = dtogd(fs, bpref);
+ /*
+ * if the requested block is available, use it
+ */
+ if (ffs_isblock(fs, blksfree, ffs_fragstoblks(fs, bno)))
+ goto gotit;
+ }
+ /*
+ * Take the next available one in this cylinder group.
+ */
+ bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
+ if (bno < 0)
+ return (0);
+ cgp->cg_rotor = ufs_rw32(bno, needswap);
+gotit:
+ blkno = ffs_fragstoblks(fs, bno);
+ ffs_clrblock(fs, blksfree, (long)blkno);
+ ffs_clusteracct(fs, cgp, blkno, -1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap);
+ fs->fs_cstotal.cs_nbfree--;
+ fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--;
+ fs->fs_fmod = 1;
+ blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno;
+ return (blkno);
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * The specified block or fragment is placed back in the
+ * free map. If a fragment is deallocated, a possible
+ * block reassembly is checked.
+ */
+void
+ffs_blkfree(struct inode *ip, daddr_t bno, long size)
+{
+ struct cg *cgp;
+ struct buf *bp;
+ int32_t fragno, cgbno;
+ int i, error, cg, blk, frags, bbase;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0 ||
+ ffs_fragnum(fs, bno) + ffs_numfrags(fs, size) > fs->fs_frag) {
+ errx(EXIT_FAILURE, "%s: bad size: bno %lld bsize %d "
+ "size %ld", __func__, (long long)bno, fs->fs_bsize, size);
+ }
+ cg = dtog(fs, bno);
+ if (bno >= fs->fs_size) {
+ warnx("bad block %lld, ino %llu", (long long)bno,
+ (unsigned long long)ip->i_number);
+ return;
+ }
+ error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, 0, &bp);
+ if (error) {
+ brelse(bp, 0);
+ return;
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp, needswap)) {
+ brelse(bp, 0);
+ return;
+ }
+ cgbno = dtogd(fs, bno);
+ if (size == fs->fs_bsize) {
+ fragno = ffs_fragstoblks(fs, cgbno);
+ if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), fragno)) {
+ errx(EXIT_FAILURE, "%s: freeing free block %lld",
+ __func__, (long long)bno);
+ }
+ ffs_setblock(fs, cg_blksfree(cgp, needswap), fragno);
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ } else {
+ bbase = cgbno - ffs_fragnum(fs, cgbno);
+ /*
+ * decrement the counts associated with the old frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap);
+ /*
+ * deallocate the fragment
+ */
+ frags = ffs_numfrags(fs, size);
+ for (i = 0; i < frags; i++) {
+ if (isset(cg_blksfree(cgp, needswap), cgbno + i)) {
+ errx(EXIT_FAILURE, "%s: freeing free frag: "
+ "block %lld", __func__,
+ (long long)(cgbno + i));
+ }
+ setbit(cg_blksfree(cgp, needswap), cgbno + i);
+ }
+ ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ /*
+ * add back in counts associated with the new frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap);
+ /*
+ * if a complete block has been reassembled, account for it
+ */
+ fragno = ffs_fragstoblks(fs, bbase);
+ if (ffs_isblock(fs, cg_blksfree(cgp, needswap), fragno)) {
+ ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap);
+ fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+ fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ }
+ }
+ fs->fs_fmod = 1;
+ bdwrite(bp);
+}
+
+
+static int
+scanc(u_int size, const u_char *cp, const u_char table[], int mask)
+{
+ const u_char *end = &cp[size];
+
+ while (cp < end && (table[*cp] & mask) == 0)
+ cp++;
+ return (end - cp);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+static int32_t
+ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz)
+{
+ int32_t bno;
+ int start, len, loc, i;
+ int blk, field, subfield, pos;
+ int ostart, olen;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ /*
+ * find the fragment by searching through the free block
+ * map for an appropriate bit pattern
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY;
+ len = howmany(fs->fs_fpg, NBBY) - start;
+ ostart = start;
+ olen = len;
+ loc = scanc((u_int)len,
+ (const u_char *)&cg_blksfree(cgp, needswap)[start],
+ (const u_char *)fragtbl[fs->fs_frag],
+ (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = scanc((u_int)len,
+ (const u_char *)&cg_blksfree(cgp, needswap)[0],
+ (const u_char *)fragtbl[fs->fs_frag],
+ (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ errx(EXIT_FAILURE, "%s: map corrupted: start %d "
+ "len %d offset %d %ld", __func__, ostart, olen,
+ ufs_rw32(cgp->cg_freeoff, needswap),
+ (long)cg_blksfree(cgp, needswap) - (long)cgp);
+ /* NOTREACHED */
+ }
+ }
+ bno = (start + len - loc) * NBBY;
+ cgp->cg_frotor = ufs_rw32(bno, needswap);
+ /*
+ * found the byte in the map
+ * sift through the bits to find the selected frag
+ */
+ for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
+ blk = blkmap(fs, cg_blksfree(cgp, needswap), bno);
+ blk <<= 1;
+ field = around[allocsiz];
+ subfield = inside[allocsiz];
+ for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
+ if ((blk & field) == subfield)
+ return (bno + pos);
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+ errx(EXIT_FAILURE, "%s: block not in map: bno %lld", __func__,
+ (long long)bno);
+ return (-1);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_balloc.c b/usr.sbin/makefs/ffs/ffs_balloc.c
new file mode 100644
index 00000000000..81ef75b8dea
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_balloc.c
@@ -0,0 +1,574 @@
+/* $NetBSD: ffs_balloc.c,v 1.21 2015/03/29 05:52:59 agc Exp $ */
+/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 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. 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.
+ *
+ * @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **);
+static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **);
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ *
+ * Assume: flags == B_SYNC | B_CLRBUF
+ */
+
+int
+ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
+ return ffs_balloc_ufs2(ip, offset, bufsize, bpp);
+ else
+ return ffs_balloc_ufs1(ip, offset, bufsize, bpp);
+}
+
+static int
+ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ int32_t nb;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[UFS_NIADDR + 2];
+ daddr_t newb, pref;
+ int32_t *bap;
+ int osize, nsize, num, i, error;
+ int32_t *allocblk, allociblk[UFS_NIADDR + 1];
+ int32_t *allocib;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ lbn = ffs_lblkno(fs, offset);
+ size = ffs_blkoff(fs, offset) + bufsize;
+ if (bpp != NULL) {
+ *bpp = NULL;
+ }
+
+ assert(size <= fs->fs_bsize);
+ if (lbn < 0)
+ return (EFBIG);
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+
+ lastlbn = ffs_lblkno(fs, ip->i_ffs1_size);
+ if (lastlbn < UFS_NDADDR && lastlbn < lbn) {
+ nb = lastlbn;
+ osize = ffs_blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ }
+
+ /*
+ * The first UFS_NDADDR blocks are direct blocks
+ */
+
+ if (lbn < UFS_NDADDR) {
+ nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs1_size >= ffs_lblktosize(fs, lbn + 1)) {
+
+ /*
+ * The block is an already-allocated direct block
+ * and the file already extends past this block,
+ * thus this must be a whole block.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, fs->fs_bsize,
+ 0, bpp);
+ if (error) {
+ brelse(*bpp, 0);
+ return (error);
+ }
+ }
+ return (0);
+ }
+ if (nb != 0) {
+
+ /*
+ * Consider need to reallocate a fragment.
+ */
+
+ osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs1_size));
+ nsize = ffs_fragroundup(fs, size);
+ if (nsize <= osize) {
+
+ /*
+ * The existing block is already
+ * at least as big as we want.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, osize,
+ 0, bpp);
+ if (error) {
+ brelse(*bpp, 0);
+ return (error);
+ }
+ }
+ return 0;
+ } else {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ } else {
+
+ /*
+ * the block was not previously allocated,
+ * allocate a new block or fragment.
+ */
+
+ if (ip->i_ffs1_size < ffs_lblktosize(fs, lbn + 1))
+ nsize = ffs_fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, lbn,
+ ffs_blkpref_ufs1(ip, lbn, (int)lbn,
+ &ip->i_ffs1_db[0]),
+ nsize, &newb);
+ if (error)
+ return (error);
+ if (bpp != NULL) {
+ bp = getblk(ip->i_devvp, lbn, nsize, 0, 0);
+ bp->b_blkno = FFS_FSBTODB(fs, newb);
+ clrbuf(bp);
+ *bpp = bp;
+ }
+ }
+ ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap);
+ return (0);
+ }
+
+ /*
+ * Determine the number of levels of indirection.
+ */
+
+ pref = 0;
+ if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+ return (error);
+
+ if (num < 1) {
+ warnx("ffs_balloc: ufs_getlbns returned indirect block");
+ abort();
+ }
+
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+
+ --num;
+ nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap);
+ allocib = NULL;
+ allocblk = allociblk;
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error)
+ return error;
+ nb = newb;
+ *allocblk++ = nb;
+ bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+ bp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0)
+ return error;
+ allocib = &ip->i_ffs1_ib[indirs[0].in_off];
+ *allocib = ufs_rw32((int32_t)nb, needswap);
+ }
+
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+
+ for (i = 1;;) {
+ error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize,
+ 0, &bp);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ bap = (int32_t *)bp->b_data;
+ nb = ufs_rw32(bap[indirs[i].in_off], needswap);
+ if (i == num)
+ break;
+ i++;
+ if (nb != 0) {
+ brelse(bp, 0);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+
+ if ((error = bwrite(nbp)) != 0) {
+ brelse(bp, 0);
+ return error;
+ }
+ bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap);
+
+ bwrite(bp);
+ }
+
+ /*
+ * Get the data block, allocating if necessary.
+ */
+
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ if (bpp != NULL) {
+ nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(nbp);
+ *bpp = nbp;
+ }
+ bap[indirs[num].in_off] = ufs_rw32(nb, needswap);
+
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ bwrite(bp);
+ return (0);
+ }
+ brelse(bp, 0);
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, 0, &nbp);
+ if (error) {
+ brelse(nbp, 0);
+ return error;
+ }
+ *bpp = nbp;
+ }
+ return (0);
+}
+
+static int
+ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[UFS_NIADDR + 2];
+ daddr_t newb, pref, nb;
+ int64_t *bap;
+ int osize, nsize, num, i, error;
+ int64_t *allocblk, allociblk[UFS_NIADDR + 1];
+ int64_t *allocib;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ lbn = ffs_lblkno(fs, offset);
+ size = ffs_blkoff(fs, offset) + bufsize;
+ if (bpp != NULL) {
+ *bpp = NULL;
+ }
+
+ assert(size <= fs->fs_bsize);
+ if (lbn < 0)
+ return (EFBIG);
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+
+ lastlbn = ffs_lblkno(fs, ip->i_ffs2_size);
+ if (lastlbn < UFS_NDADDR && lastlbn < lbn) {
+ nb = lastlbn;
+ osize = ffs_blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ }
+
+ /*
+ * The first UFS_NDADDR blocks are direct blocks
+ */
+
+ if (lbn < UFS_NDADDR) {
+ nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs2_size >= ffs_lblktosize(fs, lbn + 1)) {
+
+ /*
+ * The block is an already-allocated direct block
+ * and the file already extends past this block,
+ * thus this must be a whole block.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, fs->fs_bsize,
+ 0, bpp);
+ if (error) {
+ brelse(*bpp, 0);
+ return (error);
+ }
+ }
+ return (0);
+ }
+ if (nb != 0) {
+
+ /*
+ * Consider need to reallocate a fragment.
+ */
+
+ osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs2_size));
+ nsize = ffs_fragroundup(fs, size);
+ if (nsize <= osize) {
+
+ /*
+ * The existing block is already
+ * at least as big as we want.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, osize,
+ 0, bpp);
+ if (error) {
+ brelse(*bpp, 0);
+ return (error);
+ }
+ }
+ return 0;
+ } else {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ } else {
+
+ /*
+ * the block was not previously allocated,
+ * allocate a new block or fragment.
+ */
+
+ if (ip->i_ffs2_size < ffs_lblktosize(fs, lbn + 1))
+ nsize = ffs_fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, lbn,
+ ffs_blkpref_ufs2(ip, lbn, (int)lbn,
+ &ip->i_ffs2_db[0]),
+ nsize, &newb);
+ if (error)
+ return (error);
+ if (bpp != NULL) {
+ bp = getblk(ip->i_devvp, lbn, nsize, 0, 0);
+ bp->b_blkno = FFS_FSBTODB(fs, newb);
+ clrbuf(bp);
+ *bpp = bp;
+ }
+ }
+ ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap);
+ return (0);
+ }
+
+ /*
+ * Determine the number of levels of indirection.
+ */
+
+ pref = 0;
+ if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+ return (error);
+
+ if (num < 1) {
+ warnx("ffs_balloc: ufs_getlbns returned indirect block");
+ abort();
+ }
+
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+
+ --num;
+ nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap);
+ allocib = NULL;
+ allocblk = allociblk;
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error)
+ return error;
+ nb = newb;
+ *allocblk++ = nb;
+ bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+ bp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0)
+ return error;
+ allocib = &ip->i_ffs2_ib[indirs[0].in_off];
+ *allocib = ufs_rw64(nb, needswap);
+ }
+
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+
+ for (i = 1;;) {
+ error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize,
+ 0, &bp);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ bap = (int64_t *)bp->b_data;
+ nb = ufs_rw64(bap[indirs[i].in_off], needswap);
+ if (i == num)
+ break;
+ i++;
+ if (nb != 0) {
+ brelse(bp, 0);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+
+ if ((error = bwrite(nbp)) != 0) {
+ brelse(bp, 0);
+ return error;
+ }
+ bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap);
+
+ bwrite(bp);
+ }
+
+ /*
+ * Get the data block, allocating if necessary.
+ */
+
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp, 0);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ if (bpp != NULL) {
+ nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0);
+ nbp->b_blkno = FFS_FSBTODB(fs, nb);
+ clrbuf(nbp);
+ *bpp = nbp;
+ }
+ bap[indirs[num].in_off] = ufs_rw64(nb, needswap);
+
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ bwrite(bp);
+ return (0);
+ }
+ brelse(bp, 0);
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, 0,
+ &nbp);
+ if (error) {
+ brelse(nbp, 0);
+ return error;
+ }
+ *bpp = nbp;
+ }
+ return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h
new file mode 100644
index 00000000000..53e9fe2ee12
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_extern.h
@@ -0,0 +1,76 @@
+/* $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $ */
+/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * 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. 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.
+ *
+ * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95
+ */
+
+#include "ffs/buf.h"
+
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ daddr_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+ /* ffs.c */
+void panic(const char *, ...)
+ __attribute__((__noreturn__,__format__(__printf__,1,2)));
+
+ /* ffs_alloc.c */
+int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *);
+daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *);
+daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *);
+void ffs_blkfree(struct inode *, daddr_t, long);
+void ffs_clusteracct(struct fs *, struct cg *, int32_t, int);
+
+ /* ffs_balloc.c */
+int ffs_balloc(struct inode *, off_t, int, struct buf **);
+
+ /* ffs_bswap.c */
+void ffs_sb_swap(struct fs*, struct fs *);
+void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *);
+void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *);
+void ffs_csum_swap(struct csum *, struct csum *, int);
+void ffs_cg_swap(struct cg *, struct cg *, struct fs *);
+
+ /* ffs_subr.c */
+void ffs_fragacct(struct fs *, int, int32_t[], int, int);
+int ffs_isblock(struct fs *, u_char *, int32_t);
+int ffs_isfreeblock(struct fs *, u_char *, int32_t);
+void ffs_clrblock(struct fs *, u_char *, int32_t);
+void ffs_setblock(struct fs *, u_char *, int32_t);
+
+ /* ufs_bmap.c */
+int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *);
diff --git a/usr.sbin/makefs/ffs/mkfs.c b/usr.sbin/makefs/ffs/mkfs.c
new file mode 100644
index 00000000000..f88effdf413
--- /dev/null
+++ b/usr.sbin/makefs/ffs/mkfs.c
@@ -0,0 +1,824 @@
+/* $NetBSD: mkfs.c,v 1.34 2016/06/24 19:24:11 christos Exp $ */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1980, 1989, 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+#include "ffs/newfs_extern.h"
+
+static void initcg(int, time_t, const fsinfo_t *);
+static int ilog2(int);
+
+static int count_digits(int);
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define UMASK 0755
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+union {
+ struct fs fs;
+ char pad[SBLOCKSIZE];
+} fsun;
+#define sblock fsun.fs
+struct csum *fscs;
+
+union {
+ struct cg cg;
+ char pad[FFS_MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+char *iobuf;
+int iobufsize;
+
+char writebuf[FFS_MAXBSIZE];
+
+static int Oflag; /* format as an 4.3BSD file system */
+static int64_t fssize; /* file system size */
+static int sectorsize; /* bytes/sector */
+static int fsize; /* fragment size */
+static int bsize; /* block size */
+static int maxbsize; /* maximum clustering */
+static int maxblkspercg;
+static int minfree; /* free space threshold */
+static int opt; /* optimization preference (space or time) */
+static int density; /* number of bytes per inode */
+static int maxcontig; /* max contiguous blocks to allocate */
+static int maxbpg; /* maximum blocks per file in a cyl group */
+static int bbsize; /* boot block size */
+static int sbsize; /* superblock size */
+static int avgfilesize; /* expected average file size */
+static int avgfpdir; /* expected number of files per directory */
+
+struct fs *
+ffs_mkfs(const char *fsys, const fsinfo_t *fsopts, time_t tstamp)
+{
+ int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+ int32_t cylno, i, csfrags;
+ long long sizepb;
+ void *space;
+ int size;
+ int nprintcols, printcolwidth;
+ ffs_opt_t *ffs_opts = fsopts->fs_specific;
+
+ Oflag = ffs_opts->version;
+ fssize = fsopts->size / fsopts->sectorsize;
+ sectorsize = fsopts->sectorsize;
+ fsize = ffs_opts->fsize;
+ bsize = ffs_opts->bsize;
+ maxbsize = ffs_opts->maxbsize;
+ maxblkspercg = ffs_opts->maxblkspercg;
+ minfree = ffs_opts->minfree;
+ opt = ffs_opts->optimization;
+ density = ffs_opts->density;
+ maxcontig = ffs_opts->maxcontig;
+ maxbpg = ffs_opts->maxbpg;
+ avgfilesize = ffs_opts->avgfilesize;
+ avgfpdir = ffs_opts->avgfpdir;
+ bbsize = BBSIZE;
+ sbsize = SBLOCKSIZE;
+
+ strlcpy((char *)sblock.fs_volname, ffs_opts->label,
+ sizeof(sblock.fs_volname));
+
+ if (Oflag == 0) {
+ sblock.fs_old_inodefmt = FS_42INODEFMT;
+ sblock.fs_maxsymlinklen = 0;
+ sblock.fs_old_flags = 0;
+ } else {
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_maxsymlinklen = (Oflag == 1 ? UFS1_MAXSYMLINKLEN :
+ UFS2_MAXSYMLINKLEN);
+ sblock.fs_old_flags = FS_FLAGS_UPDATED;
+ sblock.fs_flags = 0;
+ }
+ /*
+ * Validate the given file system size.
+ * Verify that its last block can actually be accessed.
+ * Convert to file system fragment sized units.
+ */
+ if (fssize <= 0) {
+ printf("preposterous size %lld\n", (long long)fssize);
+ exit(13);
+ }
+ ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts);
+
+ /*
+ * collect and verify the filesystem density info
+ */
+ sblock.fs_avgfilesize = avgfilesize;
+ sblock.fs_avgfpdir = avgfpdir;
+ if (sblock.fs_avgfilesize <= 0)
+ printf("illegal expected average file size %d\n",
+ sblock.fs_avgfilesize), exit(14);
+ if (sblock.fs_avgfpdir <= 0)
+ printf("illegal expected number of files per directory %d\n",
+ sblock.fs_avgfpdir), exit(15);
+ /*
+ * collect and verify the block and fragment sizes
+ */
+ sblock.fs_bsize = bsize;
+ sblock.fs_fsize = fsize;
+ if (!POWEROF2(sblock.fs_bsize)) {
+ printf("block size must be a power of 2, not %d\n",
+ sblock.fs_bsize);
+ exit(16);
+ }
+ if (!POWEROF2(sblock.fs_fsize)) {
+ printf("fragment size must be a power of 2, not %d\n",
+ sblock.fs_fsize);
+ exit(17);
+ }
+ if (sblock.fs_fsize < sectorsize) {
+ printf("fragment size %d is too small, minimum is %d\n",
+ sblock.fs_fsize, sectorsize);
+ exit(18);
+ }
+ if (sblock.fs_bsize < MINBSIZE) {
+ printf("block size %d is too small, minimum is %d\n",
+ sblock.fs_bsize, MINBSIZE);
+ exit(19);
+ }
+ if (sblock.fs_bsize > FFS_MAXBSIZE) {
+ printf("block size %d is too large, maximum is %d\n",
+ sblock.fs_bsize, FFS_MAXBSIZE);
+ exit(19);
+ }
+ if (sblock.fs_bsize < sblock.fs_fsize) {
+ printf("block size (%d) cannot be smaller than fragment size (%d)\n",
+ sblock.fs_bsize, sblock.fs_fsize);
+ exit(20);
+ }
+
+ if (maxbsize < bsize || !POWEROF2(maxbsize)) {
+ sblock.fs_maxbsize = sblock.fs_bsize;
+ printf("Extent size set to %d\n", sblock.fs_maxbsize);
+ } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) {
+ sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize;
+ printf("Extent size reduced to %d\n", sblock.fs_maxbsize);
+ } else {
+ sblock.fs_maxbsize = maxbsize;
+ }
+ sblock.fs_maxcontig = maxcontig;
+ if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) {
+ sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize;
+ printf("Maxcontig raised to %d\n", sblock.fs_maxbsize);
+ }
+
+ if (sblock.fs_maxcontig > 1)
+ sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG);
+
+ sblock.fs_bmask = ~(sblock.fs_bsize - 1);
+ sblock.fs_fmask = ~(sblock.fs_fsize - 1);
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
+ sblock.fs_bshift++;
+ for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
+ sblock.fs_fshift++;
+ sblock.fs_frag = ffs_numfrags(&sblock, sblock.fs_bsize);
+ for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
+ sblock.fs_fragshift++;
+ if (sblock.fs_frag > MAXFRAG) {
+ printf("fragment size %d is too small, "
+ "minimum with block size %d is %d\n",
+ sblock.fs_fsize, sblock.fs_bsize,
+ sblock.fs_bsize / MAXFRAG);
+ exit(21);
+ }
+ sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
+ sblock.fs_size = fssize = FFS_DBTOFSB(&sblock, fssize);
+
+ if (Oflag <= 1) {
+ sblock.fs_magic = FS_UFS1_MAGIC;
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+ sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) *
+ sizeof (int32_t));
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_old_cgoffset = 0;
+ sblock.fs_old_cgmask = 0xffffffff;
+ sblock.fs_old_size = sblock.fs_size;
+ sblock.fs_old_rotdelay = 0;
+ sblock.fs_old_rps = 60;
+ sblock.fs_old_nspf = sblock.fs_fsize / sectorsize;
+ sblock.fs_old_cpg = 1;
+ sblock.fs_old_interleave = 1;
+ sblock.fs_old_trackskew = 0;
+ sblock.fs_old_cpc = 0;
+ sblock.fs_old_postblformat = 1;
+ sblock.fs_old_nrpos = 1;
+ } else {
+ sblock.fs_magic = FS_UFS2_MAGIC;
+#if 0 /* XXX makefs is used for small filesystems. */
+ sblock.fs_sblockloc = SBLOCK_UFS2;
+#else
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+#endif
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
+ sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) *
+ sizeof (int64_t));
+ }
+
+ sblock.fs_sblkno =
+ roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize),
+ sblock.fs_frag);
+ sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+ roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag));
+ sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+ sblock.fs_maxfilesize = sblock.fs_bsize * UFS_NDADDR - 1;
+ for (sizepb = sblock.fs_bsize, i = 0; i < UFS_NIADDR; i++) {
+ sizepb *= FFS_NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+
+ /*
+ * Calculate the number of blocks to put into each cylinder group.
+ *
+ * This algorithm selects the number of blocks per cylinder
+ * group. The first goal is to have at least enough data blocks
+ * in each cylinder group to meet the density requirement. Once
+ * this goal is achieved we try to expand to have at least
+ * 1 cylinder group. Once this goal is achieved, we pack as
+ * many blocks into each cylinder group map as will fit.
+ *
+ * We start by calculating the smallest number of blocks that we
+ * can put into each cylinder group. If this is too big, we reduce
+ * the density until it fits.
+ */
+ origdensity = density;
+ for (;;) {
+ fragsperinode = MAX(ffs_numfrags(&sblock, density), 1);
+ minfpg = fragsperinode * FFS_INOPB(&sblock);
+ if (minfpg > sblock.fs_size)
+ minfpg = sblock.fs_size;
+ sblock.fs_ipg = FFS_INOPB(&sblock);
+ sblock.fs_fpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_fpg < minfpg)
+ sblock.fs_fpg = minfpg;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ FFS_INOPB(&sblock));
+ sblock.fs_fpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_fpg < minfpg)
+ sblock.fs_fpg = minfpg;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ FFS_INOPB(&sblock));
+ if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+ break;
+ density -= sblock.fs_fsize;
+ }
+ if (density != origdensity)
+ printf("density reduced from %d to %d\n", origdensity, density);
+
+ if (maxblkspercg <= 0 || maxblkspercg >= fssize)
+ maxblkspercg = fssize - 1;
+ /*
+ * Start packing more blocks into the cylinder group until
+ * it cannot grow any larger, the number of cylinder groups
+ * drops below 1, or we reach the size requested.
+ */
+ for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) {
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ FFS_INOPB(&sblock));
+ if (sblock.fs_size / sblock.fs_fpg < 1)
+ break;
+ if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+ continue;
+ if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize)
+ break;
+ sblock.fs_fpg -= sblock.fs_frag;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ FFS_INOPB(&sblock));
+ break;
+ }
+ /*
+ * Check to be sure that the last cylinder group has enough blocks
+ * to be viable. If it is too small, reduce the number of blocks
+ * per cylinder group which will have the effect of moving more
+ * blocks into the last cylinder group.
+ */
+ optimalfpg = sblock.fs_fpg;
+ for (;;) {
+ sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
+ lastminfpg = roundup(sblock.fs_iblkno +
+ sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag);
+ if (sblock.fs_size < lastminfpg) {
+ printf("Filesystem size %lld < minimum size of %d\n",
+ (long long)sblock.fs_size, lastminfpg);
+ exit(28);
+ }
+ if (sblock.fs_size % sblock.fs_fpg >= lastminfpg ||
+ sblock.fs_size % sblock.fs_fpg == 0)
+ break;
+ sblock.fs_fpg -= sblock.fs_frag;
+ sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+ FFS_INOPB(&sblock));
+ }
+ if (optimalfpg != sblock.fs_fpg)
+ printf("Reduced frags per cylinder group from %d to %d %s\n",
+ optimalfpg, sblock.fs_fpg, "to enlarge last cyl group");
+ sblock.fs_cgsize = ffs_fragroundup(&sblock, CGSIZE(&sblock));
+ sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / FFS_INOPF(&sblock);
+ if (Oflag <= 1) {
+ sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf;
+ sblock.fs_old_nsect = sblock.fs_old_spc;
+ sblock.fs_old_npsect = sblock.fs_old_spc;
+ sblock.fs_old_ncyl = sblock.fs_ncg;
+ }
+
+ /*
+ * fill in remaining fields of the super block
+ */
+ sblock.fs_csaddr = cgdmin(&sblock, 0);
+ sblock.fs_cssize =
+ ffs_fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+
+ /*
+ * Setup memory for temporary in-core cylgroup summaries.
+ * Cribbed from ffs_mountfs().
+ */
+ size = sblock.fs_cssize;
+ if (sblock.fs_contigsumsize > 0)
+ size += sblock.fs_ncg * sizeof(int32_t);
+ space = ecalloc(1, size);
+ sblock.fs_csp = space;
+ space = (char *)space + sblock.fs_cssize;
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *lp;
+
+ sblock.fs_maxcluster = lp = space;
+ for (i = 0; i < sblock.fs_ncg; i++)
+ *lp++ = sblock.fs_contigsumsize;
+ }
+
+ sblock.fs_sbsize = ffs_fragroundup(&sblock, sizeof(struct fs));
+ if (sblock.fs_sbsize > SBLOCKSIZE)
+ sblock.fs_sbsize = SBLOCKSIZE;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxcontig = maxcontig;
+ sblock.fs_maxbpg = maxbpg;
+ sblock.fs_optim = opt;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_pendingblocks = 0;
+ sblock.fs_pendinginodes = 0;
+ sblock.fs_cstotal.cs_ndir = 0;
+ sblock.fs_cstotal.cs_nbfree = 0;
+ sblock.fs_cstotal.cs_nifree = 0;
+ sblock.fs_cstotal.cs_nffree = 0;
+ sblock.fs_fmod = 0;
+ sblock.fs_ronly = 0;
+ sblock.fs_state = 0;
+ sblock.fs_clean = FS_ISCLEAN;
+ sblock.fs_ronly = 0;
+ sblock.fs_id[0] = tstamp;
+ sblock.fs_id[1] = random();
+ sblock.fs_fsmnt[0] = '\0';
+ csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize);
+ sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno -
+ sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno);
+ sblock.fs_cstotal.cs_nbfree =
+ ffs_fragstoblks(&sblock, sblock.fs_dsize) -
+ howmany(csfrags, sblock.fs_frag);
+ sblock.fs_cstotal.cs_nffree =
+ ffs_fragnum(&sblock, sblock.fs_size) +
+ (ffs_fragnum(&sblock, csfrags) > 0 ?
+ sblock.fs_frag - ffs_fragnum(&sblock, csfrags) : 0);
+ sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - UFS_ROOTINO;
+ sblock.fs_cstotal.cs_ndir = 0;
+ sblock.fs_dsize -= csfrags;
+ sblock.fs_time = tstamp;
+ if (Oflag <= 1) {
+ sblock.fs_old_time = tstamp;
+ sblock.fs_old_dsize = sblock.fs_dsize;
+ sblock.fs_old_csaddr = sblock.fs_csaddr;
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+ /*
+ * Dump out summary information about file system.
+ */
+#define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("%s: %.1fMB (%lld sectors) block size %d, "
+ "fragment size %d\n",
+ fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ (long long)FFS_FSBTODB(&sblock, sblock.fs_size),
+ sblock.fs_bsize, sblock.fs_fsize);
+ printf("\tusing %d cylinder groups of %.2fMB, %d blks, "
+ "%d inodes.\n",
+ sblock.fs_ncg,
+ (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
+#undef B2MBFACTOR
+ /*
+ * Now determine how wide each column will be, and calculate how
+ * many columns will fit in a 76 char line. 76 is the width of the
+ * subwindows in sysinst.
+ */
+ printcolwidth = count_digits(
+ FFS_FSBTODB(&sblock, cgsblock(&sblock, sblock.fs_ncg -1)));
+ nprintcols = 76 / (printcolwidth + 2);
+
+ /*
+ * allocate space for superblock, cylinder group map, and
+ * two sets of inode blocks.
+ */
+ if (sblock.fs_bsize < SBLOCKSIZE)
+ iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize;
+ else
+ iobufsize = 4 * sblock.fs_bsize;
+ iobuf = ecalloc(1, iobufsize);
+ /*
+ * Make a copy of the superblock into the buffer that we will be
+ * writing out in each cylinder group.
+ */
+ memcpy(writebuf, &sblock, sbsize);
+ if (fsopts->needswap)
+ ffs_sb_swap(&sblock, (struct fs*)writebuf);
+ memcpy(iobuf, writebuf, SBLOCKSIZE);
+
+ printf("super-block backups (for fsck -b #) at:");
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, tstamp, fsopts);
+ if (cylno % nprintcols == 0)
+ printf("\n");
+ printf(" %*lld,", printcolwidth,
+ (long long)FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno)));
+ fflush(stdout);
+ }
+ printf("\n");
+
+ /*
+ * Now construct the initial file system,
+ * then write out the super-block.
+ */
+ sblock.fs_time = tstamp;
+ if (Oflag <= 1) {
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+ ffs_write_superblock(&sblock, fsopts);
+ return (&sblock);
+}
+
+/*
+ * Write out the superblock and its duplicates,
+ * and the cylinder group summaries
+ */
+void
+ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts)
+{
+ int cylno, size, blks, i;
+ void *space;
+ char *wrbuf;
+
+ memcpy(writebuf, &sblock, sbsize);
+ if (fsopts->needswap)
+ ffs_sb_swap(fs, (struct fs*)writebuf);
+ ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts);
+
+ /* Write out the duplicate super blocks */
+ for (cylno = 0; cylno < fs->fs_ncg; cylno++)
+ ffs_wtfs(FFS_FSBTODB(fs, cgsblock(fs, cylno)),
+ sbsize, writebuf, fsopts);
+
+ /* Write out the cylinder group summaries */
+ size = fs->fs_cssize;
+ blks = howmany(size, fs->fs_fsize);
+ space = (void *)fs->fs_csp;
+ wrbuf = emalloc(size);
+ for (i = 0; i < blks; i+= fs->fs_frag) {
+ size = fs->fs_bsize;
+ if (i + fs->fs_frag > blks)
+ size = (blks - i) * fs->fs_fsize;
+ if (fsopts->needswap)
+ ffs_csum_swap((struct csum *)space,
+ (struct csum *)wrbuf, size);
+ else
+ memcpy(wrbuf, space, (u_int)size);
+ ffs_wtfs(FFS_FSBTODB(fs, fs->fs_csaddr + i), size, wrbuf, fsopts);
+ space = (char *)space + size;
+ }
+ free(wrbuf);
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+static void
+initcg(int cylno, time_t utime, const fsinfo_t *fsopts)
+{
+ daddr_t cbase, dmax;
+ int i, j, d, dlower, dupper, blkno;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ int start;
+
+ /*
+ * Determine block bounds for cylinder group.
+ * Allow space for super block summary information in first
+ * cylinder group.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > sblock.fs_size)
+ dmax = sblock.fs_size;
+ dlower = cgsblock(&sblock, cylno) - cbase;
+ dupper = cgdmin(&sblock, cylno) - cbase;
+ if (cylno == 0)
+ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ memset(&acg, 0, sblock.fs_cgsize);
+ acg.cg_time = utime;
+ acg.cg_magic = CG_MAGIC;
+ acg.cg_cgx = cylno;
+ acg.cg_niblk = sblock.fs_ipg;
+ acg.cg_initediblk = sblock.fs_ipg < 2 * FFS_INOPB(&sblock) ?
+ sblock.fs_ipg : 2 * FFS_INOPB(&sblock);
+ acg.cg_ndblk = dmax - cbase;
+ if (sblock.fs_contigsumsize > 0)
+ acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift;
+ start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+ if (Oflag == 2) {
+ acg.cg_iusedoff = start;
+ } else {
+ if (cylno == sblock.fs_ncg - 1)
+ acg.cg_old_ncyl = howmany(acg.cg_ndblk,
+ sblock.fs_fpg / sblock.fs_old_cpg);
+ else
+ acg.cg_old_ncyl = sblock.fs_old_cpg;
+ acg.cg_old_time = acg.cg_time;
+ acg.cg_time = 0;
+ acg.cg_old_niblk = acg.cg_niblk;
+ acg.cg_niblk = 0;
+ acg.cg_initediblk = 0;
+ acg.cg_old_btotoff = start;
+ acg.cg_old_boff = acg.cg_old_btotoff +
+ sblock.fs_old_cpg * sizeof(int32_t);
+ acg.cg_iusedoff = acg.cg_old_boff +
+ sblock.fs_old_cpg * sizeof(u_int16_t);
+ }
+ acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
+ if (sblock.fs_contigsumsize <= 0) {
+ acg.cg_nextfreeoff = acg.cg_freeoff +
+ howmany(sblock.fs_fpg, CHAR_BIT);
+ } else {
+ acg.cg_clustersumoff = acg.cg_freeoff +
+ howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t);
+ acg.cg_clustersumoff =
+ roundup(acg.cg_clustersumoff, sizeof(int32_t));
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(int32_t);
+ acg.cg_nextfreeoff = acg.cg_clusteroff +
+ howmany(ffs_fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
+ }
+ if (acg.cg_nextfreeoff > sblock.fs_cgsize) {
+ printf("Panic: cylinder group too big\n");
+ exit(37);
+ }
+ acg.cg_cs.cs_nifree += sblock.fs_ipg;
+ if (cylno == 0) {
+ size_t r;
+
+ for (r = 0; r < UFS_ROOTINO; r++) {
+ setbit(cg_inosused(&acg, 0), r);
+ acg.cg_cs.cs_nifree--;
+ }
+ }
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0, blkno = 0; d < dlower;) {
+ ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg, 0), blkno);
+ acg.cg_cs.cs_nbfree++;
+ d += sblock.fs_frag;
+ blkno++;
+ }
+ }
+ if ((i = (dupper & (sblock.fs_frag - 1))) != 0) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree(&acg, 0), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper, blkno = dupper >> sblock.fs_fragshift;
+ d + sblock.fs_frag <= acg.cg_ndblk; ) {
+ ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg, 0), blkno);
+ acg.cg_cs.cs_nbfree++;
+ d += sblock.fs_frag;
+ blkno++;
+ }
+ if (d < acg.cg_ndblk) {
+ acg.cg_frsum[acg.cg_ndblk - d]++;
+ for (; d < acg.cg_ndblk; d++) {
+ setbit(cg_blksfree(&acg, 0), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *sump = cg_clustersum(&acg, 0);
+ u_char *mapp = cg_clustersfree(&acg, 0);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < acg.cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ sblock.fs_cs(&sblock, cylno) = acg.cg_cs;
+ /*
+ * Write out the duplicate super block, the cylinder group map
+ * and two blocks worth of inodes in a single write.
+ */
+ start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE;
+ memcpy(&iobuf[start], &acg, sblock.fs_cgsize);
+ if (fsopts->needswap)
+ ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock);
+ start += sblock.fs_bsize;
+ dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+ dp2 = (struct ufs2_dinode *)(&iobuf[start]);
+ for (i = 0; i < acg.cg_initediblk; i++) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ /* No need to swap, it'll stay random */
+ dp1->di_gen = random();
+ dp1++;
+ } else {
+ dp2->di_gen = random();
+ dp2++;
+ }
+ }
+ ffs_wtfs(FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf,
+ fsopts);
+ /*
+ * For the old file system, we have to initialize all the inodes.
+ */
+ if (Oflag <= 1) {
+ for (i = 2 * sblock.fs_frag;
+ i < sblock.fs_ipg / FFS_INOPF(&sblock);
+ i += sblock.fs_frag) {
+ dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+ for (j = 0; j < FFS_INOPB(&sblock); j++) {
+ dp1->di_gen = random();
+ dp1++;
+ }
+ ffs_wtfs(FFS_FSBTODB(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, &iobuf[start], fsopts);
+ }
+ }
+}
+
+/*
+ * read a block from the file system
+ */
+void
+ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+ int n;
+ off_t offset;
+
+ offset = bno * fsopts->sectorsize + fsopts->offset;
+ if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+ err(EXIT_FAILURE, "%s: seek error for sector %lld", __func__,
+ (long long)bno);
+ n = read(fsopts->fd, bf, size);
+ if (n == -1) {
+ err(EXIT_FAILURE, "%s: read error bno %lld size %d", __func__,
+ (long long)bno, size);
+ }
+ else if (n != size)
+ errx(EXIT_FAILURE, "%s: short read error for sector %lld", __func__,
+ (long long)bno);
+}
+
+/*
+ * write a block to the file system
+ */
+void
+ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+ int n;
+ off_t offset;
+
+ offset = bno * fsopts->sectorsize + fsopts->offset;
+ if (lseek(fsopts->fd, offset, SEEK_SET) == -1)
+ err(EXIT_FAILURE, "%s: seek error for sector %lld", __func__,
+ (long long)bno);
+ n = write(fsopts->fd, bf, size);
+ if (n == -1)
+ err(EXIT_FAILURE, "%s: write error for sector %lld", __func__,
+ (long long)bno);
+ else if (n != size)
+ errx(EXIT_FAILURE, "%s: short write error for sector %lld",
+ __func__, (long long)bno);
+}
+
+
+/* Determine how many digits are needed to print a given integer */
+static int
+count_digits(int num)
+{
+ int ndig;
+
+ for(ndig = 1; num > 9; num /=10, ndig++);
+
+ return (ndig);
+}
+
+static int
+ilog2(int val)
+{
+ u_int n;
+
+ for (n = 0; n < sizeof(n) * CHAR_BIT; n++)
+ if (1 << n == val)
+ return (n);
+ errx(EXIT_FAILURE, "%s: %d is not a power of 2", __func__, val);
+}
diff --git a/usr.sbin/makefs/ffs/newfs_extern.h b/usr.sbin/makefs/ffs/newfs_extern.h
new file mode 100644
index 00000000000..7e91f88ed3b
--- /dev/null
+++ b/usr.sbin/makefs/ffs/newfs_extern.h
@@ -0,0 +1,34 @@
+/* $NetBSD: newfs_extern.h,v 1.4 2015/12/21 00:58:08 christos Exp $ */
+/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* prototypes */
+struct fs *ffs_mkfs(const char *, const fsinfo_t *, time_t);
+void ffs_write_superblock(struct fs *, const fsinfo_t *);
+void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *);
+void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *);
+
+#define FFS_MAXBSIZE 65536
diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c
new file mode 100644
index 00000000000..11b2d93cf14
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bmap.c
@@ -0,0 +1,136 @@
+/* $NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $ */
+/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */
+
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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. 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.
+ *
+ * @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ffs_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump)
+{
+ daddr_t metalbn, realbn;
+ int64_t blockcnt;
+ int lbc;
+ int i, numlevels, off;
+ u_long lognindir;
+
+ lognindir = ffs(FFS_NINDIR(ip->i_fs)) - 1;
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ assert (bn >= UFS_NDADDR);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and UFS_NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+
+ bn -= UFS_NDADDR;
+ for (lbc = 0, i = UFS_NIADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+
+ lbc += lognindir;
+ blockcnt = (int64_t)1 << lbc;
+
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ metalbn = -((realbn >= 0 ? realbn : -realbn) - bn + UFS_NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = UFS_NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= UFS_NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ lbc -= lognindir;
+ blockcnt = (int64_t)1 << lbc;
+ off = (bn >> lbc) & (FFS_NINDIR(ip->i_fs) - 1);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + (off << lbc);
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ufs_inode.h b/usr.sbin/makefs/ffs/ufs_inode.h
new file mode 100644
index 00000000000..cc2d5893f86
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_inode.h
@@ -0,0 +1,112 @@
+/* $NetBSD: ufs_inode.h,v 1.5 2013/01/30 19:19:19 christos Exp $ */
+/* From: NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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. 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.
+ *
+ * @(#)inode.h 8.9 (Berkeley) 5/14/95
+ */
+
+union dinode {
+ struct ufs1_dinode ffs1_din;
+ struct ufs2_dinode ffs2_din;
+};
+
+struct inode {
+ ino_t i_number; /* The identity of the inode. */
+ struct vnode *i_devvp; /* device vnode for block I/O */
+ struct fs *i_fs; /* File system */
+ union dinode i_din;
+ uint64_t i_size;
+};
+
+#define i_ffs1_atime i_din.ffs1_din.di_atime
+#define i_ffs1_atimensec i_din.ffs1_din.di_atimensec
+#define i_ffs1_blocks i_din.ffs1_din.di_blocks
+#define i_ffs1_ctime i_din.ffs1_din.di_ctime
+#define i_ffs1_ctimensec i_din.ffs1_din.di_ctimensec
+#define i_ffs1_db i_din.ffs1_din.di_db
+#define i_ffs1_flags i_din.ffs1_din.di_flags
+#define i_ffs1_gen i_din.ffs1_din.di_gen
+#define i_ffs11_gid i_din.ffs1_din.di_gid
+#define i_ffs1_ib i_din.ffs1_din.di_ib
+#define i_ffs1_mode i_din.ffs1_din.di_mode
+#define i_ffs1_mtime i_din.ffs1_din.di_mtime
+#define i_ffs1_mtimensec i_din.ffs1_din.di_mtimensec
+#define i_ffs1_nlink i_din.ffs1_din.di_nlink
+#define i_ffs1_rdev i_din.ffs1_din.di_rdev
+#define i_ffs1_shortlink i_din.ffs1_din.db
+#define i_ffs1_size i_din.ffs1_din.di_size
+#define i_ffs1_uid i_din.ffs1_din.di_uid
+
+#define i_ffs2_atime i_din.ffs2_din.di_atime
+#define i_ffs2_atimensec i_din.ffs2_din.di_atimensec
+#define i_ffs2_blocks i_din.ffs2_din.di_blocks
+#define i_ffs2_ctime i_din.ffs2_din.di_ctime
+#define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec
+#define i_ffs2_birthtime i_din.ffs2_din.di_birthtime
+#define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec
+#define i_ffs2_db i_din.ffs2_din.di_db
+#define i_ffs2_flags i_din.ffs2_din.di_flags
+#define i_ffs2_gen i_din.ffs2_din.di_gen
+#define i_ffs21_gid i_din.ffs2_din.di_gid
+#define i_ffs2_ib i_din.ffs2_din.di_ib
+#define i_ffs2_mode i_din.ffs2_din.di_mode
+#define i_ffs2_mtime i_din.ffs2_din.di_mtime
+#define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec
+#define i_ffs2_nlink i_din.ffs2_din.di_nlink
+#define i_ffs2_rdev i_din.ffs2_din.di_rdev
+#define i_ffs2_shortlink i_din.ffs2_din.db
+#define i_ffs2_size i_din.ffs2_din.di_size
+#define i_ffs2_uid i_din.ffs2_din.di_uid
+
+#undef DIP
+#define DIP(ip, field) \
+ (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \
+ (ip)->i_ffs1_##field : (ip)->i_ffs2_##field)
+
+#define DIP_ASSIGN(ip, field, value) \
+ do { \
+ if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \
+ (ip)->i_ffs1_##field = (value); \
+ else \
+ (ip)->i_ffs2_##field = (value); \
+ } while(0)
+
+#define DIP_ADD(ip, field, value) \
+ do { \
+ if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \
+ (ip)->i_ffs1_##field += (value); \
+ else \
+ (ip)->i_ffs2_##field += (value); \
+ } while(0)
diff --git a/usr.sbin/makefs/fs/cd9660/cd9660_rrip.h b/usr.sbin/makefs/fs/cd9660/cd9660_rrip.h
new file mode 100644
index 00000000000..b1317f1fdc0
--- /dev/null
+++ b/usr.sbin/makefs/fs/cd9660/cd9660_rrip.h
@@ -0,0 +1,143 @@
+/* $NetBSD: cd9660_rrip.h,v 1.3 2005/12/03 17:34:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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. 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.
+ *
+ * @(#)cd9660_rrip.h 8.2 (Berkeley) 12/5/94
+ */
+
+#ifndef _ISOFS_CD9660_CD9660_RRIP_H_
+#define _ISOFS_CD9660_CD9660_RRIP_H_
+
+typedef struct {
+ char type [ISODCL ( 0, 1)];
+ u_char length [ISODCL ( 2, 2)]; /* 711 */
+ u_char version [ISODCL ( 3, 3)];
+} ISO_SUSP_HEADER;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char mode [ISODCL ( 4, 11)]; /* 733 */
+ char links [ISODCL ( 12, 19)]; /* 733 */
+ char uid [ISODCL ( 20, 27)]; /* 733 */
+ char gid [ISODCL ( 28, 35)]; /* 733 */
+} ISO_RRIP_ATTR;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dev_t_high [ISODCL ( 4, 11)]; /* 733 */
+ char dev_t_low [ISODCL ( 12, 19)]; /* 733 */
+} ISO_RRIP_DEVICE;
+
+#define ISO_SUSP_CFLAG_CONTINUE 0x01
+#define ISO_SUSP_CFLAG_CURRENT 0x02
+#define ISO_SUSP_CFLAG_PARENT 0x04
+#define ISO_SUSP_CFLAG_ROOT 0x08
+#define ISO_SUSP_CFLAG_VOLROOT 0x10
+#define ISO_SUSP_CFLAG_HOST 0x20
+
+typedef struct {
+ u_char cflag [ISODCL ( 1, 1)];
+ u_char clen [ISODCL ( 2, 2)];
+ u_char name [1]; /* XXX */
+} ISO_RRIP_SLINK_COMPONENT;
+#define ISO_RRIP_SLSIZ 2
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char component [ISODCL ( 5, 5)];
+} ISO_RRIP_SLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_ALTNAME;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_CLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char dir_loc [ISODCL ( 4, 11)]; /* 733 */
+} ISO_RRIP_PLINK;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+} ISO_RRIP_RELDIR;
+
+#define ISO_SUSP_TSTAMP_FORM17 0x80
+#define ISO_SUSP_TSTAMP_FORM7 0x00
+#define ISO_SUSP_TSTAMP_CREAT 0x01
+#define ISO_SUSP_TSTAMP_MODIFY 0x02
+#define ISO_SUSP_TSTAMP_ACCESS 0x04
+#define ISO_SUSP_TSTAMP_ATTR 0x08
+#define ISO_SUSP_TSTAMP_BACKUP 0x10
+#define ISO_SUSP_TSTAMP_EXPIRE 0x20
+#define ISO_SUSP_TSTAMP_EFFECT 0x40
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char time [ISODCL ( 5, 5)];
+} ISO_RRIP_TSTAMP;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+} ISO_RRIP_IDFLAG;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char len_id [ISODCL ( 4, 4)];
+ char len_des [ISODCL ( 5, 5)];
+ char len_src [ISODCL ( 6, 6)];
+ char version [ISODCL ( 7, 7)];
+} ISO_RRIP_EXTREF;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char check [ISODCL ( 4, 5)];
+ char skip [ISODCL ( 6, 6)];
+} ISO_RRIP_OFFSET;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ char location [ISODCL ( 4, 11)];
+ char offset [ISODCL ( 12, 19)];
+ char length [ISODCL ( 20, 27)];
+} ISO_RRIP_CONT;
+
+#endif /* _ISOFS_CD9660_CD9660_RRIP_H_ */
diff --git a/usr.sbin/makefs/fs/cd9660/iso.h b/usr.sbin/makefs/fs/cd9660/iso.h
new file mode 100644
index 00000000000..720a1cee8c7
--- /dev/null
+++ b/usr.sbin/makefs/fs/cd9660/iso.h
@@ -0,0 +1,275 @@
+/* $NetBSD: iso.h,v 1.10 2011/09/27 01:01:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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. 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.
+ *
+ * @(#)iso.h 8.6 (Berkeley) 5/10/95
+ */
+
+/*
+ * Definitions describing ISO9660 file system structure, as well as
+ * the functions necessary to access fields of ISO9660 file system
+ * structures.
+ */
+
+#ifndef _ISOFS_CD9660_ISO_H_
+#define _ISOFS_CD9660_ISO_H_
+
+#define ISODCL(from, to) (to - from + 1)
+
+struct iso_volume_descriptor {
+ char type[ISODCL(1,1)]; /* 711 */
+ char id[ISODCL(2,6)];
+ char version[ISODCL(7,7)];
+ char data[ISODCL(8,2048)];
+};
+
+/* volume descriptor types */
+#define ISO_VD_PRIMARY 1
+#define ISO_VD_SUPPLEMENTARY 2
+#define ISO_VD_END 255
+
+#define ISO_STANDARD_ID "CD001"
+#define ISO_ECMA_ID "CDW01"
+
+#define ISO_MAXNAMLEN 255
+
+struct iso_primary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char unused1 [ISODCL ( 8, 8)];
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char unused3 [ISODCL ( 89, 120)];
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+#define ISO_DEFAULT_BLOCK_SIZE 2048
+
+struct iso_supplementary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char flags [ISODCL ( 8, 8)]; /* 711? */
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char escape [ISODCL ( 89, 120)];
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+
+struct iso_directory_record {
+ char length [ISODCL (1, 1)]; /* 711 */
+ char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ u_char extent [ISODCL (3, 10)]; /* 733 */
+ u_char size [ISODCL (11, 18)]; /* 733 */
+ char date [ISODCL (19, 25)]; /* 7 by 711 */
+ char flags [ISODCL (26, 26)];
+ char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ char interleave [ISODCL (28, 28)]; /* 711 */
+ char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [1]; /* XXX */
+};
+/* can't take sizeof(iso_directory_record), because of possible alignment
+ of the last entry (34 instead of 33) */
+#define ISO_DIRECTORY_RECORD_SIZE 33
+
+struct iso_extended_attributes {
+ u_char owner [ISODCL (1, 4)]; /* 723 */
+ u_char group [ISODCL (5, 8)]; /* 723 */
+ u_char perm [ISODCL (9, 10)]; /* 9.5.3 */
+ char ctime [ISODCL (11, 27)]; /* 8.4.26.1 */
+ char mtime [ISODCL (28, 44)]; /* 8.4.26.1 */
+ char xtime [ISODCL (45, 61)]; /* 8.4.26.1 */
+ char ftime [ISODCL (62, 78)]; /* 8.4.26.1 */
+ char recfmt [ISODCL (79, 79)]; /* 711 */
+ char recattr [ISODCL (80, 80)]; /* 711 */
+ u_char reclen [ISODCL (81, 84)]; /* 723 */
+ char system_id [ISODCL (85, 116)]; /* achars */
+ char system_use [ISODCL (117, 180)];
+ char version [ISODCL (181, 181)]; /* 711 */
+ char len_esc [ISODCL (182, 182)]; /* 711 */
+ char reserved [ISODCL (183, 246)];
+ u_char len_au [ISODCL (247, 250)]; /* 723 */
+};
+
+/* 7.1.1: unsigned char */
+static __inline __unused int
+isonum_711(const u_char *p)
+{
+ return *p;
+}
+
+/* 7.1.2: signed char */
+static __inline __unused int
+isonum_712(const u_char *p)
+{
+ return (signed char) *p;
+}
+
+/* 7.2.1: unsigned little-endian 16-bit value. NOT USED IN KERNEL. */
+static __inline int
+isonum_721(p)
+ u_char *p;
+{
+#if !defined(__STRICT_ALIGNMENT) && (BYTE_ORDER == LITTLE_ENDIAN)
+ return *(u_int16_t *)p;
+#else
+ return *p|((char)p[1] << 8);
+#endif
+}
+
+/* 7.2.2: unsigned big-endian 16-bit value. NOT USED IN KERNEL. */
+static __inline int
+isonum_722(p)
+ unsigned char *p;
+{
+#if !defined(__STRICT_ALIGNMENT) && (BYTE_ORDER == BIG_ENDIAN)
+ return *(u_int16_t *)p;
+#else
+ return ((char)*p << 8)|p[1];
+#endif
+}
+
+/* 7.2.3: unsigned both-endian (little, then big) 16-bit value */
+static __inline int
+isonum_723(u_char *p)
+{
+#if !defined(__STRICT_ALIGNMENT) && \
+ ((BYTE_ORDER == LITTLE_ENDIAN) || (BYTE_ORDER == BIG_ENDIAN))
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(u_int16_t *)p;
+#else
+ return *(u_int16_t *)(p + 2);
+#endif
+#else /* __STRICT_ALIGNMENT or weird byte order */
+ return *p|(p[1] << 8);
+#endif
+}
+
+/* 7.3.1: unsigned little-endian 32-bit value. NOT USED IN KERNEL. */
+static __inline int
+isonum_731(p)
+ u_char *p;
+{
+#if !defined(__STRICT_ALIGNMENT) && (BYTE_ORDER == LITTLE_ENDIAN)
+ return *(u_int32_t *)p;
+#else
+ return *p|(p[1] << 8)|(p[2] << 16)|(p[3] << 24);
+#endif
+}
+
+/* 7.3.2: unsigned big-endian 32-bit value. NOT USED IN KERNEL. */
+static __inline int
+isonum_732(p)
+ unsigned char *p;
+{
+#if !defined(__STRICT_ALIGNMENT) && (BYTE_ORDER == BIG_ENDIAN)
+ return *(u_int32_t *)p;
+#else
+ return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3];
+#endif
+}
+
+/* 7.3.3: unsigned both-endian (little, then big) 32-bit value */
+static __inline int
+isonum_733(u_char *p)
+{
+#if !defined(__STRICT_ALIGNMENT) && \
+ ((BYTE_ORDER == LITTLE_ENDIAN) || (BYTE_ORDER == BIG_ENDIAN))
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(u_int32_t *)p;
+#else
+ return *(u_int32_t *)(p + 4);
+#endif
+#else /* __STRICT_ALIGNMENT or weird byte order */
+ return *p|(p[1] << 8)|(p[2] << 16)|(p[3] << 24);
+#endif
+}
+
+/*
+ * Associated files have a leading '='.
+ */
+#define ASSOCCHAR '='
+
+#endif /* _ISOFS_CD9660_ISO_H_ */
diff --git a/usr.sbin/makefs/fs/cd9660/iso_rrip.h b/usr.sbin/makefs/fs/cd9660/iso_rrip.h
new file mode 100644
index 00000000000..0eae5c71c29
--- /dev/null
+++ b/usr.sbin/makefs/fs/cd9660/iso_rrip.h
@@ -0,0 +1,85 @@
+/* $NetBSD: iso_rrip.h,v 1.4 2005/12/03 17:34:43 christos Exp $ */
+
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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. 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.
+ *
+ * @(#)iso_rrip.h 8.2 (Berkeley) 1/23/94
+ */
+
+#ifndef _ISOFS_CD9660_ISO_RRIP_H_
+#define _ISOFS_CD9660_ISO_RRIP_H_
+
+/*
+ * Analyze function flag (similar to RR field bits)
+ */
+#define ISO_SUSP_ATTR 0x0001
+#define ISO_SUSP_DEVICE 0x0002
+#define ISO_SUSP_SLINK 0x0004
+#define ISO_SUSP_ALTNAME 0x0008
+#define ISO_SUSP_CLINK 0x0010
+#define ISO_SUSP_PLINK 0x0020
+#define ISO_SUSP_RELDIR 0x0040
+#define ISO_SUSP_TSTAMP 0x0080
+#define ISO_SUSP_IDFLAG 0x0100
+#define ISO_SUSP_EXTREF 0x0200
+#define ISO_SUSP_CONT 0x0400
+#define ISO_SUSP_OFFSET 0x0800
+#define ISO_SUSP_STOP 0x1000
+#define ISO_SUSP_UNKNOWN 0x8000
+
+typedef struct {
+ struct iso_node *inop;
+ int fields; /* interesting fields in this analysis */
+ daddr_t iso_ce_blk; /* block of continuation area */
+ off_t iso_ce_off; /* offset of continuation area */
+ int iso_ce_len; /* length of continuation area */
+ struct iso_mnt *imp; /* mount structure */
+ ino_t *inump; /* inode number pointer */
+ char *outbuf; /* name/symbolic link output area */
+ u_short *outlen; /* length of above */
+ u_short maxlen; /* maximum length of above */
+ int cont; /* continuation of above */
+} ISO_RRIP_ANALYZE;
+
+int cd9660_rrip_analyze(struct iso_directory_record *isodir,
+ struct iso_node *inop, struct iso_mnt *imp);
+int cd9660_rrip_getname(struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ ino_t *inump, struct iso_mnt *imp);
+int cd9660_rrip_getsymname(struct iso_directory_record *isodir,
+ char *outbuf, u_short *outlen,
+ struct iso_mnt *imp);
+int cd9660_rrip_offset(struct iso_directory_record *isodir,
+ struct iso_mnt *imp);
+
+#endif /* _ISOFS_CD9660_ISO_RRIP_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/bootsect.h b/usr.sbin/makefs/fs/msdosfs/bootsect.h
new file mode 100644
index 00000000000..7a6d983e5c9
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/bootsect.h
@@ -0,0 +1,96 @@
+/* $NetBSD: bootsect.h,v 1.6 2016/01/22 22:48:18 dholland Exp $ */
+
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+#ifndef _MSDOSFS_BOOTSECT_H_
+#define _MSDOSFS_BOOTSECT_H_
+
+/*
+ * Format of a boot sector. This is the first sector on a DOS floppy disk
+ * or the fist sector of a partition on a hard disk. But, it is not the
+ * first sector of a partitioned hard disk.
+ */
+struct bootsector33 {
+ uint8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */
+ int8_t bsOemName[8]; /* OEM name and version */
+ int8_t bsBPB[19]; /* BIOS parameter block */
+ int8_t bsDriveNumber; /* drive number (0x80) */
+ int8_t bsBootCode[479]; /* pad so struct is 512b */
+ uint8_t bsBootSectSig0;
+ uint8_t bsBootSectSig1;
+#define BOOTSIG0 0x55
+#define BOOTSIG1 0xaa
+};
+
+struct extboot {
+ int8_t exDriveNumber; /* drive number (0x80) */
+ int8_t exReserved1; /* reserved */
+ int8_t exBootSignature; /* ext. boot signature (0x29) */
+#define EXBOOTSIG 0x29
+ int8_t exVolumeID[4]; /* volume ID number */
+ int8_t exVolumeLabel[11]; /* volume label */
+ int8_t exFileSysType[8]; /* fs type (FAT12 or FAT16) */
+};
+
+struct bootsector50 {
+ uint8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */
+ int8_t bsOemName[8]; /* OEM name and version */
+ int8_t bsBPB[25]; /* BIOS parameter block */
+ int8_t bsExt[26]; /* Bootsector Extension */
+ int8_t bsBootCode[448]; /* pad so structure is 512b */
+ uint8_t bsBootSectSig0;
+ uint8_t bsBootSectSig1;
+#define BOOTSIG0 0x55
+#define BOOTSIG1 0xaa
+};
+
+struct bootsector710 {
+ uint8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */
+ int8_t bsOEMName[8]; /* OEM name and version */
+ int8_t bsBPB[53]; /* BIOS parameter block */
+ int8_t bsExt[26]; /* Bootsector Extension */
+ int8_t bsBootCode[420]; /* pad so structure is 512b */
+ uint8_t bsBootSectSig0;
+ uint8_t bsBootSectSig1;
+#define BOOTSIG0 0x55
+#define BOOTSIG1 0xaa
+};
+
+union bootsector {
+ struct bootsector33 bs33;
+ struct bootsector50 bs50;
+ struct bootsector710 bs710;
+};
+
+#if 0
+/*
+ * Shorthand for fields in the bpb.
+ */
+#define bsBytesPerSec bsBPB.bpbBytesPerSec
+#define bsSectPerClust bsBPB.bpbSectPerClust
+#define bsResSectors bsBPB.bpbResSectors
+#define bsFATS bsBPB.bpbFATS
+#define bsRootDirEnts bsBPB.bpbRootDirEnts
+#define bsSectors bsBPB.bpbSectors
+#define bsMedia bsBPB.bpbMedia
+#define bsFATsecs bsBPB.bpbFATsecs
+#define bsSectPerTrack bsBPB.bpbSectPerTrack
+#define bsHeads bsBPB.bpbHeads
+#define bsHiddenSecs bsBPB.bpbHiddenSecs
+#define bsHugeSectors bsBPB.bpbHugeSectors
+#endif
+
+#endif /* _MSDOSFS_BOOTSECT_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/bpb.h b/usr.sbin/makefs/fs/msdosfs/bpb.h
new file mode 100644
index 00000000000..f45749f5fdc
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/bpb.h
@@ -0,0 +1,190 @@
+/* $NetBSD: bpb.h,v 1.8 2016/01/22 22:53:36 dholland Exp $ */
+
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#ifndef _MSDOSFS_BPB_H_
+#define _MSDOSFS_BPB_H_
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct bpb33 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint16_t bpbHiddenSecs; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct bpb50 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint32_t bpbHiddenSecs; /* # of hidden sectors */
+ uint32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */
+};
+
+/*
+ * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50.
+ */
+struct bpb710 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint32_t bpbHiddenSecs; /* # of hidden sectors */
+ uint32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */
+ uint32_t bpbBigFATsecs; /* like bpbFATsecs for FAT32 */
+ uint16_t bpbExtFlags; /* extended flags: */
+#define FATNUM 0xf /* mask for numbering active FAT */
+#define FATMIRROR 0x80 /* FAT is mirrored (like it always was) */
+ uint16_t bpbFSVers; /* filesystem version */
+#define FSVERS 0 /* currently only 0 is understood */
+ uint32_t bpbRootClust; /* start cluster for root directory */
+ uint16_t bpbFSInfo; /* filesystem info structure sector */
+ uint16_t bpbBackup; /* backup boot sector */
+ uint8_t bpbReserved[12]; /* Reserved for future expansion */
+};
+
+/*
+ * The following structures represent how the bpb's look on disk. shorts
+ * and longs are just character arrays of the appropriate length. This is
+ * because the compiler forces shorts and longs to align on word or
+ * halfword boundaries.
+ *
+ * XXX The little-endian code here assumes that the processor can access
+ * 16-bit and 32-bit quantities on byte boundaries. If this is not true,
+ * use the macros for the big-endian case.
+ */
+#include <sys/endian.h>
+#if (BYTE_ORDER == LITTLE_ENDIAN) && !defined(__STRICT_ALIGNMENT)
+#define getushort(x) *((u_int16_t *)(x))
+#define getulong(x) *((u_int32_t *)(x))
+#define putushort(p, v) (*((u_int16_t *)(p)) = (v))
+#define putulong(p, v) (*((u_int32_t *)(p)) = (v))
+#else
+#define getushort(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8))
+#define getulong(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8) \
+ + (((u_int8_t *)(x))[2] << 16) \
+ + (((u_int8_t *)(x))[3] << 24))
+#define putushort(p, v) (((u_int8_t *)(p))[0] = (v), \
+ ((u_int8_t *)(p))[1] = (v) >> 8)
+#define putulong(p, v) (((u_int8_t *)(p))[0] = (v), \
+ ((u_int8_t *)(p))[1] = (v) >> 8, \
+ ((u_int8_t *)(p))[2] = (v) >> 16,\
+ ((u_int8_t *)(p))[3] = (v) >> 24)
+#endif
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct byte_bpb33 {
+ int8_t bpbBytesPerSec[2]; /* bytes per sector */
+ int8_t bpbSecPerClust; /* sectors per cluster */
+ int8_t bpbResSectors[2]; /* number of reserved sectors */
+ int8_t bpbFATs; /* number of FATs */
+ int8_t bpbRootDirEnts[2]; /* number of root directory entries */
+ int8_t bpbSectors[2]; /* total number of sectors */
+ int8_t bpbMedia; /* media descriptor */
+ int8_t bpbFATsecs[2]; /* number of sectors per FAT */
+ int8_t bpbSecPerTrack[2]; /* sectors per track */
+ int8_t bpbHeads[2]; /* number of heads */
+ int8_t bpbHiddenSecs[2]; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct byte_bpb50 {
+ int8_t bpbBytesPerSec[2]; /* bytes per sector */
+ int8_t bpbSecPerClust; /* sectors per cluster */
+ int8_t bpbResSectors[2]; /* number of reserved sectors */
+ int8_t bpbFATs; /* number of FATs */
+ int8_t bpbRootDirEnts[2]; /* number of root directory entries */
+ int8_t bpbSectors[2]; /* total number of sectors */
+ int8_t bpbMedia; /* media descriptor */
+ int8_t bpbFATsecs[2]; /* number of sectors per FAT */
+ int8_t bpbSecPerTrack[2]; /* sectors per track */
+ int8_t bpbHeads[2]; /* number of heads */
+ int8_t bpbHiddenSecs[4]; /* number of hidden sectors */
+ int8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */
+};
+
+/*
+ * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50.
+ */
+struct byte_bpb710 {
+ uint8_t bpbBytesPerSec[2]; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint8_t bpbResSectors[2]; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint8_t bpbRootDirEnts[2]; /* number of root directory entries */
+ uint8_t bpbSectors[2]; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint8_t bpbFATsecs[2]; /* number of sectors per FAT */
+ uint8_t bpbSecPerTrack[2]; /* sectors per track */
+ uint8_t bpbHeads[2]; /* number of heads */
+ uint8_t bpbHiddenSecs[4]; /* # of hidden sectors */
+ uint8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */
+ uint8_t bpbBigFATsecs[4]; /* like bpbFATsecs for FAT32 */
+ uint8_t bpbExtFlags[2]; /* extended flags: */
+ uint8_t bpbFSVers[2]; /* filesystem version */
+ uint8_t bpbRootClust[4]; /* start cluster for root directory */
+ uint8_t bpbFSInfo[2]; /* filesystem info structure sector */
+ uint8_t bpbBackup[2]; /* backup boot sector */
+ uint8_t bpbReserved[12]; /* Reserved for future expansion */
+};
+
+/*
+ * FAT32 FSInfo block.
+ */
+struct fsinfo {
+ uint8_t fsisig1[4];
+ uint8_t fsifill1[480];
+ uint8_t fsisig2[4];
+ uint8_t fsinfree[4];
+ uint8_t fsinxtfree[4];
+ uint8_t fsifill2[12];
+ uint8_t fsisig3[4];
+ uint8_t fsifill3[508];
+ uint8_t fsisig4[4];
+};
+#endif /* _MSDOSFS_BPB_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/clock_subr.c b/usr.sbin/makefs/fs/msdosfs/clock_subr.c
new file mode 100644
index 00000000000..c145b9d86d0
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/clock_subr.c
@@ -0,0 +1,193 @@
+/* $NetBSD: clock_subr.c,v 1.27 2016/08/15 15:51:39 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1982, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * 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. 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.
+ *
+ * from: Utah $Hdr: clock.c 1.18 91/01/21$
+ *
+ * @(#)clock.c 8.2 (Berkeley) 1/12/94
+ */
+
+/*
+ * Generic routines to convert between a POSIX date
+ * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
+ * Derived from arch/hp300/hp300/clock.c
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "../sys/clock.h"
+#include <fs/msdosfs/clock_subr.h> /* XXX */
+
+#define FEBRUARY 2
+
+/* for easier alignment:
+ * time from the epoch to 2001 (there were 8 leap years): */
+#define DAYSTO2001 (365*31+8)
+
+/* 4 year intervals include 1 leap year */
+#define DAYS4YEARS (365*4+1)
+
+/* 100 year intervals include 24 leap years */
+#define DAYS100YEARS (365*100+24)
+
+/* 400 year intervals include 97 leap years */
+#define DAYS400YEARS (365*400+97)
+
+time_t
+clock_ymdhms_to_secs(struct clock_ymdhms *dt)
+{
+ uint64_t secs, i, year, days;
+
+ year = dt->dt_year;
+
+ /*
+ * Compute days since start of time
+ * First from years, then from months.
+ */
+ if (year < POSIX_BASE_YEAR)
+ return -1;
+ days = 0;
+ if (is_leap_year(year) && dt->dt_mon > FEBRUARY)
+ days++;
+
+ if (year < 2001) {
+ /* simple way for early years */
+ for (i = POSIX_BASE_YEAR; i < year; i++)
+ days += days_per_year(i);
+ } else {
+ /* years are properly aligned */
+ days += DAYSTO2001;
+ year -= 2001;
+
+ i = year / 400;
+ days += i * DAYS400YEARS;
+ year -= i * 400;
+
+ i = year / 100;
+ days += i * DAYS100YEARS;
+ year -= i * 100;
+
+ i = year / 4;
+ days += i * DAYS4YEARS;
+ year -= i * 4;
+
+ for (i = dt->dt_year-year; i < dt->dt_year; i++)
+ days += days_per_year(i);
+ }
+
+
+ /* Months */
+ for (i = 1; i < dt->dt_mon; i++)
+ days += days_in_month(i);
+ days += (dt->dt_day - 1);
+
+ /* Add hours, minutes, seconds. */
+ secs = (((uint64_t)days
+ * 24 + dt->dt_hour)
+ * 60 + dt->dt_min)
+ * 60 + dt->dt_sec;
+
+ if ((time_t)secs < 0 || secs > INT64_MAX)
+ return -1;
+ return secs;
+}
+
+int
+clock_secs_to_ymdhms(time_t secs, struct clock_ymdhms *dt)
+{
+ int leap;
+ uint64_t i;
+ time_t days;
+ time_t rsec; /* remainder seconds */
+
+ if (secs < 0)
+ return EINVAL;
+
+ days = secs / SECS_PER_DAY;
+ rsec = secs % SECS_PER_DAY;
+
+ /* Day of week (Note: 1/1/1970 was a Thursday) */
+ dt->dt_wday = (days + 4) % 7;
+
+ if (days >= DAYSTO2001) {
+ days -= DAYSTO2001;
+ dt->dt_year = 2001;
+
+ i = days / DAYS400YEARS;
+ days -= i*DAYS400YEARS;
+ dt->dt_year += i*400;
+
+ i = days / DAYS100YEARS;
+ days -= i*DAYS100YEARS;
+ dt->dt_year += i*100;
+
+ i = days / DAYS4YEARS;
+ days -= i*DAYS4YEARS;
+ dt->dt_year += i*4;
+
+ for (i = dt->dt_year; days >= days_per_year(i); i++)
+ days -= days_per_year(i);
+ dt->dt_year = i;
+ } else {
+ /* Subtract out whole years, counting them in i. */
+ for (i = POSIX_BASE_YEAR; days >= days_per_year(i); i++)
+ days -= days_per_year(i);
+ dt->dt_year = i;
+ }
+
+ /* Subtract out whole months, counting them in i. */
+ for (leap = 0, i = 1; days >= days_in_month(i)+leap; i++) {
+ days -= days_in_month(i)+leap;
+ if (i == 1 && is_leap_year(dt->dt_year))
+ leap = 1;
+ else
+ leap = 0;
+ }
+ dt->dt_mon = i;
+
+ /* Days are what is left over (+1) from all that. */
+ dt->dt_day = days + 1;
+
+ /* Hours, minutes, seconds are easy */
+ dt->dt_hour = rsec / SECS_PER_HOUR;
+ rsec = rsec % SECS_PER_HOUR;
+ dt->dt_min = rsec / SECS_PER_MINUTE;
+ rsec = rsec % SECS_PER_MINUTE;
+ dt->dt_sec = rsec;
+
+ return 0;
+}
diff --git a/usr.sbin/makefs/fs/msdosfs/clock_subr.h b/usr.sbin/makefs/fs/msdosfs/clock_subr.h
new file mode 100644
index 00000000000..861097aa0c4
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/clock_subr.h
@@ -0,0 +1,111 @@
+/* $NetBSD: clock_subr.h,v 1.25 2014/11/20 16:26:34 christos Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _DEV_CLOCK_SUBR_H_
+#define _DEV_CLOCK_SUBR_H_
+
+#include <sys/clock.h>
+
+/*
+ * "POSIX time" to/from "YY/MM/DD/hh/mm/ss"
+ */
+struct clock_ymdhms {
+ uint64_t dt_year;
+ uint8_t dt_mon;
+ uint8_t dt_day;
+ uint8_t dt_wday; /* Day of week */
+ uint8_t dt_hour;
+ uint8_t dt_min;
+ uint8_t dt_sec;
+};
+
+time_t clock_ymdhms_to_secs(struct clock_ymdhms *);
+int clock_secs_to_ymdhms(time_t, struct clock_ymdhms *);
+
+/*
+ * BCD to binary and binary to BCD.
+ */
+static inline unsigned int
+bcdtobin(unsigned int bcd)
+{
+ return ((bcd >> 4) & 0x0f) * 10 + (bcd & 0x0f);
+}
+
+static inline unsigned int
+bintobcd(unsigned int bin)
+{
+ return (((bin / 10) << 4) & 0xf0) | (bin % 10);
+}
+
+/*
+ * Interface to time-of-day clock devices.
+ *
+ * todr_gettime: convert time-of-day clock into a `struct timeval'
+ * todr_settime: set time-of-day clock from a `struct timeval'
+ *
+ * (this is probably not so useful:)
+ * todr_setwen: provide a machine-dependent TOD clock write-enable callback
+ * function which takes one boolean argument:
+ * 1 to enable writes; 0 to disable writes.
+ */
+struct timeval;
+struct todr_chip_handle {
+ void *cookie; /* Device specific data */
+ void *bus_cookie; /* Bus specific data */
+ time_t base_time; /* Base time (e.g. rootfs time) */
+
+ int (*todr_gettime)(struct todr_chip_handle *, struct timeval *);
+ int (*todr_settime)(struct todr_chip_handle *, struct timeval *);
+ int (*todr_gettime_ymdhms)(struct todr_chip_handle *,
+ struct clock_ymdhms *);
+ int (*todr_settime_ymdhms)(struct todr_chip_handle *,
+ struct clock_ymdhms *);
+ int (*todr_setwen)(struct todr_chip_handle *, int);
+
+};
+typedef struct todr_chip_handle *todr_chip_handle_t;
+
+#define todr_wenable(ct, v) if ((ct)->todr_setwen) \
+ ((*(ct)->todr_setwen)(ct, v))
+
+/*
+ * Probably these should evolve into internal routines in kern_todr.c.
+ */
+extern int todr_gettime(todr_chip_handle_t, struct timeval *);
+extern int todr_settime(todr_chip_handle_t, struct timeval *);
+
+/*
+ * Machine-dependent function that machine-independent RTC drivers can
+ * use to register their todr_chip_handle_t with inittodr()/resettodr().
+ */
+void todr_attach(todr_chip_handle_t);
+
+#endif /* _DEV_CLOCK_SUBR_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/denode.h b/usr.sbin/makefs/fs/msdosfs/denode.h
new file mode 100644
index 00000000000..324cf074f79
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/denode.h
@@ -0,0 +1,326 @@
+/* $NetBSD: denode.h,v 1.24 2014/07/08 09:21:52 hannken Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+#ifndef _MSDOSFS_DENODE_H_
+#define _MSDOSFS_DENODE_H_
+
+struct genfs_node {
+};
+struct vnode;
+struct msdosfsmount;
+struct buf;
+
+/*
+ * This is the pc filesystem specific portion of the vnode structure.
+ *
+ * To describe a file uniquely the de_dirclust, de_diroffset, and
+ * de_StartCluster fields are used.
+ *
+ * de_dirclust contains the cluster number of the directory cluster
+ * containing the entry for a file or directory.
+ * de_diroffset is the index into the cluster for the entry describing
+ * a file or directory.
+ * de_StartCluster is the number of the first cluster of the file or directory.
+ *
+ * Now to describe the quirks of the pc filesystem.
+ * - Clusters 0 and 1 are reserved.
+ * - The first allocatable cluster is 2.
+ * - The root directory is of fixed size and all blocks that make it up
+ * are contiguous.
+ * - Cluster 0 refers to the root directory when it is found in the
+ * startcluster field of a directory entry that points to another directory.
+ * - Cluster 0 implies a 0 length file when found in the start cluster field
+ * of a directory entry that points to a file.
+ * - You can't use the cluster number 0 to derive the address of the root
+ * directory.
+ * - Multiple directory entries can point to a directory. The entry in the
+ * parent directory points to a child directory. Any directories in the
+ * child directory contain a ".." entry that points back to the parent.
+ * The child directory itself contains a "." entry that points to itself.
+ * - The root directory does not contain a "." or ".." entry.
+ * - Directory entries for directories are never changed once they are created
+ * (except when removed). The size stays 0, and the last modification time
+ * is never changed. This is because so many directory entries can point to
+ * the physical clusters that make up a directory. It would lead to an
+ * update nightmare.
+ * - The length field in a directory entry pointing to a directory contains 0
+ * (always). The only way to find the end of a directory is to follow the
+ * cluster chain until the "last cluster" marker is found.
+ *
+ * My extensions to make this house of cards work. These apply only to the in
+ * memory copy of the directory entry.
+ * - A reference count for each denode will be kept since dos doesn't keep such
+ * things.
+ */
+
+/*
+ * Internal pseudo-offset for (nonexistent) directory entry for the root
+ * dir in the root dir
+ */
+#define MSDOSFSROOT_OFS 0x1fffffff
+
+/*
+ * The FAT cache structure. fc_fsrcn is the filesystem relative cluster
+ * number that corresponds to the file relative cluster number in this
+ * structure (fc_frcn).
+ */
+struct fatcache {
+ u_long fc_frcn; /* file relative cluster number */
+ u_long fc_fsrcn; /* filesystem relative cluster number */
+};
+
+/*
+ * The FAT entry cache as it stands helps make extending files a "quick"
+ * operation by avoiding having to scan the FAT to discover the last
+ * cluster of the file. The cache also helps sequential reads by
+ * remembering the last cluster read from the file. This also prevents us
+ * from having to rescan the FAT to find the next cluster to read. This
+ * cache is probably pretty worthless if a file is opened by multiple
+ * processes.
+ */
+#define FC_SIZE 3 /* number of entries in the cache */
+#define FC_LASTMAP 0 /* entry the last call to pcbmap() resolved
+ * to */
+#define FC_LASTFC 1 /* entry for the last cluster in the file */
+#define FC_NEXTTOLASTFC 2 /* entry for a close to the last cluster in the file */
+
+#define FCE_EMPTY 0xffffffff /* doesn't represent an actual cluster # */
+
+/*
+ * Set a slot in the FAT cache.
+ */
+#define fc_setcache(dep, slot, frcn, fsrcn) \
+ (dep)->de_fc[slot].fc_frcn = frcn; \
+ (dep)->de_fc[slot].fc_fsrcn = fsrcn;
+
+#define fc_last_to_nexttolast(dep) \
+ do { \
+ (dep)->de_fc[FC_NEXTTOLASTFC].fc_frcn = (dep)->de_fc[FC_LASTFC].fc_frcn; \
+ (dep)->de_fc[FC_NEXTTOLASTFC].fc_fsrcn = (dep)->de_fc[FC_LASTFC].fc_fsrcn; \
+ } while (0)
+
+
+/*
+ * This is the in memory variant of a dos directory entry. It is usually
+ * contained within a vnode.
+ */
+struct denode_key {
+ u_long dk_dirclust; /* cluster of the directory file containing this entry */
+ u_long dk_diroffset; /* offset of this entry in the directory cluster */
+ void *dk_dirgen; /* non zero and unique for unlinked nodes */
+};
+struct denode {
+ struct genfs_node de_gnode;
+ struct vnode *de_vnode; /* addr of vnode we are part of */
+ struct vnode *de_devvp; /* vnode of blk dev we live on */
+ u_long de_flag; /* flag bits */
+ dev_t de_dev; /* device where direntry lives */
+ struct denode_key de_key;
+#define de_dirclust de_key.dk_dirclust
+#define de_diroffset de_key.dk_diroffset
+#define de_dirgen de_key.dk_dirgen
+ u_long de_fndoffset; /* offset of found dir entry */
+ int de_fndcnt; /* number of slots before de_fndoffset */
+ long de_refcnt; /* reference count */
+ struct msdosfsmount *de_pmp; /* addr of our mount struct */
+ struct lockf *de_lockf; /* byte level lock list */
+ u_char de_Name[12]; /* name, from DOS directory entry */
+ u_char de_Attributes; /* attributes, from directory entry */
+ u_char de_CHun; /* Hundredth of second of CTime*/
+ u_short de_CTime; /* creation time */
+ u_short de_CDate; /* creation date */
+ u_short de_ADate; /* access date */
+ u_short de_MTime; /* modification time */
+ u_short de_MDate; /* modification date */
+ u_long de_StartCluster; /* starting cluster of file */
+ u_long de_FileSize; /* size of file in bytes */
+ struct fatcache de_fc[FC_SIZE]; /* FAT cache */
+};
+
+/*
+ * Values for the de_flag field of the denode.
+ */
+#define DE_UPDATE 0x0001 /* Modification time update request. */
+#define DE_CREATE 0x0002 /* Creation time update */
+#define DE_ACCESS 0x0004 /* Access time update */
+#define DE_MODIFIED 0x0008 /* Denode has been modified. */
+#define DE_RENAME 0x0010 /* Denode is in the process of being renamed */
+
+/*
+ * Maximum filename length in Win95
+ * Note: Must be < sizeof(dirent.d_name)
+ */
+#define WIN_MAXLEN 255
+
+/* Maximum size of a file on a FAT filesystem */
+#define MSDOSFS_FILESIZE_MAX 0xFFFFFFFFLL
+
+/*
+ * Transfer directory entries between internal and external form.
+ * dep is a struct denode * (internal form),
+ * dp is a struct direntry * (external form).
+ */
+#define DE_INTERNALIZE32(dep, dp) \
+ ((dep)->de_StartCluster |= getushort((dp)->deHighClust) << 16)
+#define DE_INTERNALIZE(dep, dp) \
+ (memcpy((dep)->de_Name, (dp)->deName, 11), \
+ (dep)->de_Attributes = (dp)->deAttributes, \
+ (dep)->de_CHun = (dp)->deCHundredth, \
+ (dep)->de_CTime = getushort((dp)->deCTime), \
+ (dep)->de_CDate = getushort((dp)->deCDate), \
+ (dep)->de_ADate = getushort((dp)->deADate), \
+ (dep)->de_MTime = getushort((dp)->deMTime), \
+ (dep)->de_MDate = getushort((dp)->deMDate), \
+ (dep)->de_StartCluster = getushort((dp)->deStartCluster), \
+ (dep)->de_FileSize = getulong((dp)->deFileSize), \
+ (FAT32((dep)->de_pmp) ? DE_INTERNALIZE32((dep), (dp)) : 0))
+
+#define DE_EXTERNALIZE32(dp, dep) \
+ putushort((dp)->deHighClust, (dep)->de_StartCluster >> 16)
+#define DE_EXTERNALIZE16(dp, dep) \
+ putushort((dp)->deHighClust, 0)
+#define DE_EXTERNALIZE(dp, dep) \
+ (memcpy((dp)->deName, (dep)->de_Name, 11), \
+ (dp)->deAttributes = (dep)->de_Attributes, \
+ (dp)->deCHundredth = (dep)->de_CHun, \
+ putushort((dp)->deCTime, (dep)->de_CTime), \
+ putushort((dp)->deCDate, (dep)->de_CDate), \
+ putushort((dp)->deADate, (dep)->de_ADate), \
+ putushort((dp)->deMTime, (dep)->de_MTime), \
+ putushort((dp)->deMDate, (dep)->de_MDate), \
+ putushort((dp)->deStartCluster, (dep)->de_StartCluster), \
+ putulong((dp)->deFileSize, \
+ ((dep)->de_Attributes & ATTR_DIRECTORY) ? 0 : (dep)->de_FileSize), \
+ (FAT32((dep)->de_pmp) ? DE_EXTERNALIZE32((dp), (dep)) : DE_EXTERNALIZE16((dp), (dep))))
+
+#define de_forw de_chain[0]
+#define de_back de_chain[1]
+
+
+#define VTODE(vp) ((struct denode *)(vp)->v_data)
+#define DETOV(de) ((de)->de_vnode)
+
+#define DETIMES(dep, acc, mod, cre, gmtoff) \
+ while ((dep)->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS)) \
+ msdosfs_detimes(dep, acc, mod, cre, gmtoff)
+
+/*
+ * This overlays the fid structure (see fstypes.h)
+ */
+struct defid {
+ u_int16_t defid_len; /* length of structure */
+ u_int16_t defid_pad; /* force 4-byte alignment */
+
+ u_int32_t defid_dirclust; /* cluster this dir entry came from */
+ u_int32_t defid_dirofs; /* offset of entry within the cluster */
+ u_int32_t defid_gen; /* generation number */
+};
+
+/*
+ * Prototypes for MSDOSFS vnode operations
+ */
+int msdosfs_lookup (void *);
+int msdosfs_create (void *);
+int msdosfs_close (void *);
+int msdosfs_access (void *);
+int msdosfs_getattr (void *);
+int msdosfs_setattr (void *);
+int msdosfs_read (void *);
+int msdosfs_write (void *);
+#define msdosfs_lease_check genfs_lease_check
+#define msdosfs_ioctl genfs_enoioctl
+#define msdosfs_poll genfs_poll
+#define msdosfs_revoke genfs_revoke
+#define msdosfs_mmap genfs_mmap
+int msdosfs_fsync (void *);
+#define msdosfs_seek genfs_seek
+int msdosfs_remove (void *);
+int msdosfs_rename (void *);
+int msdosfs_mkdir (void *);
+int msdosfs_rmdir (void *);
+int msdosfs_readdir (void *);
+#define msdosfs_abortop genfs_abortop
+int msdosfs_inactive (void *);
+int msdosfs_reclaim (void *);
+int msdosfs_bmap (void *);
+int msdosfs_strategy (void *);
+int msdosfs_print (void *);
+int msdosfs_advlock (void *);
+int msdosfs_pathconf (void *);
+
+/*
+ * Internal service routine prototypes.
+ */
+struct componentname;
+struct direntry;
+struct kauth_cred;
+int msdosfs_update(struct vnode *, const struct timespec *,
+ const struct timespec *, int);
+int createde(struct denode *, struct denode *,
+ struct denode **, struct componentname *);
+int deextend(struct denode *, u_long, struct kauth_cred *);
+int deget(struct msdosfsmount *, u_long, u_long, struct denode **);
+int detrunc(struct denode *, u_long, int, struct kauth_cred *);
+int deupdat(struct denode *, int);
+int doscheckpath(struct denode *, struct denode *);
+int dosdirempty(struct denode *);
+int readde(struct denode *, struct buf **, struct direntry **);
+int readep(struct msdosfsmount *, u_long, u_long,
+ struct buf **, struct direntry **);
+int removede(struct denode *, struct denode *);
+int uniqdosname(struct denode *, struct componentname *, u_char *);
+int findwin95(struct denode *);
+int msdosfs_gop_alloc(struct vnode *, off_t, off_t, int, struct kauth_cred *);
+void msdosfs_gop_markupdate(struct vnode *, int);
+void msdosfs_detimes(struct denode *, const struct timespec *,
+ const struct timespec *, const struct timespec *, int);
+int msdosfs_fh_enter(struct msdosfsmount *, uint32_t, uint32_t, uint32_t *);
+int msdosfs_fh_remove(struct msdosfsmount *, uint32_t, uint32_t);
+int msdosfs_fh_lookup(struct msdosfsmount *, uint32_t, uint32_t, uint32_t *);
+void msdosfs_fh_destroy(struct msdosfsmount *);
+#endif /* _MSDOSFS_DENODE_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/direntry.h b/usr.sbin/makefs/fs/msdosfs/direntry.h
new file mode 100644
index 00000000000..858a09f7504
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/direntry.h
@@ -0,0 +1,145 @@
+/* $NetBSD: direntry.h,v 1.11 2016/02/01 02:59:33 christos Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+#ifndef _MSDOSFS_DIRENTRY_H_
+#define _MSDOSFS_DIRENTRY_H_
+
+/*
+ * Structure of a dos directory entry.
+ */
+struct direntry {
+ uint8_t deName[8]; /* filename, blank filled */
+#define SLOT_EMPTY 0x00 /* slot has never been used */
+#define SLOT_E5 0x05 /* the real value is 0xe5 */
+#define SLOT_DELETED 0xe5 /* file in this slot deleted */
+ uint8_t deExtension[3]; /* extension, blank filled */
+ uint8_t deAttributes; /* file attributes */
+#define ATTR_NORMAL 0x00 /* normal file */
+#define ATTR_READONLY 0x01 /* file is readonly */
+#define ATTR_HIDDEN 0x02 /* file is hidden */
+#define ATTR_SYSTEM 0x04 /* file is a system file */
+#define ATTR_VOLUME 0x08 /* entry is a volume label */
+#define ATTR_DIRECTORY 0x10 /* entry is a directory name */
+#define ATTR_ARCHIVE 0x20 /* file is new or modified */
+ uint8_t deReserved; /* reserved */
+ uint8_t deCHundredth; /* hundredth of seconds in CTime */
+ uint8_t deCTime[2]; /* create time */
+ uint8_t deCDate[2]; /* create date */
+ uint8_t deADate[2]; /* access date */
+ uint8_t deHighClust[2]; /* high bytes of cluster number */
+ uint8_t deMTime[2]; /* last update time */
+ uint8_t deMDate[2]; /* last update date */
+ uint8_t deStartCluster[2]; /* starting cluster of file */
+ uint8_t deFileSize[4]; /* size of file in bytes */
+};
+
+static __inline uint8_t
+msdos_dirchar(const struct direntry *de, size_t i) {
+ return i < sizeof(de->deName) ? de->deName[i] :
+ de->deExtension[i - sizeof(de->deName)];
+}
+
+/*
+ * Structure of a Win95 long name directory entry
+ */
+struct winentry {
+ uint8_t weCnt;
+#define WIN_LAST 0x40
+#define WIN_CNT 0x3f
+ uint8_t wePart1[10];
+ uint8_t weAttributes;
+#define ATTR_WIN95 0x0f
+ uint8_t weReserved1;
+ uint8_t weChksum;
+ uint8_t wePart2[12];
+ uint16_t weReserved2;
+ uint8_t wePart3[4];
+};
+#define WIN_CHARS 13 /* Number of chars per winentry */
+
+/*
+ * This is the format of the contents of the deTime field in the direntry
+ * structure.
+ * We don't use bitfields because we don't know how compilers for
+ * arbitrary machines will lay them out.
+ */
+#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
+#define DT_2SECONDS_SHIFT 0
+#define DT_MINUTES_MASK 0x7E0 /* minutes */
+#define DT_MINUTES_SHIFT 5
+#define DT_HOURS_MASK 0xF800 /* hours */
+#define DT_HOURS_SHIFT 11
+
+/*
+ * This is the format of the contents of the deDate field in the direntry
+ * structure.
+ */
+#define DD_DAY_MASK 0x1F /* day of month */
+#define DD_DAY_SHIFT 0
+#define DD_MONTH_MASK 0x1E0 /* month */
+#define DD_MONTH_SHIFT 5
+#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
+#define DD_YEAR_SHIFT 9
+
+struct dirent;
+void unix2dostime(const struct timespec *tsp, int gmtoff, uint16_t *ddp,
+ uint16_t *dtp, uint8_t *dhp);
+void dos2unixtime(unsigned int dd, unsigned int dt, unsigned int dh,
+ int gmtoff, struct timespec *tsp);
+int dos2unixfn(unsigned char dn[11], unsigned char *un, int lower);
+int unix2dosfn(const unsigned char *un, unsigned char dn[12], int unlen,
+ unsigned int gen);
+int unix2winfn(const unsigned char *un, int unlen, struct winentry *wep,
+ int cnt, int chksum, int utf8);
+int winChkName(const unsigned char *un, int unlen, struct winentry *wep,
+ int chksum, int utf8);
+int win2unixfn(struct winentry *wep, struct dirent *dp, int chksum,
+ uint16_t *namlen, int utf8);
+uint8_t winChksum(uint8_t *name);
+int winSlotCnt(const unsigned char *un, int unlen, int utf8);
+#endif /* _MSDOSFS_DIRENTRY_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/fat.h b/usr.sbin/makefs/fs/msdosfs/fat.h
new file mode 100644
index 00000000000..79aa0198042
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/fat.h
@@ -0,0 +1,117 @@
+/* $NetBSD: fat.h,v 1.9 2014/10/18 08:33:28 snj Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#ifndef _MSDOSFS_FAT_H_
+#define _MSDOSFS_FAT_H_
+/*
+ * Some useful cluster numbers.
+ */
+#define MSDOSFSROOT 0 /* cluster 0 means the root dir */
+#define CLUST_FREE 0 /* cluster 0 also means a free cluster */
+#define MSDOSFSFREE CLUST_FREE
+#define CLUST_FIRST 2 /* first legal cluster number */
+#define CLUST_RSRVD 0xfffffff6 /* reserved cluster range */
+#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */
+#define CLUST_EOFS 0xfffffff8 /* start of eof cluster range */
+#define CLUST_EOFE 0xffffffff /* end of eof cluster range */
+#define CLUST_END CLUST_EOFE /* bigger than any valid cluster */
+
+#define FAT12_MASK 0x00000fff /* mask for 12 bit cluster numbers */
+#define FAT16_MASK 0x0000ffff /* mask for 16 bit cluster numbers */
+#define FAT32_MASK 0x0fffffff /* mask for FAT32 cluster numbers */
+
+/*
+ * MSDOSFS:
+ * Return true if filesystem uses 12 bit FATs. Microsoft Programmer's
+ * Reference says if the maximum cluster number in a filesystem is greater
+ * than 4084 ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK) then we've got a
+ * 16 bit FAT filesystem. While mounting, the result of this test is stored
+ * in pm_fatentrysize.
+ * GEMDOS-flavour (atari):
+ * If the filesystem is on floppy we've got a 12 bit FAT filesystem, otherwise
+ * 16 bit. We check the d_type field in the disklabel struct while mounting
+ * and store the result in the pm_fatentrysize. Note that this kind of
+ * detection gets flakey when mounting a vnd-device.
+ */
+#define FAT12(pmp) (pmp->pm_fatmask == FAT12_MASK)
+#define FAT16(pmp) (pmp->pm_fatmask == FAT16_MASK)
+#define FAT32(pmp) (pmp->pm_fatmask == FAT32_MASK)
+
+/*
+ * M$ in its unlimited wisdom decided that EOF mark is anything
+ * between 0xfffffff8 and 0xffffffff (masked by appropriate fatmask,
+ * of course).
+ * Note that cn is supposed to be already adjusted accordingly to FAT type.
+ */
+#define MSDOSFSEOF(cn, fatmask) \
+ (((cn) & CLUST_EOFS) == (CLUST_EOFS & (fatmask)))
+
+/*
+ * These are the values for the function argument to the function
+ * fatentry().
+ */
+#define FAT_GET 0x0001 /* get a FAT entry */
+#define FAT_SET 0x0002 /* set a FAT entry */
+#define FAT_GET_AND_SET (FAT_GET | FAT_SET)
+
+/*
+ * Flags to extendfile:
+ */
+#define DE_CLEAR 1 /* Zero out the blocks allocated */
+
+int pcbmap(struct denode *, u_long, daddr_t *, u_long *, int *);
+int clusterfree(struct msdosfsmount *, u_long, u_long *);
+int clusteralloc(struct msdosfsmount *, u_long, u_long,u_long *,u_long *);
+int extendfile(struct denode *, u_long, struct buf **, u_long *, int);
+int fatentry(int, struct msdosfsmount *, u_long, u_long *, u_long);
+void fc_purge(struct denode *, u_int);
+void fc_lookup(struct denode *, u_long, u_long *, u_long *);
+int fillinusemap(struct msdosfsmount *);
+int freeclusterchain(struct msdosfsmount *, u_long);
+#endif /* _MSDOSFS_FAT_H_ */
diff --git a/usr.sbin/makefs/fs/msdosfs/msdosfs_conv.c b/usr.sbin/makefs/fs/msdosfs/msdosfs_conv.c
new file mode 100644
index 00000000000..b2550c2b110
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/msdosfs_conv.c
@@ -0,0 +1,1023 @@
+/* $NetBSD: msdosfs_conv.c,v 1.17 2016/06/30 09:34:01 nonaka Exp $ */
+
+/*-
+ * Copyright (C) 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ *
+ */
+
+#include <assert.h>
+#define KASSERT(x) assert(x)
+
+/*
+ * System include files.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/endian.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/queue.h>
+#include <fs/msdosfs/clock_subr.h> /* XXX */
+
+/*
+ * MSDOSFS include files.
+ */
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+
+static int invalidname(const u_int16_t *, int);
+
+static int ucs2utf8(const u_int16_t *, u_int8_t *, int);
+static int utf8ucs2(const u_int8_t *, int, u_int16_t *);
+
+static int ucs2utf8str(const u_int16_t *, int, u_int8_t *, int);
+static int utf8ucs2str(const u_int8_t *, int, u_int16_t *, int);
+static int ucs2char8str(const u_int16_t *, int, u_int8_t *, int);
+static int char8ucs2str(const u_int8_t *, int, u_int16_t *, int);
+
+static void ucs2pad(u_int16_t *, int, int);
+
+static u_int16_t ucs2fold(u_int16_t);
+static int ucs2match(u_int16_t *, u_int16_t *, int n);
+static int char8match(u_int16_t *, u_int16_t *, int n);
+
+/*
+ * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
+ * interval there were 8 regular years and 2 leap years.
+ */
+#define DOSBIASYEAR 1980
+#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
+/*
+ * msdos fs can not store dates beyound the year 2234
+ */
+#define DOSMAXYEAR ((DD_YEAR_MASK >> DD_YEAR_SHIFT) + DOSBIASYEAR)
+
+/*
+ * Convert the unix version of time to dos's idea of time to be used in
+ * file timestamps. The passed in unix time is assumed to be in GMT.
+ */
+void
+unix2dostime(const struct timespec *tsp, int gmtoff, u_int16_t *ddp, u_int16_t *dtp, u_int8_t *dhp)
+{
+ u_long t;
+ struct clock_ymdhms ymd;
+
+ t = tsp->tv_sec + gmtoff; /* time zone correction */
+
+ /*
+ * DOS timestamps can not represent dates before 1980.
+ */
+ if (t < SECONDSTO1980)
+ goto invalid_dos_date;
+
+ /*
+ * DOS granularity is 2 seconds
+ */
+ t &= ~1;
+
+ /*
+ * Convert to year/month/day/.. format
+ */
+ clock_secs_to_ymdhms(t, &ymd);
+ if (ymd.dt_year > DOSMAXYEAR)
+ goto invalid_dos_date;
+
+ /*
+ * Now transform to DOS format
+ */
+ *ddp = (ymd.dt_day << DD_DAY_SHIFT)
+ + (ymd.dt_mon << DD_MONTH_SHIFT)
+ + ((ymd.dt_year - DOSBIASYEAR) << DD_YEAR_SHIFT);
+ if (dhp)
+ *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
+ if (dtp)
+ *dtp = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
+ + (((t / 60) % 60) << DT_MINUTES_SHIFT)
+ + (((t / 3600) % 24) << DT_HOURS_SHIFT);
+ return;
+
+invalid_dos_date:
+ *ddp = 0;
+ if (dtp)
+ *dtp = 0;
+ if (dhp)
+ *dhp = 0;
+}
+
+/*
+ * Convert from dos' idea of time to unix'. This will probably only be
+ * called from the stat(), and fstat() system calls and so probably need
+ * not be too efficient.
+ */
+void
+dos2unixtime(u_int dd, u_int dt, u_int dh, int gmtoff, struct timespec *tsp)
+{
+ time_t seconds;
+ struct clock_ymdhms ymd;
+
+ if (dd == 0) {
+ /*
+ * Uninitialized field, return the epoch.
+ */
+ tsp->tv_sec = 0;
+ tsp->tv_nsec = 0;
+ return;
+ }
+
+ memset(&ymd, 0, sizeof(ymd));
+ ymd.dt_year = ((dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT) + 1980 ;
+ ymd.dt_mon = ((dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT);
+ ymd.dt_day = ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT);
+ ymd.dt_hour = (dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT;
+ ymd.dt_min = (dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT;
+ ymd.dt_sec = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) * 2;
+
+ seconds = clock_ymdhms_to_secs(&ymd);
+
+ tsp->tv_sec = seconds;
+ tsp->tv_sec -= gmtoff; /* time zone correction */
+ tsp->tv_nsec = (dh % 100) * 10000000;
+}
+
+static const u_char
+unix2dos[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */
+ 0, '!', 0, '#', '$', '%', '&', '\'', /* 20-27 */
+ '(', ')', 0, '+', 0, '-', 0, 0, /* 28-2f */
+ '0', '1', '2', '3', '4', '5', '6', '7', /* 30-37 */
+ '8', '9', 0, 0, 0, 0, 0, 0, /* 38-3f */
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40-47 */
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 48-4f */
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 50-57 */
+ 'X', 'Y', 'Z', 0, 0, 0, '^', '_', /* 58-5f */
+ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 60-67 */
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 68-6f */
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 70-77 */
+ 'X', 'Y', 'Z', '{', 0, '}', '~', 0, /* 78-7f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */
+ 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */
+ 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */
+ 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */
+ 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */
+ 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */
+ 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */
+};
+
+static const u_char
+dos2unix[256] = {
+ '?', '?', '?', '?', '?', '?', '?', '?', /* 00-07 */
+ '?', '?', '?', '?', '?', '?', '?', '?', /* 08-0f */
+ '?', '?', '?', '?', '?', '?', '?', '?', /* 10-17 */
+ '?', '?', '?', '?', '?', '?', '?', '?', /* 18-1f */
+ ' ', '!', '"', '#', '$', '%', '&', '\'', /* 20-27 */
+ '(', ')', '*', '+', ',', '-', '.', '/', /* 28-2f */
+ '0', '1', '2', '3', '4', '5', '6', '7', /* 30-37 */
+ '8', '9', ':', ';', '<', '=', '>', '?', /* 38-3f */
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40-47 */
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 48-4f */
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 50-57 */
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /* 58-5f */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 60-67 */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 68-6f */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 70-77 */
+ 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, /* 78-7f */
+ 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */
+ 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */
+ 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */
+ 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, '?', /* 98-9f */
+ 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */
+ 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */
+ '?', '?', '?', '?', '?', 0xc1, 0xc2, 0xc0, /* b0-b7 */
+ 0xa9, '?', '?', '?', '?', 0xa2, 0xa5, '?', /* b8-bf */
+ '?', '?', '?', '?', '?', '?', 0xe3, 0xc3, /* c0-c7 */
+ '?', '?', '?', '?', '?', '?', '?', 0xa4, /* c8-cf */
+ 0xf0, 0xd0, 0xca, 0xcb, 0xc8, '?', 0xcd, 0xce, /* d0-d7 */
+ 0xcf, '?', '?', '?', '?', 0xa6, 0xcc, '?', /* d8-df */
+ 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */
+ 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */
+ 0xad, 0xb1, '?', 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */
+ 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, '?', '?', /* f8-ff */
+};
+
+static const u_char
+u2l[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */
+ ' ', '!', '"', '#', '$', '%', '&', '\'', /* 20-27 */
+ '(', ')', '*', '+', ',', '-', '.', '/', /* 28-2f */
+ '0', '1', '2', '3', '4', '5', '6', '7', /* 30-37 */
+ '8', '9', ':', ';', '<', '=', '>', '?', /* 38-3f */
+ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 40-47 */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 48-4f */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 50-57 */
+ 'x', 'y', 'z', '[', '\\', ']', '^', '_', /* 58-5f */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 60-67 */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 68-6f */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 70-77 */
+ 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, /* 78-7f */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */
+};
+
+/*
+ * DOS filenames are made of 2 parts, the name part and the extension part.
+ * The name part is 8 characters long and the extension part is 3
+ * characters long. They may contain trailing blanks if the name or
+ * extension are not long enough to fill their respective fields.
+ */
+
+/*
+ * Convert a DOS filename to a unix filename. And, return the number of
+ * characters in the resulting unix filename excluding the terminating
+ * null.
+ */
+int
+dos2unixfn(u_char dn[11], u_char *un, int lower)
+{
+ int i, j;
+ int thislong = 1;
+ u_char c;
+
+ /*
+ * If first char of the filename is SLOT_E5 (0x05), then the real
+ * first char of the filename should be 0xe5. But, they couldn't
+ * just have a 0xe5 mean 0xe5 because that is used to mean a freed
+ * directory slot. Another dos quirk.
+ */
+ if (*dn == SLOT_E5)
+ c = dos2unix[0xe5];
+ else
+ c = dos2unix[*dn];
+ *un++ = lower ? u2l[c] : c;
+
+ /*
+ * Copy the rest into the unix filename string, ignoring
+ * trailing blanks.
+ */
+
+ for (j=7; (j >= 0) && (dn[j] == ' '); j--)
+ ;
+
+ for (i = 1; i <= j; i++) {
+ c = dos2unix[dn[i]];
+ *un++ = lower ? u2l[c] : c;
+ thislong++;
+ }
+ dn += 8;
+
+ /*
+ * Now, if there is an extension then put in a period and copy in
+ * the extension.
+ */
+ if (*dn != ' ') {
+ *un++ = '.';
+ thislong++;
+ for (i = 0; i < 3 && *dn != ' '; i++) {
+ c = dos2unix[*dn++];
+ *un++ = lower ? u2l[c] : c;
+ thislong++;
+ }
+ }
+ *un++ = 0;
+
+ return (thislong);
+}
+
+/*
+ * Convert a unix filename to a DOS filename according to Win95 rules.
+ * If applicable and gen is not 0, it is inserted into the converted
+ * filename as a generation number.
+ * Returns
+ * 0 if name couldn't be converted
+ * 1 if the converted name is the same as the original
+ * (no long filename entry necessary for Win95)
+ * 2 if conversion was successful
+ * 3 if conversion was successful and generation number was inserted
+ */
+int
+unix2dosfn(const u_char *un, u_char dn[12], int unlen, u_int gen)
+{
+ int i, j, l;
+ int conv = 1;
+ const u_char *cp, *dp, *dp1;
+ u_char gentext[6], *wcp;
+ int shortlen;
+
+ /*
+ * Fill the dos filename string with blanks. These are DOS's pad
+ * characters.
+ */
+ for (i = 0; i < 11; i++)
+ dn[i] = ' ';
+ dn[11] = 0;
+
+ /*
+ * The filenames "." and ".." are handled specially, since they
+ * don't follow dos filename rules.
+ */
+ if (un[0] == '.' && unlen == 1) {
+ dn[0] = '.';
+ return gen <= 1;
+ }
+ if (un[0] == '.' && un[1] == '.' && unlen == 2) {
+ dn[0] = '.';
+ dn[1] = '.';
+ return gen <= 1;
+ }
+
+ /*
+ * Filenames with only blanks and dots are not allowed!
+ */
+ for (cp = un, i = unlen; --i >= 0; cp++)
+ if (*cp != ' ' && *cp != '.')
+ break;
+ if (i < 0)
+ return 0;
+
+ /*
+ * Now find the extension
+ * Note: dot as first char doesn't start extension
+ * and trailing dots and blanks are ignored
+ */
+ dp = dp1 = 0;
+ for (cp = un + 1, i = unlen - 1; --i >= 0;) {
+ switch (*cp++) {
+ case '.':
+ if (!dp1)
+ dp1 = cp;
+ break;
+ case ' ':
+ break;
+ default:
+ if (dp1)
+ dp = dp1;
+ dp1 = 0;
+ break;
+ }
+ }
+
+ /*
+ * Now convert it
+ */
+ if (dp) {
+ if (dp1)
+ l = dp1 - dp;
+ else
+ l = unlen - (dp - un);
+ for (i = 0, j = 8; i < l && j < 11; i++, j++) {
+ if (dp[i] != (dn[j] = unix2dos[dp[i]])
+ && conv != 3)
+ conv = 2;
+ if (!dn[j]) {
+ conv = 3;
+ dn[j--] = ' ';
+ }
+ }
+ if (i < l)
+ conv = 3;
+ dp--;
+ } else {
+ for (dp = cp; *--dp == ' ' || *dp == '.';);
+ dp++;
+ }
+
+ shortlen = (dp - un) <= 8;
+
+ /*
+ * Now convert the rest of the name
+ */
+ for (i = j = 0; un < dp && j < 8; i++, j++, un++) {
+ if ((*un == ' ') && shortlen)
+ dn[j] = ' ';
+ else
+ dn[j] = unix2dos[*un];
+ if ((*un != dn[j])
+ && conv != 3)
+ conv = 2;
+ if (!dn[j]) {
+ conv = 3;
+ dn[j--] = ' ';
+ }
+ }
+ if (un < dp)
+ conv = 3;
+ /*
+ * If we didn't have any chars in filename,
+ * generate a default
+ */
+ if (!j)
+ dn[0] = '_';
+
+ /*
+ * The first character cannot be E5,
+ * because that means a deleted entry
+ */
+ if (dn[0] == 0xe5)
+ dn[0] = SLOT_E5;
+
+ /*
+ * If there wasn't any char dropped,
+ * there is no place for generation numbers
+ */
+ if (conv != 3) {
+ if (gen > 1)
+ return 0;
+ return conv;
+ }
+
+ /*
+ * Now insert the generation number into the filename part
+ */
+ for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10)
+ *--wcp = gen % 10 + '0';
+ if (gen)
+ return 0;
+ for (i = 8; dn[--i] == ' ';);
+ i++;
+ if (gentext + sizeof(gentext) - wcp + 1 > 8 - i)
+ i = 8 - (gentext + sizeof(gentext) - wcp + 1);
+ dn[i++] = '~';
+ while (wcp < gentext + sizeof(gentext))
+ dn[i++] = *wcp++;
+ return 3;
+}
+
+/*
+ * Create a Win95 long name directory entry
+ * Note: assumes that the filename is valid,
+ * i.e. doesn't consist solely of blanks and dots
+ */
+int
+unix2winfn(const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum, int utf8)
+{
+ u_int16_t wn[WIN_MAXLEN], *p;
+ int i, len;
+ const u_char *cp;
+
+ /*
+ * Drop trailing blanks and dots
+ */
+ for (cp = un + unlen; unlen > 0; unlen--)
+ if (*--cp != ' ' && *cp != '.')
+ break;
+
+ /*
+ * Offset of this entry
+ */
+ i = (cnt - 1) * WIN_CHARS;
+
+ /*
+ * Translate UNIX name to ucs-2
+ */
+ len = utf8 ? utf8ucs2str(un, unlen, wn, WIN_MAXLEN) : char8ucs2str(un, unlen, wn, WIN_MAXLEN);
+ ucs2pad(wn, len, WIN_MAXLEN);
+
+ /*
+ * Initialize winentry to some useful default
+ */
+ memset(wep, 0xff, sizeof(*wep));
+ wep->weCnt = cnt;
+ wep->weAttributes = ATTR_WIN95;
+ wep->weReserved1 = 0;
+ wep->weChksum = chksum;
+ wep->weReserved2 = 0;
+
+ /*
+ * Store name segment into directory entry
+ */
+ p = &wn[i];
+ memcpy(wep->wePart1, p, sizeof(wep->wePart1));
+ p += sizeof(wep->wePart1) / sizeof(*p);
+ memcpy(wep->wePart2, p, sizeof(wep->wePart2));
+ p += sizeof(wep->wePart2) / sizeof(*p);
+ memcpy(wep->wePart3, p, sizeof(wep->wePart3));
+
+ if (len > i + WIN_CHARS)
+ return 1;
+
+ wep->weCnt |= WIN_LAST;
+ return 0;
+}
+
+/*
+ * Compare our filename to the one in the Win95 entry
+ * Returns the checksum or -1 if no match
+ */
+int
+winChkName(const u_char *un, int unlen, struct winentry *wep, int chksum, int utf8)
+{
+ u_int16_t wn[WIN_MAXLEN], *p;
+ u_int16_t buf[WIN_CHARS];
+ int i, len;
+
+ /*
+ * First compare checksums
+ */
+ if (wep->weCnt & WIN_LAST)
+ chksum = wep->weChksum;
+ else if (chksum != wep->weChksum)
+ chksum = -1;
+ if (chksum == -1)
+ return -1;
+
+ /*
+ * Offset of this entry
+ */
+ i = ((wep->weCnt & WIN_CNT) - 1) * WIN_CHARS;
+
+ /*
+ * Translate UNIX name to ucs-2
+ */
+ len = utf8 ? utf8ucs2str(un, unlen, wn, WIN_MAXLEN) : char8ucs2str(un, unlen, wn, WIN_MAXLEN);
+ ucs2pad(wn, len, WIN_MAXLEN);
+
+ if (i >= len + 1)
+ return -1;
+ if ((wep->weCnt & WIN_LAST) && (len - i > WIN_CHARS))
+ return -1;
+
+ /*
+ * Fetch name segment from directory entry
+ */
+ p = &buf[0];
+ memcpy(p, wep->wePart1, sizeof(wep->wePart1));
+ p += sizeof(wep->wePart1) / sizeof(*p);
+ memcpy(p, wep->wePart2, sizeof(wep->wePart2));
+ p += sizeof(wep->wePart2) / sizeof(*p);
+ memcpy(p, wep->wePart3, sizeof(wep->wePart3));
+
+ /*
+ * And compare name segment
+ */
+ if (! (utf8 ? ucs2match(&wn[i], buf, WIN_CHARS) : char8match(&wn[i], buf, WIN_CHARS)))
+ return -1;
+
+ return chksum;
+}
+
+/*
+ * Convert Win95 filename to dirbuf.
+ * Returns the checksum or -1 if impossible
+ */
+int
+win2unixfn(struct winentry *wep, struct dirent *dp, int chksum,
+ uint16_t *namlen, int utf8)
+{
+ u_int16_t wn[WIN_CHARS], *p;
+ u_int8_t buf[WIN_CHARS*3];
+ int len;
+
+ if ((wep->weCnt & WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS)
+ || !(wep->weCnt & WIN_CNT))
+ return -1;
+
+ /*
+ * First compare checksums
+ */
+ if (wep->weCnt & WIN_LAST) {
+ chksum = wep->weChksum;
+ *namlen = 0;
+ } else if (chksum != wep->weChksum)
+ chksum = -1;
+ if (chksum == -1)
+ return -1;
+
+ /*
+ * Fetch name segment from directory entry
+ */
+ p = &wn[0];
+ memcpy(p, wep->wePart1, sizeof(wep->wePart1));
+ p += sizeof(wep->wePart1) / sizeof(*p);
+ memcpy(p, wep->wePart2, sizeof(wep->wePart2));
+ p += sizeof(wep->wePart2) / sizeof(*p);
+ memcpy(p, wep->wePart3, sizeof(wep->wePart3));
+
+ /*
+ * Don't allow slashes in UNIX names. Discard that entry.
+ */
+ if (invalidname(wn, WIN_CHARS))
+ return -1;
+
+ /*
+ * Translate ucs-2 to UNIX name
+ */
+ len = utf8 ? ucs2utf8str(wn, WIN_CHARS, buf, sizeof(buf))
+ : ucs2char8str(wn, WIN_CHARS, buf, sizeof(buf));
+
+ KASSERT(len >= 0);
+ KASSERT((size_t)len <= MIN(sizeof(buf), sizeof(dp->d_name)-1));
+
+ /*
+ * Prepend name segment to directory entry
+ *
+ * This ignores the slot number from the windows entry but
+ * assumes that segments are read in reverse order.
+ *
+ * The UCS-2 name (up to 255 chars) can overflow the UNIX
+ * directory entry (up to 511 bytes). Trailing characters
+ * are silently discarded. This could also end in multiple
+ * files using the same (truncated) name.
+ */
+ *namlen += len;
+ if (*namlen > sizeof(dp->d_name) - 1)
+ *namlen = sizeof(dp->d_name) - 1;
+
+ KASSERT(*namlen >= len);
+
+ memmove(&dp->d_name[len], &dp->d_name[0], *namlen - len);
+ memcpy(dp->d_name, buf, len);
+
+ return chksum;
+}
+
+/*
+ * Compute the checksum of a DOS filename for Win95 use
+ */
+u_int8_t
+winChksum(u_int8_t *name)
+{
+ int i;
+ u_int8_t s;
+
+ for (s = 0, i = 11; --i >= 0; s += *name++)
+ s = (s << 7) | (s >> 1);
+ return s;
+}
+
+/*
+ * Determine the number of slots necessary for Win95 names
+ */
+int
+winSlotCnt(const u_char *un, int unlen, int utf8)
+{
+ const u_char *cp;
+ int len;
+
+ /*
+ * Drop trailing blanks and dots
+ */
+ for (cp = un + unlen; unlen > 0; unlen--)
+ if (*--cp != ' ' && *cp != '.')
+ break;
+
+ len = utf8 ? utf8ucs2str(un, unlen, NULL, WIN_MAXLEN) : unlen;
+
+ return howmany(len, WIN_CHARS);
+}
+
+/*
+ * Scan windows name for characters that must not
+ * appear in a UNIX filename
+ */
+static int
+invalidname(const u_int16_t *in, int n)
+{
+ while (n-- > 0) {
+ if (*in++ == '/')
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert UCS-2 character into UTF-8
+ * return number of output bytes or 0 if output
+ * buffer is too short
+ */
+static int
+ucs2utf8(const u_int16_t *in, u_int8_t *out, int n)
+{
+ uint16_t inch = le16toh(in[0]);
+
+ if (inch <= 0x007f) {
+ if (n < 1) return 0;
+ if (out)
+ *out++ = inch;
+ return 1;
+ } else if (inch <= 0x07ff) {
+ if (n < 2) return 0;
+ if (out) {
+ *out++ = 0xc0 | (inch >> 6);
+ *out++ = 0x80 | (inch & 0x3f);
+ }
+ return 2;
+ } else {
+ if (n < 3) return 0;
+ if (out) {
+ *out++ = 0xe0 | (inch >> 12);
+ *out++ = 0x80 | ((inch >> 6) & 0x3f);
+ *out++ = 0x80 | (inch & 0x3f);
+ }
+ return 3;
+ }
+}
+
+
+/*
+ * Convert UTF-8 bytes into UCS-2 character
+ * return number of input bytes, 0 if input
+ * is too short and -1 if input is invalid
+ */
+static int
+utf8ucs2(const u_int8_t *in, int n, u_int16_t *out)
+{
+ uint16_t outch;
+
+ if (n < 1) return 0;
+
+ if (in[0] <= 0x7f) {
+ outch = in[0];
+ if (out)
+ *out = htole16(outch);
+ return 1;
+ } else if (in[0] <= 0xdf) {
+ if (n < 2) return 0;
+ outch = (in[0] & 0x1f) << 6 | (in[1] & 0x3f);
+ if (out)
+ *out = htole16(outch);
+ return 2;
+ } else if (in[0] <= 0xef) {
+ if (n < 3) return 0;
+ outch = (in[0] & 0x1f) << 12 | (in[1] & 0x3f) << 6 | (in[2] & 0x3f);
+ if (out)
+ *out = htole16(outch);
+ return 3;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert UCS-2 string into UTF-8 string
+ * return total number of output bytes
+ */
+static int
+ucs2utf8str(const u_int16_t *in, int n, u_int8_t *out, int m)
+{
+ u_int8_t *p;
+ int outlen;
+
+ p = out;
+ while (n > 0 && *in != 0) {
+ outlen = ucs2utf8(in, out ? p : out, m);
+ if (outlen == 0)
+ break;
+ p += outlen;
+ m -= outlen;
+ in += 1;
+ n -= 1;
+ }
+
+ return p - out;
+}
+
+/*
+ * Convert UTF8 string into UCS-2 string
+ * return total number of output chacters
+ */
+static int
+utf8ucs2str(const u_int8_t *in, int n, u_int16_t *out, int m)
+{
+ u_int16_t *p;
+ int inlen;
+
+ p = out;
+ while (n > 0 && *in != 0) {
+ if (m < 1)
+ break;
+ inlen = utf8ucs2(in, n, out ? p : out);
+ if (inlen <= 0)
+ break;
+ in += inlen;
+ n -= inlen;
+ p += 1;
+ m -= 1;
+ }
+
+ return p - out;
+}
+
+/*
+ * Convert UCS-2 string into 8bit character string
+ * return total number of output bytes
+ */
+static int
+ucs2char8str(const u_int16_t *in, int n, u_int8_t *out, int m)
+{
+ u_int8_t *p;
+ u_int16_t inch;
+
+ p = out;
+ while (n > 0 && in[0] != 0) {
+ if (m < 1)
+ break;
+ inch = le16toh(in[0]);
+ if (inch > 255)
+ break;
+ if (p)
+ p[0] = inch;
+ p += 1;
+ m -= 1;
+ in += 1;
+ n -= 1;
+ }
+
+ return p - out;
+}
+
+/*
+ * Convert 8bit character string into UCS-2 string
+ * return total number of output chacters
+ */
+static int
+char8ucs2str(const u_int8_t *in, int n, u_int16_t *out, int m)
+{
+ u_int16_t *p;
+
+ p = out;
+ while (n > 0 && in[0] != 0) {
+ if (m < 1)
+ break;
+ if (p)
+ p[0] = htole16(in[0]);
+ p += 1;
+ m -= 1;
+ in += 1;
+ n -= 1;
+ }
+
+ return p - out;
+}
+
+static void
+ucs2pad(u_int16_t *buf, int len, int size)
+{
+
+ if (len < size-1)
+ buf[len++] = 0x0000;
+ while (len < size)
+ buf[len++] = 0xffff;
+}
+
+/*
+ * Fold UCS-2 character to uppercase
+ */
+static u_int16_t
+ucs2fold(u_int16_t w)
+{
+ int low,high,mid;
+ u_int16_t check;
+ extern const u_int16_t msdosfs_unicode_foldmap[];
+ extern size_t msdosfs_unicode_foldmap_entries;
+
+ w = le16toh(w);
+
+ low = 0;
+ high = msdosfs_unicode_foldmap_entries / 2;
+ while (low < high) {
+ mid = (low + high)/2;
+ check = msdosfs_unicode_foldmap[2*mid+0];
+
+ if (w == check) {
+ w = msdosfs_unicode_foldmap[2*mid+1];
+ break;
+ }
+
+ if (w < check)
+ high = mid;
+ else
+ low = mid+1;
+ }
+
+ w = le16toh(w);
+
+ return w;
+}
+
+/*
+ * Compare two UCS-2 strings case-insensitive
+ *
+ * uses the Unicode case folding table
+ */
+static int
+ucs2match(u_int16_t *w1, u_int16_t *w2, int n)
+{
+ u_int16_t u1, u2;
+
+ while (n > 0) {
+ if (*w1 == 0 || *w2 == 0)
+ return *w1 == *w2;
+ u1 = ucs2fold(*w1);
+ u2 = ucs2fold(*w2);
+ if (u1 != u2)
+ return 0;
+ ++w1;
+ ++w2;
+ --n;
+ }
+
+ return 1;
+}
+
+/*
+ * Compare two 8bit char conversions case-insensitive
+ *
+ * uses the DOS case folding table
+ */
+static int
+char8match(u_int16_t *w1, u_int16_t *w2, int n)
+{
+ u_int16_t u1, u2;
+
+ while (n > 0) {
+ u1 = le16toh(*w1);
+ u2 = le16toh(*w2);
+ if (u1 == 0 || u2 == 0)
+ return u1 == u2;
+ if (u1 > 255 || u2 > 255)
+ return 0;
+ u1 = u2l[u1 & 0xff];
+ u2 = u2l[u2 & 0xff];
+ if (u1 != u2)
+ return 0;
+ ++w1;
+ ++w2;
+ --n;
+ }
+
+ return 1;
+}
+
diff --git a/usr.sbin/makefs/fs/msdosfs/msdosfs_fat.c b/usr.sbin/makefs/fs/msdosfs/msdosfs_fat.c
new file mode 100644
index 00000000000..030b31091c4
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/msdosfs_fat.c
@@ -0,0 +1,1098 @@
+/* $NetBSD: msdosfs_fat.c,v 1.31 2016/05/07 16:43:02 mlelstv Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+/*
+ * kernel include files.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <ffs/buf.h>
+
+/*
+ * msdosfs include files.
+ */
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/fat.h>
+
+/*
+ * Fat cache stats.
+ */
+int fc_fileextends; /* # of file extends */
+int fc_lfcempty; /* # of time last file cluster cache entry
+ * was empty */
+int fc_bmapcalls; /* # of times pcbmap was called */
+
+#define LMMAX 20
+int fc_lmdistance[LMMAX]; /* counters for how far off the last
+ * cluster mapped entry was. */
+int fc_largedistance; /* off by more than LMMAX */
+int fc_wherefrom, fc_whereto, fc_lastclust;
+int pm_fatblocksize;
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+#ifdef MSDOSFS_DEBUG
+void print_fat_stats(void);
+
+void
+print_fat_stats(void)
+{
+ int i;
+
+ printf("fc_fileextends=%d fc_lfcempty=%d fc_bmapcalls=%d "
+ "fc_largedistance=%d [%d->%d=%d] fc_lastclust=%d pm_fatblocksize=%d\n",
+ fc_fileextends, fc_lfcempty, fc_bmapcalls, fc_largedistance,
+ fc_wherefrom, fc_whereto, fc_whereto-fc_wherefrom,
+ fc_lastclust, pm_fatblocksize);
+
+ fc_fileextends = fc_lfcempty = fc_bmapcalls = 0;
+ fc_wherefrom = fc_whereto = fc_lastclust = 0;
+
+ for (i = 0; i < LMMAX; i++) {
+ printf("%d:%d ", i, fc_lmdistance[i]);
+ fc_lmdistance[i] = 0;
+ }
+
+ printf("\n");
+}
+#endif
+
+static void fatblock(struct msdosfsmount *, u_long, u_long *, u_long *,
+ u_long *);
+void updatefats(struct msdosfsmount *, struct buf *, u_long);
+static inline void usemap_free(struct msdosfsmount *, u_long);
+static inline void usemap_alloc(struct msdosfsmount *, u_long);
+static int fatchain(struct msdosfsmount *, u_long, u_long, u_long);
+int chainlength(struct msdosfsmount *, u_long, u_long);
+int chainalloc(struct msdosfsmount *, u_long, u_long, u_long, u_long *,
+ u_long *);
+
+static void
+fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep, u_long *bop)
+{
+ u_long bn, size;
+
+ bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
+ size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
+ * pmp->pm_BytesPerSec;
+ bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
+
+ DPRINTF(("%s(ofs=%lu bn=%lu, size=%lu, bo=%lu)\n", __func__, ofs, bn,
+ size, ofs % pmp->pm_fatblocksize));
+ if (bnp)
+ *bnp = bn;
+ if (sizep)
+ *sizep = size;
+ if (bop)
+ *bop = ofs % pmp->pm_fatblocksize;
+
+ pm_fatblocksize = pmp->pm_fatblocksize;
+}
+
+/*
+ * Map the logical cluster number of a file into a physical disk sector
+ * that is filesystem relative.
+ *
+ * dep - address of denode representing the file of interest
+ * findcn - file relative cluster whose filesystem relative cluster number
+ * and/or block number are/is to be found
+ * bnp - address of where to place the file system relative block number.
+ * If this pointer is null then don't return this quantity.
+ * cnp - address of where to place the file system relative cluster number.
+ * If this pointer is null then don't return this quantity.
+ *
+ * NOTE: Either bnp or cnp must be non-null.
+ * This function has one side effect. If the requested file relative cluster
+ * is beyond the end of file, then the actual number of clusters in the file
+ * is returned in *cnp. This is useful for determining how long a directory is.
+ * If cnp is null, nothing is returned.
+ */
+int
+pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp)
+ /* findcn: file relative cluster to get */
+ /* bnp: returned filesys rel sector number */
+ /* cnp: returned cluster number */
+ /* sp: returned block size */
+{
+ int error;
+ u_long i;
+ u_long cn;
+ u_long prevcn = 0; /* XXX: prevcn could be used unititialized */
+ u_long byteoffset;
+ u_long bn;
+ u_long bo;
+ struct buf *bp = NULL;
+ u_long bp_bn = -1;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long bsize;
+
+ fc_bmapcalls++;
+
+ /*
+ * If they don't give us someplace to return a value then don't
+ * bother doing anything.
+ */
+ if (bnp == NULL && cnp == NULL && sp == NULL)
+ return (0);
+
+ cn = dep->de_StartCluster;
+ DPRINTF(("%s(start cluster=%lu)\n", __func__, cn));
+ /*
+ * The "file" that makes up the root directory is contiguous,
+ * permanently allocated, of fixed size, and is not made up of
+ * clusters. If the cluster number is beyond the end of the root
+ * directory, then return the number of clusters in the file.
+ */
+ if (cn == MSDOSFSROOT) {
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
+ if (cnp)
+ *cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
+ DPRINTF(("%s(root, %lu ETOOBIG)\n", __func__,
+ de_cn2off(pmp, findcn)));
+ return (E2BIG);
+ }
+ if (bnp)
+ *bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
+ if (cnp)
+ *cnp = MSDOSFSROOT;
+ if (sp)
+ *sp = min(pmp->pm_bpcluster,
+ dep->de_FileSize - de_cn2off(pmp, findcn));
+ DPRINTF(("%s(root, bn=%lu, cn=%u)\n", __func__,
+ pmp->pm_rootdirblk + de_cn2bn(pmp, findcn),
+ MSDOSFSROOT));
+ return (0);
+ } else { /* just an empty file */
+ if (cnp)
+ *cnp = 0;
+ DPRINTF(("%s(root, empty ETOOBIG)\n", __func__));
+ return (E2BIG);
+ }
+ }
+
+ /*
+ * All other files do I/O in cluster sized blocks
+ */
+ if (sp)
+ *sp = pmp->pm_bpcluster;
+
+ /*
+ * Rummage around in the FAT cache, maybe we can avoid tromping
+ * thru every FAT entry for the file. And, keep track of how far
+ * off the cache was from where we wanted to be.
+ */
+ i = 0;
+ fc_lookup(dep, findcn, &i, &cn);
+ DPRINTF(("%s(bpcluster=%lu i=%lu cn=%lu\n", __func__, pmp->pm_bpcluster,
+ i, cn));
+ if ((bn = findcn - i) >= LMMAX) {
+ fc_largedistance++;
+ fc_wherefrom = i;
+ fc_whereto = findcn;
+ fc_lastclust = dep->de_fc[FC_LASTFC].fc_frcn;
+ } else
+ fc_lmdistance[bn]++;
+
+ /*
+ * Handle all other files or directories the normal way.
+ */
+ for (; i < findcn; i++) {
+ /*
+ * Stop with all reserved clusters, not just with EOF.
+ */
+ if (cn >= (CLUST_RSRVD & pmp->pm_fatmask))
+ goto hiteof;
+
+ /*
+ * Also stop when cluster is not in the filesystem
+ */
+ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) {
+ DPRINTF(("%s(cn, %lu not in %lu..%lu)\n", __func__,
+ cn, (u_long)CLUST_FIRST, pmp->pm_maxcluster));
+ if (bp)
+ brelse(bp, 0);
+ return (EINVAL);
+ }
+
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (bn != bp_bn) {
+ if (bp)
+ brelse(bp, 0);
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize,
+ 0, &bp);
+ if (error) {
+ DPRINTF(("%s(bread, %d)\n", __func__, error));
+ return (error);
+ }
+ bp_bn = bn;
+ }
+ prevcn = cn;
+ if (bo >= bsize) {
+ if (bp)
+ brelse(bp, 0);
+ DPRINTF(("%s(block, %lu >= %lu)\n", __func__, bo,
+ bsize));
+ return (EIO);
+ }
+ KASSERT(bp != NULL);
+ if (FAT32(pmp))
+ cn = getulong((char *)bp->b_data + bo);
+ else
+ cn = getushort((char *)bp->b_data + bo);
+ if (FAT12(pmp) && (prevcn & 1))
+ cn >>= 4;
+ DPRINTF(("%s(cn=%lu masked=%lu)\n", __func__, cn,
+ cn & pmp->pm_fatmask));
+ cn &= pmp->pm_fatmask;
+ }
+
+ if (!MSDOSFSEOF(cn, pmp->pm_fatmask)) {
+ if (bp)
+ brelse(bp, 0);
+ if (bnp)
+ *bnp = cntobn(pmp, cn);
+ if (cnp)
+ *cnp = cn;
+ DPRINTF(("%s(bn=%lu, cn=%lu)\n", __func__, cntobn(pmp, cn),
+ cn));
+ fc_setcache(dep, FC_LASTMAP, i, cn);
+ return (0);
+ }
+
+hiteof:;
+ if (cnp)
+ *cnp = i;
+ if (bp)
+ brelse(bp, 0);
+ /* update last file cluster entry in the FAT cache */
+ fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
+ DPRINTF(("%s(eof, %lu)\n", __func__, i));
+ return (E2BIG);
+}
+
+/*
+ * Find the closest entry in the FAT cache to the cluster we are looking
+ * for.
+ */
+void
+fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp)
+{
+ int i;
+ u_long cn;
+ struct fatcache *closest = 0;
+
+ for (i = 0; i < FC_SIZE; i++) {
+ cn = dep->de_fc[i].fc_frcn;
+ if (cn != FCE_EMPTY && cn <= findcn) {
+ if (closest == 0 || cn > closest->fc_frcn)
+ closest = &dep->de_fc[i];
+ }
+ }
+ if (closest) {
+ *frcnp = closest->fc_frcn;
+ *fsrcnp = closest->fc_fsrcn;
+ }
+}
+
+/*
+ * Purge the FAT cache in denode dep of all entries relating to file
+ * relative cluster frcn and beyond.
+ */
+void
+fc_purge(struct denode *dep, u_int frcn)
+{
+ int i;
+ struct fatcache *fcp;
+
+ fcp = dep->de_fc;
+ for (i = 0; i < FC_SIZE; i++, fcp++) {
+ if (fcp->fc_frcn >= frcn)
+ fcp->fc_frcn = FCE_EMPTY;
+ }
+}
+
+/*
+ * Update the FAT.
+ * If mirroring the FAT, update all copies, with the first copy as last.
+ * Else update only the current FAT (ignoring the others).
+ *
+ * pmp - msdosfsmount structure for filesystem to update
+ * bp - addr of modified FAT block
+ * fatbn - block number relative to begin of filesystem of the modified FAT block.
+ */
+void
+updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn)
+{
+ int i, error;
+ struct buf *bpn;
+
+ DPRINTF(("%s(pmp %p, bp %p, fatbn %lu)\n", __func__, pmp, bp, fatbn));
+
+ /*
+ * If we have an FSInfo block, update it.
+ */
+ if (pmp->pm_fsinfo) {
+ u_long cn = pmp->pm_nxtfree;
+
+ if (pmp->pm_freeclustercount
+ && (pmp->pm_inusemap[cn / N_INUSEBITS]
+ & (1 << (cn % N_INUSEBITS)))) {
+ /*
+ * The cluster indicated in FSInfo isn't free
+ * any longer. Got get a new free one.
+ */
+ for (cn = 0; cn < pmp->pm_maxcluster; cn++)
+ if (pmp->pm_inusemap[cn / N_INUSEBITS] != (u_int)-1)
+ break;
+ pmp->pm_nxtfree = cn
+ + ffs(pmp->pm_inusemap[cn / N_INUSEBITS]
+ ^ (u_int)-1) - 1;
+ }
+ /*
+ * XXX If the fsinfo block is stored on media with
+ * 2KB or larger sectors, is the fsinfo structure
+ * padded at the end or in the middle?
+ */
+ if (bread(pmp->pm_devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
+ pmp->pm_BytesPerSec, B_MODIFY, &bpn) != 0) {
+ /*
+ * Ignore the error, but turn off FSInfo update for the future.
+ */
+ pmp->pm_fsinfo = 0;
+ } else {
+ struct fsinfo *fp = (struct fsinfo *)bpn->b_data;
+
+ putulong(fp->fsinfree, pmp->pm_freeclustercount);
+ putulong(fp->fsinxtfree, pmp->pm_nxtfree);
+ if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT)
+ bwrite(bpn);
+ else
+ bdwrite(bpn);
+ }
+ }
+
+ if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
+ /*
+ * Now copy the block(s) of the modified FAT to the other copies of
+ * the FAT and write them out. This is faster than reading in the
+ * other FATs and then writing them back out. This could tie up
+ * the FAT for quite a while. Preventing others from accessing it.
+ * To prevent us from going after the FAT quite so much we use
+ * delayed writes, unless they specified "synchronous" when the
+ * filesystem was mounted. If synch is asked for then use
+ * bwrite()'s and really slow things down.
+ */
+ for (i = 1; i < pmp->pm_FATs; i++) {
+ fatbn += pmp->pm_FATsecs;
+ /* getblk() never fails */
+ bpn = getblk(pmp->pm_devvp, de_bn2kb(pmp, fatbn),
+ bp->b_bcount, 0, 0);
+ memcpy(bpn->b_data, bp->b_data, bp->b_bcount);
+ if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) {
+ error = bwrite(bpn);
+ if (error)
+ printf("%s: copy FAT %d (error=%d)\n",
+ __func__, i, error);
+ } else
+ bdwrite(bpn);
+ }
+ }
+
+ /*
+ * Write out the first (or current) FAT last.
+ */
+ if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) {
+ error = bwrite(bp);
+ if (error)
+ printf("%s: write FAT (error=%d)\n",
+ __func__, error);
+ } else
+ bdwrite(bp);
+ /*
+ * Maybe update fsinfo sector here?
+ */
+}
+
+/*
+ * Updating entries in 12 bit FATs is a pain in the butt.
+ *
+ * The following picture shows where nibbles go when moving from a 12 bit
+ * cluster number into the appropriate bytes in the FAT.
+ *
+ * byte m byte m+1 byte m+2
+ * +----+----+ +----+----+ +----+----+
+ * | 0 1 | | 2 3 | | 4 5 | FAT bytes
+ * +----+----+ +----+----+ +----+----+
+ *
+ * +----+----+----+ +----+----+----+
+ * | 3 0 1 | | 4 5 2 |
+ * +----+----+----+ +----+----+----+
+ * cluster n cluster n+1
+ *
+ * Where n is even. m = n + (n >> 2)
+ *
+ */
+static inline void
+usemap_alloc(struct msdosfsmount *pmp, u_long cn)
+{
+
+ pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS);
+ pmp->pm_freeclustercount--;
+}
+
+static inline void
+usemap_free(struct msdosfsmount *pmp, u_long cn)
+{
+
+ pmp->pm_freeclustercount++;
+ pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS));
+}
+
+int
+clusterfree(struct msdosfsmount *pmp, u_long cluster, u_long *oldcnp)
+{
+ int error;
+ u_long oldcn;
+
+ usemap_free(pmp, cluster);
+ error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
+ if (error) {
+ usemap_alloc(pmp, cluster);
+ return (error);
+ }
+ /*
+ * If the cluster was successfully marked free, then update
+ * the count of free clusters, and turn off the "allocated"
+ * bit in the "in use" cluster bit map.
+ */
+ if (oldcnp)
+ *oldcnp = oldcn;
+ return (0);
+}
+
+/*
+ * Get or Set or 'Get and Set' the cluster'th entry in the FAT.
+ *
+ * function - whether to get or set a fat entry
+ * pmp - address of the msdosfsmount structure for the filesystem
+ * whose FAT is to be manipulated.
+ * cn - which cluster is of interest
+ * oldcontents - address of a word that is to receive the contents of the
+ * cluster'th entry if this is a get function
+ * newcontents - the new value to be written into the cluster'th element of
+ * the FAT if this is a set function.
+ *
+ * This function can also be used to free a cluster by setting the FAT entry
+ * for a cluster to 0.
+ *
+ * All copies of the FAT are updated if this is a set function. NOTE: If
+ * fatentry() marks a cluster as free it does not update the inusemap in
+ * the msdosfsmount structure. This is left to the caller.
+ */
+int
+fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents, u_long newcontents)
+{
+ int error;
+ u_long readcn;
+ u_long bn, bo, bsize, byteoffset;
+ struct buf *bp;
+
+ DPRINTF(("%s(func %d, pmp %p, clust %lu, oldcon %p, newcon " "%lx)\n",
+ __func__, function, pmp, cn, oldcontents, newcontents));
+
+#ifdef DIAGNOSTIC
+ /*
+ * Be sure they asked us to do something.
+ */
+ if ((function & (FAT_SET | FAT_GET)) == 0) {
+ DPRINTF(("%s(): function code doesn't specify get or set\n",
+ __func__));
+ return (EINVAL);
+ }
+
+ /*
+ * If they asked us to return a cluster number but didn't tell us
+ * where to put it, give them an error.
+ */
+ if ((function & FAT_GET) && oldcontents == NULL) {
+ DPRINTF(("%s(): get function with no place to put result\n",
+ __func__));
+ return (EINVAL);
+ }
+#endif
+
+ /*
+ * Be sure the requested cluster is in the filesystem.
+ */
+ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
+ return (EINVAL);
+
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize,
+ 0, &bp)) != 0) {
+ return (error);
+ }
+
+ if (function & FAT_GET) {
+ if (FAT32(pmp))
+ readcn = getulong((char *)bp->b_data + bo);
+ else
+ readcn = getushort((char *)bp->b_data + bo);
+ if (FAT12(pmp) & (cn & 1))
+ readcn >>= 4;
+ readcn &= pmp->pm_fatmask;
+ *oldcontents = readcn;
+ }
+ if (function & FAT_SET) {
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
+ readcn = getushort((char *)bp->b_data + bo);
+ if (cn & 1) {
+ readcn &= 0x000f;
+ readcn |= newcontents << 4;
+ } else {
+ readcn &= 0xf000;
+ readcn |= newcontents & 0xfff;
+ }
+ putushort((char *)bp->b_data + bo, readcn);
+ break;
+ case FAT16_MASK:
+ putushort((char *)bp->b_data + bo, newcontents);
+ break;
+ case FAT32_MASK:
+ /*
+ * According to spec we have to retain the
+ * high order bits of the FAT entry.
+ */
+ readcn = getulong((char *)bp->b_data + bo);
+ readcn &= ~FAT32_MASK;
+ readcn |= newcontents & FAT32_MASK;
+ putulong((char *)bp->b_data + bo, readcn);
+ break;
+ }
+ updatefats(pmp, bp, bn);
+ bp = NULL;
+ pmp->pm_fmod = 1;
+ }
+ if (bp)
+ brelse(bp, 0);
+ return (0);
+}
+
+/*
+ * Update a contiguous cluster chain
+ *
+ * pmp - mount point
+ * start - first cluster of chain
+ * count - number of clusters in chain
+ * fillwith - what to write into FAT entry of last cluster
+ */
+static int
+fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith)
+{
+ int error;
+ u_long bn, bo, bsize, byteoffset, readcn, newc;
+ struct buf *bp;
+
+ DPRINTF(("%s(pmp %p, start %lu, count %lu, fillwith %lx)\n", __func__,
+ pmp, start, count, fillwith));
+ /*
+ * Be sure the clusters are in the filesystem.
+ */
+ if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
+ return (EINVAL);
+
+ while (count > 0) {
+ byteoffset = FATOFS(pmp, start);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize,
+ B_MODIFY, &bp);
+ if (error) {
+ return (error);
+ }
+ while (count > 0) {
+ start++;
+ newc = --count > 0 ? start : fillwith;
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
+ readcn = getushort((char *)bp->b_data + bo);
+ if (start & 1) {
+ readcn &= 0xf000;
+ readcn |= newc & 0xfff;
+ } else {
+ readcn &= 0x000f;
+ readcn |= newc << 4;
+ }
+ putushort((char *)bp->b_data + bo, readcn);
+ bo++;
+ if (!(start & 1))
+ bo++;
+ break;
+ case FAT16_MASK:
+ putushort((char *)bp->b_data + bo, newc);
+ bo += 2;
+ break;
+ case FAT32_MASK:
+ readcn = getulong((char *)bp->b_data + bo);
+ readcn &= ~pmp->pm_fatmask;
+ readcn |= newc & pmp->pm_fatmask;
+ putulong((char *)bp->b_data + bo, readcn);
+ bo += 4;
+ break;
+ }
+ if (bo >= bsize)
+ break;
+ }
+ updatefats(pmp, bp, bn);
+ }
+ pmp->pm_fmod = 1;
+ return (0);
+}
+
+/*
+ * Check the length of a free cluster chain starting at start.
+ *
+ * pmp - mount point
+ * start - start of chain
+ * count - maximum interesting length
+ */
+int
+chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
+{
+ u_long idx, max_idx;
+ u_int map;
+ u_long len;
+
+ max_idx = pmp->pm_maxcluster / N_INUSEBITS;
+ idx = start / N_INUSEBITS;
+ start %= N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map &= ~((1 << start) - 1);
+ if (map) {
+ len = ffs(map) - 1 - start;
+ return (len > count ? count : len);
+ }
+ len = N_INUSEBITS - start;
+ if (len >= count)
+ return (count);
+ while (++idx <= max_idx) {
+ if (len >= count)
+ break;
+ if ((map = pmp->pm_inusemap[idx]) != 0) {
+ len += ffs(map) - 1;
+ break;
+ }
+ len += N_INUSEBITS;
+ }
+ return (len > count ? count : len);
+}
+
+/*
+ * Allocate contigous free clusters.
+ *
+ * pmp - mount point.
+ * start - start of cluster chain.
+ * count - number of clusters to allocate.
+ * fillwith - put this value into the FAT entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+chainalloc(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got)
+{
+ int error;
+ u_long cl, n;
+
+ for (cl = start, n = count; n-- > 0;)
+ usemap_alloc(pmp, cl++);
+ if ((error = fatchain(pmp, start, count, fillwith)) != 0)
+ return (error);
+
+ DPRINTF(("%s(): allocated cluster chain at %lu (%lu clusters)\n",
+ __func__, start, count));
+ if (retcluster)
+ *retcluster = start;
+ if (got)
+ *got = count;
+ return (0);
+}
+
+/*
+ * Allocate contiguous free clusters.
+ *
+ * pmp - mount point.
+ * start - preferred start of cluster chain.
+ * count - number of clusters requested.
+ * fillwith - put this value into the FAT entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count, u_long *retcluster, u_long *got)
+{
+ u_long idx;
+ u_long len, newst, foundl, cn, l;
+ u_long foundcn = 0; /* XXX: foundcn could be used unititialized */
+ u_long fillwith = CLUST_EOFE;
+ u_int map;
+
+ DPRINTF(("%s(): find %lu clusters\n", __func__, count));
+ if (start) {
+ if ((len = chainlength(pmp, start, count)) >= count)
+ return (chainalloc(pmp, start, count, fillwith, retcluster, got));
+ } else {
+ /*
+ * This is a new file, initialize start
+ */
+ struct timeval tv;
+
+ microtime(&tv);
+ start = (tv.tv_usec >> 10) | tv.tv_usec;
+ len = 0;
+ }
+
+ /*
+ * Start at a (pseudo) random place to maximize cluster runs
+ * under multiple writers.
+ */
+ newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1);
+ foundl = 0;
+
+ for (cn = newst; cn <= pmp->pm_maxcluster;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+ for (cn = 0; cn < newst;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+
+ if (!foundl)
+ return (ENOSPC);
+
+ if (len)
+ return (chainalloc(pmp, start, len, fillwith, retcluster, got));
+ else
+ return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
+}
+
+
+/*
+ * Free a chain of clusters.
+ *
+ * pmp - address of the msdosfs mount structure for the filesystem
+ * containing the cluster chain to be freed.
+ * startcluster - number of the 1st cluster in the chain of clusters to be
+ * freed.
+ */
+int
+freeclusterchain(struct msdosfsmount *pmp, u_long cluster)
+{
+ int error;
+ struct buf *bp = NULL;
+ u_long bn, bo, bsize, byteoffset;
+ u_long readcn, lbn = -1;
+
+ while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
+ byteoffset = FATOFS(pmp, cluster);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (lbn != bn) {
+ if (bp)
+ updatefats(pmp, bp, lbn);
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize,
+ B_MODIFY, &bp);
+ if (error) {
+ return (error);
+ }
+ lbn = bn;
+ }
+ usemap_free(pmp, cluster);
+ KASSERT(bp != NULL);
+ switch (pmp->pm_fatmask) {
+ case FAT12_MASK:
+ readcn = getushort((char *)bp->b_data + bo);
+ if (cluster & 1) {
+ cluster = readcn >> 4;
+ readcn &= 0x000f;
+ readcn |= MSDOSFSFREE << 4;
+ } else {
+ cluster = readcn;
+ readcn &= 0xf000;
+ readcn |= MSDOSFSFREE & 0xfff;
+ }
+ putushort((char *)bp->b_data + bo, readcn);
+ break;
+ case FAT16_MASK:
+ cluster = getushort((char *)bp->b_data + bo);
+ putushort((char *)bp->b_data + bo, MSDOSFSFREE);
+ break;
+ case FAT32_MASK:
+ cluster = getulong((char *)bp->b_data + bo);
+ putulong((char *)bp->b_data + bo,
+ (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
+ break;
+ }
+ cluster &= pmp->pm_fatmask;
+ }
+ if (bp)
+ updatefats(pmp, bp, bn);
+ return (0);
+}
+
+/*
+ * Read in FAT blocks looking for free clusters. For every free cluster
+ * found turn off its corresponding bit in the pm_inusemap.
+ */
+int
+fillinusemap(struct msdosfsmount *pmp)
+{
+ struct buf *bp = NULL;
+ u_long cn, readcn;
+ int error;
+ u_long bn, bo, bsize, byteoffset;
+
+ /*
+ * Mark all clusters in use, we mark the free ones in the FAT scan
+ * loop further down.
+ */
+ for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
+ pmp->pm_inusemap[cn] = (u_int)-1;
+
+ /*
+ * Figure how many free clusters are in the filesystem by ripping
+ * through the FAT counting the number of entries whose content is
+ * zero. These represent free clusters.
+ */
+ pmp->pm_freeclustercount = 0;
+ for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) {
+ byteoffset = FATOFS(pmp, cn);
+ bo = byteoffset % pmp->pm_fatblocksize;
+ if (!bo || !bp) {
+ /* Read new FAT block */
+ if (bp)
+ brelse(bp, 0);
+ fatblock(pmp, byteoffset, &bn, &bsize, NULL);
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), bsize,
+ 0, &bp);
+ if (error) {
+ return (error);
+ }
+ }
+ if (FAT32(pmp))
+ readcn = getulong((char *)bp->b_data + bo);
+ else
+ readcn = getushort((char *)bp->b_data + bo);
+ if (FAT12(pmp) && (cn & 1))
+ readcn >>= 4;
+ readcn &= pmp->pm_fatmask;
+
+ if (readcn == 0)
+ usemap_free(pmp, cn);
+ }
+ if (bp)
+ brelse(bp, 0);
+ return (0);
+}
+
+/*
+ * Allocate a new cluster and chain it onto the end of the file.
+ *
+ * dep - the file to extend
+ * count - number of clusters to allocate
+ * bpp - where to return the address of the buf header for the first new
+ * file block
+ * ncp - where to put cluster number of the first newly allocated cluster
+ * If this pointer is 0, do not return the cluster number.
+ * flags - see fat.h
+ *
+ * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
+ * the de_flag field of the denode and it does not change the de_FileSize
+ * field. This is left for the caller to do.
+ */
+
+int
+extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags)
+{
+ int error;
+ u_long frcn = 0, cn, got;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct buf *bp;
+
+ /*
+ * Don't try to extend the root directory
+ */
+ if (dep->de_StartCluster == MSDOSFSROOT
+ && (dep->de_Attributes & ATTR_DIRECTORY)) {
+ DPRINTF(("%s(): attempt to extend root directory\n", __func__));
+ return (ENOSPC);
+ }
+
+ /*
+ * If the "file's last cluster" cache entry is empty, and the file
+ * is not empty, then fill the cache entry by calling pcbmap().
+ */
+ fc_fileextends++;
+ if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
+ dep->de_StartCluster != 0) {
+ fc_lfcempty++;
+ error = pcbmap(dep, CLUST_END, 0, &cn, 0);
+ /* we expect it to return E2BIG */
+ if (error != E2BIG)
+ return (error);
+ }
+
+ fc_last_to_nexttolast(dep);
+
+ while (count > 0) {
+
+ /*
+ * Allocate a new cluster chain and cat onto the end of the
+ * file. If the file is empty we make de_StartCluster point
+ * to the new block. Note that de_StartCluster being 0 is
+ * sufficient to be sure the file is empty since we exclude
+ * attempts to extend the root directory above, and the root
+ * dir is the only file with a startcluster of 0 that has
+ * blocks allocated (sort of).
+ */
+
+ if (dep->de_StartCluster == 0)
+ cn = 0;
+ else
+ cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
+ error = clusteralloc(pmp, cn, count, &cn, &got);
+ if (error)
+ return (error);
+
+ count -= got;
+
+ /*
+ * Give them the filesystem relative cluster number if they want
+ * it.
+ */
+ if (ncp) {
+ *ncp = cn;
+ ncp = NULL;
+ }
+
+ if (dep->de_StartCluster == 0) {
+ dep->de_StartCluster = cn;
+ frcn = 0;
+ } else {
+ error = fatentry(FAT_SET, pmp,
+ dep->de_fc[FC_LASTFC].fc_fsrcn,
+ 0, cn);
+ if (error) {
+ clusterfree(pmp, cn, NULL);
+ return (error);
+ }
+ frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
+ }
+
+ /*
+ * Update the "last cluster of the file" entry in the
+ * denode's FAT cache.
+ */
+
+ fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
+ if ((flags & DE_CLEAR) &&
+ (dep->de_Attributes & ATTR_DIRECTORY)) {
+ while (got-- > 0) {
+ bp = getblk(pmp->pm_devvp,
+ de_bn2kb(pmp, cntobn(pmp, cn++)),
+ pmp->pm_bpcluster, 0, 0);
+ clrbuf(bp);
+ if (bpp) {
+ *bpp = bp;
+ bpp = NULL;
+ } else {
+ bdwrite(bp);
+ }
+ }
+ }
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/makefs/fs/msdosfs/msdosfs_lookup.c b/usr.sbin/makefs/fs/msdosfs/msdosfs_lookup.c
new file mode 100644
index 00000000000..dfc3d070236
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/msdosfs_lookup.c
@@ -0,0 +1,654 @@
+/* $NetBSD: msdosfs_lookup.c,v 1.35 2016/01/30 09:59:27 mlelstv Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+
+
+/*
+ * dep - directory entry to copy into the directory
+ * ddep - directory to add to
+ * depp - return the address of the denode for the created directory entry
+ * if depp != 0
+ * cnp - componentname needed for Win95 long filenames
+ */
+int
+createde(struct denode *dep, struct denode *ddep, struct denode **depp, struct componentname *cnp)
+{
+ int error, rberror;
+ u_long dirclust, clusoffset;
+ u_long fndoffset, havecnt = 0, wcnt = 1, i;
+ struct direntry *ndep;
+ struct msdosfsmount *pmp = ddep->de_pmp;
+ struct buf *bp;
+ daddr_t bn;
+ int blsize;
+#define async 0
+
+#ifdef MSDOSFS_DEBUG
+ printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
+ dep, ddep, depp, cnp);
+#endif
+
+ /*
+ * If no space left in the directory then allocate another cluster
+ * and chain it onto the end of the file. There is one exception
+ * to this. That is, if the root directory has no more space it
+ * can NOT be expanded. extendfile() checks for and fails attempts
+ * to extend the root directory. We just return an error in that
+ * case.
+ */
+ if (ddep->de_fndoffset >= ddep->de_FileSize) {
+ u_long needlen = ddep->de_fndoffset + sizeof(struct direntry)
+ - ddep->de_FileSize;
+ dirclust = de_clcount(pmp, needlen);
+ if ((error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR)) != 0) {
+ (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED);
+ goto err_norollback;
+ }
+
+ /*
+ * Update the size of the directory
+ */
+ ddep->de_FileSize += de_cn2off(pmp, dirclust);
+ }
+
+ /*
+ * We just read in the cluster with space. Copy the new directory
+ * entry in. Then write it to disk. NOTE: DOS directories
+ * do not get smaller as clusters are emptied.
+ */
+ error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
+ &bn, &dirclust, &blsize);
+ if (error)
+ goto err_norollback;
+ clusoffset = ddep->de_fndoffset;
+ if (dirclust != MSDOSFSROOT)
+ clusoffset &= pmp->pm_crbomask;
+ if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ B_MODIFY, &bp)) != 0) {
+ goto err_norollback;
+ }
+ ndep = bptoep(pmp, bp, clusoffset);
+
+ DE_EXTERNALIZE(ndep, dep);
+
+ /*
+ * Now write the Win95 long name
+ */
+ if (ddep->de_fndcnt > 0) {
+ u_int8_t chksum = winChksum(ndep->deName);
+ const u_char *un = (const u_char *)cnp->cn_nameptr;
+ int unlen = cnp->cn_namelen;
+ u_long xhavecnt;
+
+ fndoffset = ddep->de_fndoffset;
+ xhavecnt = ddep->de_fndcnt + 1;
+
+ for(; wcnt < xhavecnt; wcnt++) {
+ if ((fndoffset & pmp->pm_crbomask) == 0) {
+ /* we should never get here if ddep is root
+ * directory */
+
+ if (async)
+ (void) bdwrite(bp);
+ else if ((error = bwrite(bp)) != 0)
+ goto rollback;
+
+ fndoffset -= sizeof(struct direntry);
+ error = pcbmap(ddep,
+ de_cluster(pmp, fndoffset),
+ &bn, 0, &blsize);
+ if (error)
+ goto rollback;
+
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
+ blsize, B_MODIFY, &bp);
+ if (error) {
+ goto rollback;
+ }
+ ndep = bptoep(pmp, bp,
+ fndoffset & pmp->pm_crbomask);
+ } else {
+ ndep--;
+ fndoffset -= sizeof(struct direntry);
+ }
+ if (!unix2winfn(un, unlen, (struct winentry *)ndep,
+ wcnt, chksum,
+ ddep->de_pmp->pm_flags & MSDOSFSMNT_UTF8))
+ break;
+ }
+ }
+
+ if (async)
+ bdwrite(bp);
+ else if ((error = bwrite(bp)) != 0)
+ goto rollback;
+
+ /*
+ * If they want us to return with the denode gotten.
+ */
+ if (depp) {
+ u_long diroffset = clusoffset;
+
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ dirclust = dep->de_StartCluster;
+ if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
+ dirclust = MSDOSFSROOT;
+ if (dirclust == MSDOSFSROOT)
+ diroffset = MSDOSFSROOT_OFS;
+ else
+ diroffset = 0;
+ }
+ error = deget(pmp, dirclust, diroffset, depp);
+ return error;
+ }
+
+ return 0;
+
+ rollback:
+ /*
+ * Mark all slots modified so far as deleted. Note that we
+ * can't just call removede(), since directory is not in
+ * consistent state.
+ */
+ fndoffset = ddep->de_fndoffset;
+ rberror = pcbmap(ddep, de_cluster(pmp, fndoffset),
+ &bn, NULL, &blsize);
+ if (rberror)
+ goto err_norollback;
+ if ((rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ B_MODIFY, &bp)) != 0) {
+ goto err_norollback;
+ }
+ ndep = bptoep(pmp, bp, clusoffset);
+
+ havecnt = ddep->de_fndcnt + 1;
+ for(i = wcnt; i <= havecnt; i++) {
+ /* mark entry as deleted */
+ ndep->deName[0] = SLOT_DELETED;
+
+ if ((fndoffset & pmp->pm_crbomask) == 0) {
+ /* we should never get here if ddep is root
+ * directory */
+
+ if (async)
+ bdwrite(bp);
+ else if ((rberror = bwrite(bp)) != 0)
+ goto err_norollback;
+
+ fndoffset -= sizeof(struct direntry);
+ rberror = pcbmap(ddep,
+ de_cluster(pmp, fndoffset),
+ &bn, 0, &blsize);
+ if (rberror)
+ goto err_norollback;
+
+ rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
+ blsize, B_MODIFY, &bp);
+ if (rberror) {
+ goto err_norollback;
+ }
+ ndep = bptoep(pmp, bp, fndoffset);
+ } else {
+ ndep--;
+ fndoffset -= sizeof(struct direntry);
+ }
+ }
+
+ /* ignore any further error */
+ if (async)
+ (void) bdwrite(bp);
+ else
+ (void) bwrite(bp);
+
+ err_norollback:
+ return error;
+}
+
+/*
+ * Be sure a directory is empty except for "." and "..". Return 1 if empty,
+ * return 0 if not empty or error.
+ */
+int
+dosdirempty(struct denode *dep)
+{
+ int blsize;
+ int error;
+ u_long cn;
+ daddr_t bn;
+ struct buf *bp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+
+ /*
+ * Since the filesize field in directory entries for a directory is
+ * zero, we just have to feel our way through the directory until
+ * we hit end of file.
+ */
+ for (cn = 0;; cn++) {
+ if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
+ if (error == E2BIG)
+ return (1); /* it's empty */
+ return (0);
+ }
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, &bp);
+ if (error) {
+ return (0);
+ }
+ for (dentp = (struct direntry *)bp->b_data;
+ (char *)dentp < (char *)bp->b_data + blsize;
+ dentp++) {
+ if (dentp->deName[0] != SLOT_DELETED &&
+ (dentp->deAttributes & ATTR_VOLUME) == 0) {
+ /*
+ * In dos directories an entry whose name
+ * starts with SLOT_EMPTY (0) starts the
+ * beginning of the unused part of the
+ * directory, so we can just return that it
+ * is empty.
+ */
+ if (dentp->deName[0] == SLOT_EMPTY) {
+ brelse(bp, 0);
+ return (1);
+ }
+ /*
+ * Any names other than "." and ".." in a
+ * directory mean it is not empty.
+ */
+ if (memcmp(dentp->deName, ". ", 11) &&
+ memcmp(dentp->deName, ".. ", 11)) {
+ brelse(bp, 0);
+#ifdef MSDOSFS_DEBUG
+ printf("dosdirempty(): found %.11s, %d, %d\n",
+ dentp->deName, dentp->deName[0],
+ dentp->deName[1]);
+#endif
+ return (0); /* not empty */
+ }
+ }
+ }
+ brelse(bp, 0);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Check to see if the directory described by target is in some
+ * subdirectory of source. This prevents something like the following from
+ * succeeding and leaving a bunch or files and directories orphaned. mv
+ * /a/b/c /a/b/c/d/e/f Where c and f are directories.
+ *
+ * source - the inode for /a/b/c
+ * target - the inode for /a/b/c/d/e/f
+ *
+ * Returns 0 if target is NOT a subdirectory of source.
+ * Otherwise returns a non-zero error number.
+ * The target inode is always unlocked on return.
+ */
+int
+doscheckpath(struct denode *source, struct denode *target)
+{
+ u_long scn;
+ struct msdosfsmount *pmp;
+ struct direntry *ep;
+ struct denode *dep;
+ struct buf *bp = NULL;
+ int error = 0;
+
+ dep = target;
+ if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
+ (source->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (dep->de_StartCluster == source->de_StartCluster) {
+ error = EEXIST;
+ goto out;
+ }
+ if (dep->de_StartCluster == MSDOSFSROOT)
+ goto out;
+ pmp = dep->de_pmp;
+#ifdef DIAGNOSTIC
+ if (pmp != source->de_pmp)
+ panic("doscheckpath: source and target on different filesystems");
+#endif
+ if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
+ goto out;
+
+ for (;;) {
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ break;
+ }
+ scn = dep->de_StartCluster;
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, scn)),
+ pmp->pm_bpcluster, 0, &bp);
+ if (error)
+ break;
+
+ ep = (struct direntry *) bp->b_data + 1;
+ if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
+ memcmp(ep->deName, ".. ", 11) != 0) {
+ error = ENOTDIR;
+ break;
+ }
+ scn = getushort(ep->deStartCluster);
+ if (FAT32(pmp))
+ scn |= getushort(ep->deHighClust) << 16;
+
+ if (scn == source->de_StartCluster) {
+ error = EINVAL;
+ break;
+ }
+ if (scn == MSDOSFSROOT)
+ break;
+ if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
+ /*
+ * scn should be 0 in this case,
+ * but we silently ignore the error.
+ */
+ break;
+ }
+
+ vput(DETOV(dep));
+ brelse(bp, 0);
+ bp = NULL;
+ /* NOTE: deget() clears dep on error */
+ if ((error = deget(pmp, scn, 0, &dep)) != 0)
+ break;
+ }
+out:
+ if (bp)
+ brelse(bp, 0);
+ if (error == ENOTDIR)
+ printf("doscheckpath(): .. not a directory?\n");
+ if (dep != NULL)
+ vput(DETOV(dep));
+ return (error);
+}
+
+/*
+ * Read in the disk block containing the directory entry (dirclu, dirofs)
+ * and return the address of the buf header, and the address of the
+ * directory entry within the block.
+ */
+int
+readep(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, struct buf **bpp, struct direntry **epp)
+{
+ int error;
+ daddr_t bn;
+ int blsize;
+
+ blsize = pmp->pm_bpcluster;
+ if (dirclust == MSDOSFSROOT
+ && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
+ blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
+ bn = detobn(pmp, dirclust, diroffset);
+ if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, bpp)) != 0) {
+ *bpp = NULL;
+ return (error);
+ }
+ if (epp)
+ *epp = bptoep(pmp, *bpp, diroffset);
+ return (0);
+}
+
+/*
+ * Read in the disk block containing the directory entry dep came from and
+ * return the address of the buf header, and the address of the directory
+ * entry within the block.
+ */
+int
+readde(struct denode *dep, struct buf **bpp, struct direntry **epp)
+{
+ return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
+ bpp, epp));
+}
+
+/*
+ * Remove a directory entry. At this point the file represented by the
+ * directory entry to be removed is still full length until noone has it
+ * open. When the file no longer being used msdosfs_inactive() is called
+ * and will truncate the file to 0 length. When the vnode containing the
+ * denode is needed for some other purpose by VFS it will call
+ * msdosfs_reclaim() which will remove the denode from the denode cache.
+ */
+int
+removede(struct denode *pdep, struct denode *dep)
+ /* pdep: directory where the entry is removed */
+ /* dep: file to be removed */
+{
+ int error;
+ struct direntry *ep;
+ struct buf *bp;
+ daddr_t bn;
+ int blsize;
+ struct msdosfsmount *pmp = pdep->de_pmp;
+ u_long offset = pdep->de_fndoffset;
+#define async 0
+
+#ifdef MSDOSFS_DEBUG
+ printf("removede(): filename %s, dep %p, offset %08lx\n",
+ dep->de_Name, dep, offset);
+#endif
+
+ if (--dep->de_refcnt == 0) {
+ }
+ offset += sizeof(struct direntry);
+ do {
+ offset -= sizeof(struct direntry);
+ error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
+ if (error)
+ return error;
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ B_MODIFY, &bp);
+ if (error) {
+ return error;
+ }
+ ep = bptoep(pmp, bp, offset);
+ /*
+ * Check whether, if we came here the second time, i.e.
+ * when underflowing into the previous block, the last
+ * entry in this block is a longfilename entry, too.
+ */
+ if (ep->deAttributes != ATTR_WIN95
+ && offset != pdep->de_fndoffset) {
+ brelse(bp, 0);
+ break;
+ }
+ offset += sizeof(struct direntry);
+ while (1) {
+ /*
+ * We are a bit agressive here in that we delete any Win95
+ * entries preceding this entry, not just the ones we "own".
+ * Since these presumably aren't valid anyway,
+ * there should be no harm.
+ */
+ offset -= sizeof(struct direntry);
+ ep--->deName[0] = SLOT_DELETED;
+ if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
+ || !(offset & pmp->pm_crbomask)
+ || ep->deAttributes != ATTR_WIN95)
+ break;
+ }
+ if (async)
+ bdwrite(bp);
+ else if ((error = bwrite(bp)) != 0)
+ return error;
+ } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
+ && !(offset & pmp->pm_crbomask)
+ && offset);
+ return 0;
+}
+
+/*
+ * Create a unique DOS name in dvp
+ */
+int
+uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)
+{
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+ int gen;
+ int blsize;
+ u_long cn;
+ daddr_t bn;
+ struct buf *bp;
+ int error;
+
+ for (gen = 1;; gen++) {
+ /*
+ * Generate DOS name with generation number
+ */
+ if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
+ cnp->cn_namelen, gen))
+ return gen == 1 ? EINVAL : EEXIST;
+
+ /*
+ * Now look for a dir entry with this exact name
+ */
+ for (cn = error = 0; !error; cn++) {
+ if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
+ if (error == E2BIG) /* EOF reached and not found */
+ return 0;
+ return error;
+ }
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, &bp);
+ if (error) {
+ return error;
+ }
+ for (dentp = (struct direntry *)bp->b_data;
+ (char *)dentp < (char *)bp->b_data + blsize;
+ dentp++) {
+ if (dentp->deName[0] == SLOT_EMPTY) {
+ /*
+ * Last used entry and not found
+ */
+ brelse(bp, 0);
+ return 0;
+ }
+ /*
+ * Ignore volume labels and Win95 entries
+ */
+ if (dentp->deAttributes & ATTR_VOLUME)
+ continue;
+ if (!memcmp(dentp->deName, cp, 11)) {
+ error = EEXIST;
+ break;
+ }
+ }
+ brelse(bp, 0);
+ }
+ }
+}
+
+/*
+ * Find any Win'95 long filename entry in directory dep
+ */
+int
+findwin95(struct denode *dep)
+{
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+ int blsize, win95;
+ u_long cn;
+ daddr_t bn;
+ struct buf *bp;
+
+ win95 = 1;
+ /*
+ * Read through the directory looking for Win'95 entries
+ * XXX Note: Error currently handled just as EOF
+ */
+ for (cn = 0;; cn++) {
+ if (pcbmap(dep, cn, &bn, 0, &blsize))
+ return win95;
+ if (bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, &bp)) {
+ return win95;
+ }
+ for (dentp = (struct direntry *)bp->b_data;
+ (char *)dentp < (char *)bp->b_data + blsize;
+ dentp++) {
+ if (dentp->deName[0] == SLOT_EMPTY) {
+ /*
+ * Last used entry and not found
+ */
+ brelse(bp, 0);
+ return win95;
+ }
+ if (dentp->deName[0] == SLOT_DELETED) {
+ /*
+ * Ignore deleted files
+ * Note: might be an indication of Win'95
+ * anyway XXX
+ */
+ continue;
+ }
+ if (dentp->deAttributes == ATTR_WIN95) {
+ brelse(bp, 0);
+ return 1;
+ }
+ win95 = 0;
+ }
+ brelse(bp, 0);
+ }
+}
diff --git a/usr.sbin/makefs/fs/msdosfs/msdosfs_unicode.c b/usr.sbin/makefs/fs/msdosfs/msdosfs_unicode.c
new file mode 100644
index 00000000000..b59f29a294e
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/msdosfs_unicode.c
@@ -0,0 +1,960 @@
+/* $NetBSD: msdosfs_unicode.c,v 1.2 2016/02/06 14:11:58 joerg Exp $ */
+
+/*
+ * Unicode 5.0 case folding derived from
+ *
+ * http://www.unicode.org/Public/5.0.0/ucd/CaseFolding.txt
+ */
+
+/*
+ * Unicode Character Database
+ * Copyright (c) 1991-2006 Unicode, Inc.
+ * For terms of use, see http://www.unicode.org/terms_of_use.html
+ * For documentation, see UCD.html
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright © 1991-2015 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software,
+ * (b) this copyright and permission notice appear in associated
+ * documentation, and
+ * (c) there is clear notice in each modified Data File or in the Software
+ * as well as in the documentation associated with the Data File(s) or
+ * Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
+ *
+ */
+
+#include <sys/types.h>
+
+/*
+ * Unicode case folding for codes 0x0000..0xffff
+ *
+ * common case folding + simple case folding only
+ */
+const u_int16_t
+msdosfs_unicode_foldmap[] = {
+ 0x0041, 0x0061, /* LATIN CAPITAL LETTER A */
+ 0x0042, 0x0062, /* LATIN CAPITAL LETTER B */
+ 0x0043, 0x0063, /* LATIN CAPITAL LETTER C */
+ 0x0044, 0x0064, /* LATIN CAPITAL LETTER D */
+ 0x0045, 0x0065, /* LATIN CAPITAL LETTER E */
+ 0x0046, 0x0066, /* LATIN CAPITAL LETTER F */
+ 0x0047, 0x0067, /* LATIN CAPITAL LETTER G */
+ 0x0048, 0x0068, /* LATIN CAPITAL LETTER H */
+ 0x0049, 0x0069, /* LATIN CAPITAL LETTER I */
+ 0x004A, 0x006A, /* LATIN CAPITAL LETTER J */
+ 0x004B, 0x006B, /* LATIN CAPITAL LETTER K */
+ 0x004C, 0x006C, /* LATIN CAPITAL LETTER L */
+ 0x004D, 0x006D, /* LATIN CAPITAL LETTER M */
+ 0x004E, 0x006E, /* LATIN CAPITAL LETTER N */
+ 0x004F, 0x006F, /* LATIN CAPITAL LETTER O */
+ 0x0050, 0x0070, /* LATIN CAPITAL LETTER P */
+ 0x0051, 0x0071, /* LATIN CAPITAL LETTER Q */
+ 0x0052, 0x0072, /* LATIN CAPITAL LETTER R */
+ 0x0053, 0x0073, /* LATIN CAPITAL LETTER S */
+ 0x0054, 0x0074, /* LATIN CAPITAL LETTER T */
+ 0x0055, 0x0075, /* LATIN CAPITAL LETTER U */
+ 0x0056, 0x0076, /* LATIN CAPITAL LETTER V */
+ 0x0057, 0x0077, /* LATIN CAPITAL LETTER W */
+ 0x0058, 0x0078, /* LATIN CAPITAL LETTER X */
+ 0x0059, 0x0079, /* LATIN CAPITAL LETTER Y */
+ 0x005A, 0x007A, /* LATIN CAPITAL LETTER Z */
+ 0x00B5, 0x03BC, /* MICRO SIGN */
+ 0x00C0, 0x00E0, /* LATIN CAPITAL LETTER A WITH GRAVE */
+ 0x00C1, 0x00E1, /* LATIN CAPITAL LETTER A WITH ACUTE */
+ 0x00C2, 0x00E2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+ 0x00C3, 0x00E3, /* LATIN CAPITAL LETTER A WITH TILDE */
+ 0x00C4, 0x00E4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */
+ 0x00C5, 0x00E5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */
+ 0x00C6, 0x00E6, /* LATIN CAPITAL LETTER AE */
+ 0x00C7, 0x00E7, /* LATIN CAPITAL LETTER C WITH CEDILLA */
+ 0x00C8, 0x00E8, /* LATIN CAPITAL LETTER E WITH GRAVE */
+ 0x00C9, 0x00E9, /* LATIN CAPITAL LETTER E WITH ACUTE */
+ 0x00CA, 0x00EA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
+ 0x00CB, 0x00EB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
+ 0x00CC, 0x00EC, /* LATIN CAPITAL LETTER I WITH GRAVE */
+ 0x00CD, 0x00ED, /* LATIN CAPITAL LETTER I WITH ACUTE */
+ 0x00CE, 0x00EE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+ 0x00CF, 0x00EF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */
+ 0x00D0, 0x00F0, /* LATIN CAPITAL LETTER ETH */
+ 0x00D1, 0x00F1, /* LATIN CAPITAL LETTER N WITH TILDE */
+ 0x00D2, 0x00F2, /* LATIN CAPITAL LETTER O WITH GRAVE */
+ 0x00D3, 0x00F3, /* LATIN CAPITAL LETTER O WITH ACUTE */
+ 0x00D4, 0x00F4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+ 0x00D5, 0x00F5, /* LATIN CAPITAL LETTER O WITH TILDE */
+ 0x00D6, 0x00F6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */
+ 0x00D8, 0x00F8, /* LATIN CAPITAL LETTER O WITH STROKE */
+ 0x00D9, 0x00F9, /* LATIN CAPITAL LETTER U WITH GRAVE */
+ 0x00DA, 0x00FA, /* LATIN CAPITAL LETTER U WITH ACUTE */
+ 0x00DB, 0x00FB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
+ 0x00DC, 0x00FC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */
+ 0x00DD, 0x00FD, /* LATIN CAPITAL LETTER Y WITH ACUTE */
+ 0x00DE, 0x00FE, /* LATIN CAPITAL LETTER THORN */
+ 0x0100, 0x0101, /* LATIN CAPITAL LETTER A WITH MACRON */
+ 0x0102, 0x0103, /* LATIN CAPITAL LETTER A WITH BREVE */
+ 0x0104, 0x0105, /* LATIN CAPITAL LETTER A WITH OGONEK */
+ 0x0106, 0x0107, /* LATIN CAPITAL LETTER C WITH ACUTE */
+ 0x0108, 0x0109, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+ 0x010A, 0x010B, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */
+ 0x010C, 0x010D, /* LATIN CAPITAL LETTER C WITH CARON */
+ 0x010E, 0x010F, /* LATIN CAPITAL LETTER D WITH CARON */
+ 0x0110, 0x0111, /* LATIN CAPITAL LETTER D WITH STROKE */
+ 0x0112, 0x0113, /* LATIN CAPITAL LETTER E WITH MACRON */
+ 0x0114, 0x0115, /* LATIN CAPITAL LETTER E WITH BREVE */
+ 0x0116, 0x0117, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */
+ 0x0118, 0x0119, /* LATIN CAPITAL LETTER E WITH OGONEK */
+ 0x011A, 0x011B, /* LATIN CAPITAL LETTER E WITH CARON */
+ 0x011C, 0x011D, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+ 0x011E, 0x011F, /* LATIN CAPITAL LETTER G WITH BREVE */
+ 0x0120, 0x0121, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */
+ 0x0122, 0x0123, /* LATIN CAPITAL LETTER G WITH CEDILLA */
+ 0x0124, 0x0125, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+ 0x0126, 0x0127, /* LATIN CAPITAL LETTER H WITH STROKE */
+ 0x0128, 0x0129, /* LATIN CAPITAL LETTER I WITH TILDE */
+ 0x012A, 0x012B, /* LATIN CAPITAL LETTER I WITH MACRON */
+ 0x012C, 0x012D, /* LATIN CAPITAL LETTER I WITH BREVE */
+ 0x012E, 0x012F, /* LATIN CAPITAL LETTER I WITH OGONEK */
+ 0x0132, 0x0133, /* LATIN CAPITAL LIGATURE IJ */
+ 0x0134, 0x0135, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+ 0x0136, 0x0137, /* LATIN CAPITAL LETTER K WITH CEDILLA */
+ 0x0139, 0x013A, /* LATIN CAPITAL LETTER L WITH ACUTE */
+ 0x013B, 0x013C, /* LATIN CAPITAL LETTER L WITH CEDILLA */
+ 0x013D, 0x013E, /* LATIN CAPITAL LETTER L WITH CARON */
+ 0x013F, 0x0140, /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */
+ 0x0141, 0x0142, /* LATIN CAPITAL LETTER L WITH STROKE */
+ 0x0143, 0x0144, /* LATIN CAPITAL LETTER N WITH ACUTE */
+ 0x0145, 0x0146, /* LATIN CAPITAL LETTER N WITH CEDILLA */
+ 0x0147, 0x0148, /* LATIN CAPITAL LETTER N WITH CARON */
+ 0x014A, 0x014B, /* LATIN CAPITAL LETTER ENG */
+ 0x014C, 0x014D, /* LATIN CAPITAL LETTER O WITH MACRON */
+ 0x014E, 0x014F, /* LATIN CAPITAL LETTER O WITH BREVE */
+ 0x0150, 0x0151, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+ 0x0152, 0x0153, /* LATIN CAPITAL LIGATURE OE */
+ 0x0154, 0x0155, /* LATIN CAPITAL LETTER R WITH ACUTE */
+ 0x0156, 0x0157, /* LATIN CAPITAL LETTER R WITH CEDILLA */
+ 0x0158, 0x0159, /* LATIN CAPITAL LETTER R WITH CARON */
+ 0x015A, 0x015B, /* LATIN CAPITAL LETTER S WITH ACUTE */
+ 0x015C, 0x015D, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+ 0x015E, 0x015F, /* LATIN CAPITAL LETTER S WITH CEDILLA */
+ 0x0160, 0x0161, /* LATIN CAPITAL LETTER S WITH CARON */
+ 0x0162, 0x0163, /* LATIN CAPITAL LETTER T WITH CEDILLA */
+ 0x0164, 0x0165, /* LATIN CAPITAL LETTER T WITH CARON */
+ 0x0166, 0x0167, /* LATIN CAPITAL LETTER T WITH STROKE */
+ 0x0168, 0x0169, /* LATIN CAPITAL LETTER U WITH TILDE */
+ 0x016A, 0x016B, /* LATIN CAPITAL LETTER U WITH MACRON */
+ 0x016C, 0x016D, /* LATIN CAPITAL LETTER U WITH BREVE */
+ 0x016E, 0x016F, /* LATIN CAPITAL LETTER U WITH RING ABOVE */
+ 0x0170, 0x0171, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+ 0x0172, 0x0173, /* LATIN CAPITAL LETTER U WITH OGONEK */
+ 0x0174, 0x0175, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */
+ 0x0176, 0x0177, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */
+ 0x0178, 0x00FF, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
+ 0x0179, 0x017A, /* LATIN CAPITAL LETTER Z WITH ACUTE */
+ 0x017B, 0x017C, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+ 0x017D, 0x017E, /* LATIN CAPITAL LETTER Z WITH CARON */
+ 0x017F, 0x0073, /* LATIN SMALL LETTER LONG S */
+ 0x0181, 0x0253, /* LATIN CAPITAL LETTER B WITH HOOK */
+ 0x0182, 0x0183, /* LATIN CAPITAL LETTER B WITH TOPBAR */
+ 0x0184, 0x0185, /* LATIN CAPITAL LETTER TONE SIX */
+ 0x0186, 0x0254, /* LATIN CAPITAL LETTER OPEN O */
+ 0x0187, 0x0188, /* LATIN CAPITAL LETTER C WITH HOOK */
+ 0x0189, 0x0256, /* LATIN CAPITAL LETTER AFRICAN D */
+ 0x018A, 0x0257, /* LATIN CAPITAL LETTER D WITH HOOK */
+ 0x018B, 0x018C, /* LATIN CAPITAL LETTER D WITH TOPBAR */
+ 0x018E, 0x01DD, /* LATIN CAPITAL LETTER REVERSED E */
+ 0x018F, 0x0259, /* LATIN CAPITAL LETTER SCHWA */
+ 0x0190, 0x025B, /* LATIN CAPITAL LETTER OPEN E */
+ 0x0191, 0x0192, /* LATIN CAPITAL LETTER F WITH HOOK */
+ 0x0193, 0x0260, /* LATIN CAPITAL LETTER G WITH HOOK */
+ 0x0194, 0x0263, /* LATIN CAPITAL LETTER GAMMA */
+ 0x0196, 0x0269, /* LATIN CAPITAL LETTER IOTA */
+ 0x0197, 0x0268, /* LATIN CAPITAL LETTER I WITH STROKE */
+ 0x0198, 0x0199, /* LATIN CAPITAL LETTER K WITH HOOK */
+ 0x019C, 0x026F, /* LATIN CAPITAL LETTER TURNED M */
+ 0x019D, 0x0272, /* LATIN CAPITAL LETTER N WITH LEFT HOOK */
+ 0x019F, 0x0275, /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE */
+ 0x01A0, 0x01A1, /* LATIN CAPITAL LETTER O WITH HORN */
+ 0x01A2, 0x01A3, /* LATIN CAPITAL LETTER OI */
+ 0x01A4, 0x01A5, /* LATIN CAPITAL LETTER P WITH HOOK */
+ 0x01A6, 0x0280, /* LATIN LETTER YR */
+ 0x01A7, 0x01A8, /* LATIN CAPITAL LETTER TONE TWO */
+ 0x01A9, 0x0283, /* LATIN CAPITAL LETTER ESH */
+ 0x01AC, 0x01AD, /* LATIN CAPITAL LETTER T WITH HOOK */
+ 0x01AE, 0x0288, /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */
+ 0x01AF, 0x01B0, /* LATIN CAPITAL LETTER U WITH HORN */
+ 0x01B1, 0x028A, /* LATIN CAPITAL LETTER UPSILON */
+ 0x01B2, 0x028B, /* LATIN CAPITAL LETTER V WITH HOOK */
+ 0x01B3, 0x01B4, /* LATIN CAPITAL LETTER Y WITH HOOK */
+ 0x01B5, 0x01B6, /* LATIN CAPITAL LETTER Z WITH STROKE */
+ 0x01B7, 0x0292, /* LATIN CAPITAL LETTER EZH */
+ 0x01B8, 0x01B9, /* LATIN CAPITAL LETTER EZH REVERSED */
+ 0x01BC, 0x01BD, /* LATIN CAPITAL LETTER TONE FIVE */
+ 0x01C4, 0x01C6, /* LATIN CAPITAL LETTER DZ WITH CARON */
+ 0x01C5, 0x01C6, /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON */
+ 0x01C7, 0x01C9, /* LATIN CAPITAL LETTER LJ */
+ 0x01C8, 0x01C9, /* LATIN CAPITAL LETTER L WITH SMALL LETTER J */
+ 0x01CA, 0x01CC, /* LATIN CAPITAL LETTER NJ */
+ 0x01CB, 0x01CC, /* LATIN CAPITAL LETTER N WITH SMALL LETTER J */
+ 0x01CD, 0x01CE, /* LATIN CAPITAL LETTER A WITH CARON */
+ 0x01CF, 0x01D0, /* LATIN CAPITAL LETTER I WITH CARON */
+ 0x01D1, 0x01D2, /* LATIN CAPITAL LETTER O WITH CARON */
+ 0x01D3, 0x01D4, /* LATIN CAPITAL LETTER U WITH CARON */
+ 0x01D5, 0x01D6, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */
+ 0x01D7, 0x01D8, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */
+ 0x01D9, 0x01DA, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */
+ 0x01DB, 0x01DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */
+ 0x01DE, 0x01DF, /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */
+ 0x01E0, 0x01E1, /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */
+ 0x01E2, 0x01E3, /* LATIN CAPITAL LETTER AE WITH MACRON */
+ 0x01E4, 0x01E5, /* LATIN CAPITAL LETTER G WITH STROKE */
+ 0x01E6, 0x01E7, /* LATIN CAPITAL LETTER G WITH CARON */
+ 0x01E8, 0x01E9, /* LATIN CAPITAL LETTER K WITH CARON */
+ 0x01EA, 0x01EB, /* LATIN CAPITAL LETTER O WITH OGONEK */
+ 0x01EC, 0x01ED, /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */
+ 0x01EE, 0x01EF, /* LATIN CAPITAL LETTER EZH WITH CARON */
+ 0x01F1, 0x01F3, /* LATIN CAPITAL LETTER DZ */
+ 0x01F2, 0x01F3, /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z */
+ 0x01F4, 0x01F5, /* LATIN CAPITAL LETTER G WITH ACUTE */
+ 0x01F6, 0x0195, /* LATIN CAPITAL LETTER HWAIR */
+ 0x01F7, 0x01BF, /* LATIN CAPITAL LETTER WYNN */
+ 0x01F8, 0x01F9, /* LATIN CAPITAL LETTER N WITH GRAVE */
+ 0x01FA, 0x01FB, /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */
+ 0x01FC, 0x01FD, /* LATIN CAPITAL LETTER AE WITH ACUTE */
+ 0x01FE, 0x01FF, /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */
+ 0x0200, 0x0201, /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */
+ 0x0202, 0x0203, /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */
+ 0x0204, 0x0205, /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */
+ 0x0206, 0x0207, /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */
+ 0x0208, 0x0209, /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */
+ 0x020A, 0x020B, /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */
+ 0x020C, 0x020D, /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */
+ 0x020E, 0x020F, /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */
+ 0x0210, 0x0211, /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */
+ 0x0212, 0x0213, /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */
+ 0x0214, 0x0215, /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */
+ 0x0216, 0x0217, /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */
+ 0x0218, 0x0219, /* LATIN CAPITAL LETTER S WITH COMMA BELOW */
+ 0x021A, 0x021B, /* LATIN CAPITAL LETTER T WITH COMMA BELOW */
+ 0x021C, 0x021D, /* LATIN CAPITAL LETTER YOGH */
+ 0x021E, 0x021F, /* LATIN CAPITAL LETTER H WITH CARON */
+ 0x0220, 0x019E, /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */
+ 0x0222, 0x0223, /* LATIN CAPITAL LETTER OU */
+ 0x0224, 0x0225, /* LATIN CAPITAL LETTER Z WITH HOOK */
+ 0x0226, 0x0227, /* LATIN CAPITAL LETTER A WITH DOT ABOVE */
+ 0x0228, 0x0229, /* LATIN CAPITAL LETTER E WITH CEDILLA */
+ 0x022A, 0x022B, /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */
+ 0x022C, 0x022D, /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */
+ 0x022E, 0x022F, /* LATIN CAPITAL LETTER O WITH DOT ABOVE */
+ 0x0230, 0x0231, /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */
+ 0x0232, 0x0233, /* LATIN CAPITAL LETTER Y WITH MACRON */
+ 0x023A, 0x2C65, /* LATIN CAPITAL LETTER A WITH STROKE */
+ 0x023B, 0x023C, /* LATIN CAPITAL LETTER C WITH STROKE */
+ 0x023D, 0x019A, /* LATIN CAPITAL LETTER L WITH BAR */
+ 0x023E, 0x2C66, /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */
+ 0x0241, 0x0242, /* LATIN CAPITAL LETTER GLOTTAL STOP */
+ 0x0243, 0x0180, /* LATIN CAPITAL LETTER B WITH STROKE */
+ 0x0244, 0x0289, /* LATIN CAPITAL LETTER U BAR */
+ 0x0245, 0x028C, /* LATIN CAPITAL LETTER TURNED V */
+ 0x0246, 0x0247, /* LATIN CAPITAL LETTER E WITH STROKE */
+ 0x0248, 0x0249, /* LATIN CAPITAL LETTER J WITH STROKE */
+ 0x024A, 0x024B, /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL */
+ 0x024C, 0x024D, /* LATIN CAPITAL LETTER R WITH STROKE */
+ 0x024E, 0x024F, /* LATIN CAPITAL LETTER Y WITH STROKE */
+ 0x0345, 0x03B9, /* COMBINING GREEK YPOGEGRAMMENI */
+ 0x0386, 0x03AC, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */
+ 0x0388, 0x03AD, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */
+ 0x0389, 0x03AE, /* GREEK CAPITAL LETTER ETA WITH TONOS */
+ 0x038A, 0x03AF, /* GREEK CAPITAL LETTER IOTA WITH TONOS */
+ 0x038C, 0x03CC, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */
+ 0x038E, 0x03CD, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */
+ 0x038F, 0x03CE, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */
+ 0x0391, 0x03B1, /* GREEK CAPITAL LETTER ALPHA */
+ 0x0392, 0x03B2, /* GREEK CAPITAL LETTER BETA */
+ 0x0393, 0x03B3, /* GREEK CAPITAL LETTER GAMMA */
+ 0x0394, 0x03B4, /* GREEK CAPITAL LETTER DELTA */
+ 0x0395, 0x03B5, /* GREEK CAPITAL LETTER EPSILON */
+ 0x0396, 0x03B6, /* GREEK CAPITAL LETTER ZETA */
+ 0x0397, 0x03B7, /* GREEK CAPITAL LETTER ETA */
+ 0x0398, 0x03B8, /* GREEK CAPITAL LETTER THETA */
+ 0x0399, 0x03B9, /* GREEK CAPITAL LETTER IOTA */
+ 0x039A, 0x03BA, /* GREEK CAPITAL LETTER KAPPA */
+ 0x039B, 0x03BB, /* GREEK CAPITAL LETTER LAMDA */
+ 0x039C, 0x03BC, /* GREEK CAPITAL LETTER MU */
+ 0x039D, 0x03BD, /* GREEK CAPITAL LETTER NU */
+ 0x039E, 0x03BE, /* GREEK CAPITAL LETTER XI */
+ 0x039F, 0x03BF, /* GREEK CAPITAL LETTER OMICRON */
+ 0x03A0, 0x03C0, /* GREEK CAPITAL LETTER PI */
+ 0x03A1, 0x03C1, /* GREEK CAPITAL LETTER RHO */
+ 0x03A3, 0x03C3, /* GREEK CAPITAL LETTER SIGMA */
+ 0x03A4, 0x03C4, /* GREEK CAPITAL LETTER TAU */
+ 0x03A5, 0x03C5, /* GREEK CAPITAL LETTER UPSILON */
+ 0x03A6, 0x03C6, /* GREEK CAPITAL LETTER PHI */
+ 0x03A7, 0x03C7, /* GREEK CAPITAL LETTER CHI */
+ 0x03A8, 0x03C8, /* GREEK CAPITAL LETTER PSI */
+ 0x03A9, 0x03C9, /* GREEK CAPITAL LETTER OMEGA */
+ 0x03AA, 0x03CA, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+ 0x03AB, 0x03CB, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+ 0x03C2, 0x03C3, /* GREEK SMALL LETTER FINAL SIGMA */
+ 0x03D0, 0x03B2, /* GREEK BETA SYMBOL */
+ 0x03D1, 0x03B8, /* GREEK THETA SYMBOL */
+ 0x03D5, 0x03C6, /* GREEK PHI SYMBOL */
+ 0x03D6, 0x03C0, /* GREEK PI SYMBOL */
+ 0x03D8, 0x03D9, /* GREEK LETTER ARCHAIC KOPPA */
+ 0x03DA, 0x03DB, /* GREEK LETTER STIGMA */
+ 0x03DC, 0x03DD, /* GREEK LETTER DIGAMMA */
+ 0x03DE, 0x03DF, /* GREEK LETTER KOPPA */
+ 0x03E0, 0x03E1, /* GREEK LETTER SAMPI */
+ 0x03E2, 0x03E3, /* COPTIC CAPITAL LETTER SHEI */
+ 0x03E4, 0x03E5, /* COPTIC CAPITAL LETTER FEI */
+ 0x03E6, 0x03E7, /* COPTIC CAPITAL LETTER KHEI */
+ 0x03E8, 0x03E9, /* COPTIC CAPITAL LETTER HORI */
+ 0x03EA, 0x03EB, /* COPTIC CAPITAL LETTER GANGIA */
+ 0x03EC, 0x03ED, /* COPTIC CAPITAL LETTER SHIMA */
+ 0x03EE, 0x03EF, /* COPTIC CAPITAL LETTER DEI */
+ 0x03F0, 0x03BA, /* GREEK KAPPA SYMBOL */
+ 0x03F1, 0x03C1, /* GREEK RHO SYMBOL */
+ 0x03F4, 0x03B8, /* GREEK CAPITAL THETA SYMBOL */
+ 0x03F5, 0x03B5, /* GREEK LUNATE EPSILON SYMBOL */
+ 0x03F7, 0x03F8, /* GREEK CAPITAL LETTER SHO */
+ 0x03F9, 0x03F2, /* GREEK CAPITAL LUNATE SIGMA SYMBOL */
+ 0x03FA, 0x03FB, /* GREEK CAPITAL LETTER SAN */
+ 0x03FD, 0x037B, /* GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL */
+ 0x03FE, 0x037C, /* GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL */
+ 0x03FF, 0x037D, /* GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL */
+ 0x0400, 0x0450, /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
+ 0x0401, 0x0451, /* CYRILLIC CAPITAL LETTER IO */
+ 0x0402, 0x0452, /* CYRILLIC CAPITAL LETTER DJE */
+ 0x0403, 0x0453, /* CYRILLIC CAPITAL LETTER GJE */
+ 0x0404, 0x0454, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+ 0x0405, 0x0455, /* CYRILLIC CAPITAL LETTER DZE */
+ 0x0406, 0x0456, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+ 0x0407, 0x0457, /* CYRILLIC CAPITAL LETTER YI */
+ 0x0408, 0x0458, /* CYRILLIC CAPITAL LETTER JE */
+ 0x0409, 0x0459, /* CYRILLIC CAPITAL LETTER LJE */
+ 0x040A, 0x045A, /* CYRILLIC CAPITAL LETTER NJE */
+ 0x040B, 0x045B, /* CYRILLIC CAPITAL LETTER TSHE */
+ 0x040C, 0x045C, /* CYRILLIC CAPITAL LETTER KJE */
+ 0x040D, 0x045D, /* CYRILLIC CAPITAL LETTER I WITH GRAVE */
+ 0x040E, 0x045E, /* CYRILLIC CAPITAL LETTER SHORT U */
+ 0x040F, 0x045F, /* CYRILLIC CAPITAL LETTER DZHE */
+ 0x0410, 0x0430, /* CYRILLIC CAPITAL LETTER A */
+ 0x0411, 0x0431, /* CYRILLIC CAPITAL LETTER BE */
+ 0x0412, 0x0432, /* CYRILLIC CAPITAL LETTER VE */
+ 0x0413, 0x0433, /* CYRILLIC CAPITAL LETTER GHE */
+ 0x0414, 0x0434, /* CYRILLIC CAPITAL LETTER DE */
+ 0x0415, 0x0435, /* CYRILLIC CAPITAL LETTER IE */
+ 0x0416, 0x0436, /* CYRILLIC CAPITAL LETTER ZHE */
+ 0x0417, 0x0437, /* CYRILLIC CAPITAL LETTER ZE */
+ 0x0418, 0x0438, /* CYRILLIC CAPITAL LETTER I */
+ 0x0419, 0x0439, /* CYRILLIC CAPITAL LETTER SHORT I */
+ 0x041A, 0x043A, /* CYRILLIC CAPITAL LETTER KA */
+ 0x041B, 0x043B, /* CYRILLIC CAPITAL LETTER EL */
+ 0x041C, 0x043C, /* CYRILLIC CAPITAL LETTER EM */
+ 0x041D, 0x043D, /* CYRILLIC CAPITAL LETTER EN */
+ 0x041E, 0x043E, /* CYRILLIC CAPITAL LETTER O */
+ 0x041F, 0x043F, /* CYRILLIC CAPITAL LETTER PE */
+ 0x0420, 0x0440, /* CYRILLIC CAPITAL LETTER ER */
+ 0x0421, 0x0441, /* CYRILLIC CAPITAL LETTER ES */
+ 0x0422, 0x0442, /* CYRILLIC CAPITAL LETTER TE */
+ 0x0423, 0x0443, /* CYRILLIC CAPITAL LETTER U */
+ 0x0424, 0x0444, /* CYRILLIC CAPITAL LETTER EF */
+ 0x0425, 0x0445, /* CYRILLIC CAPITAL LETTER HA */
+ 0x0426, 0x0446, /* CYRILLIC CAPITAL LETTER TSE */
+ 0x0427, 0x0447, /* CYRILLIC CAPITAL LETTER CHE */
+ 0x0428, 0x0448, /* CYRILLIC CAPITAL LETTER SHA */
+ 0x0429, 0x0449, /* CYRILLIC CAPITAL LETTER SHCHA */
+ 0x042A, 0x044A, /* CYRILLIC CAPITAL LETTER HARD SIGN */
+ 0x042B, 0x044B, /* CYRILLIC CAPITAL LETTER YERU */
+ 0x042C, 0x044C, /* CYRILLIC CAPITAL LETTER SOFT SIGN */
+ 0x042D, 0x044D, /* CYRILLIC CAPITAL LETTER E */
+ 0x042E, 0x044E, /* CYRILLIC CAPITAL LETTER YU */
+ 0x042F, 0x044F, /* CYRILLIC CAPITAL LETTER YA */
+ 0x0460, 0x0461, /* CYRILLIC CAPITAL LETTER OMEGA */
+ 0x0462, 0x0463, /* CYRILLIC CAPITAL LETTER YAT */
+ 0x0464, 0x0465, /* CYRILLIC CAPITAL LETTER IOTIFIED E */
+ 0x0466, 0x0467, /* CYRILLIC CAPITAL LETTER LITTLE YUS */
+ 0x0468, 0x0469, /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */
+ 0x046A, 0x046B, /* CYRILLIC CAPITAL LETTER BIG YUS */
+ 0x046C, 0x046D, /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */
+ 0x046E, 0x046F, /* CYRILLIC CAPITAL LETTER KSI */
+ 0x0470, 0x0471, /* CYRILLIC CAPITAL LETTER PSI */
+ 0x0472, 0x0473, /* CYRILLIC CAPITAL LETTER FITA */
+ 0x0474, 0x0475, /* CYRILLIC CAPITAL LETTER IZHITSA */
+ 0x0476, 0x0477, /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
+ 0x0478, 0x0479, /* CYRILLIC CAPITAL LETTER UK */
+ 0x047A, 0x047B, /* CYRILLIC CAPITAL LETTER ROUND OMEGA */
+ 0x047C, 0x047D, /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */
+ 0x047E, 0x047F, /* CYRILLIC CAPITAL LETTER OT */
+ 0x0480, 0x0481, /* CYRILLIC CAPITAL LETTER KOPPA */
+ 0x048A, 0x048B, /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */
+ 0x048C, 0x048D, /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */
+ 0x048E, 0x048F, /* CYRILLIC CAPITAL LETTER ER WITH TICK */
+ 0x0490, 0x0491, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */
+ 0x0492, 0x0493, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */
+ 0x0494, 0x0495, /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */
+ 0x0496, 0x0497, /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */
+ 0x0498, 0x0499, /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */
+ 0x049A, 0x049B, /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */
+ 0x049C, 0x049D, /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */
+ 0x049E, 0x049F, /* CYRILLIC CAPITAL LETTER KA WITH STROKE */
+ 0x04A0, 0x04A1, /* CYRILLIC CAPITAL LETTER BASHKIR KA */
+ 0x04A2, 0x04A3, /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */
+ 0x04A4, 0x04A5, /* CYRILLIC CAPITAL LIGATURE EN GHE */
+ 0x04A6, 0x04A7, /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */
+ 0x04A8, 0x04A9, /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */
+ 0x04AA, 0x04AB, /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */
+ 0x04AC, 0x04AD, /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */
+ 0x04AE, 0x04AF, /* CYRILLIC CAPITAL LETTER STRAIGHT U */
+ 0x04B0, 0x04B1, /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */
+ 0x04B2, 0x04B3, /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */
+ 0x04B4, 0x04B5, /* CYRILLIC CAPITAL LIGATURE TE TSE */
+ 0x04B6, 0x04B7, /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */
+ 0x04B8, 0x04B9, /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */
+ 0x04BA, 0x04BB, /* CYRILLIC CAPITAL LETTER SHHA */
+ 0x04BC, 0x04BD, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */
+ 0x04BE, 0x04BF, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */
+ 0x04C0, 0x04CF, /* CYRILLIC LETTER PALOCHKA */
+ 0x04C1, 0x04C2, /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */
+ 0x04C3, 0x04C4, /* CYRILLIC CAPITAL LETTER KA WITH HOOK */
+ 0x04C5, 0x04C6, /* CYRILLIC CAPITAL LETTER EL WITH TAIL */
+ 0x04C7, 0x04C8, /* CYRILLIC CAPITAL LETTER EN WITH HOOK */
+ 0x04C9, 0x04CA, /* CYRILLIC CAPITAL LETTER EN WITH TAIL */
+ 0x04CB, 0x04CC, /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */
+ 0x04CD, 0x04CE, /* CYRILLIC CAPITAL LETTER EM WITH TAIL */
+ 0x04D0, 0x04D1, /* CYRILLIC CAPITAL LETTER A WITH BREVE */
+ 0x04D2, 0x04D3, /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */
+ 0x04D4, 0x04D5, /* CYRILLIC CAPITAL LIGATURE A IE */
+ 0x04D6, 0x04D7, /* CYRILLIC CAPITAL LETTER IE WITH BREVE */
+ 0x04D8, 0x04D9, /* CYRILLIC CAPITAL LETTER SCHWA */
+ 0x04DA, 0x04DB, /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */
+ 0x04DC, 0x04DD, /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */
+ 0x04DE, 0x04DF, /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */
+ 0x04E0, 0x04E1, /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */
+ 0x04E2, 0x04E3, /* CYRILLIC CAPITAL LETTER I WITH MACRON */
+ 0x04E4, 0x04E5, /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */
+ 0x04E6, 0x04E7, /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */
+ 0x04E8, 0x04E9, /* CYRILLIC CAPITAL LETTER BARRED O */
+ 0x04EA, 0x04EB, /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */
+ 0x04EC, 0x04ED, /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */
+ 0x04EE, 0x04EF, /* CYRILLIC CAPITAL LETTER U WITH MACRON */
+ 0x04F0, 0x04F1, /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */
+ 0x04F2, 0x04F3, /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */
+ 0x04F4, 0x04F5, /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */
+ 0x04F6, 0x04F7, /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */
+ 0x04F8, 0x04F9, /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */
+ 0x04FA, 0x04FB, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */
+ 0x04FC, 0x04FD, /* CYRILLIC CAPITAL LETTER HA WITH HOOK */
+ 0x04FE, 0x04FF, /* CYRILLIC CAPITAL LETTER HA WITH STROKE */
+ 0x0500, 0x0501, /* CYRILLIC CAPITAL LETTER KOMI DE */
+ 0x0502, 0x0503, /* CYRILLIC CAPITAL LETTER KOMI DJE */
+ 0x0504, 0x0505, /* CYRILLIC CAPITAL LETTER KOMI ZJE */
+ 0x0506, 0x0507, /* CYRILLIC CAPITAL LETTER KOMI DZJE */
+ 0x0508, 0x0509, /* CYRILLIC CAPITAL LETTER KOMI LJE */
+ 0x050A, 0x050B, /* CYRILLIC CAPITAL LETTER KOMI NJE */
+ 0x050C, 0x050D, /* CYRILLIC CAPITAL LETTER KOMI SJE */
+ 0x050E, 0x050F, /* CYRILLIC CAPITAL LETTER KOMI TJE */
+ 0x0510, 0x0511, /* CYRILLIC CAPITAL LETTER REVERSED ZE */
+ 0x0512, 0x0513, /* CYRILLIC CAPITAL LETTER EL WITH HOOK */
+ 0x0531, 0x0561, /* ARMENIAN CAPITAL LETTER AYB */
+ 0x0532, 0x0562, /* ARMENIAN CAPITAL LETTER BEN */
+ 0x0533, 0x0563, /* ARMENIAN CAPITAL LETTER GIM */
+ 0x0534, 0x0564, /* ARMENIAN CAPITAL LETTER DA */
+ 0x0535, 0x0565, /* ARMENIAN CAPITAL LETTER ECH */
+ 0x0536, 0x0566, /* ARMENIAN CAPITAL LETTER ZA */
+ 0x0537, 0x0567, /* ARMENIAN CAPITAL LETTER EH */
+ 0x0538, 0x0568, /* ARMENIAN CAPITAL LETTER ET */
+ 0x0539, 0x0569, /* ARMENIAN CAPITAL LETTER TO */
+ 0x053A, 0x056A, /* ARMENIAN CAPITAL LETTER ZHE */
+ 0x053B, 0x056B, /* ARMENIAN CAPITAL LETTER INI */
+ 0x053C, 0x056C, /* ARMENIAN CAPITAL LETTER LIWN */
+ 0x053D, 0x056D, /* ARMENIAN CAPITAL LETTER XEH */
+ 0x053E, 0x056E, /* ARMENIAN CAPITAL LETTER CA */
+ 0x053F, 0x056F, /* ARMENIAN CAPITAL LETTER KEN */
+ 0x0540, 0x0570, /* ARMENIAN CAPITAL LETTER HO */
+ 0x0541, 0x0571, /* ARMENIAN CAPITAL LETTER JA */
+ 0x0542, 0x0572, /* ARMENIAN CAPITAL LETTER GHAD */
+ 0x0543, 0x0573, /* ARMENIAN CAPITAL LETTER CHEH */
+ 0x0544, 0x0574, /* ARMENIAN CAPITAL LETTER MEN */
+ 0x0545, 0x0575, /* ARMENIAN CAPITAL LETTER YI */
+ 0x0546, 0x0576, /* ARMENIAN CAPITAL LETTER NOW */
+ 0x0547, 0x0577, /* ARMENIAN CAPITAL LETTER SHA */
+ 0x0548, 0x0578, /* ARMENIAN CAPITAL LETTER VO */
+ 0x0549, 0x0579, /* ARMENIAN CAPITAL LETTER CHA */
+ 0x054A, 0x057A, /* ARMENIAN CAPITAL LETTER PEH */
+ 0x054B, 0x057B, /* ARMENIAN CAPITAL LETTER JHEH */
+ 0x054C, 0x057C, /* ARMENIAN CAPITAL LETTER RA */
+ 0x054D, 0x057D, /* ARMENIAN CAPITAL LETTER SEH */
+ 0x054E, 0x057E, /* ARMENIAN CAPITAL LETTER VEW */
+ 0x054F, 0x057F, /* ARMENIAN CAPITAL LETTER TIWN */
+ 0x0550, 0x0580, /* ARMENIAN CAPITAL LETTER REH */
+ 0x0551, 0x0581, /* ARMENIAN CAPITAL LETTER CO */
+ 0x0552, 0x0582, /* ARMENIAN CAPITAL LETTER YIWN */
+ 0x0553, 0x0583, /* ARMENIAN CAPITAL LETTER PIWR */
+ 0x0554, 0x0584, /* ARMENIAN CAPITAL LETTER KEH */
+ 0x0555, 0x0585, /* ARMENIAN CAPITAL LETTER OH */
+ 0x0556, 0x0586, /* ARMENIAN CAPITAL LETTER FEH */
+ 0x10A0, 0x2D00, /* GEORGIAN CAPITAL LETTER AN */
+ 0x10A1, 0x2D01, /* GEORGIAN CAPITAL LETTER BAN */
+ 0x10A2, 0x2D02, /* GEORGIAN CAPITAL LETTER GAN */
+ 0x10A3, 0x2D03, /* GEORGIAN CAPITAL LETTER DON */
+ 0x10A4, 0x2D04, /* GEORGIAN CAPITAL LETTER EN */
+ 0x10A5, 0x2D05, /* GEORGIAN CAPITAL LETTER VIN */
+ 0x10A6, 0x2D06, /* GEORGIAN CAPITAL LETTER ZEN */
+ 0x10A7, 0x2D07, /* GEORGIAN CAPITAL LETTER TAN */
+ 0x10A8, 0x2D08, /* GEORGIAN CAPITAL LETTER IN */
+ 0x10A9, 0x2D09, /* GEORGIAN CAPITAL LETTER KAN */
+ 0x10AA, 0x2D0A, /* GEORGIAN CAPITAL LETTER LAS */
+ 0x10AB, 0x2D0B, /* GEORGIAN CAPITAL LETTER MAN */
+ 0x10AC, 0x2D0C, /* GEORGIAN CAPITAL LETTER NAR */
+ 0x10AD, 0x2D0D, /* GEORGIAN CAPITAL LETTER ON */
+ 0x10AE, 0x2D0E, /* GEORGIAN CAPITAL LETTER PAR */
+ 0x10AF, 0x2D0F, /* GEORGIAN CAPITAL LETTER ZHAR */
+ 0x10B0, 0x2D10, /* GEORGIAN CAPITAL LETTER RAE */
+ 0x10B1, 0x2D11, /* GEORGIAN CAPITAL LETTER SAN */
+ 0x10B2, 0x2D12, /* GEORGIAN CAPITAL LETTER TAR */
+ 0x10B3, 0x2D13, /* GEORGIAN CAPITAL LETTER UN */
+ 0x10B4, 0x2D14, /* GEORGIAN CAPITAL LETTER PHAR */
+ 0x10B5, 0x2D15, /* GEORGIAN CAPITAL LETTER KHAR */
+ 0x10B6, 0x2D16, /* GEORGIAN CAPITAL LETTER GHAN */
+ 0x10B7, 0x2D17, /* GEORGIAN CAPITAL LETTER QAR */
+ 0x10B8, 0x2D18, /* GEORGIAN CAPITAL LETTER SHIN */
+ 0x10B9, 0x2D19, /* GEORGIAN CAPITAL LETTER CHIN */
+ 0x10BA, 0x2D1A, /* GEORGIAN CAPITAL LETTER CAN */
+ 0x10BB, 0x2D1B, /* GEORGIAN CAPITAL LETTER JIL */
+ 0x10BC, 0x2D1C, /* GEORGIAN CAPITAL LETTER CIL */
+ 0x10BD, 0x2D1D, /* GEORGIAN CAPITAL LETTER CHAR */
+ 0x10BE, 0x2D1E, /* GEORGIAN CAPITAL LETTER XAN */
+ 0x10BF, 0x2D1F, /* GEORGIAN CAPITAL LETTER JHAN */
+ 0x10C0, 0x2D20, /* GEORGIAN CAPITAL LETTER HAE */
+ 0x10C1, 0x2D21, /* GEORGIAN CAPITAL LETTER HE */
+ 0x10C2, 0x2D22, /* GEORGIAN CAPITAL LETTER HIE */
+ 0x10C3, 0x2D23, /* GEORGIAN CAPITAL LETTER WE */
+ 0x10C4, 0x2D24, /* GEORGIAN CAPITAL LETTER HAR */
+ 0x10C5, 0x2D25, /* GEORGIAN CAPITAL LETTER HOE */
+ 0x1E00, 0x1E01, /* LATIN CAPITAL LETTER A WITH RING BELOW */
+ 0x1E02, 0x1E03, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */
+ 0x1E04, 0x1E05, /* LATIN CAPITAL LETTER B WITH DOT BELOW */
+ 0x1E06, 0x1E07, /* LATIN CAPITAL LETTER B WITH LINE BELOW */
+ 0x1E08, 0x1E09, /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */
+ 0x1E0A, 0x1E0B, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
+ 0x1E0C, 0x1E0D, /* LATIN CAPITAL LETTER D WITH DOT BELOW */
+ 0x1E0E, 0x1E0F, /* LATIN CAPITAL LETTER D WITH LINE BELOW */
+ 0x1E10, 0x1E11, /* LATIN CAPITAL LETTER D WITH CEDILLA */
+ 0x1E12, 0x1E13, /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */
+ 0x1E14, 0x1E15, /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */
+ 0x1E16, 0x1E17, /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */
+ 0x1E18, 0x1E19, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */
+ 0x1E1A, 0x1E1B, /* LATIN CAPITAL LETTER E WITH TILDE BELOW */
+ 0x1E1C, 0x1E1D, /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */
+ 0x1E1E, 0x1E1F, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */
+ 0x1E20, 0x1E21, /* LATIN CAPITAL LETTER G WITH MACRON */
+ 0x1E22, 0x1E23, /* LATIN CAPITAL LETTER H WITH DOT ABOVE */
+ 0x1E24, 0x1E25, /* LATIN CAPITAL LETTER H WITH DOT BELOW */
+ 0x1E26, 0x1E27, /* LATIN CAPITAL LETTER H WITH DIAERESIS */
+ 0x1E28, 0x1E29, /* LATIN CAPITAL LETTER H WITH CEDILLA */
+ 0x1E2A, 0x1E2B, /* LATIN CAPITAL LETTER H WITH BREVE BELOW */
+ 0x1E2C, 0x1E2D, /* LATIN CAPITAL LETTER I WITH TILDE BELOW */
+ 0x1E2E, 0x1E2F, /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */
+ 0x1E30, 0x1E31, /* LATIN CAPITAL LETTER K WITH ACUTE */
+ 0x1E32, 0x1E33, /* LATIN CAPITAL LETTER K WITH DOT BELOW */
+ 0x1E34, 0x1E35, /* LATIN CAPITAL LETTER K WITH LINE BELOW */
+ 0x1E36, 0x1E37, /* LATIN CAPITAL LETTER L WITH DOT BELOW */
+ 0x1E38, 0x1E39, /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */
+ 0x1E3A, 0x1E3B, /* LATIN CAPITAL LETTER L WITH LINE BELOW */
+ 0x1E3C, 0x1E3D, /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */
+ 0x1E3E, 0x1E3F, /* LATIN CAPITAL LETTER M WITH ACUTE */
+ 0x1E40, 0x1E41, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */
+ 0x1E42, 0x1E43, /* LATIN CAPITAL LETTER M WITH DOT BELOW */
+ 0x1E44, 0x1E45, /* LATIN CAPITAL LETTER N WITH DOT ABOVE */
+ 0x1E46, 0x1E47, /* LATIN CAPITAL LETTER N WITH DOT BELOW */
+ 0x1E48, 0x1E49, /* LATIN CAPITAL LETTER N WITH LINE BELOW */
+ 0x1E4A, 0x1E4B, /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */
+ 0x1E4C, 0x1E4D, /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */
+ 0x1E4E, 0x1E4F, /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */
+ 0x1E50, 0x1E51, /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */
+ 0x1E52, 0x1E53, /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */
+ 0x1E54, 0x1E55, /* LATIN CAPITAL LETTER P WITH ACUTE */
+ 0x1E56, 0x1E57, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */
+ 0x1E58, 0x1E59, /* LATIN CAPITAL LETTER R WITH DOT ABOVE */
+ 0x1E5A, 0x1E5B, /* LATIN CAPITAL LETTER R WITH DOT BELOW */
+ 0x1E5C, 0x1E5D, /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */
+ 0x1E5E, 0x1E5F, /* LATIN CAPITAL LETTER R WITH LINE BELOW */
+ 0x1E60, 0x1E61, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */
+ 0x1E62, 0x1E63, /* LATIN CAPITAL LETTER S WITH DOT BELOW */
+ 0x1E64, 0x1E65, /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */
+ 0x1E66, 0x1E67, /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */
+ 0x1E68, 0x1E69, /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */
+ 0x1E6A, 0x1E6B, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */
+ 0x1E6C, 0x1E6D, /* LATIN CAPITAL LETTER T WITH DOT BELOW */
+ 0x1E6E, 0x1E6F, /* LATIN CAPITAL LETTER T WITH LINE BELOW */
+ 0x1E70, 0x1E71, /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */
+ 0x1E72, 0x1E73, /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */
+ 0x1E74, 0x1E75, /* LATIN CAPITAL LETTER U WITH TILDE BELOW */
+ 0x1E76, 0x1E77, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */
+ 0x1E78, 0x1E79, /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */
+ 0x1E7A, 0x1E7B, /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */
+ 0x1E7C, 0x1E7D, /* LATIN CAPITAL LETTER V WITH TILDE */
+ 0x1E7E, 0x1E7F, /* LATIN CAPITAL LETTER V WITH DOT BELOW */
+ 0x1E80, 0x1E81, /* LATIN CAPITAL LETTER W WITH GRAVE */
+ 0x1E82, 0x1E83, /* LATIN CAPITAL LETTER W WITH ACUTE */
+ 0x1E84, 0x1E85, /* LATIN CAPITAL LETTER W WITH DIAERESIS */
+ 0x1E86, 0x1E87, /* LATIN CAPITAL LETTER W WITH DOT ABOVE */
+ 0x1E88, 0x1E89, /* LATIN CAPITAL LETTER W WITH DOT BELOW */
+ 0x1E8A, 0x1E8B, /* LATIN CAPITAL LETTER X WITH DOT ABOVE */
+ 0x1E8C, 0x1E8D, /* LATIN CAPITAL LETTER X WITH DIAERESIS */
+ 0x1E8E, 0x1E8F, /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */
+ 0x1E90, 0x1E91, /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */
+ 0x1E92, 0x1E93, /* LATIN CAPITAL LETTER Z WITH DOT BELOW */
+ 0x1E94, 0x1E95, /* LATIN CAPITAL LETTER Z WITH LINE BELOW */
+ 0x1E9B, 0x1E61, /* LATIN SMALL LETTER LONG S WITH DOT ABOVE */
+ 0x1EA0, 0x1EA1, /* LATIN CAPITAL LETTER A WITH DOT BELOW */
+ 0x1EA2, 0x1EA3, /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */
+ 0x1EA4, 0x1EA5, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */
+ 0x1EA6, 0x1EA7, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */
+ 0x1EA8, 0x1EA9, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */
+ 0x1EAA, 0x1EAB, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */
+ 0x1EAC, 0x1EAD, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */
+ 0x1EAE, 0x1EAF, /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */
+ 0x1EB0, 0x1EB1, /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */
+ 0x1EB2, 0x1EB3, /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */
+ 0x1EB4, 0x1EB5, /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */
+ 0x1EB6, 0x1EB7, /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */
+ 0x1EB8, 0x1EB9, /* LATIN CAPITAL LETTER E WITH DOT BELOW */
+ 0x1EBA, 0x1EBB, /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */
+ 0x1EBC, 0x1EBD, /* LATIN CAPITAL LETTER E WITH TILDE */
+ 0x1EBE, 0x1EBF, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */
+ 0x1EC0, 0x1EC1, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */
+ 0x1EC2, 0x1EC3, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */
+ 0x1EC4, 0x1EC5, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */
+ 0x1EC6, 0x1EC7, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */
+ 0x1EC8, 0x1EC9, /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */
+ 0x1ECA, 0x1ECB, /* LATIN CAPITAL LETTER I WITH DOT BELOW */
+ 0x1ECC, 0x1ECD, /* LATIN CAPITAL LETTER O WITH DOT BELOW */
+ 0x1ECE, 0x1ECF, /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */
+ 0x1ED0, 0x1ED1, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */
+ 0x1ED2, 0x1ED3, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */
+ 0x1ED4, 0x1ED5, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */
+ 0x1ED6, 0x1ED7, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */
+ 0x1ED8, 0x1ED9, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */
+ 0x1EDA, 0x1EDB, /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */
+ 0x1EDC, 0x1EDD, /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */
+ 0x1EDE, 0x1EDF, /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */
+ 0x1EE0, 0x1EE1, /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */
+ 0x1EE2, 0x1EE3, /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */
+ 0x1EE4, 0x1EE5, /* LATIN CAPITAL LETTER U WITH DOT BELOW */
+ 0x1EE6, 0x1EE7, /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */
+ 0x1EE8, 0x1EE9, /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */
+ 0x1EEA, 0x1EEB, /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */
+ 0x1EEC, 0x1EED, /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */
+ 0x1EEE, 0x1EEF, /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */
+ 0x1EF0, 0x1EF1, /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */
+ 0x1EF2, 0x1EF3, /* LATIN CAPITAL LETTER Y WITH GRAVE */
+ 0x1EF4, 0x1EF5, /* LATIN CAPITAL LETTER Y WITH DOT BELOW */
+ 0x1EF6, 0x1EF7, /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */
+ 0x1EF8, 0x1EF9, /* LATIN CAPITAL LETTER Y WITH TILDE */
+ 0x1F08, 0x1F00, /* GREEK CAPITAL LETTER ALPHA WITH PSILI */
+ 0x1F09, 0x1F01, /* GREEK CAPITAL LETTER ALPHA WITH DASIA */
+ 0x1F0A, 0x1F02, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */
+ 0x1F0B, 0x1F03, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */
+ 0x1F0C, 0x1F04, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */
+ 0x1F0D, 0x1F05, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */
+ 0x1F0E, 0x1F06, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */
+ 0x1F0F, 0x1F07, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */
+ 0x1F18, 0x1F10, /* GREEK CAPITAL LETTER EPSILON WITH PSILI */
+ 0x1F19, 0x1F11, /* GREEK CAPITAL LETTER EPSILON WITH DASIA */
+ 0x1F1A, 0x1F12, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */
+ 0x1F1B, 0x1F13, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */
+ 0x1F1C, 0x1F14, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */
+ 0x1F1D, 0x1F15, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */
+ 0x1F28, 0x1F20, /* GREEK CAPITAL LETTER ETA WITH PSILI */
+ 0x1F29, 0x1F21, /* GREEK CAPITAL LETTER ETA WITH DASIA */
+ 0x1F2A, 0x1F22, /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */
+ 0x1F2B, 0x1F23, /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */
+ 0x1F2C, 0x1F24, /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */
+ 0x1F2D, 0x1F25, /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */
+ 0x1F2E, 0x1F26, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */
+ 0x1F2F, 0x1F27, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */
+ 0x1F38, 0x1F30, /* GREEK CAPITAL LETTER IOTA WITH PSILI */
+ 0x1F39, 0x1F31, /* GREEK CAPITAL LETTER IOTA WITH DASIA */
+ 0x1F3A, 0x1F32, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */
+ 0x1F3B, 0x1F33, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */
+ 0x1F3C, 0x1F34, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */
+ 0x1F3D, 0x1F35, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */
+ 0x1F3E, 0x1F36, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */
+ 0x1F3F, 0x1F37, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */
+ 0x1F48, 0x1F40, /* GREEK CAPITAL LETTER OMICRON WITH PSILI */
+ 0x1F49, 0x1F41, /* GREEK CAPITAL LETTER OMICRON WITH DASIA */
+ 0x1F4A, 0x1F42, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */
+ 0x1F4B, 0x1F43, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */
+ 0x1F4C, 0x1F44, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */
+ 0x1F4D, 0x1F45, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */
+ 0x1F59, 0x1F51, /* GREEK CAPITAL LETTER UPSILON WITH DASIA */
+ 0x1F5B, 0x1F53, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */
+ 0x1F5D, 0x1F55, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */
+ 0x1F5F, 0x1F57, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */
+ 0x1F68, 0x1F60, /* GREEK CAPITAL LETTER OMEGA WITH PSILI */
+ 0x1F69, 0x1F61, /* GREEK CAPITAL LETTER OMEGA WITH DASIA */
+ 0x1F6A, 0x1F62, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */
+ 0x1F6B, 0x1F63, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */
+ 0x1F6C, 0x1F64, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */
+ 0x1F6D, 0x1F65, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */
+ 0x1F6E, 0x1F66, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */
+ 0x1F6F, 0x1F67, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */
+ 0x1F88, 0x1F80, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
+ 0x1F89, 0x1F81, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
+ 0x1F8A, 0x1F82, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+ 0x1F8B, 0x1F83, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+ 0x1F8C, 0x1F84, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+ 0x1F8D, 0x1F85, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+ 0x1F8E, 0x1F86, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1F8F, 0x1F87, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1F98, 0x1F90, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */
+ 0x1F99, 0x1F91, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */
+ 0x1F9A, 0x1F92, /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+ 0x1F9B, 0x1F93, /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+ 0x1F9C, 0x1F94, /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+ 0x1F9D, 0x1F95, /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+ 0x1F9E, 0x1F96, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1F9F, 0x1F97, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1FA8, 0x1FA0, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
+ 0x1FA9, 0x1FA1, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
+ 0x1FAA, 0x1FA2, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+ 0x1FAB, 0x1FA3, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+ 0x1FAC, 0x1FA4, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+ 0x1FAD, 0x1FA5, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+ 0x1FAE, 0x1FA6, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1FAF, 0x1FA7, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+ 0x1FB8, 0x1FB0, /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */
+ 0x1FB9, 0x1FB1, /* GREEK CAPITAL LETTER ALPHA WITH MACRON */
+ 0x1FBA, 0x1F70, /* GREEK CAPITAL LETTER ALPHA WITH VARIA */
+ 0x1FBB, 0x1F71, /* GREEK CAPITAL LETTER ALPHA WITH OXIA */
+ 0x1FBC, 0x1FB3, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
+ 0x1FBE, 0x03B9, /* GREEK PROSGEGRAMMENI */
+ 0x1FC8, 0x1F72, /* GREEK CAPITAL LETTER EPSILON WITH VARIA */
+ 0x1FC9, 0x1F73, /* GREEK CAPITAL LETTER EPSILON WITH OXIA */
+ 0x1FCA, 0x1F74, /* GREEK CAPITAL LETTER ETA WITH VARIA */
+ 0x1FCB, 0x1F75, /* GREEK CAPITAL LETTER ETA WITH OXIA */
+ 0x1FCC, 0x1FC3, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
+ 0x1FD8, 0x1FD0, /* GREEK CAPITAL LETTER IOTA WITH VRACHY */
+ 0x1FD9, 0x1FD1, /* GREEK CAPITAL LETTER IOTA WITH MACRON */
+ 0x1FDA, 0x1F76, /* GREEK CAPITAL LETTER IOTA WITH VARIA */
+ 0x1FDB, 0x1F77, /* GREEK CAPITAL LETTER IOTA WITH OXIA */
+ 0x1FE8, 0x1FE0, /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */
+ 0x1FE9, 0x1FE1, /* GREEK CAPITAL LETTER UPSILON WITH MACRON */
+ 0x1FEA, 0x1F7A, /* GREEK CAPITAL LETTER UPSILON WITH VARIA */
+ 0x1FEB, 0x1F7B, /* GREEK CAPITAL LETTER UPSILON WITH OXIA */
+ 0x1FEC, 0x1FE5, /* GREEK CAPITAL LETTER RHO WITH DASIA */
+ 0x1FF8, 0x1F78, /* GREEK CAPITAL LETTER OMICRON WITH VARIA */
+ 0x1FF9, 0x1F79, /* GREEK CAPITAL LETTER OMICRON WITH OXIA */
+ 0x1FFA, 0x1F7C, /* GREEK CAPITAL LETTER OMEGA WITH VARIA */
+ 0x1FFB, 0x1F7D, /* GREEK CAPITAL LETTER OMEGA WITH OXIA */
+ 0x1FFC, 0x1FF3, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
+ 0x2126, 0x03C9, /* OHM SIGN */
+ 0x212A, 0x006B, /* KELVIN SIGN */
+ 0x212B, 0x00E5, /* ANGSTROM SIGN */
+ 0x2132, 0x214E, /* TURNED CAPITAL F */
+ 0x2160, 0x2170, /* ROMAN NUMERAL ONE */
+ 0x2161, 0x2171, /* ROMAN NUMERAL TWO */
+ 0x2162, 0x2172, /* ROMAN NUMERAL THREE */
+ 0x2163, 0x2173, /* ROMAN NUMERAL FOUR */
+ 0x2164, 0x2174, /* ROMAN NUMERAL FIVE */
+ 0x2165, 0x2175, /* ROMAN NUMERAL SIX */
+ 0x2166, 0x2176, /* ROMAN NUMERAL SEVEN */
+ 0x2167, 0x2177, /* ROMAN NUMERAL EIGHT */
+ 0x2168, 0x2178, /* ROMAN NUMERAL NINE */
+ 0x2169, 0x2179, /* ROMAN NUMERAL TEN */
+ 0x216A, 0x217A, /* ROMAN NUMERAL ELEVEN */
+ 0x216B, 0x217B, /* ROMAN NUMERAL TWELVE */
+ 0x216C, 0x217C, /* ROMAN NUMERAL FIFTY */
+ 0x216D, 0x217D, /* ROMAN NUMERAL ONE HUNDRED */
+ 0x216E, 0x217E, /* ROMAN NUMERAL FIVE HUNDRED */
+ 0x216F, 0x217F, /* ROMAN NUMERAL ONE THOUSAND */
+ 0x2183, 0x2184, /* ROMAN NUMERAL REVERSED ONE HUNDRED */
+ 0x24B6, 0x24D0, /* CIRCLED LATIN CAPITAL LETTER A */
+ 0x24B7, 0x24D1, /* CIRCLED LATIN CAPITAL LETTER B */
+ 0x24B8, 0x24D2, /* CIRCLED LATIN CAPITAL LETTER C */
+ 0x24B9, 0x24D3, /* CIRCLED LATIN CAPITAL LETTER D */
+ 0x24BA, 0x24D4, /* CIRCLED LATIN CAPITAL LETTER E */
+ 0x24BB, 0x24D5, /* CIRCLED LATIN CAPITAL LETTER F */
+ 0x24BC, 0x24D6, /* CIRCLED LATIN CAPITAL LETTER G */
+ 0x24BD, 0x24D7, /* CIRCLED LATIN CAPITAL LETTER H */
+ 0x24BE, 0x24D8, /* CIRCLED LATIN CAPITAL LETTER I */
+ 0x24BF, 0x24D9, /* CIRCLED LATIN CAPITAL LETTER J */
+ 0x24C0, 0x24DA, /* CIRCLED LATIN CAPITAL LETTER K */
+ 0x24C1, 0x24DB, /* CIRCLED LATIN CAPITAL LETTER L */
+ 0x24C2, 0x24DC, /* CIRCLED LATIN CAPITAL LETTER M */
+ 0x24C3, 0x24DD, /* CIRCLED LATIN CAPITAL LETTER N */
+ 0x24C4, 0x24DE, /* CIRCLED LATIN CAPITAL LETTER O */
+ 0x24C5, 0x24DF, /* CIRCLED LATIN CAPITAL LETTER P */
+ 0x24C6, 0x24E0, /* CIRCLED LATIN CAPITAL LETTER Q */
+ 0x24C7, 0x24E1, /* CIRCLED LATIN CAPITAL LETTER R */
+ 0x24C8, 0x24E2, /* CIRCLED LATIN CAPITAL LETTER S */
+ 0x24C9, 0x24E3, /* CIRCLED LATIN CAPITAL LETTER T */
+ 0x24CA, 0x24E4, /* CIRCLED LATIN CAPITAL LETTER U */
+ 0x24CB, 0x24E5, /* CIRCLED LATIN CAPITAL LETTER V */
+ 0x24CC, 0x24E6, /* CIRCLED LATIN CAPITAL LETTER W */
+ 0x24CD, 0x24E7, /* CIRCLED LATIN CAPITAL LETTER X */
+ 0x24CE, 0x24E8, /* CIRCLED LATIN CAPITAL LETTER Y */
+ 0x24CF, 0x24E9, /* CIRCLED LATIN CAPITAL LETTER Z */
+ 0x2C00, 0x2C30, /* GLAGOLITIC CAPITAL LETTER AZU */
+ 0x2C01, 0x2C31, /* GLAGOLITIC CAPITAL LETTER BUKY */
+ 0x2C02, 0x2C32, /* GLAGOLITIC CAPITAL LETTER VEDE */
+ 0x2C03, 0x2C33, /* GLAGOLITIC CAPITAL LETTER GLAGOLI */
+ 0x2C04, 0x2C34, /* GLAGOLITIC CAPITAL LETTER DOBRO */
+ 0x2C05, 0x2C35, /* GLAGOLITIC CAPITAL LETTER YESTU */
+ 0x2C06, 0x2C36, /* GLAGOLITIC CAPITAL LETTER ZHIVETE */
+ 0x2C07, 0x2C37, /* GLAGOLITIC CAPITAL LETTER DZELO */
+ 0x2C08, 0x2C38, /* GLAGOLITIC CAPITAL LETTER ZEMLJA */
+ 0x2C09, 0x2C39, /* GLAGOLITIC CAPITAL LETTER IZHE */
+ 0x2C0A, 0x2C3A, /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */
+ 0x2C0B, 0x2C3B, /* GLAGOLITIC CAPITAL LETTER I */
+ 0x2C0C, 0x2C3C, /* GLAGOLITIC CAPITAL LETTER DJERVI */
+ 0x2C0D, 0x2C3D, /* GLAGOLITIC CAPITAL LETTER KAKO */
+ 0x2C0E, 0x2C3E, /* GLAGOLITIC CAPITAL LETTER LJUDIJE */
+ 0x2C0F, 0x2C3F, /* GLAGOLITIC CAPITAL LETTER MYSLITE */
+ 0x2C10, 0x2C40, /* GLAGOLITIC CAPITAL LETTER NASHI */
+ 0x2C11, 0x2C41, /* GLAGOLITIC CAPITAL LETTER ONU */
+ 0x2C12, 0x2C42, /* GLAGOLITIC CAPITAL LETTER POKOJI */
+ 0x2C13, 0x2C43, /* GLAGOLITIC CAPITAL LETTER RITSI */
+ 0x2C14, 0x2C44, /* GLAGOLITIC CAPITAL LETTER SLOVO */
+ 0x2C15, 0x2C45, /* GLAGOLITIC CAPITAL LETTER TVRIDO */
+ 0x2C16, 0x2C46, /* GLAGOLITIC CAPITAL LETTER UKU */
+ 0x2C17, 0x2C47, /* GLAGOLITIC CAPITAL LETTER FRITU */
+ 0x2C18, 0x2C48, /* GLAGOLITIC CAPITAL LETTER HERU */
+ 0x2C19, 0x2C49, /* GLAGOLITIC CAPITAL LETTER OTU */
+ 0x2C1A, 0x2C4A, /* GLAGOLITIC CAPITAL LETTER PE */
+ 0x2C1B, 0x2C4B, /* GLAGOLITIC CAPITAL LETTER SHTA */
+ 0x2C1C, 0x2C4C, /* GLAGOLITIC CAPITAL LETTER TSI */
+ 0x2C1D, 0x2C4D, /* GLAGOLITIC CAPITAL LETTER CHRIVI */
+ 0x2C1E, 0x2C4E, /* GLAGOLITIC CAPITAL LETTER SHA */
+ 0x2C1F, 0x2C4F, /* GLAGOLITIC CAPITAL LETTER YERU */
+ 0x2C20, 0x2C50, /* GLAGOLITIC CAPITAL LETTER YERI */
+ 0x2C21, 0x2C51, /* GLAGOLITIC CAPITAL LETTER YATI */
+ 0x2C22, 0x2C52, /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */
+ 0x2C23, 0x2C53, /* GLAGOLITIC CAPITAL LETTER YU */
+ 0x2C24, 0x2C54, /* GLAGOLITIC CAPITAL LETTER SMALL YUS */
+ 0x2C25, 0x2C55, /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */
+ 0x2C26, 0x2C56, /* GLAGOLITIC CAPITAL LETTER YO */
+ 0x2C27, 0x2C57, /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */
+ 0x2C28, 0x2C58, /* GLAGOLITIC CAPITAL LETTER BIG YUS */
+ 0x2C29, 0x2C59, /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */
+ 0x2C2A, 0x2C5A, /* GLAGOLITIC CAPITAL LETTER FITA */
+ 0x2C2B, 0x2C5B, /* GLAGOLITIC CAPITAL LETTER IZHITSA */
+ 0x2C2C, 0x2C5C, /* GLAGOLITIC CAPITAL LETTER SHTAPIC */
+ 0x2C2D, 0x2C5D, /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */
+ 0x2C2E, 0x2C5E, /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */
+ 0x2C60, 0x2C61, /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */
+ 0x2C62, 0x026B, /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */
+ 0x2C63, 0x1D7D, /* LATIN CAPITAL LETTER P WITH STROKE */
+ 0x2C64, 0x027D, /* LATIN CAPITAL LETTER R WITH TAIL */
+ 0x2C67, 0x2C68, /* LATIN CAPITAL LETTER H WITH DESCENDER */
+ 0x2C69, 0x2C6A, /* LATIN CAPITAL LETTER K WITH DESCENDER */
+ 0x2C6B, 0x2C6C, /* LATIN CAPITAL LETTER Z WITH DESCENDER */
+ 0x2C75, 0x2C76, /* LATIN CAPITAL LETTER HALF H */
+ 0x2C80, 0x2C81, /* COPTIC CAPITAL LETTER ALFA */
+ 0x2C82, 0x2C83, /* COPTIC CAPITAL LETTER VIDA */
+ 0x2C84, 0x2C85, /* COPTIC CAPITAL LETTER GAMMA */
+ 0x2C86, 0x2C87, /* COPTIC CAPITAL LETTER DALDA */
+ 0x2C88, 0x2C89, /* COPTIC CAPITAL LETTER EIE */
+ 0x2C8A, 0x2C8B, /* COPTIC CAPITAL LETTER SOU */
+ 0x2C8C, 0x2C8D, /* COPTIC CAPITAL LETTER ZATA */
+ 0x2C8E, 0x2C8F, /* COPTIC CAPITAL LETTER HATE */
+ 0x2C90, 0x2C91, /* COPTIC CAPITAL LETTER THETHE */
+ 0x2C92, 0x2C93, /* COPTIC CAPITAL LETTER IAUDA */
+ 0x2C94, 0x2C95, /* COPTIC CAPITAL LETTER KAPA */
+ 0x2C96, 0x2C97, /* COPTIC CAPITAL LETTER LAULA */
+ 0x2C98, 0x2C99, /* COPTIC CAPITAL LETTER MI */
+ 0x2C9A, 0x2C9B, /* COPTIC CAPITAL LETTER NI */
+ 0x2C9C, 0x2C9D, /* COPTIC CAPITAL LETTER KSI */
+ 0x2C9E, 0x2C9F, /* COPTIC CAPITAL LETTER O */
+ 0x2CA0, 0x2CA1, /* COPTIC CAPITAL LETTER PI */
+ 0x2CA2, 0x2CA3, /* COPTIC CAPITAL LETTER RO */
+ 0x2CA4, 0x2CA5, /* COPTIC CAPITAL LETTER SIMA */
+ 0x2CA6, 0x2CA7, /* COPTIC CAPITAL LETTER TAU */
+ 0x2CA8, 0x2CA9, /* COPTIC CAPITAL LETTER UA */
+ 0x2CAA, 0x2CAB, /* COPTIC CAPITAL LETTER FI */
+ 0x2CAC, 0x2CAD, /* COPTIC CAPITAL LETTER KHI */
+ 0x2CAE, 0x2CAF, /* COPTIC CAPITAL LETTER PSI */
+ 0x2CB0, 0x2CB1, /* COPTIC CAPITAL LETTER OOU */
+ 0x2CB2, 0x2CB3, /* COPTIC CAPITAL LETTER DIALECT-P ALEF */
+ 0x2CB4, 0x2CB5, /* COPTIC CAPITAL LETTER OLD COPTIC AIN */
+ 0x2CB6, 0x2CB7, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */
+ 0x2CB8, 0x2CB9, /* COPTIC CAPITAL LETTER DIALECT-P KAPA */
+ 0x2CBA, 0x2CBB, /* COPTIC CAPITAL LETTER DIALECT-P NI */
+ 0x2CBC, 0x2CBD, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */
+ 0x2CBE, 0x2CBF, /* COPTIC CAPITAL LETTER OLD COPTIC OOU */
+ 0x2CC0, 0x2CC1, /* COPTIC CAPITAL LETTER SAMPI */
+ 0x2CC2, 0x2CC3, /* COPTIC CAPITAL LETTER CROSSED SHEI */
+ 0x2CC4, 0x2CC5, /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */
+ 0x2CC6, 0x2CC7, /* COPTIC CAPITAL LETTER OLD COPTIC ESH */
+ 0x2CC8, 0x2CC9, /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */
+ 0x2CCA, 0x2CCB, /* COPTIC CAPITAL LETTER DIALECT-P HORI */
+ 0x2CCC, 0x2CCD, /* COPTIC CAPITAL LETTER OLD COPTIC HORI */
+ 0x2CCE, 0x2CCF, /* COPTIC CAPITAL LETTER OLD COPTIC HA */
+ 0x2CD0, 0x2CD1, /* COPTIC CAPITAL LETTER L-SHAPED HA */
+ 0x2CD2, 0x2CD3, /* COPTIC CAPITAL LETTER OLD COPTIC HEI */
+ 0x2CD4, 0x2CD5, /* COPTIC CAPITAL LETTER OLD COPTIC HAT */
+ 0x2CD6, 0x2CD7, /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */
+ 0x2CD8, 0x2CD9, /* COPTIC CAPITAL LETTER OLD COPTIC DJA */
+ 0x2CDA, 0x2CDB, /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */
+ 0x2CDC, 0x2CDD, /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */
+ 0x2CDE, 0x2CDF, /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */
+ 0x2CE0, 0x2CE1, /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */
+ 0x2CE2, 0x2CE3, /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */
+ 0xFF21, 0xFF41, /* FULLWIDTH LATIN CAPITAL LETTER A */
+ 0xFF22, 0xFF42, /* FULLWIDTH LATIN CAPITAL LETTER B */
+ 0xFF23, 0xFF43, /* FULLWIDTH LATIN CAPITAL LETTER C */
+ 0xFF24, 0xFF44, /* FULLWIDTH LATIN CAPITAL LETTER D */
+ 0xFF25, 0xFF45, /* FULLWIDTH LATIN CAPITAL LETTER E */
+ 0xFF26, 0xFF46, /* FULLWIDTH LATIN CAPITAL LETTER F */
+ 0xFF27, 0xFF47, /* FULLWIDTH LATIN CAPITAL LETTER G */
+ 0xFF28, 0xFF48, /* FULLWIDTH LATIN CAPITAL LETTER H */
+ 0xFF29, 0xFF49, /* FULLWIDTH LATIN CAPITAL LETTER I */
+ 0xFF2A, 0xFF4A, /* FULLWIDTH LATIN CAPITAL LETTER J */
+ 0xFF2B, 0xFF4B, /* FULLWIDTH LATIN CAPITAL LETTER K */
+ 0xFF2C, 0xFF4C, /* FULLWIDTH LATIN CAPITAL LETTER L */
+ 0xFF2D, 0xFF4D, /* FULLWIDTH LATIN CAPITAL LETTER M */
+ 0xFF2E, 0xFF4E, /* FULLWIDTH LATIN CAPITAL LETTER N */
+ 0xFF2F, 0xFF4F, /* FULLWIDTH LATIN CAPITAL LETTER O */
+ 0xFF30, 0xFF50, /* FULLWIDTH LATIN CAPITAL LETTER P */
+ 0xFF31, 0xFF51, /* FULLWIDTH LATIN CAPITAL LETTER Q */
+ 0xFF32, 0xFF52, /* FULLWIDTH LATIN CAPITAL LETTER R */
+ 0xFF33, 0xFF53, /* FULLWIDTH LATIN CAPITAL LETTER S */
+ 0xFF34, 0xFF54, /* FULLWIDTH LATIN CAPITAL LETTER T */
+ 0xFF35, 0xFF55, /* FULLWIDTH LATIN CAPITAL LETTER U */
+ 0xFF36, 0xFF56, /* FULLWIDTH LATIN CAPITAL LETTER V */
+ 0xFF37, 0xFF57, /* FULLWIDTH LATIN CAPITAL LETTER W */
+ 0xFF38, 0xFF58, /* FULLWIDTH LATIN CAPITAL LETTER X */
+ 0xFF39, 0xFF59, /* FULLWIDTH LATIN CAPITAL LETTER Y */
+ 0xFF3A, 0xFF5A, /* FULLWIDTH LATIN CAPITAL LETTER Z */
+};
+
+size_t msdosfs_unicode_foldmap_entries = sizeof(msdosfs_unicode_foldmap) / sizeof(msdosfs_unicode_foldmap[0]);
diff --git a/usr.sbin/makefs/fs/msdosfs/msdosfsmount.h b/usr.sbin/makefs/fs/msdosfs/msdosfsmount.h
new file mode 100644
index 00000000000..9b4399bd8d8
--- /dev/null
+++ b/usr.sbin/makefs/fs/msdosfs/msdosfsmount.h
@@ -0,0 +1,229 @@
+/* $NetBSD: msdosfsmount.h,v 1.21 2016/01/30 09:59:27 mlelstv Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#ifndef _MSDOSFS_MSDOSFSMOUNT_H_
+#define _MSDOSFS_MSDOSFSMOUNT_H_
+
+
+/*
+ * Msdosfs mount options:
+ */
+#define MSDOSFSMNT_SHORTNAME 1 /* Force old DOS short names only */
+#define MSDOSFSMNT_LONGNAME 2 /* Force Win'95 long names */
+#define MSDOSFSMNT_NOWIN95 4 /* Completely ignore Win95 entries */
+#define MSDOSFSMNT_GEMDOSFS 8 /* This is a GEMDOS-flavour */
+#define MSDOSFSMNT_VERSIONED 16 /* Struct is versioned */
+#define MSDOSFSMNT_UTF8 32 /* Use UTF8 filenames */
+
+/* All flags above: */
+#define MSDOSFSMNT_MNTOPT \
+ (MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \
+ |MSDOSFSMNT_GEMDOSFS|MSDOSFSMNT_VERSIONED|MSDOSFSMNT_UTF8)
+
+#define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */
+#define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */
+#define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */
+
+#define MSDOSFSMNT_BITS "\177\20" \
+ "b\00shortname\0b\01longname\0b\02nowin95\0b\03gemdosfs\0b\04mntversioned\0" \
+ "b\05utf8\0b\037ronly\0b\036waitonfat\0b\035fatmirror\0"
+
+
+/*
+ * Layout of the mount control block for a MSDOSFS file system.
+ */
+struct msdosfsmount {
+ struct mount *pm_mountp;/* vfs mount struct for this fs */
+ dev_t pm_dev; /* block special device mounted */
+ uid_t pm_uid; /* uid to set as owner of the files */
+ gid_t pm_gid; /* gid to set as owner of the files */
+ mode_t pm_mask; /* mask to and with file protection bits
+ for files */
+ mode_t pm_dirmask; /* mask to and with file protection bits
+ for directories */
+ int pm_gmtoff; /* offset from UTC in seconds */
+ struct vnode *pm_devvp; /* vnode for block device mntd */
+ struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */
+ u_long pm_FATsecs; /* actual number of FAT sectors */
+ u_long pm_fatblk; /* sector # of first FAT */
+ u_long pm_rootdirblk; /* sector # (cluster # for FAT32) of root directory number */
+ u_long pm_rootdirsize; /* size in sectors (not clusters) */
+ u_long pm_firstcluster; /* sector number of first cluster */
+ u_long pm_nmbrofclusters; /* # of clusters in filesystem */
+ u_long pm_maxcluster; /* maximum cluster number */
+ u_long pm_freeclustercount; /* number of free clusters */
+ u_long pm_cnshift; /* shift file offset right this amount to get a cluster number */
+ u_long pm_crbomask; /* and a file offset with this mask to get cluster rel offset */
+ u_long pm_bnshift; /* shift file offset right this amount to get a sector number */
+ u_long pm_bpcluster; /* bytes per cluster */
+ u_long pm_fmod; /* ~0 if fs is modified, this can rollover to 0 */
+ u_long pm_fatblocksize; /* size of FAT blocks in bytes */
+ u_long pm_fatblocksec; /* size of FAT blocks in sectors */
+ u_long pm_fatsize; /* size of FAT in bytes */
+ u_long pm_fatmask; /* mask to use for FAT numbers */
+ u_long pm_fsinfo; /* fsinfo block number */
+ u_long pm_nxtfree; /* next free cluster in fsinfo block */
+ u_int pm_fatmult; /* these 2 values are used in FAT */
+ u_int pm_fatdiv; /* offset computation */
+ u_int pm_curfat; /* current FAT for FAT32 (0 otherwise) */
+ u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */
+ u_int pm_flags; /* see below */
+};
+/* Byte offset in FAT on filesystem pmp, cluster cn */
+#define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv)
+
+#define VFSTOMSDOSFS(mp) ((struct msdosfsmount *)mp->mnt_data)
+
+/* Number of bits in one pm_inusemap item: */
+#define N_INUSEBITS (8 * sizeof(u_int))
+
+/*
+ * Shorthand for fields in the bpb contained in the msdosfsmount structure.
+ */
+#define pm_BytesPerSec pm_bpb.bpbBytesPerSec
+#define pm_ResSectors pm_bpb.bpbResSectors
+#define pm_FATs pm_bpb.bpbFATs
+#define pm_RootDirEnts pm_bpb.bpbRootDirEnts
+#define pm_Sectors pm_bpb.bpbSectors
+#define pm_Media pm_bpb.bpbMedia
+#define pm_SecPerTrack pm_bpb.bpbSecPerTrack
+#define pm_Heads pm_bpb.bpbHeads
+#define pm_HiddenSects pm_bpb.bpbHiddenSecs
+#define pm_HugeSectors pm_bpb.bpbHugeSectors
+
+/*
+ * Convert pointer to buffer -> pointer to direntry
+ */
+#define bptoep(pmp, bp, dirofs) \
+ ((struct direntry *)(((char *)(bp)->b_data) \
+ + ((dirofs) & (pmp)->pm_crbomask)))
+
+/*
+ * Convert sector number to cluster number
+ */
+#define de_bn2cn(pmp, bn) \
+ ((bn) >> ((pmp)->pm_cnshift - (pmp)->pm_bnshift))
+
+/*
+ * Convert cluster number to sector number
+ */
+#define de_cn2bn(pmp, cn) \
+ ((cn) << ((pmp)->pm_cnshift - (pmp)->pm_bnshift))
+
+/*
+ * Convert sector number to kernel block number
+ */
+#define de_bn2kb(pmp, bn) \
+ ((bn) << ((pmp)->pm_bnshift - DEV_BSHIFT))
+
+/*
+ * Convert kernel block number to sector number
+ */
+#define de_kb2bn(pmp, kb) \
+ ((kb) >> ((pmp)->pm_bnshift - DEV_BSHIFT))
+
+/*
+ * Convert file offset to cluster number
+ */
+#define de_cluster(pmp, off) \
+ ((off) >> (pmp)->pm_cnshift)
+
+/*
+ * Clusters required to hold size bytes
+ */
+#define de_clcount(pmp, size) \
+ (((size) + (pmp)->pm_bpcluster - 1) >> (pmp)->pm_cnshift)
+
+/*
+ * Convert file offset to sector number
+ */
+#define de_blk(pmp, off) \
+ (de_cn2bn(pmp, de_cluster((pmp), (off))))
+
+/*
+ * Convert cluster number to file offset
+ */
+#define de_cn2off(pmp, cn) \
+ ((cn) << (pmp)->pm_cnshift)
+
+/*
+ * Convert sector number to file offset
+ */
+#define de_bn2off(pmp, bn) \
+ ((bn) << (pmp)->pm_bnshift)
+/*
+ * Map a cluster number into a filesystem relative sector number.
+ */
+#define cntobn(pmp, cn) \
+ (de_cn2bn((pmp), (cn)-CLUST_FIRST) + (pmp)->pm_firstcluster)
+
+/*
+ * Calculate sector number for directory entry in root dir, offset dirofs
+ */
+#define roottobn(pmp, dirofs) \
+ (de_blk((pmp), (dirofs)) + (pmp)->pm_rootdirblk)
+
+/*
+ * Calculate sector number for directory entry at cluster dirclu, offset
+ * dirofs
+ */
+#define detobn(pmp, dirclu, dirofs) \
+ ((dirclu) == MSDOSFSROOT \
+ ? roottobn((pmp), (dirofs)) \
+ : cntobn((pmp), (dirclu)))
+
+/*
+ * Prototypes for MSDOSFS virtual filesystem operations
+ */
+void msdosfs_init(void);
+void msdosfs_reinit(void);
+void msdosfs_done(void);
+
+
+#endif /* _MSDOSFS_MSDOSFSMOUNT_H_ */
diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
new file mode 100644
index 00000000000..97b6a81e97d
--- /dev/null
+++ b/usr.sbin/makefs/makefs.8
@@ -0,0 +1,341 @@
+.\" $NetBSD: makefs.8,v 1.55 2015/11/25 16:32:00 wiz Exp $
+.\"
+.\" Copyright (c) 2001-2003 Wasabi Systems, Inc.
+.\" All rights reserved.
+.\"
+.\" Written by Luke Mewburn for Wasabi Systems, Inc.
+.\"
+.\" 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 for the NetBSD Project by
+.\" Wasabi Systems, Inc.
+.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+.\" 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.
+.\"
+.Dd November 23, 2015
+.Dt MAKEFS 8
+.Os
+.Sh NAME
+.Nm makefs
+.Nd create a file system image from a directory tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl rZ
+.Op Fl B Ar endian
+.Op Fl b Ar free-blocks
+.Op Fl d Ar debug-mask
+.Op Fl f Ar free-files
+.Op Fl M Ar minimum-size
+.Op Fl m Ar maximum-size
+.Op Fl O Ar offset
+.Op Fl o Ar fs-options
+.Op Fl S Ar sector-size
+.Op Fl s Ar image-size
+.Op Fl T Ar timestamp
+.Op Fl t Ar fs-type
+.Ar image-file
+.Ar directory
+.Op Ar extra-directory ...
+.Sh DESCRIPTION
+The utility
+.Nm
+creates a file system image into
+.Ar image-file
+from the directory tree
+.Ar directory .
+If any optional directory trees are passed in the
+.Ar extra-directory
+arguments, then the directory tree of each argument will be merged
+into the
+.Ar directory
+first before creating
+.Ar image-file .
+No special devices or privileges are required to perform this task.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl B Ar endian
+Set the byte order of the image to
+.Ar endian .
+Valid byte orders are
+.Ql 4321 ,
+.Ql big ,
+or
+.Ql be
+for big endian, and
+.Ql 1234 ,
+.Ql little ,
+or
+.Ql le
+for little endian.
+Some file systems may have a fixed byte order; in those cases this
+argument will be ignored.
+.It Fl b Ar free-blocks
+Ensure that a minimum of
+.Ar free-blocks
+free blocks exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-blocks
+indicates a percentage of the calculated image size.
+.It Fl d Ar debug-mask
+Enable various levels of debugging, depending upon which bits are
+set in
+.Ar debug-mask .
+XXX: document these
+.It Fl f Ar free-files
+Ensure that a minimum of
+.Ar free-files
+free files (inodes) exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-files
+indicates a percentage of the calculated image size.
+.It Fl M Ar minimum-size
+Set the minimum size of the file system image to
+.Ar minimum-size .
+.It Fl m Ar maximum-size
+Set the maximum size of the file system image to
+.Ar maximum-size .
+An error will be raised if the target file system needs to be larger
+than this to accommodate the provided directory tree.
+.It Fl O Ar offset
+Instead of creating the file system at the beginning of the file, start
+at offset.
+Valid only for
+.Sy ffs
+and
+.Sy msdos .
+.It Fl o Ar fs-options
+Set file system specific options.
+.Ar fs-options
+is a comma separated list of options.
+Valid file system specific options are detailed below.
+.It Fl r
+When merging multiple directories replace duplicate files with the last found.
+.It Fl S Ar sector-size
+Set the file system sector size to
+.Ar sector-size .
+.\" XXX: next line also true for cd9660?
+Defaults to 512.
+.It Fl s Ar image-size
+Set the size of the file system image to
+.Ar image-size .
+.It Fl T Ar timestamp
+Specify a timestamp to be set for all file system files and directories
+created so that repeatable builds are possible.
+The
+.Ar timestamp
+can be a
+.Pa pathname ,
+where the timestamps are derived from that file, a parseable date
+for
+.Xr parsedate 3
+(this option is not yet available in the tools build), or an integer
+value interpreted as the number of seconds from the Epoch.
+.It Fl t Ar fs-type
+Create an
+.Ar fs-type
+file system image.
+The following file system types are supported:
+.Bl -tag -width cd9660 -offset indent
+.It Sy ffs
+BSD fast file system (default).
+.It Sy cd9660
+ISO 9660 file system.
+.It Sy msdos
+FAT12, FAT16, or FAT32 file system.
+.El
+.It Fl Z
+Create a sparse file for
+.Sy ffs .
+This is useful for virtual machine images.
+.El
+.Pp
+Where sizes are specified, a decimal number of bytes is expected.
+Two or more numbers may be separated by an
+.Dq x
+to indicate a product.
+Each number may have one of the following optional suffixes:
+.Bl -tag -width 3n -offset indent -compact
+.It b
+Block; multiply by 512
+.It k
+Kibi; multiply by 1024 (1 KiB)
+.It m
+Mebi; multiply by 1048576 (1 MiB)
+.It g
+Gibi; multiply by 1073741824 (1 GiB)
+.It t
+Tebi; multiply by 1099511627776 (1 TiB)
+.It w
+Word; multiply by the number of bytes in an integer
+.El
+.\"
+.\"
+.Ss FFS-specific options
+.Sy ffs
+images have ffs-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy avgfilesize
+Expected average file size.
+.It Sy avgfpdir
+Expected number of files per directory.
+.It Sy bsize
+Block size.
+.It Sy density
+Bytes per inode.
+.It Sy fsize
+Fragment size.
+.It Sy label
+Label name of the image.
+.It Sy maxbpg
+Maximum blocks per file in a cylinder group.
+.It Sy minfree
+Minimum % free.
+.It Sy optimization
+Optimization preference; one of
+.Ql space
+or
+.Ql time .
+.It Sy extent
+Maximum extent size.
+.It Sy maxbpcg
+Maximum total number of blocks in a cylinder group.
+.It Sy version
+UFS version.
+1 for FFS (default), 2 for UFS2.
+.El
+.Ss CD9660-specific options
+.Sy cd9660
+images have ISO9660-specific optional parameters that may be
+provided.
+The arguments consist of a keyword and, optionally, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width omit-trailing-period -offset indent -compact
+.It Sy allow-deep-trees
+Allow the directory structure to exceed the maximum specified in
+the spec.
+.\" .It Sy allow-illegal-chars
+.\" Unknown
+.\" .It Sy allow-lowercase
+.\" Unknown
+.It Sy allow-max-name
+Allow 37 instead of 33 characters for filenames by omitting the
+version id.
+.It Sy allow-multidot
+Allow multiple dots in a filename.
+.It Sy applicationid
+Application ID of the image.
+.It Sy archimedes
+Use the
+.Ql ARCHIMEDES
+extension to encode
+.Tn RISC OS
+metadata.
+.It Sy chrp-boot
+Write an MBR partition table to the image to allow older CHRP hardware to
+boot.
+.It Sy boot-load-segment
+Set load segment for the boot image.
+.It Sy bootimage
+Filename of a boot image in the format
+.Dq sysid;filename ,
+where
+.Dq sysid
+is one of
+.Ql i386 ,
+.Ql mac68k ,
+.Ql macppc ,
+or
+.Ql powerpc .
+.It Sy generic-bootimage
+Load a generic boot image into the first 32K of the cd9660 image.
+.It Sy hard-disk-boot
+Boot image is a hard disk image.
+.It Sy keep-bad-images
+Don't throw away images whose write was aborted due to an error.
+For debugging purposes.
+.It Sy label
+Label name of the image.
+.It Sy no-boot
+Boot image is not bootable.
+.It Sy no-emul-boot
+Boot image is a
+.Dq no emulation
+ElTorito image.
+.It Sy no-trailing-padding
+Do not pad the image (apparently Linux needs the padding).
+.\" .It Sy omit-trailing-period
+.\" Unknown
+.It Sy preparer
+Preparer ID of the image.
+.It Sy publisher
+Publisher ID of the image.
+.It Sy rockridge
+Use RockRidge extensions (for longer filenames, etc.).
+.It Sy volumeid
+Volume set identifier of the image.
+.El
+.Ss msdos-specific options
+See
+.Xr newfs_msdos 8
+for fs specific options.
+.Sh SEE ALSO
+.Xr scan_scaled 3 ,
+.Xr installboot 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An Luke Mewburn
+.Aq lukem@NetBSD.org
+(original program),
+.An Daniel Watt ,
+.An Walter Deignan ,
+.An Ryan Gabrys ,
+.An Alan Perez-Rathke ,
+.An Ram Vedam
+(cd9660 support),
+.An UCHIYAMA Yasushi
+(v7fs support),
+.An Tamas Toth
+(chfs support).
+.An Christos Zoulas
+(msdos support).
+.An Reinoud Zandijk
+(udf support).
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
new file mode 100644
index 00000000000..081bdfe39cc
--- /dev/null
+++ b/usr.sbin/makefs/makefs.c
@@ -0,0 +1,438 @@
+/* $NetBSD: makefs.c,v 1.53 2015/11/27 15:10:32 joerg Exp $ */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+
+/*
+ * list of supported file systems and dispatch functions
+ */
+typedef struct {
+ const char *type;
+ void (*prepare_options)(fsinfo_t *);
+ int (*parse_options)(const char *, fsinfo_t *);
+ void (*cleanup_options)(fsinfo_t *);
+ void (*make_fs)(const char *, const char *, fsnode *,
+ fsinfo_t *);
+} fstype_t;
+
+static fstype_t fstypes[] = {
+#define ENTRY(name) { \
+ # name, name ## _prep_opts, name ## _parse_opts, \
+ name ## _cleanup_opts, name ## _makefs \
+}
+ ENTRY(ffs),
+ ENTRY(cd9660),
+ ENTRY(msdos),
+ { .type = NULL },
+};
+
+u_int debug;
+struct timespec start_time;
+struct stat stampst;
+
+static fstype_t *get_fstype(const char *);
+static int get_tstamp(const char *, struct stat *);
+static long long strsuftoll(const char *, const char *, long long, long long);
+static void usage(fstype_t *, fsinfo_t *) __dead;
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval start;
+ fstype_t *fstype;
+ fsinfo_t fsoptions;
+ fsnode *root;
+ int ch, i, len;
+
+ setprogname(argv[0]);
+
+ debug = 0;
+ if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
+ errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
+
+ /* set default fsoptions */
+ (void)memset(&fsoptions, 0, sizeof(fsoptions));
+ fsoptions.fd = -1;
+ fsoptions.sectorsize = -1;
+
+ if (fstype->prepare_options)
+ fstype->prepare_options(&fsoptions);
+
+ ch = clock_gettime(CLOCK_REALTIME, &start_time);
+ if (ch == -1)
+ err(1, "Unable to get system time");
+
+
+ while ((ch = getopt(argc, argv, "B:b:d:f:M:m:O:o:rs:S:t:T:Z")) != -1) {
+ switch (ch) {
+
+ case 'B':
+ if (strcmp(optarg, "be") == 0 ||
+ strcmp(optarg, "4321") == 0 ||
+ strcmp(optarg, "big") == 0) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ fsoptions.needswap = 1;
+#endif
+ } else if (strcmp(optarg, "le") == 0 ||
+ strcmp(optarg, "1234") == 0 ||
+ strcmp(optarg, "little") == 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ fsoptions.needswap = 1;
+#endif
+ } else {
+ warnx("Invalid endian `%s'.", optarg);
+ usage(fstype, &fsoptions);
+ }
+ break;
+
+ case 'b':
+ len = strlen(optarg) - 1;
+ if (optarg[len] == '%') {
+ optarg[len] = '\0';
+ fsoptions.freeblockpc =
+ strsuftoll("free block percentage",
+ optarg, 0, 99);
+ } else {
+ fsoptions.freeblocks =
+ strsuftoll("free blocks",
+ optarg, 0, LLONG_MAX);
+ }
+ break;
+
+ case 'd':
+ debug = strtoll(optarg, NULL, 0);
+ break;
+
+ case 'f':
+ len = strlen(optarg) - 1;
+ if (optarg[len] == '%') {
+ optarg[len] = '\0';
+ fsoptions.freefilepc =
+ strsuftoll("free file percentage",
+ optarg, 0, 99);
+ } else {
+ fsoptions.freefiles =
+ strsuftoll("free files",
+ optarg, 0, LLONG_MAX);
+ }
+ break;
+
+ case 'M':
+ fsoptions.minsize =
+ strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'm':
+ fsoptions.maxsize =
+ strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'O':
+ fsoptions.offset =
+ strsuftoll("offset", optarg, 0LL, LLONG_MAX);
+ break;
+
+ case 'o':
+ {
+ char *p;
+
+ while ((p = strsep(&optarg, ",")) != NULL) {
+ if (*p == '\0')
+ errx(1, "Empty option");
+ if (! fstype->parse_options(p, &fsoptions))
+ usage(fstype, &fsoptions);
+ }
+ break;
+ }
+
+ case 'r':
+ fsoptions.replace = 1;
+ break;
+
+ case 's':
+ fsoptions.minsize = fsoptions.maxsize =
+ strsuftoll("size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'S':
+ fsoptions.sectorsize =
+ (int)strsuftoll("sector size", optarg,
+ 1LL, INT_MAX);
+ break;
+
+ case 't':
+ /* Check current one and cleanup if necessary. */
+ if (fstype->cleanup_options)
+ fstype->cleanup_options(&fsoptions);
+ fsoptions.fs_specific = NULL;
+ if ((fstype = get_fstype(optarg)) == NULL)
+ errx(1, "Unknown fs type `%s'.", optarg);
+ fstype->prepare_options(&fsoptions);
+ break;
+
+ case 'T':
+ if (get_tstamp(optarg, &stampst) == -1)
+ errx(1, "Cannot get timestamp from `%s'",
+ optarg);
+ break;
+
+ case 'Z':
+ fsoptions.sparse = 1;
+ break;
+
+ case '?':
+ default:
+ usage(fstype, &fsoptions);
+ /* NOTREACHED */
+
+ }
+ }
+ if (debug) {
+ printf("debug mask: 0x%08x\n", debug);
+ printf("start time: %ld.%ld, %s",
+ (long)start_time.tv_sec, (long)start_time.tv_nsec,
+ ctime(&start_time.tv_sec));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage(fstype, &fsoptions);
+
+ /* walk the tree */
+ TIMER_START(start);
+ root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace);
+ TIMER_RESULTS(start, "walk_dir");
+
+ /* append extra directory */
+ for (i = 2; i < argc; i++) {
+ struct stat sb;
+ if (stat(argv[i], &sb) == -1)
+ err(1, "Can't stat `%s'", argv[i]);
+ if (!S_ISDIR(sb.st_mode))
+ errx(1, "%s: not a directory", argv[i]);
+ TIMER_START(start);
+ root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace);
+ TIMER_RESULTS(start, "walk_dir2");
+ }
+
+ if (debug & DEBUG_DUMP_FSNODES) {
+ printf("\nparent: %s\n", argv[1]);
+ dump_fsnodes(root);
+ putchar('\n');
+ }
+
+ /* build the file system */
+ TIMER_START(start);
+ fstype->make_fs(argv[0], argv[1], root, &fsoptions);
+ TIMER_RESULTS(start, "make_fs");
+
+ free_fsnodes(root);
+
+ exit(0);
+ /* NOTREACHED */
+}
+
+int
+set_option(const option_t *options, const char *option, char *buf, size_t len)
+{
+ char *var, *val;
+ int retval;
+
+ assert(option != NULL);
+
+ var = estrdup(option);
+ for (val = var; *val; val++)
+ if (*val == '=') {
+ *val++ = '\0';
+ break;
+ }
+ retval = set_option_var(options, var, val, buf, len);
+ free(var);
+ return retval;
+}
+
+int
+set_option_var(const option_t *options, const char *var, const char *val,
+ char *buf, size_t len)
+{
+ char *s;
+ size_t i;
+
+#define NUM(type) \
+ if (!*val) { \
+ *(type *)options[i].value = 1; \
+ break; \
+ } \
+ *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
+ options[i].minimum, options[i].maximum); break
+
+ for (i = 0; options[i].name != NULL; i++) {
+ if (var[1] == '\0') {
+ if (options[i].letter != var[0])
+ continue;
+ } else if (strcmp(options[i].name, var) != 0)
+ continue;
+ switch (options[i].type) {
+ case OPT_BOOL:
+ *(bool *)options[i].value = 1;
+ break;
+ case OPT_STRARRAY:
+ strlcpy((void *)options[i].value, val, (size_t)
+ options[i].maximum);
+ break;
+ case OPT_STRPTR:
+ s = estrdup(val);
+ *(char **)options[i].value = s;
+ break;
+ case OPT_STRBUF:
+ if (buf == NULL)
+ abort();
+ strlcpy(buf, val, len);
+ break;
+ case OPT_INT64:
+ NUM(uint64_t);
+ case OPT_INT32:
+ NUM(uint32_t);
+ case OPT_INT16:
+ NUM(uint16_t);
+ case OPT_INT8:
+ NUM(uint8_t);
+ default:
+ warnx("Unknown type %d in option %s", options[i].type,
+ val);
+ return 0;
+ }
+ return i;
+ }
+ warnx("Unknown option `%s'", var);
+ return -1;
+}
+
+
+static fstype_t *
+get_fstype(const char *type)
+{
+ int i;
+
+ for (i = 0; fstypes[i].type != NULL; i++)
+ if (strcmp(fstypes[i].type, type) == 0)
+ return (&fstypes[i]);
+ return (NULL);
+}
+
+option_t *
+copy_opts(const option_t *o)
+{
+ size_t i;
+ for (i = 0; o[i].name; i++)
+ continue;
+ i++;
+ return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
+}
+
+static int
+get_tstamp(const char *b, struct stat *st)
+{
+ time_t when;
+ char *eb;
+
+ if (stat(b, st) != -1)
+ return 0;
+
+ errno = 0;
+ when = strtoll(b, &eb, 0);
+ if (b == eb || *eb || errno)
+ return -1;
+
+ st->st_ino = 1;
+ st->st_mtime = st->st_ctime = st->st_atime = when;
+ return 0;
+}
+
+/* XXX */
+static long long
+strsuftoll(const char *desc, const char *val, long long min, long long max)
+{
+ long long res;
+
+ if (scan_scaled((char *)val, &res) == -1)
+ err(1, "%s", desc);
+ if (res < min || res > max)
+ errc(1, ERANGE, "%s", desc);
+ return res;
+}
+
+static void
+usage(fstype_t *fstype, fsinfo_t *fsoptions)
+{
+ const char *prog;
+
+ prog = getprogname();
+ fprintf(stderr,
+"Usage: %s [-rZ] [-B endian] [-b free-blocks] [-d debug-mask]\n"
+"\t[-f free-files] [-M minimum-size] [-m maximum-size]\n"
+"\t[-O offset] [-o fs-options] [-S sector-size]\n"
+"\t[-s image-size] [-T <timestamp/file>] [-t fs-type]"
+" image-file directory [extra-directory ...]\n",
+ prog);
+
+ if (fstype) {
+ size_t i;
+ option_t *o = fsoptions->fs_options;
+
+ fprintf(stderr, "\n%s specific options:\n", fstype->type);
+ for (i = 0; o[i].name != NULL; i++)
+ fprintf(stderr, "\t%c%c%20.20s\t%s\n",
+ o[i].letter ? o[i].letter : ' ',
+ o[i].letter ? ',' : ' ',
+ o[i].name, o[i].desc);
+ }
+ exit(1);
+}
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
new file mode 100644
index 00000000000..595db701abf
--- /dev/null
+++ b/usr.sbin/makefs/makefs.h
@@ -0,0 +1,248 @@
+/* $NetBSD: makefs.h,v 1.36 2015/11/25 00:48:49 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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 _MAKEFS_H
+#define _MAKEFS_H
+
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_GEN 1
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
+#define HAVE_STRUCT_STATVFS_F_IOSIZE 0
+#define HAVE_FSTATVFS 1
+
+#include <sys/stat.h>
+#include <err.h>
+
+/*
+ * fsnode -
+ * a component of the tree; contains a filename, a pointer to
+ * fsinode, optional symlink name, and tree pointers
+ *
+ * fsinode -
+ * equivalent to an inode, containing target file system inode number,
+ * refcount (nlink), and stat buffer
+ *
+ * A tree of fsnodes looks like this:
+ *
+ * name "." "bin" "netbsd"
+ * type S_IFDIR S_IFDIR S_IFREG
+ * next > > NULL
+ * parent NULL NULL NULL
+ * child NULL v
+ *
+ * name "." "ls"
+ * type S_IFDIR S_IFREG
+ * next > NULL
+ * parent ^ ^ (to "bin")
+ * child NULL NULL
+ *
+ * Notes:
+ * - first always points to first entry, at current level, which
+ * must be "." when the tree has been built; during build it may
+ * not be if "." hasn't yet been found by readdir(2).
+ */
+
+enum fi_flags {
+ FI_SIZED = 1<<0, /* inode sized */
+ FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */
+ FI_WRITTEN = 1<<2, /* inode written */
+};
+
+typedef struct {
+ uint32_t ino; /* inode number used on target fs */
+ uint32_t nlink; /* number of links to this entry */
+ enum fi_flags flags; /* flags used by fs specific code */
+ struct stat st; /* stat entry */
+ void *fsuse; /* for storing FS dependent info */
+} fsinode;
+
+typedef struct _fsnode {
+ struct _fsnode *parent; /* parent (NULL if root) */
+ struct _fsnode *child; /* child (if type == S_IFDIR) */
+ struct _fsnode *next; /* next */
+ struct _fsnode *first; /* first node of current level (".") */
+ uint32_t type; /* type of entry */
+ fsinode *inode; /* actual inode data */
+ char *symlink; /* symlink target */
+ const char *root; /* root path */
+ char *path; /* directory name */
+ char *name; /* file name */
+ int flags; /* misc flags */
+} fsnode;
+
+#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */
+
+/*
+ * option_t - contains option name, description, pointer to location to store
+ * result, and range checks for the result. Used to simplify fs specific
+ * option setting
+ */
+typedef enum {
+ OPT_STRARRAY,
+ OPT_STRPTR,
+ OPT_STRBUF,
+ OPT_BOOL,
+ OPT_INT8,
+ OPT_INT16,
+ OPT_INT32,
+ OPT_INT64
+} opttype_t;
+
+typedef struct {
+ char letter; /* option letter NUL for none */
+ const char *name; /* option name */
+ void *value; /* where to stuff the value */
+ opttype_t type; /* type of entry */
+ long long minimum; /* minimum for value */
+ long long maximum; /* maximum for value */
+ const char *desc; /* option description */
+} option_t;
+
+/*
+ * fsinfo_t - contains various settings and parameters pertaining to
+ * the image, including current settings, global options, and fs
+ * specific options
+ */
+typedef struct makefs_fsinfo {
+ /* current settings */
+ off_t size; /* total size */
+ off_t inodes; /* number of inodes */
+ uint32_t curinode; /* current inode */
+
+ /* image settings */
+ int fd; /* file descriptor of image */
+ void *superblock; /* superblock */
+
+ /* global options */
+ off_t minsize; /* minimum size image should be */
+ off_t maxsize; /* maximum size image can be */
+ off_t freefiles; /* free file entries to leave */
+ off_t freeblocks; /* free blocks to leave */
+ off_t offset; /* offset from start of file */
+ int freefilepc; /* free file % */
+ int freeblockpc; /* free block % */
+ int needswap; /* non-zero if byte swapping needed */
+ int sectorsize; /* sector size */
+ int sparse; /* sparse image, don't fill it with zeros */
+ int replace; /* replace files when merging */
+
+ void *fs_specific; /* File system specific additions. */
+ option_t *fs_options; /* File system specific options */
+} fsinfo_t;
+
+
+
+
+void dump_fsnodes(fsnode *);
+const char * inode_type(mode_t);
+int set_option(const option_t *, const char *, char *, size_t);
+int set_option_var(const option_t *, const char *, const char *,
+ char *, size_t);
+fsnode * walk_dir(const char *, const char *, fsnode *, fsnode *, int);
+void free_fsnodes(fsnode *);
+option_t * copy_opts(const option_t *);
+
+#define DECLARE_FUN(fs) \
+void fs ## _prep_opts(fsinfo_t *); \
+int fs ## _parse_opts(const char *, fsinfo_t *); \
+void fs ## _cleanup_opts(fsinfo_t *); \
+void fs ## _makefs(const char *, const char *, fsnode *, fsinfo_t *)
+
+DECLARE_FUN(ffs);
+DECLARE_FUN(cd9660);
+DECLARE_FUN(msdos);
+
+extern u_int debug;
+extern struct timespec start_time;
+extern struct stat stampst;
+
+#define DEBUG_TIME 0x00000001
+ /* debug bits 1..3 unused at this time */
+#define DEBUG_WALK_DIR 0x00000010
+#define DEBUG_WALK_DIR_NODE 0x00000020
+#define DEBUG_WALK_DIR_LINKCHECK 0x00000040
+#define DEBUG_DUMP_FSNODES 0x00000080
+#define DEBUG_DUMP_FSNODES_VERBOSE 0x00000100
+#define DEBUG_FS_PARSE_OPTS 0x00000200
+#define DEBUG_FS_MAKEFS 0x00000400
+#define DEBUG_FS_VALIDATE 0x00000800
+#define DEBUG_FS_CREATE_IMAGE 0x00001000
+#define DEBUG_FS_SIZE_DIR 0x00002000
+#define DEBUG_FS_SIZE_DIR_NODE 0x00004000
+#define DEBUG_FS_SIZE_DIR_ADD_DIRENT 0x00008000
+#define DEBUG_FS_POPULATE 0x00010000
+#define DEBUG_FS_POPULATE_DIRBUF 0x00020000
+#define DEBUG_FS_POPULATE_NODE 0x00040000
+#define DEBUG_FS_WRITE_FILE 0x00080000
+#define DEBUG_FS_WRITE_FILE_BLOCK 0x00100000
+#define DEBUG_FS_MAKE_DIRBUF 0x00200000
+#define DEBUG_FS_WRITE_INODE 0x00400000
+#define DEBUG_BUF_BREAD 0x00800000
+#define DEBUG_BUF_BWRITE 0x01000000
+#define DEBUG_BUF_GETBLK 0x02000000
+#define DEBUG_APPLY_SPECFILE 0x04000000
+#define DEBUG_APPLY_SPECENTRY 0x08000000
+#define DEBUG_APPLY_SPECONLY 0x10000000
+
+
+#define TIMER_START(x) \
+ if (debug & DEBUG_TIME) \
+ gettimeofday(&(x), NULL)
+
+#define TIMER_RESULTS(x,d) \
+ if (debug & DEBUG_TIME) { \
+ struct timeval end, td; \
+ gettimeofday(&end, NULL); \
+ timersub(&end, &(x), &td); \
+ printf("%s took %lld.%06ld seconds\n", \
+ (d), (long long)td.tv_sec, \
+ (long)td.tv_usec); \
+ }
+
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE "ffs"
+#endif
+
+
+/* xmalloc.c */
+void *emalloc(size_t);
+void *ecalloc(size_t, size_t);
+void *erealloc(void *, size_t);
+char *estrdup(const char *);
+
+#endif /* _MAKEFS_H */
diff --git a/usr.sbin/makefs/msdos.c b/usr.sbin/makefs/msdos.c
new file mode 100644
index 00000000000..002c4910a09
--- /dev/null
+++ b/usr.sbin/makefs/msdos.c
@@ -0,0 +1,258 @@
+/* $NetBSD: msdos.c,v 1.16 2016/01/30 09:59:27 mlelstv Exp $ */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <util.h>
+
+#include <ffs/buf.h>
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include "makefs.h"
+#include "msdos.h"
+#include "mkfs_msdos.h"
+
+static int msdos_populate_dir(const char *, struct denode *, fsnode *,
+ fsnode *, fsinfo_t *);
+
+struct msdos_options_ex {
+ struct msdos_options options;
+ bool utf8;
+};
+
+void
+msdos_prep_opts(fsinfo_t *fsopts)
+{
+ struct msdos_options_ex *msdos_opt = ecalloc(1, sizeof(*msdos_opt));
+ const option_t msdos_options[] = {
+#define AOPT(_opt, _type, _name, _min, _desc) { \
+ .letter = _opt, \
+ .name = # _name, \
+ .type = _min == -1 ? OPT_STRPTR : \
+ (_min == -2 ? OPT_BOOL : \
+ (sizeof(_type) == 1 ? OPT_INT8 : \
+ (sizeof(_type) == 2 ? OPT_INT16 : \
+ (sizeof(_type) == 4 ? OPT_INT32 : OPT_INT64)))), \
+ .value = &msdos_opt->options._name, \
+ .minimum = _min, \
+ .maximum = sizeof(_type) == 1 ? 0xff : \
+ (sizeof(_type) == 2 ? 0xffff : \
+ (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)), \
+ .desc = _desc, \
+},
+ALLOPTS
+#undef AOPT
+ { 'U', "utf8", &msdos_opt->utf8, OPT_BOOL,
+ 0, 1, "Use UTF8 names" },
+ { .name = NULL }
+ };
+
+ fsopts->fs_specific = msdos_opt;
+ fsopts->fs_options = copy_opts(msdos_options);
+}
+
+void
+msdos_cleanup_opts(fsinfo_t *fsopts)
+{
+ free(fsopts->fs_specific);
+ free(fsopts->fs_options);
+}
+
+int
+msdos_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ struct msdos_options *msdos_opt = fsopts->fs_specific;
+ option_t *msdos_options = fsopts->fs_options;
+
+ int rv;
+
+ assert(option != NULL);
+ assert(fsopts != NULL);
+ assert(msdos_opt != NULL);
+
+ if (debug & DEBUG_FS_PARSE_OPTS)
+ printf("msdos_parse_opts: got `%s'\n", option);
+
+ rv = set_option(msdos_options, option, NULL, 0);
+ if (rv == -1)
+ return rv;
+
+ if (strcmp(msdos_options[rv].name, "volume_id") == 0)
+ msdos_opt->volume_id_set = 1;
+ else if (strcmp(msdos_options[rv].name, "media_descriptor") == 0)
+ msdos_opt->media_descriptor_set = 1;
+ else if (strcmp(msdos_options[rv].name, "hidden_sectors") == 0)
+ msdos_opt->hidden_sectors_set = 1;
+ return 1;
+}
+
+
+void
+msdos_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ struct msdos_options_ex *msdos_opt = fsopts->fs_specific;
+ struct vnode vp, rootvp;
+ struct timeval start;
+ struct msdosfsmount *pmp;
+ uint32_t flags;
+
+ assert(image != NULL);
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ /*
+ * XXX: pick up other options from the msdos specific ones?
+ * Is minsize right here?
+ */
+ msdos_opt->options.create_size = MAX(msdos_opt->options.create_size,
+ fsopts->minsize);
+ msdos_opt->options.offset = fsopts->offset;
+ if (msdos_opt->options.bytes_per_sector == 0) {
+ if (fsopts->sectorsize == -1)
+ fsopts->sectorsize = 512;
+ msdos_opt->options.bytes_per_sector = fsopts->sectorsize;
+ } else if (fsopts->sectorsize == -1) {
+ fsopts->sectorsize = msdos_opt->options.bytes_per_sector;
+ } else if (fsopts->sectorsize != msdos_opt->options.bytes_per_sector) {
+ err(1, "inconsistent sectorsize -S %u"
+ "!= -o bytes_per_sector %u",
+ fsopts->sectorsize, msdos_opt->options.bytes_per_sector);
+ }
+
+ /* create image */
+ printf("Creating `%s'\n", image);
+ TIMER_START(start);
+ if (mkfs_msdos(image, NULL, &msdos_opt->options) == -1)
+ return;
+ TIMER_RESULTS(start, "mkfs_msdos");
+
+ fsopts->fd = open(image, O_RDWR);
+ vp.fs = fsopts;
+
+ flags = 0;
+ if (msdos_opt->utf8)
+ flags |= MSDOSFSMNT_UTF8;
+
+ if ((pmp = msdosfs_mount(&vp, flags)) == NULL)
+ err(1, "msdosfs_mount");
+
+ if (msdosfs_root(pmp, &rootvp) != 0)
+ err(1, "msdosfs_root");
+
+ if (debug & DEBUG_FS_MAKEFS)
+ printf("msdos_makefs: image %s directory %s root %p\n",
+ image, dir, root);
+
+ /* populate image */
+ printf("Populating `%s'\n", image);
+ TIMER_START(start);
+ if (msdos_populate_dir(dir, VTODE(&rootvp), root, root, fsopts) == -1)
+ errx(1, "Image file `%s' not created.", image);
+ TIMER_RESULTS(start, "msdos_populate_dir");
+
+ if (debug & DEBUG_FS_MAKEFS)
+ putchar('\n');
+
+ /* ensure no outstanding buffers remain */
+ if (debug & DEBUG_FS_MAKEFS)
+ bcleanup();
+
+ printf("Image `%s' complete\n", image);
+}
+
+static int
+msdos_populate_dir(const char *path, struct denode *dir, fsnode *root,
+ fsnode *parent, fsinfo_t *fsopts)
+{
+ fsnode *cur;
+ char pbuf[MAXPATHLEN];
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ for (cur = root->next; cur != NULL; cur = cur->next) {
+ if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s", path,
+ cur->name) >= sizeof(pbuf)) {
+ warnx("path %s too long", pbuf);
+ return -1;
+ }
+
+ if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+ cur->inode->flags |= FI_ALLOCATED;
+ if (cur != root) {
+ fsopts->curinode++;
+ cur->inode->ino = fsopts->curinode;
+ cur->parent = parent;
+ }
+ }
+
+ if (cur->inode->flags & FI_WRITTEN) {
+ continue; // hard link
+ }
+ cur->inode->flags |= FI_WRITTEN;
+
+ if (cur->child) {
+ struct denode *de;
+ if ((de = msdosfs_mkdire(pbuf, dir, cur)) == NULL) {
+ warn("msdosfs_mkdire %s", pbuf);
+ return -1;
+ }
+ if (msdos_populate_dir(pbuf, de, cur->child, cur,
+ fsopts) == -1) {
+ warn("msdos_populate_dir %s", pbuf);
+ return -1;
+ }
+ continue;
+ } else if (!S_ISREG(cur->type)) {
+ warnx("skipping non-regular file %s/%s", cur->path,
+ cur->name);
+ continue;
+ }
+ if (msdosfs_mkfile(pbuf, dir, cur) == NULL) {
+ warn("msdosfs_mkfile %s", pbuf);
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/makefs/msdos.h b/usr.sbin/makefs/msdos.h
new file mode 100644
index 00000000000..0357f6d9351
--- /dev/null
+++ b/usr.sbin/makefs/msdos.h
@@ -0,0 +1,39 @@
+/* $NetBSD: msdos.h,v 1.3 2015/10/16 16:40:02 christos Exp $ */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+struct vnode;
+struct denode;
+
+struct msdosfsmount *msdosfs_mount(struct vnode *, int);
+int msdosfs_root(struct msdosfsmount *, struct vnode *);
+
+struct denode *msdosfs_mkfile(const char *, struct denode *, fsnode *);
+struct denode *msdosfs_mkdire(const char *, struct denode *, fsnode *);
diff --git a/usr.sbin/makefs/msdos/msdosfs_denode.c b/usr.sbin/makefs/msdos/msdosfs_denode.c
new file mode 100644
index 00000000000..d77294260fd
--- /dev/null
+++ b/usr.sbin/makefs/msdos/msdosfs_denode.c
@@ -0,0 +1,358 @@
+/* $NetBSD: msdosfs_denode.c,v 1.7 2015/03/29 05:52:59 agc Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/fat.h>
+
+#include <util.h>
+
+#include "makefs.h"
+
+/*
+ * If deget() succeeds it returns with the gotten denode locked().
+ *
+ * pmp - address of msdosfsmount structure of the filesystem containing
+ * the denode of interest. The pm_dev field and the address of
+ * the msdosfsmount structure are used.
+ * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
+ * diroffset is relative to the beginning of the root directory,
+ * otherwise it is cluster relative.
+ * diroffset - offset past begin of cluster of denode we want
+ * depp - returns the address of the gotten denode.
+ */
+int
+deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
+ struct denode **depp)
+ /* pmp: so we know the maj/min number */
+ /* dirclust: cluster this dir entry came from */
+ /* diroffset: index of entry within the cluster */
+ /* depp: returns the addr of the gotten denode */
+{
+ int error;
+ struct direntry *direntptr;
+ struct denode *ldep;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
+ pmp, dirclust, diroffset, depp);
+#endif
+
+ /*
+ * On FAT32 filesystems, root is a (more or less) normal
+ * directory
+ */
+ if (FAT32(pmp) && dirclust == MSDOSFSROOT)
+ dirclust = pmp->pm_rootdirblk;
+
+ ldep = ecalloc(1, sizeof(*ldep));
+ ldep->de_vnode = NULL;
+ ldep->de_flag = 0;
+ ldep->de_devvp = 0;
+ ldep->de_lockf = 0;
+ ldep->de_dev = pmp->pm_dev;
+ ldep->de_dirclust = dirclust;
+ ldep->de_diroffset = diroffset;
+ ldep->de_pmp = pmp;
+ ldep->de_devvp = pmp->pm_devvp;
+ ldep->de_refcnt = 1;
+ fc_purge(ldep, 0);
+ /*
+ * Copy the directory entry into the denode area of the vnode.
+ */
+ if ((dirclust == MSDOSFSROOT
+ || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
+ && diroffset == MSDOSFSROOT_OFS) {
+ /*
+ * Directory entry for the root directory. There isn't one,
+ * so we manufacture one. We should probably rummage
+ * through the root directory and find a label entry (if it
+ * exists), and then use the time and date from that entry
+ * as the time and date for the root denode.
+ */
+ ldep->de_vnode = (struct vnode *)-1;
+
+ ldep->de_Attributes = ATTR_DIRECTORY;
+ if (FAT32(pmp))
+ ldep->de_StartCluster = pmp->pm_rootdirblk;
+ /* de_FileSize will be filled in further down */
+ else {
+ ldep->de_StartCluster = MSDOSFSROOT;
+ ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
+ }
+ /*
+ * fill in time and date so that dos2unixtime() doesn't
+ * spit up when called from msdosfs_getattr() with root
+ * denode
+ */
+ ldep->de_CHun = 0;
+ ldep->de_CTime = 0x0000; /* 00:00:00 */
+ ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
+ | (1 << DD_DAY_SHIFT);
+ /* Jan 1, 1980 */
+ ldep->de_ADate = ldep->de_CDate;
+ ldep->de_MTime = ldep->de_CTime;
+ ldep->de_MDate = ldep->de_CDate;
+ /* leave the other fields as garbage */
+ } else {
+ error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
+ if (error) {
+ ldep->de_devvp = NULL;
+ ldep->de_Name[0] = SLOT_DELETED;
+ return (error);
+ }
+ DE_INTERNALIZE(ldep, direntptr);
+ brelse(bp, 0);
+ }
+
+ /*
+ * Fill in a few fields of the vnode and finish filling in the
+ * denode. Then return the address of the found denode.
+ */
+ if (ldep->de_Attributes & ATTR_DIRECTORY) {
+ /*
+ * Since DOS directory entries that describe directories
+ * have 0 in the filesize field, we take this opportunity
+ * to find out the length of the directory and plug it into
+ * the denode structure.
+ */
+ u_long size;
+
+ if (ldep->de_StartCluster != MSDOSFSROOT) {
+ error = pcbmap(ldep, CLUST_END, 0, &size, 0);
+ if (error == E2BIG) {
+ ldep->de_FileSize = de_cn2off(pmp, size);
+ error = 0;
+ } else
+ printf("deget(): pcbmap returned %d\n", error);
+ }
+ }
+ *depp = ldep;
+ return (0);
+}
+
+/*
+ * Truncate the file described by dep to the length specified by length.
+ */
+int
+detrunc(struct denode *dep, u_long length, int flags, struct kauth_cred *cred)
+{
+ int error;
+ int allerror = 0;
+ u_long eofentry;
+ u_long chaintofree = 0;
+ daddr_t bn, lastblock;
+ int boff;
+ int isadir = dep->de_Attributes & ATTR_DIRECTORY;
+ struct buf *bp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
+#endif
+
+ /*
+ * Disallow attempts to truncate the root directory since it is of
+ * fixed size. That's just the way dos filesystems are. We use
+ * the VROOT bit in the vnode because checking for the directory
+ * bit and a startcluster of 0 in the denode is not adequate to
+ * recognize the root directory at this point in a file or
+ * directory's life.
+ */
+ if (dep->de_vnode != NULL && !FAT32(pmp)) {
+ printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
+ dep->de_dirclust, dep->de_diroffset);
+ return (EINVAL);
+ }
+
+ if (dep->de_FileSize < length)
+ return (deextend(dep, length, cred));
+ lastblock = de_clcount(pmp, length) - 1;
+
+ /*
+ * If the desired length is 0 then remember the starting cluster of
+ * the file and set the StartCluster field in the directory entry
+ * to 0. If the desired length is not zero, then get the number of
+ * the last cluster in the shortened file. Then get the number of
+ * the first cluster in the part of the file that is to be freed.
+ * Then set the next cluster pointer in the last cluster of the
+ * file to CLUST_EOFE.
+ */
+ if (length == 0) {
+ chaintofree = dep->de_StartCluster;
+ dep->de_StartCluster = 0;
+ eofentry = ~0;
+ } else {
+ error = pcbmap(dep, lastblock, 0, &eofentry, 0);
+ if (error) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): pcbmap fails %d\n", error);
+#endif
+ return (error);
+ }
+ }
+
+ /*
+ * If the new length is not a multiple of the cluster size then we
+ * must zero the tail end of the new last cluster in case it
+ * becomes part of the file again because of a seek.
+ */
+ if ((boff = length & pmp->pm_crbomask) != 0) {
+ if (isadir) {
+ bn = cntobn(pmp, eofentry);
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
+ pmp->pm_bpcluster, B_MODIFY, &bp);
+ if (error) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): bread fails %d\n", error);
+#endif
+ return (error);
+ }
+ memset((char *)bp->b_data + boff, 0,
+ pmp->pm_bpcluster - boff);
+ if (flags & IO_SYNC)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+ }
+ }
+
+ /*
+ * Write out the updated directory entry. Even if the update fails
+ * we free the trailing clusters.
+ */
+ dep->de_FileSize = length;
+ if (!isadir)
+ dep->de_flag |= DE_UPDATE|DE_MODIFIED;
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): allerror %d, eofentry %lu\n",
+ allerror, eofentry);
+#endif
+
+ /*
+ * If we need to break the cluster chain for the file then do it
+ * now.
+ */
+ if (eofentry != (u_long)~0) {
+ error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
+ &chaintofree, CLUST_EOFE);
+ if (error) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): fatentry errors %d\n", error);
+#endif
+ return (error);
+ }
+ }
+
+ /*
+ * Now free the clusters removed from the file because of the
+ * truncation.
+ */
+ if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
+ freeclusterchain(pmp, chaintofree);
+
+ return (allerror);
+}
+
+/*
+ * Extend the file described by dep to length specified by length.
+ */
+int
+deextend(struct denode *dep, u_long length, struct kauth_cred *cred)
+{
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long count;
+ int error;
+
+ /*
+ * The root of a DOS filesystem cannot be extended.
+ */
+ if (dep->de_vnode != NULL && !FAT32(pmp))
+ return EINVAL;
+
+ /*
+ * Directories cannot be extended.
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY)
+ return EISDIR;
+
+ if (length <= dep->de_FileSize)
+ return E2BIG;
+
+ /*
+ * Compute the number of clusters to allocate.
+ */
+ count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
+ if (count > 0) {
+ if (count > pmp->pm_freeclustercount)
+ return (ENOSPC);
+ error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
+ if (error) {
+ /* truncate the added clusters away again */
+ (void) detrunc(dep, dep->de_FileSize, 0, cred);
+ return (error);
+ }
+ }
+
+ /*
+ * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
+ * memset(); we set the write size so ubc won't read in file data that
+ * is zero'd later.
+ */
+ dep->de_FileSize = length;
+ dep->de_flag |= DE_UPDATE|DE_MODIFIED;
+ return 0;
+}
diff --git a/usr.sbin/makefs/msdos/msdosfs_vfsops.c b/usr.sbin/makefs/msdos/msdosfs_vfsops.c
new file mode 100644
index 00000000000..e5453753cfc
--- /dev/null
+++ b/usr.sbin/makefs/msdos/msdosfs_vfsops.c
@@ -0,0 +1,424 @@
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/bootsect.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "msdos.h"
+#include "mkfs_msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+
+struct msdosfsmount *
+msdosfs_mount(struct vnode *devvp, int flags)
+{
+ struct msdosfsmount *pmp = NULL;
+ struct buf *bp;
+ union bootsector *bsp;
+ struct byte_bpb33 *b33;
+ struct byte_bpb50 *b50;
+ struct byte_bpb710 *b710;
+ uint8_t SecPerClust;
+ int ronly = 0, error, tmp;
+ int bsize;
+ struct msdos_options *m = devvp->fs->fs_specific;
+ uint64_t psize = m->create_size;
+ unsigned secsize = 512;
+
+ DPRINTF(("%s(bread 0)\n", __func__));
+ if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
+ goto error_exit;
+
+ bsp = (union bootsector *)bp->b_data;
+ b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
+ b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
+ b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
+
+ if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
+ if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
+ || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
+ DPRINTF(("bootsig0 %d bootsig1 %d\n",
+ bsp->bs50.bsBootSectSig0,
+ bsp->bs50.bsBootSectSig1));
+ error = EINVAL;
+ goto error_exit;
+ }
+ bsize = 0;
+ } else
+ bsize = 512;
+
+ pmp = ecalloc(1, sizeof *pmp);
+ /*
+ * Compute several useful quantities from the bpb in the
+ * bootsector. Copy in the dos 5 variant of the bpb then fix up
+ * the fields that are different between dos 5 and dos 3.3.
+ */
+ SecPerClust = b50->bpbSecPerClust;
+ pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
+ pmp->pm_ResSectors = getushort(b50->bpbResSectors);
+ pmp->pm_FATs = b50->bpbFATs;
+ pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
+ pmp->pm_Sectors = getushort(b50->bpbSectors);
+ pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
+ pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
+ pmp->pm_Heads = getushort(b50->bpbHeads);
+ pmp->pm_Media = b50->bpbMedia;
+
+ DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, "
+ "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n",
+ __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs,
+ pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs,
+ pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media));
+ if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
+ /* XXX - We should probably check more values here */
+ if (!pmp->pm_BytesPerSec || !SecPerClust
+ || pmp->pm_SecPerTrack > 63) {
+ DPRINTF(("bytespersec %d secperclust %d "
+ "secpertrack %d\n",
+ pmp->pm_BytesPerSec, SecPerClust,
+ pmp->pm_SecPerTrack));
+ error = EINVAL;
+ goto error_exit;
+ }
+ }
+
+ pmp->pm_flags = flags & MSDOSFSMNT_MNTOPT;
+ if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
+ pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
+ if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
+ pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
+
+ if (pmp->pm_Sectors == 0) {
+ pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
+ pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
+ } else {
+ pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
+ pmp->pm_HugeSectors = pmp->pm_Sectors;
+ }
+
+ if (pmp->pm_RootDirEnts == 0) {
+ unsigned short vers = getushort(b710->bpbFSVers);
+ /*
+ * Some say that bsBootSectSig[23] must be zero, but
+ * Windows does not require this and some digital cameras
+ * do not set these to zero. Therefore, do not insist.
+ */
+ if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
+ DPRINTF(("sectors %d fatsecs %lu vers %d\n",
+ pmp->pm_Sectors, pmp->pm_FATsecs, vers));
+ error = EINVAL;
+ goto error_exit;
+ }
+ pmp->pm_fatmask = FAT32_MASK;
+ pmp->pm_fatmult = 4;
+ pmp->pm_fatdiv = 1;
+ pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
+
+ /* mirrorring is enabled if the FATMIRROR bit is not set */
+ if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
+ pmp->pm_flags |= MSDOSFS_FATMIRROR;
+ else
+ pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
+ } else
+ pmp->pm_flags |= MSDOSFS_FATMIRROR;
+
+ if (flags & MSDOSFSMNT_GEMDOSFS) {
+ if (FAT32(pmp)) {
+ DPRINTF(("FAT32 for GEMDOS\n"));
+ /*
+ * GEMDOS doesn't know FAT32.
+ */
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ /*
+ * Check a few values (could do some more):
+ * - logical sector size: power of 2, >= block size
+ * - sectors per cluster: power of 2, >= 1
+ * - number of sectors: >= 1, <= size of partition
+ */
+ if ( (SecPerClust == 0)
+ || (SecPerClust & (SecPerClust - 1))
+ || (pmp->pm_BytesPerSec < bsize)
+ || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
+ || (pmp->pm_HugeSectors == 0)
+ || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
+ > psize)) {
+ DPRINTF(("consistency checks for GEMDOS\n"));
+ error = EINVAL;
+ goto error_exit;
+ }
+ /*
+ * XXX - Many parts of the msdosfs driver seem to assume that
+ * the number of bytes per logical sector (BytesPerSec) will
+ * always be the same as the number of bytes per disk block
+ * Let's pretend it is.
+ */
+ tmp = pmp->pm_BytesPerSec / bsize;
+ pmp->pm_BytesPerSec = bsize;
+ pmp->pm_HugeSectors *= tmp;
+ pmp->pm_HiddenSects *= tmp;
+ pmp->pm_ResSectors *= tmp;
+ pmp->pm_Sectors *= tmp;
+ pmp->pm_FATsecs *= tmp;
+ SecPerClust *= tmp;
+ }
+
+ /* Check that fs has nonzero FAT size */
+ if (pmp->pm_FATsecs == 0) {
+ DPRINTF(("FATsecs is 0\n"));
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ pmp->pm_fatblk = pmp->pm_ResSectors;
+ if (FAT32(pmp)) {
+ pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
+ pmp->pm_firstcluster = pmp->pm_fatblk
+ + (pmp->pm_FATs * pmp->pm_FATsecs);
+ pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
+ } else {
+ pmp->pm_rootdirblk = pmp->pm_fatblk +
+ (pmp->pm_FATs * pmp->pm_FATsecs);
+ pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
+ + pmp->pm_BytesPerSec - 1)
+ / pmp->pm_BytesPerSec;/* in sectors */
+ pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
+ }
+
+ pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
+ SecPerClust;
+ pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
+ pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
+
+ if (flags & MSDOSFSMNT_GEMDOSFS) {
+ if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
+ pmp->pm_fatmask = FAT12_MASK;
+ pmp->pm_fatmult = 3;
+ pmp->pm_fatdiv = 2;
+ } else {
+ pmp->pm_fatmask = FAT16_MASK;
+ pmp->pm_fatmult = 2;
+ pmp->pm_fatdiv = 1;
+ }
+ } else if (pmp->pm_fatmask == 0) {
+ if (pmp->pm_maxcluster
+ <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
+ /*
+ * This will usually be a floppy disk. This size makes
+ * sure that one FAT entry will not be split across
+ * multiple blocks.
+ */
+ pmp->pm_fatmask = FAT12_MASK;
+ pmp->pm_fatmult = 3;
+ pmp->pm_fatdiv = 2;
+ } else {
+ pmp->pm_fatmask = FAT16_MASK;
+ pmp->pm_fatmult = 2;
+ pmp->pm_fatdiv = 1;
+ }
+ }
+ if (FAT12(pmp))
+ pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
+ else
+ pmp->pm_fatblocksize = MAXBSIZE;
+
+ pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
+ pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
+
+ /*
+ * Compute mask and shift value for isolating cluster relative byte
+ * offsets and cluster numbers from a file offset.
+ */
+ pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
+ pmp->pm_crbomask = pmp->pm_bpcluster - 1;
+ pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
+
+ DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
+ "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
+ "cnshift=%lu)\n",
+ __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv,
+ pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift,
+ pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift));
+ /*
+ * Check for valid cluster size
+ * must be a power of 2
+ */
+ if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
+ DPRINTF(("bpcluster %lu cnshift %lu\n",
+ pmp->pm_bpcluster, pmp->pm_cnshift));
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ /*
+ * Release the bootsector buffer.
+ */
+ brelse(bp, BC_AGE);
+ bp = NULL;
+
+ /*
+ * Check FSInfo.
+ */
+ if (pmp->pm_fsinfo) {
+ struct fsinfo *fp;
+
+ /*
+ * XXX If the fsinfo block is stored on media with
+ * 2KB or larger sectors, is the fsinfo structure
+ * padded at the end or in the middle?
+ */
+ DPRINTF(("%s(bread %lu)\n", __func__,
+ (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo)));
+ if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
+ pmp->pm_BytesPerSec, 0, &bp)) != 0)
+ goto error_exit;
+ fp = (struct fsinfo *)bp->b_data;
+ if (!memcmp(fp->fsisig1, "RRaA", 4)
+ && !memcmp(fp->fsisig2, "rrAa", 4)
+ && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
+ && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
+ pmp->pm_nxtfree = getulong(fp->fsinxtfree);
+ else
+ pmp->pm_fsinfo = 0;
+ brelse(bp, 0);
+ bp = NULL;
+ }
+
+ /*
+ * Check and validate (or perhaps invalidate?) the fsinfo structure?
+ * XXX
+ */
+ if (pmp->pm_fsinfo) {
+ if ((pmp->pm_nxtfree == 0xffffffffUL) ||
+ (pmp->pm_nxtfree > pmp->pm_maxcluster))
+ pmp->pm_fsinfo = 0;
+ }
+
+ /*
+ * Allocate memory for the bitmap of allocated clusters, and then
+ * fill it in.
+ */
+ pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
+ ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
+ /*
+ * fillinusemap() needs pm_devvp.
+ */
+ pmp->pm_dev = 0;
+ pmp->pm_devvp = devvp;
+
+ /*
+ * Have the inuse map filled in.
+ */
+ if ((error = fillinusemap(pmp)) != 0) {
+ DPRINTF(("fillinusemap %d\n", error));
+ goto error_exit;
+ }
+
+ /*
+ * Finish up.
+ */
+ if (ronly)
+ pmp->pm_flags |= MSDOSFSMNT_RONLY;
+ else
+ pmp->pm_fmod = 1;
+
+ /*
+ * If we ever do quotas for DOS filesystems this would be a place
+ * to fill in the info in the msdosfsmount structure. You dolt,
+ * quotas on dos filesystems make no sense because files have no
+ * owners on dos filesystems. of course there is some empty space
+ * in the directory entry where we could put uid's and gid's.
+ */
+
+ return pmp;
+
+error_exit:
+ if (bp)
+ brelse(bp, BC_AGE);
+ if (pmp) {
+ if (pmp->pm_inusemap)
+ free(pmp->pm_inusemap);
+ free(pmp);
+ }
+ errno = error;
+ return NULL;
+}
+
+int
+msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) {
+ struct denode *ndep;
+ int error;
+
+ *vp = *pmp->pm_devvp;
+ if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) {
+ errno = error;
+ return -1;
+ }
+ vp->v_data = ndep;
+ return 0;
+}
diff --git a/usr.sbin/makefs/msdos/msdosfs_vnops.c b/usr.sbin/makefs/msdos/msdosfs_vnops.c
new file mode 100644
index 00000000000..c166715f7e3
--- /dev/null
+++ b/usr.sbin/makefs/msdos/msdosfs_vnops.c
@@ -0,0 +1,629 @@
+/* $NetBSD: msdosfs_vnops.c,v 1.17 2016/01/30 09:59:27 mlelstv Exp $ */
+
+/*-
+ * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
+ * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
+ * All rights reserved.
+ * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
+ *
+ * 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ */
+/*
+ * Written by Paul Popelka (paulp@uts.amdahl.com)
+ *
+ * You can do anything you want with this software, just don't say you wrote
+ * it, and don't remove this notice.
+ *
+ * This software is provided "as is".
+ *
+ * The author supplies this software to be publicly redistributed on the
+ * understanding that the author is not responsible for the correct
+ * functioning of this software in any circumstances and is not liable for
+ * any damages caused by this software.
+ *
+ * October 1992
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include "makefs.h"
+#include "msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+/*
+ * Some general notes:
+ *
+ * In the ufs filesystem the inodes, superblocks, and indirect blocks are
+ * read/written using the vnode for the filesystem. Blocks that represent
+ * the contents of a file are read/written using the vnode for the file
+ * (including directories when they are read/written as files). This
+ * presents problems for the dos filesystem because data that should be in
+ * an inode (if dos had them) resides in the directory itself. Since we
+ * must update directory entries without the benefit of having the vnode
+ * for the directory we must use the vnode for the filesystem. This means
+ * that when a directory is actually read/written (via read, write, or
+ * readdir, or seek) we must use the vnode for the filesystem instead of
+ * the vnode for the directory as would happen in ufs. This is to insure we
+ * retrieve the correct block from the buffer cache since the hash value is
+ * based upon the vnode address and the desired block number.
+ */
+
+static int msdosfs_wfile(const char *, struct denode *, fsnode *);
+
+static void
+msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
+ const struct stat *st)
+{
+ struct timespec at = st->st_atimespec;
+ struct timespec mt = st->st_mtimespec;
+ unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
+ unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
+}
+
+/*
+ * When we search a directory the blocks containing directory entries are
+ * read and examined. The directory entries contain information that would
+ * normally be in the inode of a unix filesystem. This means that some of
+ * a directory's contents may also be in memory resident denodes (sort of
+ * an inode). This can cause problems if we are searching while some other
+ * process is modifying a directory. To prevent one process from accessing
+ * incompletely modified directory information we depend upon being the
+ * sole owner of a directory block. bread/brelse provide this service.
+ * This being the case, when a process modifies a directory it must first
+ * acquire the disk block that contains the directory entry to be modified.
+ * Then update the disk block and the denode, and then write the disk block
+ * out to disk. This way disk blocks containing directory entries and in
+ * memory denode's will be in synch.
+ */
+static int
+msdosfs_findslot(struct denode *dp, struct componentname *cnp)
+{
+ daddr_t bn;
+ int error;
+ int slotcount;
+ int slotoffset = 0;
+ int frcn;
+ u_long cluster;
+ int blkoff;
+ u_int diroff;
+ int blsize;
+ struct msdosfsmount *pmp;
+ struct buf *bp = 0;
+ struct direntry *dep;
+ u_char dosfilename[12];
+ int wincnt = 1;
+ int chksum = -1, chksum_ok;
+ int olddos = 1;
+
+ pmp = dp->de_pmp;
+
+ switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
+ cnp->cn_namelen, 0)) {
+ case 0:
+ return (EINVAL);
+ case 1:
+ break;
+ case 2:
+ wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
+ cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
+ break;
+ case 3:
+ olddos = 0;
+ wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,
+ cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
+ break;
+ }
+
+ if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
+ wincnt = 1;
+
+ /*
+ * Suppress search for slots unless creating
+ * file and at end of pathname, in which case
+ * we watch for a place to put the new file in
+ * case it doesn't already exist.
+ */
+ slotcount = 0;
+ DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
+ /*
+ * Search the directory pointed at by vdp for the name pointed at
+ * by cnp->cn_nameptr.
+ */
+ /*
+ * The outer loop ranges over the clusters that make up the
+ * directory. Note that the root directory is different from all
+ * other directories. It has a fixed number of blocks that are not
+ * part of the pool of allocatable clusters. So, we treat it a
+ * little differently. The root directory starts at "cluster" 0.
+ */
+ diroff = 0;
+ for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
+ if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
+ if (error == E2BIG)
+ break;
+ return (error);
+ }
+ error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, &bp);
+ if (error) {
+ return (error);
+ }
+ for (blkoff = 0; blkoff < blsize;
+ blkoff += sizeof(struct direntry),
+ diroff += sizeof(struct direntry)) {
+ dep = (struct direntry *)((char *)bp->b_data + blkoff);
+ /*
+ * If the slot is empty and we are still looking
+ * for an empty then remember this one. If the
+ * slot is not empty then check to see if it
+ * matches what we are looking for. If the slot
+ * has never been filled with anything, then the
+ * remainder of the directory has never been used,
+ * so there is no point in searching it.
+ */
+ if (dep->deName[0] == SLOT_EMPTY ||
+ dep->deName[0] == SLOT_DELETED) {
+ /*
+ * Drop memory of previous long matches
+ */
+ chksum = -1;
+
+ if (slotcount < wincnt) {
+ slotcount++;
+ slotoffset = diroff;
+ }
+ if (dep->deName[0] == SLOT_EMPTY) {
+ brelse(bp, 0);
+ goto notfound;
+ }
+ } else {
+ /*
+ * If there wasn't enough space for our
+ * winentries, forget about the empty space
+ */
+ if (slotcount < wincnt)
+ slotcount = 0;
+
+ /*
+ * Check for Win95 long filename entry
+ */
+ if (dep->deAttributes == ATTR_WIN95) {
+ if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
+ continue;
+
+ chksum = winChkName((const u_char *)cnp->cn_nameptr,
+ cnp->cn_namelen,
+ (struct winentry *)dep,
+ chksum,
+ pmp->pm_flags & MSDOSFSMNT_UTF8);
+ continue;
+ }
+
+ /*
+ * Ignore volume labels (anywhere, not just
+ * the root directory).
+ */
+ if (dep->deAttributes & ATTR_VOLUME) {
+ chksum = -1;
+ continue;
+ }
+
+ /*
+ * Check for a checksum or name match
+ */
+ chksum_ok = (chksum == winChksum(dep->deName));
+ if (!chksum_ok
+ && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
+ chksum = -1;
+ continue;
+ }
+ DPRINTF(("%s(): match blkoff %d, diroff %d\n",
+ __func__, blkoff, diroff));
+ /*
+ * Remember where this directory
+ * entry came from for whoever did
+ * this lookup.
+ */
+ dp->de_fndoffset = diroff;
+ dp->de_fndcnt = 0;
+
+ return EEXIST;
+ }
+ } /* for (blkoff = 0; .... */
+ /*
+ * Release the buffer holding the directory cluster just
+ * searched.
+ */
+ brelse(bp, 0);
+ } /* for (frcn = 0; ; frcn++) */
+
+notfound:
+ /*
+ * We hold no disk buffers at this point.
+ */
+
+ /*
+ * If we get here we didn't find the entry we were looking for. But
+ * that's ok if we are creating or renaming and are at the end of
+ * the pathname and the directory hasn't been removed.
+ */
+ DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
+ __func__, dp->de_refcnt, slotcount, slotoffset));
+ /*
+ * Fixup the slot description to point to the place where
+ * we might put the new DOS direntry (putting the Win95
+ * long name entries before that)
+ */
+ if (!slotcount) {
+ slotcount = 1;
+ slotoffset = diroff;
+ }
+ if (wincnt > slotcount) {
+ slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
+ }
+
+ /*
+ * Return an indication of where the new directory
+ * entry should be put.
+ */
+ dp->de_fndoffset = slotoffset;
+ dp->de_fndcnt = wincnt - 1;
+
+ /*
+ * We return with the directory locked, so that
+ * the parameters we set up above will still be
+ * valid if we actually decide to do a direnter().
+ * We return ni_vp == NULL to indicate that the entry
+ * does not currently exist; we leave a pointer to
+ * the (locked) directory inode in ndp->ni_dvp.
+ *
+ * NB - if the directory is unlocked, then this
+ * information cannot be used.
+ */
+ return 0;
+}
+
+/*
+ * Create a regular file. On entry the directory to contain the file being
+ * created is locked. We must release before we return.
+ */
+struct denode *
+msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
+{
+ struct componentname cn;
+ struct denode ndirent;
+ struct denode *dep;
+ int error;
+ struct stat *st = &node->inode->st;
+ struct msdosfsmount *pmp = pdep->de_pmp;
+
+ cn.cn_nameptr = node->name;
+ cn.cn_namelen = strlen(node->name);
+
+ DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name,
+ st->st_mode, (size_t)st->st_size));
+
+ /*
+ * If this is the root directory and there is no space left we
+ * can't do anything. This is because the root directory can not
+ * change size.
+ */
+ if (pdep->de_StartCluster == MSDOSFSROOT
+ && pdep->de_fndoffset >= pdep->de_FileSize) {
+ error = ENOSPC;
+ goto bad;
+ }
+
+ /*
+ * Create a directory entry for the file, then call createde() to
+ * have it installed. NOTE: DOS files are always executable. We
+ * use the absence of the owner write bit to make the file
+ * readonly.
+ */
+ memset(&ndirent, 0, sizeof(ndirent));
+ if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
+ goto bad;
+
+ ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
+ ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
+ ndirent.de_StartCluster = 0;
+ ndirent.de_FileSize = 0;
+ ndirent.de_dev = pdep->de_dev;
+ ndirent.de_devvp = pdep->de_devvp;
+ ndirent.de_pmp = pdep->de_pmp;
+ ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
+ msdosfs_times(pmp, &ndirent, st);
+ if ((error = msdosfs_findslot(pdep, &cn)) != 0)
+ goto bad;
+ if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
+ goto bad;
+ if ((error = msdosfs_wfile(path, dep, node)) != 0)
+ goto bad;
+ return dep;
+
+bad:
+ errno = error;
+ return NULL;
+}
+static int
+msdosfs_updatede(struct denode *dep)
+{
+ struct buf *bp;
+ struct direntry *dirp;
+ int error;
+
+ dep->de_flag &= ~DE_MODIFIED;
+ error = readde(dep, &bp, &dirp);
+ if (error)
+ return error;
+ DE_EXTERNALIZE(dirp, dep);
+ error = bwrite(bp);
+ return error;
+}
+
+/*
+ * Write data to a file or directory.
+ */
+static int
+msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
+{
+ int error, fd;
+ size_t osize = dep->de_FileSize;
+ struct stat *st = &node->inode->st;
+ size_t nsize, offs;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct buf *bp;
+ char *dat;
+ u_long cn = 0;
+
+ error = 0; /* XXX: gcc/vax */
+ DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__,
+ dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster));
+ if (st->st_size == 0)
+ return 0;
+
+ /* Don't bother to try to write files larger than the fs limit */
+ if (st->st_size > MSDOSFS_FILESIZE_MAX) {
+ errno = EFBIG;
+ return -1;
+ }
+
+ nsize = st->st_size;
+ DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
+ if (nsize > osize) {
+ if ((error = deextend(dep, nsize, NULL)) != 0) {
+ errno = error;
+ return -1;
+ }
+ if ((error = msdosfs_updatede(dep)) != 0) {
+ errno = error;
+ return -1;
+ }
+ }
+
+ if ((fd = open(path, O_RDONLY)) == -1)
+ err(1, "open %s", path);
+
+ if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
+ == MAP_FAILED) {
+ DPRINTF(("%s: mmap %s %s", __func__, node->name,
+ strerror(errno)));
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ for (offs = 0; offs < nsize;) {
+ int blsize, cpsize;
+ daddr_t bn;
+ u_long on = offs & pmp->pm_crbomask;
+#ifdef HACK
+ cn = dep->de_StartCluster;
+ if (cn == MSDOSFSROOT) {
+ DPRINTF(("%s: bad lbn %lu", __func__, cn));
+ goto out;
+ }
+ bn = cntobn(pmp, cn);
+ blsize = pmp->pm_bpcluster;
+#else
+ if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) {
+ DPRINTF(("%s: pcbmap %lu", __func__, bn));
+ goto out;
+ }
+#endif
+ DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__,
+ cn, (unsigned long long)bn,
+ (unsigned long long)de_bn2kb(pmp, bn), blsize));
+ if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
+ 0, &bp)) != 0) {
+ DPRINTF(("bread %d\n", error));
+ goto out;
+ }
+ cpsize = MIN((nsize - offs), blsize - on);
+ memcpy((char *)bp->b_data + on, dat + offs, cpsize);
+ bwrite(bp);
+ offs += cpsize;
+ }
+
+ munmap(dat, nsize);
+ return 0;
+out:
+ munmap(dat, nsize);
+ return error;
+}
+
+
+static const struct {
+ struct direntry dot;
+ struct direntry dotdot;
+} dosdirtemplate = {
+ { ". ", " ", /* the . entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, /* reserved */
+ 0, { 0, 0 }, { 0, 0 }, /* create time & date */
+ { 0, 0 }, /* access date */
+ { 0, 0 }, /* high bits of start cluster */
+ { 210, 4 }, { 210, 4 }, /* modify time & date */
+ { 0, 0 }, /* startcluster */
+ { 0, 0, 0, 0 } /* filesize */
+ },
+ { ".. ", " ", /* the .. entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, /* reserved */
+ 0, { 0, 0 }, { 0, 0 }, /* create time & date */
+ { 0, 0 }, /* access date */
+ { 0, 0 }, /* high bits of start cluster */
+ { 210, 4 }, { 210, 4 }, /* modify time & date */
+ { 0, 0 }, /* startcluster */
+ { 0, 0, 0, 0 } /* filesize */
+ }
+};
+
+struct denode *
+msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
+ struct denode ndirent;
+ struct denode *dep;
+ struct componentname cn;
+ struct stat *st = &node->inode->st;
+ struct msdosfsmount *pmp = pdep->de_pmp;
+ int error;
+ u_long newcluster, pcl, bn;
+ daddr_t lbn;
+ struct direntry *denp;
+ struct buf *bp;
+
+ cn.cn_nameptr = node->name;
+ cn.cn_namelen = strlen(node->name);
+ /*
+ * If this is the root directory and there is no space left we
+ * can't do anything. This is because the root directory can not
+ * change size.
+ */
+ if (pdep->de_StartCluster == MSDOSFSROOT
+ && pdep->de_fndoffset >= pdep->de_FileSize) {
+ error = ENOSPC;
+ goto bad2;
+ }
+
+ /*
+ * Allocate a cluster to hold the about to be created directory.
+ */
+ error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
+ if (error)
+ goto bad2;
+
+ memset(&ndirent, 0, sizeof(ndirent));
+ ndirent.de_pmp = pmp;
+ ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
+ msdosfs_times(pmp, &ndirent, st);
+
+ /*
+ * Now fill the cluster with the "." and ".." entries. And write
+ * the cluster to disk. This way it is there for the parent
+ * directory to be pointing at if there were a crash.
+ */
+ bn = cntobn(pmp, newcluster);
+ lbn = de_bn2kb(pmp, bn);
+ DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster,
+ bn, lbn));
+ /* always succeeds */
+ bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
+ memset(bp->b_data, 0, pmp->pm_bpcluster);
+ memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
+ denp = (struct direntry *)bp->b_data;
+ putushort(denp[0].deStartCluster, newcluster);
+ putushort(denp[0].deCDate, ndirent.de_CDate);
+ putushort(denp[0].deCTime, ndirent.de_CTime);
+ denp[0].deCHundredth = ndirent.de_CHun;
+ putushort(denp[0].deADate, ndirent.de_ADate);
+ putushort(denp[0].deMDate, ndirent.de_MDate);
+ putushort(denp[0].deMTime, ndirent.de_MTime);
+ pcl = pdep->de_StartCluster;
+ DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
+ pmp->pm_rootdirblk));
+ if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
+ pcl = 0;
+ putushort(denp[1].deStartCluster, pcl);
+ putushort(denp[1].deCDate, ndirent.de_CDate);
+ putushort(denp[1].deCTime, ndirent.de_CTime);
+ denp[1].deCHundredth = ndirent.de_CHun;
+ putushort(denp[1].deADate, ndirent.de_ADate);
+ putushort(denp[1].deMDate, ndirent.de_MDate);
+ putushort(denp[1].deMTime, ndirent.de_MTime);
+ if (FAT32(pmp)) {
+ putushort(denp[0].deHighClust, newcluster >> 16);
+ putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
+ } else {
+ putushort(denp[0].deHighClust, 0);
+ putushort(denp[1].deHighClust, 0);
+ }
+
+ if ((error = bwrite(bp)) != 0)
+ goto bad;
+
+ /*
+ * Now build up a directory entry pointing to the newly allocated
+ * cluster. This will be written to an empty slot in the parent
+ * directory.
+ */
+ if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
+ goto bad;
+
+ ndirent.de_Attributes = ATTR_DIRECTORY;
+ ndirent.de_StartCluster = newcluster;
+ ndirent.de_FileSize = 0;
+ ndirent.de_dev = pdep->de_dev;
+ ndirent.de_devvp = pdep->de_devvp;
+ ndirent.de_pmp = pdep->de_pmp;
+ if ((error = msdosfs_findslot(pdep, &cn)) != 0)
+ goto bad;
+ if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
+ goto bad;
+ if ((error = msdosfs_updatede(dep)) != 0)
+ goto bad;
+ return dep;
+
+bad:
+ clusterfree(pmp, newcluster, NULL);
+bad2:
+ errno = error;
+ return NULL;
+}
diff --git a/usr.sbin/makefs/newfs_msdos/mkfs_msdos.c b/usr.sbin/makefs/newfs_msdos/mkfs_msdos.c
new file mode 100644
index 00000000000..a0ba769a020
--- /dev/null
+++ b/usr.sbin/makefs/newfs_msdos/mkfs_msdos.c
@@ -0,0 +1,902 @@
+/* $NetBSD: mkfs_msdos.c,v 1.10 2016/04/03 11:00:13 mlelstv Exp $ */
+
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <util.h>
+#include <disktab.h>
+
+#include "mkfs_msdos.h"
+
+#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
+#define BPN 4 /* bits per nibble */
+#define NPB 2 /* nibbles per byte */
+
+#define DOSMAGIC 0xaa55 /* DOS magic number */
+#define MINBPS 512 /* minimum bytes per sector */
+#define MAXSPC 128 /* maximum sectors per cluster */
+#define MAXNFT 16 /* maximum number of FATs */
+#define DEFBLK 4096 /* default block size */
+#define DEFBLK16 2048 /* default block size FAT16 */
+#define DEFRDE 512 /* default root directory entries */
+#define RESFTE 2 /* reserved FAT entries */
+#define MINCLS12 1 /* minimum FAT12 clusters */
+#define MINCLS16 0xff5 /* minimum FAT16 clusters */
+#define MINCLS32 0xfff5 /* minimum FAT32 clusters */
+#define MAXCLS12 0xff4 /* maximum FAT12 clusters */
+#define MAXCLS16 0xfff4 /* maximum FAT16 clusters */
+#define MAXCLS32 0xffffff4 /* maximum FAT32 clusters */
+
+#define mincls(fat_type) ((fat_type) == 12 ? MINCLS12 : \
+ (fat_type) == 16 ? MINCLS16 : \
+ MINCLS32)
+
+#define maxcls(fat_type) ((fat_type) == 12 ? MAXCLS12 : \
+ (fat_type) == 16 ? MAXCLS16 : \
+ MAXCLS32)
+
+#define mk1(p, x) \
+ (p) = (u_int8_t)(x)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010), \
+ (p)[2] = (u_int8_t)((x) >> 020), \
+ (p)[3] = (u_int8_t)((x) >> 030)
+
+struct bs {
+ u_int8_t jmp[3]; /* bootstrap entry point */
+ u_int8_t oem[8]; /* OEM name and version */
+};
+
+struct bsbpb {
+ u_int8_t bps[2]; /* bytes per sector */
+ u_int8_t spc; /* sectors per cluster */
+ u_int8_t res[2]; /* reserved sectors */
+ u_int8_t nft; /* number of FATs */
+ u_int8_t rde[2]; /* root directory entries */
+ u_int8_t sec[2]; /* total sectors */
+ u_int8_t mid; /* media descriptor */
+ u_int8_t spf[2]; /* sectors per FAT */
+ u_int8_t spt[2]; /* sectors per track */
+ u_int8_t hds[2]; /* drive heads */
+ u_int8_t hid[4]; /* hidden sectors */
+ u_int8_t bsec[4]; /* big total sectors */
+};
+
+struct bsxbpb {
+ u_int8_t bspf[4]; /* big sectors per FAT */
+ u_int8_t xflg[2]; /* FAT control flags */
+ u_int8_t vers[2]; /* file system version */
+ u_int8_t rdcl[4]; /* root directory start cluster */
+ u_int8_t infs[2]; /* file system info sector */
+ u_int8_t bkbs[2]; /* backup boot sector */
+ u_int8_t rsvd[12]; /* reserved */
+};
+
+struct bsx {
+ u_int8_t drv; /* drive number */
+ u_int8_t rsvd; /* reserved */
+ u_int8_t sig; /* extended boot signature */
+ u_int8_t volid[4]; /* volume ID number */
+ u_int8_t label[11]; /* volume label */
+ u_int8_t type[8]; /* file system type */
+};
+
+struct de {
+ u_int8_t namext[11]; /* name and extension */
+ u_int8_t attr; /* attributes */
+ u_int8_t rsvd[10]; /* reserved */
+ u_int8_t time[2]; /* creation time */
+ u_int8_t date[2]; /* creation date */
+ u_int8_t clus[2]; /* starting cluster */
+ u_int8_t size[4]; /* size */
+};
+
+struct bpb {
+ u_int bps; /* bytes per sector */
+ u_int spc; /* sectors per cluster */
+ u_int res; /* reserved sectors */
+ u_int nft; /* number of FATs */
+ u_int rde; /* root directory entries */
+ u_int sec; /* total sectors */
+ u_int mid; /* media descriptor */
+ u_int spf; /* sectors per FAT */
+ u_int spt; /* sectors per track */
+ u_int hds; /* drive heads */
+ u_int hid; /* hidden sectors */
+ u_int bsec; /* big total sectors */
+ u_int bspf; /* big sectors per FAT */
+ u_int rdcl; /* root directory start cluster */
+ u_int infs; /* file system info sector */
+ u_int bkbs; /* backup boot sector */
+};
+
+#define INIT(a, b, c, d, e, f, g, h, i, j) \
+ { .bps = a, .spc = b, .res = c, .nft = d, .rde = e, \
+ .sec = f, .mid = g, .spf = h, .spt = i, .hds = j, }
+static struct {
+ const char *name;
+ struct bpb bpb;
+} stdfmt[] = {
+ {"160", INIT(512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1)},
+ {"180", INIT(512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1)},
+ {"320", INIT(512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2)},
+ {"360", INIT(512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2)},
+ {"640", INIT(512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2)},
+ {"720", INIT(512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2)},
+ {"1200", INIT(512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2)},
+ {"1232", INIT(1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2)},
+ {"1440", INIT(512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2)},
+ {"2880", INIT(512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2)}
+};
+
+static u_int8_t bootcode[] = {
+ 0xfa, /* cli */
+ 0x31, 0xc0, /* xor ax,ax */
+ 0x8e, 0xd0, /* mov ss,ax */
+ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
+ 0xfb, /* sti */
+ 0x8e, 0xd8, /* mov ds,ax */
+ 0xe8, 0x00, 0x00, /* call $ + 3 */
+ 0x5e, /* pop si */
+ 0x83, 0xc6, 0x19, /* add si,+19h */
+ 0xbb, 0x07, 0x00, /* mov bx,0007h */
+ 0xfc, /* cld */
+ 0xac, /* lodsb */
+ 0x84, 0xc0, /* test al,al */
+ 0x74, 0x06, /* jz $ + 8 */
+ 0xb4, 0x0e, /* mov ah,0eh */
+ 0xcd, 0x10, /* int 10h */
+ 0xeb, 0xf5, /* jmp $ - 9 */
+ 0x30, 0xe4, /* xor ah,ah */
+ 0xcd, 0x16, /* int 16h */
+ 0xcd, 0x19, /* int 19h */
+ 0x0d, 0x0a,
+ 'N', 'o', 'n', '-', 's', 'y', 's', 't',
+ 'e', 'm', ' ', 'd', 'i', 's', 'k',
+ 0x0d, 0x0a,
+ 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+ 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+ ' ', 'r', 'e', 'b', 'o', 'o', 't',
+ 0x0d, 0x0a,
+ 0
+};
+
+static int got_siginfo = 0; /* received a SIGINFO */
+
+static int getstdfmt(const char *, struct bpb *);
+static int getbpbinfo(int, const char *, const char *, int, struct bpb *, int);
+static void print_bpb(struct bpb *);
+static int ckgeom(const char *, u_int, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void infohandler(int sig);
+
+int
+mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op)
+{
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ struct timeval tv;
+ struct bpb bpb;
+ struct tm *tm;
+ struct bs *bs;
+ struct bsbpb *bsbpb;
+ struct bsxbpb *bsxbpb;
+ struct bsx *bsx;
+ struct de *de;
+ u_int8_t *img;
+ const char *bname;
+ ssize_t n;
+ time_t now;
+ u_int bss, rds, cls, dir, lsn, x, x1, x2;
+ int ch, fd, fd1;
+ struct msdos_options o = *op;
+ int oflags = O_RDWR | O_CREAT;
+
+ if (o.block_size && o.sectors_per_cluster) {
+ warnx("Cannot specify both block size and sectors per cluster");
+ return -1;
+ }
+ if (o.OEM_string && strlen(o.OEM_string) > 8) {
+ warnx("%s: bad OEM string", o.OEM_string);
+ return -1;
+ }
+ if (o.create_size) {
+ if (o.no_create) {
+ warnx("create (-C) is incompatible with -N");
+ return -1;
+ }
+ if (o.offset == 0)
+ oflags |= O_TRUNC;
+ fd = open(fname, oflags, 0644);
+ if (fd == -1) {
+ warnx("failed to create %s", fname);
+ return -1;
+ }
+ (void)lseek(fd, o.create_size - 1, SEEK_SET);
+ if (write(fd, "\0", 1) != 1) {
+ warn("failed to set file size");
+ return -1;
+ }
+ (void)lseek(fd, 0, SEEK_SET);
+ } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1 ||
+ fstat(fd, &sb)) {
+ warn("%s", fname);
+ return -1;
+ }
+ if (!S_ISCHR(sb.st_mode) && !o.create_size) {
+ warnx("warning, %s is not a character device", fname);
+ return -1;
+ }
+ if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) {
+ warnx("cannot seek to %jd", (intmax_t)o.offset);
+ return -1;
+ }
+ memset(&bpb, 0, sizeof(bpb));
+ if (o.floppy) {
+ if (getstdfmt(o.floppy, &bpb) == -1)
+ return -1;
+ bpb.bsec = bpb.sec;
+ bpb.sec = 0;
+ bpb.bspf = bpb.spf;
+ bpb.spf = 0;
+ }
+ if (o.drive_heads)
+ bpb.hds = o.drive_heads;
+ if (o.sectors_per_track)
+ bpb.spt = o.sectors_per_track;
+ if (o.bytes_per_sector)
+ bpb.bps = o.bytes_per_sector;
+ if (o.size)
+ bpb.bsec = o.size;
+ if (o.hidden_sectors_set)
+ bpb.hid = o.hidden_sectors;
+ if (!(o.floppy || (o.drive_heads && o.sectors_per_track &&
+ o.bytes_per_sector && o.size && o.hidden_sectors_set))) {
+ if (getbpbinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb,
+ o.create_size != 0) == -1)
+ return -1;
+ bpb.bsec -= (o.offset / bpb.bps);
+ if (bpb.spc == 0) { /* set defaults */
+ /* minimum cluster size */
+ switch (o.fat_type) {
+ case 12:
+ bpb.spc = 1; /* use 512 bytes */
+ x = 2; /* up to 2MB */
+ break;
+ case 16:
+ bpb.spc = 1; /* use 512 bytes */
+ x = 32; /* up to 32MB */
+ break;
+ default:
+ bpb.spc = 8; /* use 4k */
+ x = 8192; /* up to 8GB */
+ break;
+ }
+ x1 = howmany(bpb.bsec, (1048576 / 512)); /* -> MB */
+ while (bpb.spc < 128 && x < x1) {
+ x *= 2;
+ bpb.spc *= 2;
+ }
+ }
+ }
+
+ if (o.volume_label && !oklabel(o.volume_label)) {
+ warnx("%s: bad volume label", o.volume_label);
+ return -1;
+ }
+
+ switch (o.fat_type) {
+ case 0:
+ if (o.floppy)
+ o.fat_type = 12;
+ else if (!o.directory_entries && (o.info_sector || o.backup_sector))
+ o.fat_type = 32;
+ break;
+ case 12:
+ case 16:
+ if (o.info_sector) {
+ warnx("Cannot specify info sector with FAT%u", o.fat_type);
+ return -1;
+ }
+ if (o.backup_sector) {
+ warnx("Cannot specify backup sector with FAT%u", o.fat_type);
+ return -1;
+ }
+ break;
+ case 32:
+ if (o.directory_entries) {
+ warnx("Cannot specify directory entries with FAT32");
+ return -1;
+ }
+ break;
+ default:
+ warnx("%d: bad FAT type", o.fat_type);
+ return -1;
+ }
+ if (!powerof2(bpb.bps)) {
+ warnx("bytes/sector (%u) is not a power of 2", bpb.bps);
+ return -1;
+ }
+ if (bpb.bps < MINBPS) {
+ warnx("bytes/sector (%u) is too small; minimum is %u",
+ bpb.bps, MINBPS);
+ return -1;
+ }
+
+ if (o.floppy && o.fat_type == 32)
+ bpb.rde = 0;
+ if (o.block_size) {
+ if (!powerof2(o.block_size)) {
+ warnx("block size (%u) is not a power of 2", o.block_size);
+ return -1;
+ }
+ if (o.block_size < bpb.bps) {
+ warnx("block size (%u) is too small; minimum is %u",
+ o.block_size, bpb.bps);
+ return -1;
+ }
+ if (o.block_size > bpb.bps * MAXSPC) {
+ warnx("block size (%u) is too large; maximum is %u",
+ o.block_size, bpb.bps * MAXSPC);
+ return -1;
+ }
+ bpb.spc = o.block_size / bpb.bps;
+ }
+ if (o.sectors_per_cluster) {
+ if (!powerof2(o.sectors_per_cluster)) {
+ warnx("sectors/cluster (%u) is not a power of 2",
+ o.sectors_per_cluster);
+ return -1;
+ }
+ bpb.spc = o.sectors_per_cluster;
+ }
+ if (o.reserved_sectors)
+ bpb.res = o.reserved_sectors;
+ if (o.num_FAT) {
+ if (o.num_FAT > MAXNFT) {
+ warnx("number of FATs (%u) is too large; maximum is %u",
+ o.num_FAT, MAXNFT);
+ return -1;
+ }
+ bpb.nft = o.num_FAT;
+ }
+ if (o.directory_entries)
+ bpb.rde = o.directory_entries;
+ if (o.media_descriptor_set) {
+ if (o.media_descriptor < 0xf0) {
+ warnx("illegal media descriptor (%#x)", o.media_descriptor);
+ return -1;
+ }
+ bpb.mid = o.media_descriptor;
+ }
+ if (o.sectors_per_fat)
+ bpb.bspf = o.sectors_per_fat;
+ if (o.info_sector)
+ bpb.infs = o.info_sector;
+ if (o.backup_sector)
+ bpb.bkbs = o.backup_sector;
+ bss = 1;
+ bname = NULL;
+ fd1 = -1;
+ if (o.bootstrap) {
+ bname = o.bootstrap;
+ if (!strchr(bname, '/')) {
+ snprintf(buf, sizeof(buf), "/boot/%s", bname);
+ if (!(bname = strdup(buf))) {
+ warn(NULL);
+ return -1;
+ }
+ }
+ if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) {
+ warn("%s", bname);
+ return -1;
+ }
+ if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+ sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) {
+ warnx("%s: inappropriate file type or format", bname);
+ return -1;
+ }
+ bss = sb.st_size / bpb.bps;
+ }
+ if (!bpb.nft)
+ bpb.nft = 2;
+ if (!o.fat_type) {
+ if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+ ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+ bpb.nft +
+ howmany(bpb.rde ? bpb.rde : DEFRDE,
+ bpb.bps / sizeof(struct de)) +
+ (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+ (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+ o.fat_type = 12;
+ else if (bpb.rde || bpb.bsec <
+ (bpb.res ? bpb.res : bss) +
+ howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+ howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+ (MAXCLS16 + 1) *
+ (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+ o.fat_type = 16;
+ else
+ o.fat_type = 32;
+ }
+ x = bss;
+ if (o.fat_type == 32) {
+ if (!bpb.infs) {
+ if (x == MAXU16 || x == bpb.bkbs) {
+ warnx("no room for info sector");
+ return -1;
+ }
+ bpb.infs = x;
+ }
+ if (bpb.infs != MAXU16 && x <= bpb.infs)
+ x = bpb.infs + 1;
+ if (!bpb.bkbs) {
+ if (x == MAXU16) {
+ warnx("no room for backup sector");
+ return -1;
+ }
+ bpb.bkbs = x;
+ } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) {
+ warnx("backup sector would overwrite info sector");
+ return -1;
+ }
+ if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+ x = bpb.bkbs + 1;
+ }
+ if (!bpb.res)
+ bpb.res = o.fat_type == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+ else if (bpb.res < x) {
+ warnx("too few reserved sectors (need %d have %d)", x, bpb.res);
+ return -1;
+ }
+ if (o.fat_type != 32 && !bpb.rde)
+ bpb.rde = DEFRDE;
+ rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+ if (!bpb.spc)
+ for (bpb.spc = howmany(o.fat_type == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+ bpb.spc < MAXSPC &&
+ bpb.res +
+ howmany((RESFTE + maxcls(o.fat_type)) * (o.fat_type / BPN),
+ bpb.bps * NPB) * bpb.nft +
+ rds +
+ (u_int64_t)(maxcls(o.fat_type) + 1) * bpb.spc <= bpb.bsec;
+ bpb.spc <<= 1);
+ if (o.fat_type != 32 && bpb.bspf > MAXU16) {
+ warnx("too many sectors/FAT for FAT12/16");
+ return -1;
+ }
+ x1 = bpb.res + rds;
+ x = bpb.bspf ? bpb.bspf : 1;
+ if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) {
+ warnx("meta data exceeds file system size");
+ return -1;
+ }
+ x1 += x * bpb.nft;
+ x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+ (bpb.spc * bpb.bps * NPB + o.fat_type / BPN * bpb.nft);
+ x2 = howmany((RESFTE + MIN(x, maxcls(o.fat_type))) * (o.fat_type / BPN),
+ bpb.bps * NPB);
+ if (!bpb.bspf) {
+ bpb.bspf = x2;
+ x1 += (bpb.bspf - 1) * bpb.nft;
+ }
+ cls = (bpb.bsec - x1) / bpb.spc;
+ x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (o.fat_type / BPN) - RESFTE;
+ if (cls > x)
+ cls = x;
+ if (bpb.bspf < x2) {
+ warnx("warning: sectors/FAT limits file system to %u clusters",
+ cls);
+ return -1;
+ }
+ if (cls < mincls(o.fat_type)) {
+ warnx("%u clusters too few clusters for FAT%u, need %u", cls,
+ o.fat_type, mincls(o.fat_type));
+ return -1;
+ }
+ if (cls > maxcls(o.fat_type)) {
+ cls = maxcls(o.fat_type);
+ bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+ warnx("warning: FAT type limits file system to %u sectors",
+ bpb.bsec);
+ return -1;
+ }
+ printf("%s: %u sector%s in %u FAT%u cluster%s "
+ "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+ cls * bpb.spc == 1 ? "" : "s", cls, o.fat_type,
+ cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+ if (!bpb.mid)
+ bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+ if (o.fat_type == 32)
+ bpb.rdcl = RESFTE;
+ if (bpb.hid + bpb.bsec <= MAXU16) {
+ bpb.sec = bpb.bsec;
+ bpb.bsec = 0;
+ }
+ if (o.fat_type != 32) {
+ bpb.spf = bpb.bspf;
+ bpb.bspf = 0;
+ }
+ ch = 0;
+ if (o.fat_type == 12)
+ ch = 1; /* 001 Primary DOS with 12 bit FAT */
+ else if (o.fat_type == 16) {
+ if (bpb.bsec == 0)
+ ch = 4; /* 004 Primary DOS with 16 bit FAT <32M */
+ else
+ ch = 6; /* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */
+ /*
+ * XXX: what about:
+ * 014 DOS (16-bit FAT) - LBA
+ * ?
+ */
+ } else if (o.fat_type == 32) {
+ ch = 11; /* 011 Primary DOS with 32 bit FAT */
+ /*
+ * XXX: what about:
+ * 012 Primary DOS with 32 bit FAT - LBA
+ * ?
+ */
+ }
+ if (ch != 0)
+ printf("MBR type: %d\n", ch);
+ print_bpb(&bpb);
+ if (!o.no_create) {
+ gettimeofday(&tv, NULL);
+ now = tv.tv_sec;
+ tm = localtime(&now);
+ if (!(img = malloc(bpb.bps)))
+ err(1, NULL);
+ dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+ signal(SIGINFO, infohandler);
+ for (lsn = 0; lsn < dir + (o.fat_type == 32 ? bpb.spc : rds); lsn++) {
+ if (got_siginfo) {
+ fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n",
+ fname,lsn,(dir + (o.fat_type == 32 ? bpb.spc : rds)),
+ (lsn*100)/(dir + (o.fat_type == 32 ? bpb.spc : rds)));
+ got_siginfo = 0;
+ }
+ x = lsn;
+ if (o.bootstrap &&
+ o.fat_type == 32 && bpb.bkbs != MAXU16 &&
+ bss <= bpb.bkbs && x >= bpb.bkbs) {
+ x -= bpb.bkbs;
+ if (!x && lseek(fd1, o.offset, SEEK_SET)) {
+ warn("%s", bname);
+ return -1;
+ }
+ }
+ if (o.bootstrap && x < bss) {
+ if ((n = read(fd1, img, bpb.bps)) == -1) {
+ warn("%s", bname);
+ return -1;
+ }
+ if ((size_t)n != bpb.bps) {
+ warnx("%s: can't read sector %u", bname, x);
+ return -1;
+ }
+ } else
+ memset(img, 0, bpb.bps);
+ if (!lsn ||
+ (o.fat_type == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+ x1 = sizeof(struct bs);
+ bsbpb = (struct bsbpb *)(img + x1);
+ mk2(bsbpb->bps, bpb.bps);
+ mk1(bsbpb->spc, bpb.spc);
+ mk2(bsbpb->res, bpb.res);
+ mk1(bsbpb->nft, bpb.nft);
+ mk2(bsbpb->rde, bpb.rde);
+ mk2(bsbpb->sec, bpb.sec);
+ mk1(bsbpb->mid, bpb.mid);
+ mk2(bsbpb->spf, bpb.spf);
+ mk2(bsbpb->spt, bpb.spt);
+ mk2(bsbpb->hds, bpb.hds);
+ mk4(bsbpb->hid, bpb.hid);
+ mk4(bsbpb->bsec, bpb.bsec);
+ x1 += sizeof(struct bsbpb);
+ if (o.fat_type == 32) {
+ bsxbpb = (struct bsxbpb *)(img + x1);
+ mk4(bsxbpb->bspf, bpb.bspf);
+ mk2(bsxbpb->xflg, 0);
+ mk2(bsxbpb->vers, 0);
+ mk4(bsxbpb->rdcl, bpb.rdcl);
+ mk2(bsxbpb->infs, bpb.infs);
+ mk2(bsxbpb->bkbs, bpb.bkbs);
+ x1 += sizeof(struct bsxbpb);
+ }
+ bsx = (struct bsx *)(img + x1);
+ mk1(bsx->sig, 0x29);
+ if (o.volume_id_set)
+ x = o.volume_id;
+ else
+ x = (((u_int)(1 + tm->tm_mon) << 8 |
+ (u_int)tm->tm_mday) +
+ ((u_int)tm->tm_sec << 8 |
+ (u_int)(tv.tv_usec / 10))) << 16 |
+ ((u_int)(1900 + tm->tm_year) +
+ ((u_int)tm->tm_hour << 8 |
+ (u_int)tm->tm_min));
+ mk4(bsx->volid, x);
+ mklabel(bsx->label, o.volume_label ? o.volume_label : "NO NAME");
+ snprintf(buf, sizeof(buf), "FAT%u", o.fat_type);
+ setstr(bsx->type, buf, sizeof(bsx->type));
+ if (!o.bootstrap) {
+ x1 += sizeof(struct bsx);
+ bs = (struct bs *)img;
+ mk1(bs->jmp[0], 0xeb);
+ mk1(bs->jmp[1], x1 - 2);
+ mk1(bs->jmp[2], 0x90);
+ setstr(bs->oem, o.OEM_string ? o.OEM_string : "NetBSD",
+ sizeof(bs->oem));
+ memcpy(img + x1, bootcode, sizeof(bootcode));
+ mk2(img + MINBPS - 2, DOSMAGIC);
+ }
+ } else if (o.fat_type == 32 && bpb.infs != MAXU16 &&
+ (lsn == bpb.infs ||
+ (bpb.bkbs != MAXU16 &&
+ lsn == bpb.bkbs + bpb.infs))) {
+ mk4(img, 0x41615252);
+ mk4(img + MINBPS - 28, 0x61417272);
+ mk4(img + MINBPS - 24, 0xffffffff);
+ mk4(img + MINBPS - 20, 0xffffffff);
+ mk2(img + MINBPS - 2, DOSMAGIC);
+ } else if (lsn >= bpb.res && lsn < dir &&
+ !((lsn - bpb.res) %
+ (bpb.spf ? bpb.spf : bpb.bspf))) {
+ mk1(img[0], bpb.mid);
+ for (x = 1; x < o.fat_type * (o.fat_type == 32 ? 3U : 2U) / 8U; x++)
+ mk1(img[x], o.fat_type == 32 && x % 4 == 3 ? 0x0f : 0xff);
+ } else if (lsn == dir && o.volume_label) {
+ de = (struct de *)img;
+ mklabel(de->namext, o.volume_label);
+ mk1(de->attr, 050);
+ x = (u_int)tm->tm_hour << 11 |
+ (u_int)tm->tm_min << 5 |
+ (u_int)tm->tm_sec >> 1;
+ mk2(de->time, x);
+ x = (u_int)(tm->tm_year - 80) << 9 |
+ (u_int)(tm->tm_mon + 1) << 5 |
+ (u_int)tm->tm_mday;
+ mk2(de->date, x);
+ }
+ if ((n = write(fd, img, bpb.bps)) == -1) {
+ warn("%s", fname);
+ return -1;
+ }
+ if ((size_t)n != bpb.bps) {
+ warnx("%s: can't write sector %u", fname, lsn);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Get a standard format.
+ */
+static int
+getstdfmt(const char *fmt, struct bpb *bpb)
+{
+ u_int x, i;
+
+ x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+ for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+ if (i == x) {
+ warnx("%s: unknown standard format", fmt);
+ return -1;
+ }
+ *bpb = stdfmt[i].bpb;
+ return 0;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+static int
+getbpbinfo(int fd, const char *fname, const char *dtype, int iflag,
+ struct bpb *bpb, int create)
+{
+ const char *s1, *s2;
+ int part;
+
+ part = -1;
+ s1 = fname;
+ if ((s2 = strrchr(s1, '/')))
+ s1 = s2 + 1;
+ for (s2 = s1; *s2 && !isdigit((unsigned char)*s2); s2++);
+ if (!*s2 || s2 == s1)
+ s2 = NULL;
+ else
+ while (isdigit((unsigned char)*++s2));
+ s1 = s2;
+
+ if (((part != -1) && ((!iflag && part != -1) || !bpb->bsec)) ||
+ !bpb->bps || !bpb->spt || !bpb->hds) {
+ u_int sector_size;
+ u_int nsectors;
+ u_int ntracks;
+ u_int size;
+ {
+ struct stat st;
+
+ if (fstat(fd, &st) == -1) {
+ warnx("Can't get disk size for `%s'", fname);
+ return -1;
+ }
+ /* create a fake geometry for a file image */
+ sector_size = 512;
+ nsectors = 63;
+ ntracks = 255;
+ size = st.st_size / sector_size;
+ }
+ if (!bpb->bps) {
+ if (ckgeom(fname, sector_size, "bytes/sector") == -1)
+ return -1;
+ bpb->bps = sector_size;
+ }
+
+ if (nsectors > 63) {
+ /*
+ * The kernel doesn't accept BPB with spt > 63.
+ * (see sys/fs/msdosfs/msdosfs_vfsops.c:msdosfs_mountfs())
+ * If values taken from disklabel don't match these
+ * restrictions, use popular BIOS default values instead.
+ */
+ nsectors = 63;
+ }
+ if (!bpb->spt) {
+ if (ckgeom(fname, nsectors, "sectors/track") == -1)
+ return -1;
+ bpb->spt = nsectors;
+ }
+ if (!bpb->hds)
+ if (ckgeom(fname, ntracks, "drive heads") == -1)
+ return -1;
+ bpb->hds = ntracks;
+ if (!bpb->bsec)
+ bpb->bsec = size;
+ }
+ return 0;
+}
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+ printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+ bpb->nft);
+ if (bpb->rde)
+ printf(" rde=%u", bpb->rde);
+ if (bpb->sec)
+ printf(" sec=%u", bpb->sec);
+ printf(" mid=%#x", bpb->mid);
+ if (bpb->spf)
+ printf(" spf=%u", bpb->spf);
+ printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+ if (bpb->bsec)
+ printf(" bsec=%u", bpb->bsec);
+ if (!bpb->spf) {
+ printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+ printf(" infs=");
+ printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+ printf(" bkbs=");
+ printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+ }
+ printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+ if (!val) {
+ warnx("%s: no default %s", fname, msg);
+ return -1;
+ }
+ if (val > MAXU16) {
+ warnx("%s: illegal %s", fname, msg);
+ return -1;
+ }
+ return 0;
+}
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+ int c, i;
+
+ for (i = 0; i <= 11; i++) {
+ c = (u_char)*src++;
+ if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+ break;
+ }
+ return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+ int c, i;
+
+ for (i = 0; i < 11; i++) {
+ c = *src ? toupper((unsigned char)*src++) : ' ';
+ *dest++ = !i && c == '\xe5' ? 5 : c;
+ }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+ while (len--)
+ *dest++ = *src ? *src++ : ' ';
+}
+
+static void
+infohandler(int sig)
+{
+ got_siginfo = 1;
+}
diff --git a/usr.sbin/makefs/newfs_msdos/mkfs_msdos.h b/usr.sbin/makefs/newfs_msdos/mkfs_msdos.h
new file mode 100644
index 00000000000..86d555e1beb
--- /dev/null
+++ b/usr.sbin/makefs/newfs_msdos/mkfs_msdos.h
@@ -0,0 +1,68 @@
+/* $NetBSD: mkfs_msdos.h,v 1.3 2015/10/16 17:38:17 christos Exp $ */
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/types.h>
+#include <stdbool.h>
+#define ALLOPTS \
+AOPT('@', off_t, offset, 0, "Offset in device") \
+AOPT('B', char *, bootstrap, -1, "Bootstrap file") \
+AOPT('C', off_t, create_size, 0, "Create file") \
+AOPT('F', uint8_t, fat_type, 12, "FAT type (12, 16, or 32)") \
+AOPT('I', uint32_t, volume_id, 0, "Volume ID") \
+AOPT('L', char *, volume_label, -1, "Volume Label") \
+AOPT('N', bool, no_create, -2, "Don't create filesystem, print params only") \
+AOPT('O', char *, OEM_string, -1, "OEM string") \
+AOPT('S', uint16_t, bytes_per_sector, 1, "Bytes per sector") \
+AOPT('a', uint32_t, sectors_per_fat, 1, "Sectors per FAT") \
+AOPT('b', uint32_t, block_size, 1, "Block size") \
+AOPT('c', uint8_t, sectors_per_cluster, 1, "Sectors per cluster") \
+AOPT('e', uint16_t, directory_entries, 1, "Directory entries") \
+AOPT('f', char *, floppy, -1, "Standard format floppies (160,180,320,360,640,720,1200,1232,1440,2880)") \
+AOPT('h', uint16_t, drive_heads, 1, "Drive heads") \
+AOPT('i', uint16_t, info_sector, 1, "Info sector") \
+AOPT('k', uint16_t, backup_sector, 1, "Backup sector") \
+AOPT('m', uint8_t, media_descriptor, 0, "Media descriptor") \
+AOPT('n', uint8_t, num_FAT, 1, "Number of FATs") \
+AOPT('o', uint32_t, hidden_sectors, 0, "Hidden sectors") \
+AOPT('r', uint16_t, reserved_sectors, 1, "Reserved sectors") \
+AOPT('s', uint32_t, size, 1, "File System size") \
+AOPT('u', uint16_t, sectors_per_track, 1, "Sectors per track")
+
+struct msdos_options {
+#define AOPT(_opt, _type, _name, _min, _desc) _type _name;
+ALLOPTS
+#undef AOPT
+ uint32_t volume_id_set:1;
+ uint32_t media_descriptor_set:1;
+ uint32_t hidden_sectors_set:1;
+};
+
+int mkfs_msdos(const char *, const char *, const struct msdos_options *);
diff --git a/usr.sbin/makefs/sys/bootblock.h b/usr.sbin/makefs/sys/bootblock.h
new file mode 100644
index 00000000000..66335cf8a94
--- /dev/null
+++ b/usr.sbin/makefs/sys/bootblock.h
@@ -0,0 +1,1449 @@
+/* $NetBSD: bootblock.h,v 1.57 2014/09/14 17:39:06 nonaka Exp $ */
+
+/*-
+ * Copyright (c) 2002-2004 The NetBSD Foundation, Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+/*-
+ * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo,
+ * Michael L. Finch, Bradley A. Grantham, and
+ * Lawrence A. Kesteloot
+ * 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 Alice Group.
+ * 4. The names of the Alice Group or any of its members may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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.
+ *
+ */
+/*
+ * Copyright (c) 1994, 1999 Christopher G. Demetriou
+ * 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 Christopher G. Demetriou
+ * for the NetBSD Project.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+/*
+ * Copyright (c) 1994 Rolf Grossmann
+ * 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 Rolf Grossmann.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SYS_BOOTBLOCK_H
+#define _SYS_BOOTBLOCK_H
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+/* ------------------------------------------
+ * MBR (Master Boot Record) --
+ * definitions for systems that use MBRs
+ */
+
+/*
+ * Layout of boot records:
+ *
+ * Byte range Use Description
+ * ---------- --- -----------
+ *
+ * 0 - 2 FMP JMP xxx, NOP
+ * 3 - 10 FP OEM Name
+ *
+ * 11 - 61 FMP FAT12/16 BPB
+ * Whilst not strictly necessary for MBR,
+ * GRUB reserves this area
+ *
+ * 11 - 89 P FAT32 BPB
+ * (are we ever going to boot off this?)
+ *
+ *
+ * 62 - 217 FMP Boot code
+ *
+ * 90 - 217 P FAT32 boot code
+ *
+ * 218 - 223 M Win95b/98/me "drive time"
+ * http://www.geocities.com/thestarman3/asm/mbr/95BMEMBR.htm#MYST
+ * only changed if all 6 bytes are 0
+ *
+ * 224 - 436 FMP boot code (continued)
+ *
+ * 437 - 439 M WinNT/2K/XP MBR "boot language"
+ * http://www.geocities.com/thestarman3/asm/mbr/Win2kmbr.htm
+ * not needed by us
+ *
+ * 400 - 439 MP NetBSD: mbr_bootsel
+ *
+ * 424 - 439 M NetBSD: bootptn_guid (in GPT PMBR only)
+ *
+ * 440 - 443 M WinNT/2K/XP Drive Serial Number (NT DSN)
+ * http://www.geocities.com/thestarman3/asm/mbr/Win2kmbr.htm
+ *
+ * 444 - 445 FMP bootcode or unused
+ * NetBSD: mbr_bootsel_magic
+ *
+ * 446 - 509 M partition table
+ *
+ * 510 - 511 FMP magic number (0xAA55)
+ *
+ * Use:
+ * ----
+ * F Floppy boot sector
+ * M Master Boot Record
+ * P Partition Boot record
+ *
+ */
+
+/*
+ * MBR (Master Boot Record)
+ */
+#define MBR_BBSECTOR 0 /* MBR relative sector # */
+#define MBR_BPB_OFFSET 11 /* offsetof(mbr_sector, mbr_bpb) */
+#define MBR_BOOTCODE_OFFSET 90 /* offsetof(mbr_sector, mbr_bootcode) */
+#define MBR_BS_OFFSET 400 /* offsetof(mbr_sector, mbr_bootsel) */
+#define MBR_BS_OLD_OFFSET 404 /* where mbr_bootsel used to be */
+#define MBR_GPT_GUID_OFFSET 424 /* location of partition GUID to boot */
+#define MBR_GPT_GUID_DEFAULT /* default uninitialized GUID */ \
+ {0xeee69d04,0x02f4,0x11e0,0x8f,0x5d,{0x00,0xe0,0x81,0x52,0x9a,0x6b}}
+#define MBR_DSN_OFFSET 440 /* offsetof(mbr_sector, mbr_dsn) */
+#define MBR_BS_MAGIC_OFFSET 444 /* offsetof(mbr_sector, mbr_bootsel_magic) */
+#define MBR_PART_OFFSET 446 /* offsetof(mbr_sector, mbr_part[0]) */
+#define MBR_MAGIC_OFFSET 510 /* offsetof(mbr_sector, mbr_magic) */
+#define MBR_MAGIC 0xaa55 /* MBR magic number */
+#define MBR_BS_MAGIC 0xb5e1 /* mbr_bootsel magic number */
+#define MBR_PART_COUNT 4 /* Number of partitions in MBR */
+#define MBR_BS_PARTNAMESIZE 8 /* Size of name mbr_bootsel nametab */
+ /* (excluding trailing NUL) */
+
+ /* values for mbr_partition.mbrp_flag */
+#define MBR_PFLAG_ACTIVE 0x80 /* The active partition */
+
+ /* values for mbr_partition.mbrp_type */
+#define MBR_PTYPE_UNUSED 0x00 /* Unused */
+#define MBR_PTYPE_FAT12 0x01 /* 12-bit FAT */
+#define MBR_PTYPE_XENIX_ROOT 0x02 /* XENIX / */
+#define MBR_PTYPE_XENIX_USR 0x03 /* XENIX /usr */
+#define MBR_PTYPE_FAT16S 0x04 /* 16-bit FAT, less than 32M */
+#define MBR_PTYPE_EXT 0x05 /* extended partition */
+#define MBR_PTYPE_FAT16B 0x06 /* 16-bit FAT, more than 32M */
+#define MBR_PTYPE_NTFS 0x07 /* OS/2 HPFS, NTFS, QNX2, Adv. UNIX */
+#define MBR_PTYPE_DELL 0x08 /* AIX or os, or etc. */
+#define MBR_PTYPE_AIX_BOOT 0x09 /* AIX boot partition or Coherent */
+#define MBR_PTYPE_OS2_BOOT 0x0a /* O/2 boot manager or Coherent swap */
+#define MBR_PTYPE_FAT32 0x0b /* 32-bit FAT */
+#define MBR_PTYPE_FAT32L 0x0c /* 32-bit FAT, LBA-mapped */
+#define MBR_PTYPE_7XXX 0x0d /* 7XXX, LBA-mapped */
+#define MBR_PTYPE_FAT16L 0x0e /* 16-bit FAT, LBA-mapped */
+#define MBR_PTYPE_EXT_LBA 0x0f /* extended partition, LBA-mapped */
+#define MBR_PTYPE_OPUS 0x10 /* OPUS */
+#define MBR_PTYPE_OS2_DOS12 0x11 /* OS/2 DOS 12-bit FAT */
+#define MBR_PTYPE_COMPAQ_DIAG 0x12 /* Compaq diagnostics */
+#define MBR_PTYPE_OS2_DOS16S 0x14 /* OS/2 DOS 16-bit FAT <32M */
+#define MBR_PTYPE_OS2_DOS16B 0x16 /* OS/2 DOS 16-bit FAT >=32M */
+#define MBR_PTYPE_OS2_IFS 0x17 /* OS/2 hidden IFS */
+#define MBR_PTYPE_AST_SWAP 0x18 /* AST Windows swapfile */
+#define MBR_PTYPE_WILLOWTECH 0x19 /* Willowtech Photon coS */
+#define MBR_PTYPE_HID_FAT32 0x1b /* hidden win95 fat 32 */
+#define MBR_PTYPE_HID_FAT32_LBA 0x1c /* hidden win95 fat 32 lba */
+#define MBR_PTYPE_HID_FAT16_LBA 0x1d /* hidden win95 fat 16 lba */
+#define MBR_PTYPE_WILLOWSOFT 0x20 /* Willowsoft OFS1 */
+#define MBR_PTYPE_RESERVED_x21 0x21 /* reserved */
+#define MBR_PTYPE_RESERVED_x23 0x23 /* reserved */
+#define MBR_PTYPE_RESERVED_x24 0x24 /* NEC DOS */
+#define MBR_PTYPE_RESERVED_x26 0x26 /* reserved */
+#define MBR_PTYPE_RESERVED_x31 0x31 /* reserved */
+#define MBR_PTYPE_NOS 0x32 /* Alien Internet Services NOS */
+#define MBR_PTYPE_RESERVED_x33 0x33 /* reserved */
+#define MBR_PTYPE_RESERVED_x34 0x34 /* reserved */
+#define MBR_PTYPE_OS2_JFS 0x35 /* JFS on OS2 */
+#define MBR_PTYPE_RESERVED_x36 0x36 /* reserved */
+#define MBR_PTYPE_THEOS 0x38 /* Theos */
+#define MBR_PTYPE_PLAN9 0x39 /* Plan 9, or Theos spanned */
+#define MBR_PTYPE_THEOS_4GB 0x3a /* Theos ver 4 4gb partition */
+#define MBR_PTYPE_THEOS_EXT 0x3b /* Theos ve 4 extended partition */
+#define MBR_PTYPE_PMRECOVERY 0x3c /* PartitionMagic recovery */
+#define MBR_PTYPE_HID_NETWARE 0x3d /* Hidden Netware */
+#define MBR_PTYPE_VENIX 0x40 /* VENIX 286 or LynxOS */
+#define MBR_PTYPE_PREP 0x41 /* PReP */
+#define MBR_PTYPE_DRDOS_LSWAP 0x42 /* linux swap sharing DRDOS disk */
+#define MBR_PTYPE_DRDOS_LINUX 0x43 /* linux sharing DRDOS disk */
+#define MBR_PTYPE_GOBACK 0x44 /* GoBack change utility */
+#define MBR_PTYPE_BOOT_US 0x45 /* Boot US Boot manager */
+#define MBR_PTYPE_EUMEL_x46 0x46 /* EUMEL/Elan or Ergos 3 */
+#define MBR_PTYPE_EUMEL_x47 0x47 /* EUMEL/Elan or Ergos 3 */
+#define MBR_PTYPE_EUMEL_x48 0x48 /* EUMEL/Elan or Ergos 3 */
+#define MBR_PTYPE_ALFS_THIN 0x4a /* ALFX/THIN filesystem for DOS */
+#define MBR_PTYPE_OBERON 0x4c /* Oberon partition */
+#define MBR_PTYPE_QNX4X 0x4d /* QNX4.x */
+#define MBR_PTYPE_QNX4X_2 0x4e /* QNX4.x 2nd part */
+#define MBR_PTYPE_QNX4X_3 0x4f /* QNX4.x 3rd part */
+#define MBR_PTYPE_DM 0x50 /* DM (disk manager) */
+#define MBR_PTYPE_DM6_AUX1 0x51 /* DM6 Aux1 (or Novell) */
+#define MBR_PTYPE_CPM 0x52 /* CP/M or Microport SysV/AT */
+#define MBR_PTYPE_DM6_AUX3 0x53 /* DM6 Aux3 */
+#define MBR_PTYPE_DM6_DDO 0x54 /* DM6 DDO */
+#define MBR_PTYPE_EZDRIVE 0x55 /* EZ-Drive (disk manager) */
+#define MBR_PTYPE_GOLDEN_BOW 0x56 /* Golden Bow (disk manager) */
+#define MBR_PTYPE_DRIVE_PRO 0x57 /* Drive PRO */
+#define MBR_PTYPE_PRIAM_EDISK 0x5c /* Priam Edisk (disk manager) */
+#define MBR_PTYPE_SPEEDSTOR 0x61 /* SpeedStor */
+#define MBR_PTYPE_HURD 0x63 /* GNU HURD or Mach or Sys V/386 */
+#define MBR_PTYPE_NOVELL_2XX 0x64 /* Novell Netware 2.xx or Speedstore */
+#define MBR_PTYPE_NOVELL_3XX 0x65 /* Novell Netware 3.xx */
+#define MBR_PTYPE_NOVELL_386 0x66 /* Novell 386 Netware */
+#define MBR_PTYPE_NOVELL_x67 0x67 /* Novell */
+#define MBR_PTYPE_NOVELL_x68 0x68 /* Novell */
+#define MBR_PTYPE_NOVELL_x69 0x69 /* Novell */
+#define MBR_PTYPE_DISKSECURE 0x70 /* DiskSecure Multi-Boot */
+#define MBR_PTYPE_RESERVED_x71 0x71 /* reserved */
+#define MBR_PTYPE_RESERVED_x73 0x73 /* reserved */
+#define MBR_PTYPE_RESERVED_x74 0x74 /* reserved */
+#define MBR_PTYPE_PCIX 0x75 /* PC/IX */
+#define MBR_PTYPE_RESERVED_x76 0x76 /* reserved */
+#define MBR_PTYPE_M2FS_M2CS 0x77 /* M2FS/M2CS partition */
+#define MBR_PTYPE_XOSL_FS 0x78 /* XOSL boot loader filesystem */
+#define MBR_PTYPE_MINIX_14A 0x80 /* MINIX until 1.4a */
+#define MBR_PTYPE_MINIX_14B 0x81 /* MINIX since 1.4b */
+#define MBR_PTYPE_LNXSWAP 0x82 /* Linux swap or Solaris */
+#define MBR_PTYPE_LNXEXT2 0x83 /* Linux native */
+#define MBR_PTYPE_OS2_C 0x84 /* OS/2 hidden C: drive */
+#define MBR_PTYPE_EXT_LNX 0x85 /* Linux extended partition */
+#define MBR_PTYPE_NTFATVOL 0x86 /* NT FAT volume set */
+#define MBR_PTYPE_NTFSVOL 0x87 /* NTFS volume set or HPFS mirrored */
+#define MBR_PTYPE_LNX_KERNEL 0x8a /* Linux Kernel AiR-BOOT partition */
+#define MBR_PTYPE_FT_FAT32 0x8b /* Legacy Fault tolerant FAT32 */
+#define MBR_PTYPE_FT_FAT32_EXT 0x8c /* Legacy Fault tolerant FAT32 ext */
+#define MBR_PTYPE_HID_FR_FD_12 0x8d /* Hidden free FDISK FAT12 */
+#define MBR_PTYPE_LNX_LVM 0x8e /* Linux Logical Volume Manager */
+#define MBR_PTYPE_HID_FR_FD_16 0x90 /* Hidden free FDISK FAT16 */
+#define MBR_PTYPE_HID_FR_FD_EXT 0x91 /* Hidden free FDISK DOS EXT */
+#define MBR_PTYPE_HID_FR_FD_16B 0x92 /* Hidden free FDISK FAT16 Big */
+#define MBR_PTYPE_AMOEBA_FS 0x93 /* Amoeba filesystem */
+#define MBR_PTYPE_AMOEBA_BAD 0x94 /* Amoeba bad block table */
+#define MBR_PTYPE_MIT_EXOPC 0x95 /* MIT EXOPC native partitions */
+#define MBR_PTYPE_HID_FR_FD_32 0x97 /* Hidden free FDISK FAT32 */
+#define MBR_PTYPE_DATALIGHT 0x98 /* Datalight ROM-DOS Super-Boot */
+#define MBR_PTYPE_MYLEX 0x99 /* Mylex EISA SCSI */
+#define MBR_PTYPE_HID_FR_FD_16L 0x9a /* Hidden free FDISK FAT16 LBA */
+#define MBR_PTYPE_HID_FR_FD_EXL 0x9b /* Hidden free FDISK EXT LBA */
+#define MBR_PTYPE_BSDI 0x9f /* BSDI? */
+#define MBR_PTYPE_IBM_HIB 0xa0 /* IBM Thinkpad hibernation */
+#define MBR_PTYPE_HP_VOL_xA1 0xa1 /* HP Volume expansion (SpeedStor) */
+#define MBR_PTYPE_HP_VOL_xA3 0xa3 /* HP Volume expansion (SpeedStor) */
+#define MBR_PTYPE_HP_VOL_xA4 0xa4 /* HP Volume expansion (SpeedStor) */
+#define MBR_PTYPE_386BSD 0xa5 /* 386BSD partition type */
+#define MBR_PTYPE_OPENBSD 0xa6 /* OpenBSD partition type */
+#define MBR_PTYPE_NEXTSTEP_486 0xa7 /* NeXTSTEP 486 */
+#define MBR_PTYPE_APPLE_UFS 0xa8 /* Apple UFS */
+#define MBR_PTYPE_NETBSD 0xa9 /* NetBSD partition type */
+#define MBR_PTYPE_OLIVETTI 0xaa /* Olivetty Fat12 1.44MB Service part */
+#define MBR_PTYPE_APPLE_BOOT 0xab /* Apple Boot */
+#define MBR_PTYPE_SHAG_OS 0xae /* SHAG OS filesystem */
+#define MBR_PTYPE_APPLE_HFS 0xaf /* Apple HFS */
+#define MBR_PTYPE_BOOTSTAR_DUM 0xb0 /* BootStar Dummy */
+#define MBR_PTYPE_RESERVED_xB1 0xb1 /* reserved */
+#define MBR_PTYPE_RESERVED_xB3 0xb3 /* reserved */
+#define MBR_PTYPE_RESERVED_xB4 0xb4 /* reserved */
+#define MBR_PTYPE_RESERVED_xB6 0xb6 /* reserved */
+#define MBR_PTYPE_BSDI_386 0xb7 /* BSDI BSD/386 filesystem */
+#define MBR_PTYPE_BSDI_SWAP 0xb8 /* BSDI BSD/386 swap */
+#define MBR_PTYPE_BOOT_WIZARD 0xbb /* Boot Wizard Hidden */
+#define MBR_PTYPE_SOLARIS_8 0xbe /* Solaris 8 partition type */
+#define MBR_PTYPE_SOLARIS 0xbf /* Solaris partition type */
+#define MBR_PTYPE_CTOS 0xc0 /* CTOS */
+#define MBR_PTYPE_DRDOS_FAT12 0xc1 /* DRDOS/sec (FAT-12) */
+#define MBR_PTYPE_HID_LNX 0xc2 /* Hidden Linux */
+#define MBR_PTYPE_HID_LNX_SWAP 0xc3 /* Hidden Linux swap */
+#define MBR_PTYPE_DRDOS_FAT16S 0xc4 /* DRDOS/sec (FAT-16, < 32M) */
+#define MBR_PTYPE_DRDOS_EXT 0xc5 /* DRDOS/sec (EXT) */
+#define MBR_PTYPE_DRDOS_FAT16B 0xc6 /* DRDOS/sec (FAT-16, >= 32M) */
+#define MBR_PTYPE_SYRINX 0xc7 /* Syrinx (Cyrnix?) or HPFS disabled */
+#define MBR_PTYPE_DRDOS_8_xC8 0xc8 /* Reserved for DR-DOS 8.0+ */
+#define MBR_PTYPE_DRDOS_8_xC9 0xc9 /* Reserved for DR-DOS 8.0+ */
+#define MBR_PTYPE_DRDOS_8_xCA 0xca /* Reserved for DR-DOS 8.0+ */
+#define MBR_PTYPE_DRDOS_74_CHS 0xcb /* DR-DOS 7.04+ Secured FAT32 CHS */
+#define MBR_PTYPE_DRDOS_74_LBA 0xcc /* DR-DOS 7.04+ Secured FAT32 LBA */
+#define MBR_PTYPE_CTOS_MEMDUMP 0xcd /* CTOS Memdump */
+#define MBR_PTYPE_DRDOS_74_16X 0xce /* DR-DOS 7.04+ FAT16X LBA */
+#define MBR_PTYPE_DRDOS_74_EXT 0xcf /* DR-DOS 7.04+ EXT LBA */
+#define MBR_PTYPE_REAL32 0xd0 /* REAL/32 secure big partition */
+#define MBR_PTYPE_MDOS_FAT12 0xd1 /* Old Multiuser DOS FAT12 */
+#define MBR_PTYPE_MDOS_FAT16S 0xd4 /* Old Multiuser DOS FAT16 Small */
+#define MBR_PTYPE_MDOS_EXT 0xd5 /* Old Multiuser DOS Extended */
+#define MBR_PTYPE_MDOS_FAT16B 0xd6 /* Old Multiuser DOS FAT16 Big */
+#define MBR_PTYPE_CPM_86 0xd8 /* CP/M 86 */
+#define MBR_PTYPE_CONCURRENT 0xdb /* CP/M or Concurrent CP/M */
+#define MBR_PTYPE_HID_CTOS_MEM 0xdd /* Hidden CTOS Memdump */
+#define MBR_PTYPE_DELL_UTIL 0xde /* Dell PowerEdge Server utilities */
+#define MBR_PTYPE_DGUX_VIRTUAL 0xdf /* DG/UX virtual disk manager */
+#define MBR_PTYPE_STMICROELEC 0xe0 /* STMicroelectronics ST AVFS */
+#define MBR_PTYPE_DOS_ACCESS 0xe1 /* DOS access or SpeedStor 12-bit */
+#define MBR_PTYPE_STORDIM 0xe3 /* DOS R/O or Storage Dimensions */
+#define MBR_PTYPE_SPEEDSTOR_16S 0xe4 /* SpeedStor 16-bit FAT < 1024 cyl. */
+#define MBR_PTYPE_RESERVED_xE5 0xe5 /* reserved */
+#define MBR_PTYPE_RESERVED_xE6 0xe6 /* reserved */
+#define MBR_PTYPE_BEOS 0xeb /* BeOS */
+#define MBR_PTYPE_PMBR 0xee /* GPT Protective MBR */
+#define MBR_PTYPE_EFI 0xef /* EFI system partition */
+#define MBR_PTYPE_LNX_PA_RISC 0xf0 /* Linux PA-RISC boot loader */
+#define MBR_PTYPE_SPEEDSTOR_X 0xf1 /* SpeedStor or Storage Dimensions */
+#define MBR_PTYPE_DOS33_SEC 0xf2 /* DOS 3.3+ Secondary */
+#define MBR_PTYPE_RESERVED_xF3 0xf3 /* reserved */
+#define MBR_PTYPE_SPEEDSTOR_L 0xf4 /* SpeedStor large partition */
+#define MBR_PTYPE_PROLOGUE 0xf5 /* Prologue multi-volumen partition */
+#define MBR_PTYPE_RESERVED_xF6 0xf6 /* reserved */
+#define MBR_PTYPE_PCACHE 0xf9 /* pCache: ext2/ext3 persistent cache */
+#define MBR_PTYPE_BOCHS 0xfa /* Bochs x86 emulator */
+#define MBR_PTYPE_VMWARE 0xfb /* VMware File System */
+#define MBR_PTYPE_VMWARE_SWAP 0xfc /* VMware Swap */
+#define MBR_PTYPE_LNX_RAID 0xfd /* Linux RAID partition persistent sb */
+#define MBR_PTYPE_LANSTEP 0xfe /* LANstep or IBM PS/2 IML */
+#define MBR_PTYPE_XENIX_BAD 0xff /* Xenix Bad Block Table */
+
+#ifdef MBRPTYPENAMES
+static const struct mbr_ptype {
+ int id;
+ const char *name;
+} mbr_ptypes[] = {
+ { MBR_PTYPE_UNUSED, "<UNUSED>" },
+ { MBR_PTYPE_FAT12, "Primary DOS with 12 bit FAT" },
+ { MBR_PTYPE_XENIX_ROOT, "XENIX / filesystem" },
+ { MBR_PTYPE_XENIX_USR, "XENIX /usr filesystem" },
+ { MBR_PTYPE_FAT16S, "Primary DOS with 16 bit FAT <32M" },
+ { MBR_PTYPE_EXT, "Extended partition" },
+ { MBR_PTYPE_FAT16B, "Primary 'big' DOS, 16-bit FAT (> 32MB)" },
+ { MBR_PTYPE_NTFS, "NTFS, OS/2 HPFS, QNX2 or Advanced UNIX" },
+ { MBR_PTYPE_DELL, "AIX filesystem or OS/2 (thru v1.3) or DELL "
+ "multiple drives or Commodore DOS or SplitDrive" },
+ { MBR_PTYPE_AIX_BOOT, "AIX boot partition or Coherent" },
+ { MBR_PTYPE_OS2_BOOT, "OS/2 Boot Manager or Coherent swap or OPUS" },
+ { MBR_PTYPE_FAT32, "Primary DOS with 32 bit FAT" },
+ { MBR_PTYPE_FAT32L, "Primary DOS with 32 bit FAT - LBA" },
+ { MBR_PTYPE_7XXX, "Type 7??? - LBA" },
+ { MBR_PTYPE_FAT16L, "DOS (16-bit FAT) - LBA" },
+ { MBR_PTYPE_EXT_LBA, "Ext. partition - LBA" },
+ { MBR_PTYPE_OPUS, "OPUS" },
+ { MBR_PTYPE_OS2_DOS12, "OS/2 BM: hidden DOS 12-bit FAT" },
+ { MBR_PTYPE_COMPAQ_DIAG, "Compaq diagnostics" },
+ { MBR_PTYPE_OS2_DOS16S, "OS/2 BM: hidden DOS 16-bit FAT <32M "
+ "or Novell DOS 7.0 bug" },
+ { MBR_PTYPE_OS2_DOS16B, "OS/2 BM: hidden DOS 16-bit FAT >=32M" },
+ { MBR_PTYPE_OS2_IFS, "OS/2 BM: hidden IFS" },
+ { MBR_PTYPE_AST_SWAP, "AST Windows swapfile" },
+ { MBR_PTYPE_WILLOWTECH, "Willowtech Photon coS" },
+ { MBR_PTYPE_HID_FAT32, "hidden Windows/95 FAT32" },
+ { MBR_PTYPE_HID_FAT32_LBA, "hidden Windows/95 FAT32 LBA" },
+ { MBR_PTYPE_HID_FAT16_LBA, "hidden Windows/95 FAT16 LBA" },
+ { MBR_PTYPE_WILLOWSOFT, "Willowsoft OFS1" },
+ { MBR_PTYPE_RESERVED_x21, "reserved" },
+ { MBR_PTYPE_RESERVED_x23, "reserved" },
+ { MBR_PTYPE_RESERVED_x24, "NEC DOS"},
+ { MBR_PTYPE_RESERVED_x26, "reserved" },
+ { MBR_PTYPE_RESERVED_x31, "reserved" },
+ { MBR_PTYPE_NOS, "Alien Internet Services NOS" },
+ { MBR_PTYPE_RESERVED_x33, "reserved" },
+ { MBR_PTYPE_RESERVED_x34, "reserved" },
+ { MBR_PTYPE_OS2_JFS, "JFS on OS2" },
+ { MBR_PTYPE_RESERVED_x36, "reserved" },
+ { MBR_PTYPE_THEOS, "Theos" },
+ { MBR_PTYPE_PLAN9, "Plan 9" },
+ { MBR_PTYPE_PLAN9, "Plan 9, or Theos spanned" },
+ { MBR_PTYPE_THEOS_4GB, "Theos ver 4 4gb partition" },
+ { MBR_PTYPE_THEOS_EXT, "Theos ve 4 extended partition" },
+ { MBR_PTYPE_PMRECOVERY, "PartitionMagic recovery" },
+ { MBR_PTYPE_HID_NETWARE, "Hidden Netware" },
+ { MBR_PTYPE_VENIX, "VENIX 286 or LynxOS" },
+ { MBR_PTYPE_PREP, "Linux/MINIX (sharing disk with DRDOS) "
+ "or Personal RISC boot" },
+ { MBR_PTYPE_DRDOS_LSWAP, "SFS or Linux swap "
+ "(sharing disk with DRDOS)" },
+ { MBR_PTYPE_DRDOS_LINUX, "Linux native (sharing disk with DRDOS)" },
+ { MBR_PTYPE_GOBACK, "GoBack change utility" },
+ { MBR_PTYPE_BOOT_US, "Boot US Boot manager" },
+ { MBR_PTYPE_EUMEL_x46, "EUMEL/Elan or Ergos 3" },
+ { MBR_PTYPE_EUMEL_x47, "EUMEL/Elan or Ergos 3" },
+ { MBR_PTYPE_EUMEL_x48, "EUMEL/Elan or Ergos 3" },
+ { MBR_PTYPE_ALFS_THIN, "ALFX/THIN filesystem for DOS" },
+ { MBR_PTYPE_OBERON, "Oberon partition" },
+ { MBR_PTYPE_QNX4X, "QNX4.x" },
+ { MBR_PTYPE_QNX4X_2, "QNX4.x 2nd part" },
+ { MBR_PTYPE_QNX4X_3, "QNX4.x 3rd part" },
+ { MBR_PTYPE_DM, "DM (disk manager)" },
+ { MBR_PTYPE_DM6_AUX1, "DM6 Aux1 (or Novell)" },
+ { MBR_PTYPE_CPM, "CP/M or Microport SysV/AT" },
+ { MBR_PTYPE_DM6_AUX3, "DM6 Aux3" },
+ { MBR_PTYPE_DM6_DDO, "DM6 DDO" },
+ { MBR_PTYPE_EZDRIVE, "EZ-Drive (disk manager)" },
+ { MBR_PTYPE_GOLDEN_BOW, "Golden Bow (disk manager)" },
+ { MBR_PTYPE_DRIVE_PRO, "Drive PRO" },
+ { MBR_PTYPE_PRIAM_EDISK, "Priam Edisk (disk manager)" },
+ { MBR_PTYPE_SPEEDSTOR, "SpeedStor" },
+ { MBR_PTYPE_HURD, "GNU HURD or Mach or Sys V/386 "
+ "(such as ISC UNIX) or MtXinu" },
+ { MBR_PTYPE_NOVELL_2XX, "Novell Netware 2.xx or Speedstore" },
+ { MBR_PTYPE_NOVELL_3XX, "Novell Netware 3.xx" },
+ { MBR_PTYPE_NOVELL_386, "Novell 386 Netware" },
+ { MBR_PTYPE_NOVELL_x67, "Novell" },
+ { MBR_PTYPE_NOVELL_x68, "Novell" },
+ { MBR_PTYPE_NOVELL_x69, "Novell" },
+ { MBR_PTYPE_DISKSECURE, "DiskSecure Multi-Boot" },
+ { MBR_PTYPE_RESERVED_x71, "reserved" },
+ { MBR_PTYPE_RESERVED_x73, "reserved" },
+ { MBR_PTYPE_RESERVED_x74, "reserved" },
+ { MBR_PTYPE_PCIX, "PC/IX" },
+ { MBR_PTYPE_RESERVED_x76, "reserved" },
+ { MBR_PTYPE_M2FS_M2CS, "M2FS/M2CS partition" },
+ { MBR_PTYPE_XOSL_FS, "XOSL boot loader filesystem" },
+ { MBR_PTYPE_MINIX_14A, "MINIX until 1.4a" },
+ { MBR_PTYPE_MINIX_14B, "MINIX since 1.4b, early Linux, Mitac dmgr" },
+ { MBR_PTYPE_LNXSWAP, "Linux swap or Prime or Solaris" },
+ { MBR_PTYPE_LNXEXT2, "Linux native" },
+ { MBR_PTYPE_OS2_C, "OS/2 hidden C: drive" },
+ { MBR_PTYPE_EXT_LNX, "Linux extended" },
+ { MBR_PTYPE_NTFATVOL, "NT FAT volume set" },
+ { MBR_PTYPE_NTFSVOL, "NTFS volume set or HPFS mirrored" },
+ { MBR_PTYPE_LNX_KERNEL, "Linux Kernel AiR-BOOT partition" },
+ { MBR_PTYPE_FT_FAT32, "Legacy Fault tolerant FAT32" },
+ { MBR_PTYPE_FT_FAT32_EXT, "Legacy Fault tolerant FAT32 ext" },
+ { MBR_PTYPE_HID_FR_FD_12, "Hidden free FDISK FAT12" },
+ { MBR_PTYPE_LNX_LVM, "Linux Logical Volume Manager" },
+ { MBR_PTYPE_HID_FR_FD_16, "Hidden free FDISK FAT16" },
+ { MBR_PTYPE_HID_FR_FD_EXT, "Hidden free FDISK DOS EXT" },
+ { MBR_PTYPE_HID_FR_FD_16L, "Hidden free FDISK FAT16 Large" },
+ { MBR_PTYPE_AMOEBA_FS, "Amoeba filesystem" },
+ { MBR_PTYPE_AMOEBA_BAD, "Amoeba bad block table" },
+ { MBR_PTYPE_MIT_EXOPC, "MIT EXOPC native partitions" },
+ { MBR_PTYPE_HID_FR_FD_32, "Hidden free FDISK FAT32" },
+ { MBR_PTYPE_DATALIGHT, "Datalight ROM-DOS Super-Boot" },
+ { MBR_PTYPE_MYLEX, "Mylex EISA SCSI" },
+ { MBR_PTYPE_HID_FR_FD_16L, "Hidden free FDISK FAT16 LBA" },
+ { MBR_PTYPE_HID_FR_FD_EXL, "Hidden free FDISK EXT LBA" },
+ { MBR_PTYPE_BSDI, "BSDI?" },
+ { MBR_PTYPE_IBM_HIB, "IBM Thinkpad hibernation" },
+ { MBR_PTYPE_HP_VOL_xA1, "HP Volume expansion (SpeedStor)" },
+ { MBR_PTYPE_HP_VOL_xA3, "HP Volume expansion (SpeedStor)" },
+ { MBR_PTYPE_HP_VOL_xA4, "HP Volume expansion (SpeedStor)" },
+ { MBR_PTYPE_386BSD, "FreeBSD or 386BSD or old NetBSD" },
+ { MBR_PTYPE_OPENBSD, "OpenBSD" },
+ { MBR_PTYPE_NEXTSTEP_486, "NeXTSTEP 486" },
+ { MBR_PTYPE_APPLE_UFS, "Apple UFS" },
+ { MBR_PTYPE_NETBSD, "NetBSD" },
+ { MBR_PTYPE_OLIVETTI, "Olivetty Fat12 1.44MB Service part" },
+ { MBR_PTYPE_SHAG_OS, "SHAG OS filesystem" },
+ { MBR_PTYPE_BOOTSTAR_DUM, "BootStar Dummy" },
+ { MBR_PTYPE_BOOT_WIZARD, "Boot Wizard Hidden" },
+ { MBR_PTYPE_APPLE_BOOT, "Apple Boot" },
+ { MBR_PTYPE_APPLE_HFS, "Apple HFS" },
+ { MBR_PTYPE_RESERVED_xB6, "reserved" },
+ { MBR_PTYPE_RESERVED_xB6, "reserved" },
+ { MBR_PTYPE_RESERVED_xB6, "reserved" },
+ { MBR_PTYPE_RESERVED_xB6, "reserved" },
+ { MBR_PTYPE_BSDI_386, "BSDI BSD/386 filesystem" },
+ { MBR_PTYPE_BSDI_SWAP, "BSDI BSD/386 swap" },
+ { MBR_PTYPE_SOLARIS_8, "Solaris 8 boot partition" },
+ { MBR_PTYPE_SOLARIS, "Solaris boot partition" },
+ { MBR_PTYPE_CTOS, "CTOS" },
+ { MBR_PTYPE_DRDOS_FAT12, "DRDOS/sec (FAT-12)" },
+ { MBR_PTYPE_HID_LNX, "Hidden Linux" },
+ { MBR_PTYPE_HID_LNX_SWAP, "Hidden Linux Swap" },
+ { MBR_PTYPE_DRDOS_FAT16S, "DRDOS/sec (FAT-16, < 32M)" },
+ { MBR_PTYPE_DRDOS_EXT, "DRDOS/sec (EXT)" },
+ { MBR_PTYPE_DRDOS_FAT16B, "DRDOS/sec (FAT-16, >= 32M)" },
+ { MBR_PTYPE_SYRINX, "Syrinx (Cyrnix?) or HPFS disabled" },
+ { MBR_PTYPE_DRDOS_8_xC8, "Reserved for DR-DOS 8.0+" },
+ { MBR_PTYPE_DRDOS_8_xC9, "Reserved for DR-DOS 8.0+" },
+ { MBR_PTYPE_DRDOS_8_xCA, "Reserved for DR-DOS 8.0+" },
+ { MBR_PTYPE_DRDOS_74_CHS, "DR-DOS 7.04+ Secured FAT32 CHS" },
+ { MBR_PTYPE_DRDOS_74_LBA, "DR-DOS 7.04+ Secured FAT32 LBA" },
+ { MBR_PTYPE_CTOS_MEMDUMP, "CTOS Memdump" },
+ { MBR_PTYPE_DRDOS_74_16X, "DR-DOS 7.04+ FAT16X LBA" },
+ { MBR_PTYPE_DRDOS_74_EXT, "DR-DOS 7.04+ EXT LBA" },
+ { MBR_PTYPE_REAL32, "REAL/32 secure big partition" },
+ { MBR_PTYPE_MDOS_FAT12, "Old Multiuser DOS FAT12" },
+ { MBR_PTYPE_MDOS_FAT16S, "Old Multiuser DOS FAT16 Small" },
+ { MBR_PTYPE_MDOS_EXT, "Old Multiuser DOS Extended" },
+ { MBR_PTYPE_MDOS_FAT16B, "Old Multiuser DOS FAT16 Big" },
+ { MBR_PTYPE_CPM_86, "CP/M 86" },
+ { MBR_PTYPE_CONCURRENT, "CP/M or Concurrent CP/M or Concurrent DOS "
+ "or CTOS" },
+ { MBR_PTYPE_HID_CTOS_MEM, "Hidden CTOS Memdump" },
+ { MBR_PTYPE_DELL_UTIL, "Dell PowerEdge Server utilities" },
+ { MBR_PTYPE_DGUX_VIRTUAL, "DG/UX virtual disk manager" },
+ { MBR_PTYPE_STMICROELEC, "STMicroelectronics ST AVFS" },
+ { MBR_PTYPE_DOS_ACCESS, "DOS access or SpeedStor 12-bit FAT "
+ "extended partition" },
+ { MBR_PTYPE_STORDIM, "DOS R/O or SpeedStor or Storage Dimensions" },
+ { MBR_PTYPE_SPEEDSTOR_16S, "SpeedStor 16-bit FAT extended partition "
+ "< 1024 cyl." },
+ { MBR_PTYPE_RESERVED_xE5, "reserved" },
+ { MBR_PTYPE_RESERVED_xE6, "reserved" },
+ { MBR_PTYPE_BEOS, "BeOS" },
+ { MBR_PTYPE_PMBR, "GPT Protective MBR" },
+ { MBR_PTYPE_EFI, "EFI system partition" },
+ { MBR_PTYPE_LNX_PA_RISC, "Linux PA-RISC boot loader" },
+ { MBR_PTYPE_SPEEDSTOR_X, "SpeedStor or Storage Dimensions" },
+ { MBR_PTYPE_DOS33_SEC, "DOS 3.3+ Secondary" },
+ { MBR_PTYPE_RESERVED_xF3, "reserved" },
+ { MBR_PTYPE_SPEEDSTOR_L, "SpeedStor large partition or "
+ "Storage Dimensions" },
+ { MBR_PTYPE_PROLOGUE, "Prologue multi-volumen partition" },
+ { MBR_PTYPE_RESERVED_xF6, "reserved" },
+ { MBR_PTYPE_PCACHE, "pCache: ext2/ext3 persistent cache" },
+ { MBR_PTYPE_BOCHS, "Bochs x86 emulator" },
+ { MBR_PTYPE_VMWARE, "VMware File System" },
+ { MBR_PTYPE_VMWARE_SWAP, "VMware Swap" },
+ { MBR_PTYPE_LNX_RAID, "Linux RAID partition persistent sb" },
+ { MBR_PTYPE_LANSTEP, "SpeedStor >1024 cyl. or LANstep "
+ "or IBM PS/2 IML" },
+ { MBR_PTYPE_XENIX_BAD, "Xenix Bad Block Table" },
+};
+#endif
+
+#define MBR_PSECT(s) ((s) & 0x3f)
+#define MBR_PCYL(c, s) ((c) + (((s) & 0xc0) << 2))
+
+#define MBR_IS_EXTENDED(x) ((x) == MBR_PTYPE_EXT || \
+ (x) == MBR_PTYPE_EXT_LBA || \
+ (x) == MBR_PTYPE_EXT_LNX)
+
+ /* values for mbr_bootsel.mbrbs_flags */
+#define MBR_BS_ACTIVE 0x01 /* Bootselector active (or code present) */
+#define MBR_BS_EXTINT13 0x02 /* Set by fdisk if LBA needed (deprecated) */
+#define MBR_BS_READ_LBA 0x04 /* Force LBA reads (deprecated) */
+#define MBR_BS_EXTLBA 0x08 /* Extended ptn capable (LBA reads) */
+#define MBR_BS_ASCII 0x10 /* Bootselect code needs ascii key code */
+/* This is always set, the bootsel is located using the magic number... */
+#define MBR_BS_NEWMBR 0x80 /* New bootsel at offset 440 */
+
+
+/*
+ * (x86) BIOS Parameter Block for FAT12
+ */
+struct mbr_bpbFAT12 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint16_t bpbHiddenSecs; /* # of hidden sectors */
+} __packed;
+
+/*
+ * (x86) BIOS Parameter Block for FAT16
+ */
+struct mbr_bpbFAT16 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint32_t bpbHiddenSecs; /* # of hidden sectors */
+ uint32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */
+ uint8_t bsDrvNum; /* Int 0x13 drive number (e.g. 0x80) */
+ uint8_t bsReserved1; /* Reserved; set to 0 */
+ uint8_t bsBootSig; /* 0x29 if next 3 fields are present */
+ uint8_t bsVolID[4]; /* Volume serial number */
+ uint8_t bsVolLab[11]; /* Volume label */
+ uint8_t bsFileSysType[8];
+ /* "FAT12 ", "FAT16 ", "FAT " */
+} __packed;
+
+/*
+ * (x86) BIOS Parameter Block for FAT32
+ */
+struct mbr_bpbFAT32 {
+ uint16_t bpbBytesPerSec; /* bytes per sector */
+ uint8_t bpbSecPerClust; /* sectors per cluster */
+ uint16_t bpbResSectors; /* number of reserved sectors */
+ uint8_t bpbFATs; /* number of FATs */
+ uint16_t bpbRootDirEnts; /* number of root directory entries */
+ uint16_t bpbSectors; /* total number of sectors */
+ uint8_t bpbMedia; /* media descriptor */
+ uint16_t bpbFATsecs; /* number of sectors per FAT */
+ uint16_t bpbSecPerTrack; /* sectors per track */
+ uint16_t bpbHeads; /* number of heads */
+ uint32_t bpbHiddenSecs; /* # of hidden sectors */
+ uint32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */
+ uint32_t bpbBigFATsecs; /* like bpbFATsecs for FAT32 */
+ uint16_t bpbExtFlags; /* extended flags: */
+#define MBR_FAT32_FATNUM 0x0F /* mask for numbering active FAT */
+#define MBR_FAT32_FATMIRROR 0x80 /* FAT is mirrored (as previously) */
+ uint16_t bpbFSVers; /* filesystem version */
+#define MBR_FAT32_FSVERS 0 /* currently only 0 is understood */
+ uint32_t bpbRootClust; /* start cluster for root directory */
+ uint16_t bpbFSInfo; /* filesystem info structure sector */
+ uint16_t bpbBackup; /* backup boot sector */
+ uint8_t bsReserved[12]; /* Reserved for future expansion */
+ uint8_t bsDrvNum; /* Int 0x13 drive number (e.g. 0x80) */
+ uint8_t bsReserved1; /* Reserved; set to 0 */
+ uint8_t bsBootSig; /* 0x29 if next 3 fields are present */
+ uint8_t bsVolID[4]; /* Volume serial number */
+ uint8_t bsVolLab[11]; /* Volume label */
+ uint8_t bsFileSysType[8]; /* "FAT32 " */
+} __packed;
+
+/*
+ * (x86) MBR boot selector
+ */
+struct mbr_bootsel {
+ uint8_t mbrbs_defkey;
+ uint8_t mbrbs_flags;
+ uint16_t mbrbs_timeo;
+ char mbrbs_nametab[MBR_PART_COUNT][MBR_BS_PARTNAMESIZE + 1];
+} __packed;
+
+/*
+ * MBR partition
+ */
+struct mbr_partition {
+ uint8_t mbrp_flag; /* MBR partition flags */
+ uint8_t mbrp_shd; /* Starting head */
+ uint8_t mbrp_ssect; /* Starting sector */
+ uint8_t mbrp_scyl; /* Starting cylinder */
+ uint8_t mbrp_type; /* Partition type (see below) */
+ uint8_t mbrp_ehd; /* End head */
+ uint8_t mbrp_esect; /* End sector */
+ uint8_t mbrp_ecyl; /* End cylinder */
+ uint32_t mbrp_start; /* Absolute starting sector number */
+ uint32_t mbrp_size; /* Partition size in sectors */
+} __packed;
+
+int xlat_mbr_fstype(int); /* in sys/lib/libkern/xlat_mbr_fstype.c */
+
+/*
+ * MBR boot sector.
+ * This is used by both the MBR (Master Boot Record) in sector 0 of the disk
+ * and the PBR (Partition Boot Record) in sector 0 of an MBR partition.
+ */
+struct mbr_sector {
+ /* Jump instruction to boot code. */
+ /* Usually 0xE9nnnn or 0xEBnn90 */
+ uint8_t mbr_jmpboot[3];
+ /* OEM name and version */
+ uint8_t mbr_oemname[8];
+ union { /* BIOS Parameter Block */
+ struct mbr_bpbFAT12 bpb12;
+ struct mbr_bpbFAT16 bpb16;
+ struct mbr_bpbFAT32 bpb32;
+ } mbr_bpb;
+ /* Boot code */
+ uint8_t mbr_bootcode[310];
+ /* Config for /usr/mdec/mbr_bootsel */
+ struct mbr_bootsel mbr_bootsel;
+ /* NT Drive Serial Number */
+ uint32_t mbr_dsn;
+ /* mbr_bootsel magic */
+ uint16_t mbr_bootsel_magic;
+ /* MBR partition table */
+ struct mbr_partition mbr_parts[MBR_PART_COUNT];
+ /* MBR magic (0xaa55) */
+ uint16_t mbr_magic;
+} __packed;
+
+
+
+/* ------------------------------------------
+ * shared --
+ * definitions shared by many platforms
+ */
+
+
+ /* Maximum # of blocks in bbi_block_table, each bbi_block_size long */
+#define SHARED_BBINFO_MAXBLOCKS 118 /* so sizeof(shared_bbinfo) == 512 */
+
+struct shared_bbinfo {
+ uint8_t bbi_magic[32];
+ int32_t bbi_block_size;
+ int32_t bbi_block_count;
+ int32_t bbi_block_table[SHARED_BBINFO_MAXBLOCKS];
+};
+
+/* ------------------------------------------
+ * alpha --
+ * Alpha (disk, but also tape) Boot Block.
+ *
+ * See Section (III) 3.6.1 of the Alpha Architecture Reference Manual.
+ */
+
+struct alpha_boot_block {
+ uint64_t bb_data[63]; /* data (disklabel, also as below) */
+ uint64_t bb_cksum; /* checksum of the boot block,
+ * taken as uint64_t's
+ */
+};
+#define bb_secsize bb_data[60] /* secondary size (blocks) */
+#define bb_secstart bb_data[61] /* secondary start (blocks) */
+#define bb_flags bb_data[62] /* unknown flags (set to zero) */
+
+#define ALPHA_BOOT_BLOCK_OFFSET 0 /* offset of boot block. */
+#define ALPHA_BOOT_BLOCK_BLOCKSIZE 512 /* block size for sector
+ * size/start, and for boot
+ * block itself.
+ */
+
+#define ALPHA_BOOT_BLOCK_CKSUM(bb,cksum) \
+ do { \
+ const struct alpha_boot_block *_bb = (bb); \
+ uint64_t _cksum; \
+ size_t _i; \
+ \
+ _cksum = 0; \
+ for (_i = 0; \
+ _i < (sizeof _bb->bb_data / sizeof _bb->bb_data[0]); \
+ _i++) \
+ _cksum += le64toh(_bb->bb_data[_i]); \
+ *(cksum) = htole64(_cksum); \
+ } while (/*CONSTCOND*/ 0)
+
+/* ------------------------------------------
+ * apple --
+ * Apple computers boot block related information
+ */
+
+/*
+ * Driver Descriptor Map, from Inside Macintosh: Devices, SCSI Manager
+ * pp 12-13. The driver descriptor map always resides on physical block 0.
+ */
+struct apple_drvr_descriptor {
+ uint32_t descBlock; /* first block of driver */
+ uint16_t descSize; /* driver size in blocks */
+ uint16_t descType; /* system type */
+} __packed;
+
+/*
+ * system types; Apple reserves 0-15
+ */
+#define APPLE_DRVR_TYPE_MACINTOSH 1
+
+#define APPLE_DRVR_MAP_MAGIC 0x4552
+#define APPLE_DRVR_MAP_MAX_DESCRIPTORS 61
+
+struct apple_drvr_map {
+ uint16_t sbSig; /* map signature */
+ uint16_t sbBlockSize; /* block size of device */
+ uint32_t sbBlkCount; /* number of blocks on device */
+ uint16_t sbDevType; /* (used internally by ROM) */
+ uint16_t sbDevID; /* (used internally by ROM) */
+ uint32_t sbData; /* (used internally by ROM) */
+ uint16_t sbDrvrCount; /* number of driver descriptors */
+ struct apple_drvr_descriptor sb_dd[APPLE_DRVR_MAP_MAX_DESCRIPTORS];
+ uint16_t pad[3];
+} __packed;
+
+/*
+ * Partition map structure from Inside Macintosh: Devices, SCSI Manager
+ * pp. 13-14. The partition map always begins on physical block 1.
+ *
+ * With the exception of block 0, all blocks on the disk must belong to
+ * exactly one partition. The partition map itself belongs to a partition
+ * of type `APPLE_PARTITION_MAP', and is not limited in size by anything
+ * other than available disk space. The partition map is not necessarily
+ * the first partition listed.
+ */
+#define APPLE_PART_MAP_ENTRY_MAGIC 0x504d
+
+struct apple_part_map_entry {
+ uint16_t pmSig; /* partition signature */
+ uint16_t pmSigPad; /* (reserved) */
+ uint32_t pmMapBlkCnt; /* number of blocks in partition map */
+ uint32_t pmPyPartStart; /* first physical block of partition */
+ uint32_t pmPartBlkCnt; /* number of blocks in partition */
+ uint8_t pmPartName[32]; /* partition name */
+ uint8_t pmPartType[32]; /* partition type */
+ uint32_t pmLgDataStart; /* first logical block of data area */
+ uint32_t pmDataCnt; /* number of blocks in data area */
+ uint32_t pmPartStatus; /* partition status information */
+/*
+ * Partition Status Information from Apple Tech Note 1189
+ */
+#define APPLE_PS_VALID 0x00000001 /* Entry is valid */
+#define APPLE_PS_ALLOCATED 0x00000002 /* Entry is allocated */
+#define APPLE_PS_IN_USE 0x00000004 /* Entry in use */
+#define APPLE_PS_BOOT_INFO 0x00000008 /* Entry contains boot info */
+#define APPLE_PS_READABLE 0x00000010 /* Entry is readable */
+#define APPLE_PS_WRITABLE 0x00000020 /* Entry is writable */
+#define APPLE_PS_BOOT_CODE_PIC 0x00000040 /* Boot code has position
+ * independent code */
+#define APPLE_PS_CC_DRVR 0x00000100 /* Partition contains chain-
+ * compatible driver */
+#define APPLE_PS_RL_DRVR 0x00000200 /* Partition contains real
+ * driver */
+#define APPLE_PS_CH_DRVR 0x00000400 /* Partition contains chain
+ * driver */
+#define APPLE_PS_AUTO_MOUNT 0x40000000 /* Mount automatically at
+ * startup */
+#define APPLE_PS_STARTUP 0x80000000 /* Is the startup partition */
+ uint32_t pmLgBootStart; /* first logical block of boot code */
+ uint32_t pmBootSize; /* size of boot code, in bytes */
+ uint32_t pmBootLoad; /* boot code load address */
+ uint32_t pmBootLoad2; /* (reserved) */
+ uint32_t pmBootEntry; /* boot code entry point */
+ uint32_t pmBootEntry2; /* (reserved) */
+ uint32_t pmBootCksum; /* boot code checksum */
+ int8_t pmProcessor[16]; /* processor type (e.g. "68020") */
+ uint8_t pmBootArgs[128]; /* A/UX boot arguments */
+ uint8_t pad[248]; /* pad to end of block */
+};
+
+#define APPLE_PART_TYPE_DRIVER "APPLE_DRIVER"
+#define APPLE_PART_TYPE_DRIVER43 "APPLE_DRIVER43"
+#define APPLE_PART_TYPE_DRIVERATA "APPLE_DRIVER_ATA"
+#define APPLE_PART_TYPE_DRIVERIOKIT "APPLE_DRIVER_IOKIT"
+#define APPLE_PART_TYPE_FWDRIVER "APPLE_FWDRIVER"
+#define APPLE_PART_TYPE_FWB_COMPONENT "FWB DRIVER COMPONENTS"
+#define APPLE_PART_TYPE_FREE "APPLE_FREE"
+#define APPLE_PART_TYPE_MAC "APPLE_HFS"
+#define APPLE_PART_TYPE_NETBSD "NETBSD"
+#define APPLE_PART_TYPE_NBSD_PPCBOOT "NETBSD/MACPPC"
+#define APPLE_PART_TYPE_NBSD_68KBOOT "NETBSD/MAC68K"
+#define APPLE_PART_TYPE_PATCHES "APPLE_PATCHES"
+#define APPLE_PART_TYPE_PARTMAP "APPLE_PARTITION_MAP"
+#define APPLE_PART_TYPE_PATCHES "APPLE_PATCHES"
+#define APPLE_PART_TYPE_SCRATCH "APPLE_SCRATCH"
+#define APPLE_PART_TYPE_UNIX "APPLE_UNIX_SVR2"
+
+/*
+ * "pmBootArgs" for APPLE_UNIX_SVR2 partition.
+ * NetBSD/mac68k only uses Magic, Cluster, Type, and Flags.
+ */
+struct apple_blockzeroblock {
+ uint32_t bzbMagic;
+ uint8_t bzbCluster;
+ uint8_t bzbType;
+ uint16_t bzbBadBlockInode;
+ uint16_t bzbFlags;
+ uint16_t bzbReserved;
+ uint32_t bzbCreationTime;
+ uint32_t bzbMountTime;
+ uint32_t bzbUMountTime;
+};
+
+#define APPLE_BZB_MAGIC 0xABADBABE
+#define APPLE_BZB_TYPEFS 1
+#define APPLE_BZB_TYPESWAP 3
+#define APPLE_BZB_ROOTFS 0x8000
+#define APPLE_BZB_USRFS 0x4000
+
+/* ------------------------------------------
+ * ews4800mips
+ *
+ */
+
+#define EWS4800MIPS_BBINFO_MAGIC "NetBSD/ews4800mips 20040611"
+#define EWS4800MIPS_BOOT_BLOCK_OFFSET 0
+#define EWS4800MIPS_BOOT_BLOCK_BLOCKSIZE 512
+#define EWS4800MIPS_BOOT_BLOCK_MAX_SIZE (512 * 8)
+
+/* ------------------------------------------
+ * hp300
+ *
+ */
+
+/* volume header for "LIF" format volumes */
+
+struct hp300_lifvol {
+ int16_t vol_id;
+ char vol_label[6];
+ int32_t vol_addr;
+ int16_t vol_oct;
+ int16_t vol_dummy;
+ int32_t vol_dirsize;
+ int16_t vol_version;
+ int16_t vol_zero;
+ int32_t vol_huh1;
+ int32_t vol_huh2;
+ int32_t vol_length;
+};
+
+/* LIF directory entry format */
+
+struct hp300_lifdir {
+ char dir_name[10];
+ int16_t dir_type;
+ int32_t dir_addr;
+ int32_t dir_length;
+ char dir_toc[6];
+ int16_t dir_flag;
+ int32_t dir_exec;
+};
+
+/* load header for boot rom */
+struct hp300_load {
+ int32_t address;
+ int32_t count;
+};
+
+#define HP300_VOL_ID -32768
+#define HP300_VOL_OCT 4096
+#define HP300_DIR_TYPE -5822
+#define HP300_DIR_FLAG 0x8001 /* dont ask me! */
+#define HP300_SECTSIZE 256
+
+
+/* ------------------------------------------
+ * hppa
+ *
+ */
+
+/*
+ * volume header for "LIF" format volumes
+ */
+struct hppa_lifvol {
+ uint16_t vol_id;
+ uint8_t vol_label[6];
+ uint32_t vol_addr;
+ uint16_t vol_oct;
+ uint16_t vol_dummy;
+
+ uint32_t vol_dirsize;
+ uint16_t vol_version;
+ uint16_t vol_zero;
+ uint32_t vol_number;
+ uint32_t vol_lastvol;
+
+ uint32_t vol_length;
+ uint8_t vol_toc[6];
+ uint8_t vol_dummy1[198];
+
+ uint32_t ipl_addr;
+ uint32_t ipl_size;
+ uint32_t ipl_entry;
+
+ uint32_t vol_dummy2;
+};
+
+struct hppa_lifdir {
+ uint8_t dir_name[10];
+ uint16_t dir_type;
+ uint32_t dir_addr;
+ uint32_t dir_length;
+ uint8_t dir_toc[6];
+ uint16_t dir_flag;
+ uint32_t dir_implement;
+};
+
+struct hppa_lifload {
+ int address;
+ int count;
+};
+
+#define HPPA_LIF_VOL_ID 0x8000
+#define HPPA_LIF_VOL_OCT 0x1000
+#define HPPA_LIF_DIR_SWAP 0x5243
+#define HPPA_LIF_DIR_FS 0xcd38
+#define HPPA_LIF_DIR_IOMAP 0xcd60
+#define HPPA_LIF_DIR_HPUX 0xcd80
+#define HPPA_LIF_DIR_ISL 0xce00
+#define HPPA_LIF_DIR_PAD 0xcffe
+#define HPPA_LIF_DIR_AUTO 0xcfff
+#define HPPA_LIF_DIR_EST 0xd001
+#define HPPA_LIF_DIR_TYPE 0xe942
+
+#define HPPA_LIF_DIR_FLAG 0x8001 /* dont ask me! */
+#define HPPA_LIF_SECTSIZE 256
+
+#define HPPA_LIF_NUMDIR 8
+
+#define HPPA_LIF_VOLSTART 0
+#define HPPA_LIF_VOLSIZE sizeof(struct hppa_lifvol)
+#define HPPA_LIF_DIRSTART 2048
+#define HPPA_LIF_DIRSIZE (HPPA_LIF_NUMDIR * sizeof(struct hppa_lifdir))
+#define HPPA_LIF_FILESTART 4096
+
+#define hppa_btolifs(b) (((b) + (HPPA_LIF_SECTSIZE - 1)) / HPPA_LIF_SECTSIZE)
+#define hppa_lifstob(s) ((s) * HPPA_LIF_SECTSIZE)
+#define hppa_lifstodb(s) ((s) * HPPA_LIF_SECTSIZE / DEV_BSIZE)
+
+
+/* ------------------------------------------
+ * x86
+ *
+ */
+
+/*
+ * Parameters for NetBSD /boot written to start of pbr code by installboot
+ */
+
+struct x86_boot_params {
+ uint32_t bp_length; /* length of patchable data */
+ uint32_t bp_flags;
+ uint32_t bp_timeout; /* boot timeout in seconds */
+ uint32_t bp_consdev;
+ uint32_t bp_conspeed;
+ uint8_t bp_password[16]; /* md5 hash of password */
+ char bp_keymap[64]; /* keyboard translation map */
+ uint32_t bp_consaddr; /* ioaddr for console */
+};
+
+
+#define X86_BOOT_MAGIC(n) ('x' << 24 | 0x86b << 12 | 'm' << 4 | (n))
+#define X86_BOOT_MAGIC_1 X86_BOOT_MAGIC(1) /* pbr.S */
+#define X86_BOOT_MAGIC_2 X86_BOOT_MAGIC(2) /* bootxx.S */
+#define X86_BOOT_MAGIC_PXE X86_BOOT_MAGIC(3) /* start_pxe.S */
+#define X86_BOOT_MAGIC_FAT X86_BOOT_MAGIC(4) /* fatboot.S */
+#define X86_MBR_GPT_MAGIC 0xedb88320 /* gpt.S */
+
+ /* values for bp_flags */
+#define X86_BP_FLAGS_RESET_VIDEO 1
+#define X86_BP_FLAGS_PASSWORD 2
+#define X86_BP_FLAGS_NOMODULES 4
+#define X86_BP_FLAGS_NOBOOTCONF 8
+#define X86_BP_FLAGS_LBA64VALID 0x10
+
+ /* values for bp_consdev */
+#define X86_BP_CONSDEV_PC 0
+#define X86_BP_CONSDEV_COM0 1
+#define X86_BP_CONSDEV_COM1 2
+#define X86_BP_CONSDEV_COM2 3
+#define X86_BP_CONSDEV_COM3 4
+#define X86_BP_CONSDEV_COM0KBD 5
+#define X86_BP_CONSDEV_COM1KBD 6
+#define X86_BP_CONSDEV_COM2KBD 7
+#define X86_BP_CONSDEV_COM3KBD 8
+
+/* ------------------------------------------
+ * landisk
+ */
+
+
+/*
+ * Parameters for NetBSD /boot written to start of pbr code by installboot
+ */
+struct landisk_boot_params {
+ uint32_t bp_length; /* length of patchable data */
+ uint32_t bp_flags;
+ uint32_t bp_timeout; /* boot timeout in seconds */
+ uint32_t bp_consdev;
+ uint32_t bp_conspeed;
+};
+
+
+#define LANDISK_BOOT_MAGIC_1 0x20031125
+#define LANDISK_BOOT_MAGIC_2 0x20041110
+
+
+/* ------------------------------------------
+ * macppc
+ */
+
+#define MACPPC_BOOT_BLOCK_OFFSET 2048
+#define MACPPC_BOOT_BLOCK_BLOCKSIZE 512
+#define MACPPC_BOOT_BLOCK_MAX_SIZE 2048 /* XXX: could be up to 6144 */
+ /* Magic string -- 32 bytes long (including the NUL) */
+#define MACPPC_BBINFO_MAGIC "NetBSD/macppc bootxx 20020515"
+
+/* ------------------------------------------
+ * news68k, newsmips
+ */
+
+#define NEWS_BOOT_BLOCK_LABELOFFSET 64 /* XXX from <machine/disklabel.h> */
+#define NEWS_BOOT_BLOCK_OFFSET 0
+#define NEWS_BOOT_BLOCK_BLOCKSIZE 512
+#define NEWS_BOOT_BLOCK_MAX_SIZE (512 * 16)
+
+ /* Magic string -- 32 bytes long (including the NUL) */
+#define NEWS68K_BBINFO_MAGIC "NetBSD/news68k bootxx 20020518"
+#define NEWSMIPS_BBINFO_MAGIC "NetBSD/newsmips bootxx 20020518"
+
+/* ------------------------------------------
+ * next68k
+ */
+
+#define NEXT68K_LABEL_MAXPARTITIONS 8 /* number of partitions in next68k_disklabel */
+#define NEXT68K_LABEL_CPULBLLEN 24
+#define NEXT68K_LABEL_MAXDNMLEN 24
+#define NEXT68K_LABEL_MAXTYPLEN 24
+#define NEXT68K_LABEL_MAXBFLEN 24
+#define NEXT68K_LABEL_MAXHNLEN 32
+#define NEXT68K_LABEL_MAXMPTLEN 16
+#define NEXT68K_LABEL_MAXFSTLEN 8
+#define NEXT68K_LABEL_NBAD 1670 /* sized to make label ~= 8KB */
+
+struct next68k_partition {
+ int32_t cp_offset; /* starting sector */
+ int32_t cp_size; /* number of sectors in partition */
+ int16_t cp_bsize; /* block size in bytes */
+ int16_t cp_fsize; /* filesystem basic fragment size */
+ char cp_opt; /* optimization type: 's'pace/'t'ime */
+ char cp_pad1;
+ int16_t cp_cpg; /* filesystem cylinders per group */
+ int16_t cp_density; /* bytes per inode density */
+ int8_t cp_minfree; /* minfree (%) */
+ int8_t cp_newfs; /* run newfs during init */
+ char cp_mountpt[NEXT68K_LABEL_MAXMPTLEN];
+ /* default/standard mount point */
+ int8_t cp_automnt; /* auto-mount when inserted */
+ char cp_type[NEXT68K_LABEL_MAXFSTLEN]; /* file system type name */
+ char cp_pad2;
+} __packed;
+
+/* The disklabel the way it is on the disk */
+struct next68k_disklabel {
+ int32_t cd_version; /* label version */
+ int32_t cd_label_blkno; /* block # of this label */
+ int32_t cd_size; /* size of media area (sectors) */
+ char cd_label[NEXT68K_LABEL_CPULBLLEN]; /* disk name (label) */
+ uint32_t cd_flags; /* flags */
+ uint32_t cd_tag; /* volume tag */
+ char cd_name[NEXT68K_LABEL_MAXDNMLEN]; /* drive (hardware) name */
+ char cd_type[NEXT68K_LABEL_MAXTYPLEN]; /* drive type */
+ int32_t cd_secsize; /* # of bytes per sector */
+ int32_t cd_ntracks; /* # of tracks per cylinder */
+ int32_t cd_nsectors; /* # of data sectors per track */
+ int32_t cd_ncylinders; /* # of data cylinders per unit */
+ int32_t cd_rpm; /* rotational speed */
+ int16_t cd_front; /* # of sectors in "front porch" */
+ int16_t cd_back; /* # of sectors in "back porch" */
+ int16_t cd_ngroups; /* # of alt groups */
+ int16_t cd_ag_size; /* alt group size (sectors) */
+ int16_t cd_ag_alts; /* alternate sectors / alt group */
+ int16_t cd_ag_off; /* sector offset to first alternate */
+ int32_t cd_boot_blkno[2]; /* boot program locations */
+ char cd_kernel[NEXT68K_LABEL_MAXBFLEN]; /* default kernel name */
+ char cd_hostname[NEXT68K_LABEL_MAXHNLEN];
+ /* host name (usu. where disk was labeled) */
+ char cd_rootpartition; /* root partition letter e.g. 'a' */
+ char cd_rwpartition; /* r/w partition letter e.g. 'b' */
+ struct next68k_partition cd_partitions[NEXT68K_LABEL_MAXPARTITIONS];
+
+ union {
+ uint16_t CD_v3_checksum; /* label version 3 checksum */
+ int32_t CD_bad[NEXT68K_LABEL_NBAD];
+ /* block number that is bad */
+ } cd_un;
+ uint16_t cd_checksum; /* label version 1 or 2 checksum */
+} __packed;
+
+#define NEXT68K_LABEL_cd_checksum cd_checksum
+#define NEXT68K_LABEL_cd_v3_checksum cd_un.CD_v3_checksum
+#define NEXT68K_LABEL_cd_bad cd_un.CD_bad
+
+#define NEXT68K_LABEL_SECTOR 0 /* sector containing label */
+#define NEXT68K_LABEL_OFFSET 0 /* offset of label in sector */
+#define NEXT68K_LABEL_SIZE 8192 /* size of label */
+#define NEXT68K_LABEL_CD_V1 0x4e655854 /* version #1: "NeXT" */
+#define NEXT68K_LABEL_CD_V2 0x646c5632 /* version #2: "dlV2" */
+#define NEXT68K_LABEL_CD_V3 0x646c5633 /* version #3: "dlV3" */
+#define NEXT68K_LABEL_DEFAULTFRONTPORCH (160 * 2)
+#define NEXT68K_LABEL_DEFAULTBOOT0_1 (32 * 2)
+#define NEXT68K_LABEL_DEFAULTBOOT0_2 (96 * 2)
+
+/* ------------------------------------------
+ * pmax --
+ * PMAX (DECstation / MIPS) boot block information
+ */
+
+/*
+ * If mode is 0, there is just one sequence of blocks and one Dec_BootMap
+ * is used. If mode is 1, there are multiple sequences of blocks
+ * and multiple Dec_BootMaps are used, the last with numBlocks = 0.
+ */
+struct pmax_boot_map {
+ int32_t num_blocks; /* Number of blocks to read. */
+ int32_t start_block; /* Starting block on disk. */
+};
+
+/*
+ * This is the structure of a disk or tape boot block. The boot_map
+ * can either be a single boot count and start block (contiguous mode)
+ * or a list of up to 61 (to fill a 512 byte sector) block count and
+ * start block pairs. Under NetBSD, contiguous mode is always used.
+ */
+struct pmax_boot_block {
+ uint8_t pad[8];
+ int32_t magic; /* PMAX_BOOT_MAGIC */
+ int32_t mode; /* Mode for boot info. */
+ uint32_t load_addr; /* Address to start loading. */
+ uint32_t exec_addr; /* Address to start execing. */
+ struct pmax_boot_map map[61]; /* boot program section(s). */
+} __packed;
+
+#define PMAX_BOOT_MAGIC 0x0002757a
+#define PMAX_BOOTMODE_CONTIGUOUS 0
+#define PMAX_BOOTMODE_SCATTERED 1
+
+#define PMAX_BOOT_BLOCK_OFFSET 0
+#define PMAX_BOOT_BLOCK_BLOCKSIZE 512
+
+
+/* ------------------------------------------
+ * sgimips
+ */
+
+/*
+ * Some IRIX man pages refer to the size being a multiple of whole cylinders.
+ * Later ones only refer to the size being "typically" 2MB. IRIX fx(1)
+ * uses a default drive geometry if one can't be determined, suggesting
+ * that "whole cylinder" multiples are not required.
+ */
+
+#define SGI_BOOT_BLOCK_SIZE_VOLHDR 3135
+#define SGI_BOOT_BLOCK_MAGIC 0xbe5a941
+#define SGI_BOOT_BLOCK_MAXPARTITIONS 16
+#define SGI_BOOT_BLOCK_MAXVOLDIRS 15
+#define SGI_BOOT_BLOCK_BLOCKSIZE 512
+
+/*
+ * SGI partition conventions:
+ *
+ * Partition 0 - root
+ * Partition 1 - swap
+ * Partition 6 - usr
+ * Partition 7 - volume body
+ * Partition 8 - volume header
+ * Partition 10 - whole disk
+ */
+
+struct sgi_boot_devparms {
+ uint8_t dp_skew;
+ uint8_t dp_gap1;
+ uint8_t dp_gap2;
+ uint8_t dp_spares_cyl;
+ uint16_t dp_cyls;
+ uint16_t dp_shd0;
+ uint16_t dp_trks0;
+ uint8_t dp_ctq_depth;
+ uint8_t dp_cylshi;
+ uint16_t dp_unused;
+ uint16_t dp_secs;
+ uint16_t dp_secbytes;
+ uint16_t dp_interleave;
+ uint32_t dp_flags;
+ uint32_t dp_datarate;
+ uint32_t dp_nretries;
+ uint32_t dp_mspw;
+ uint16_t dp_xgap1;
+ uint16_t dp_xsync;
+ uint16_t dp_xrdly;
+ uint16_t dp_xgap2;
+ uint16_t dp_xrgate;
+ uint16_t dp_xwcont;
+} __packed;
+
+struct sgi_boot_block {
+ uint32_t magic;
+ int16_t root;
+ int16_t swap;
+ char bootfile[16];
+ struct sgi_boot_devparms dp;
+ struct {
+ char name[8];
+ int32_t block;
+ int32_t bytes;
+ } voldir[SGI_BOOT_BLOCK_MAXVOLDIRS];
+ struct {
+ int32_t blocks;
+ int32_t first;
+ int32_t type;
+ } partitions[SGI_BOOT_BLOCK_MAXPARTITIONS];
+ int32_t checksum;
+ int32_t _pad;
+} __packed;
+
+#define SGI_PTYPE_VOLHDR 0
+#define SGI_PTYPE_TRKREPL 1
+#define SGI_PTYPE_SECREPL 2
+#define SGI_PTYPE_RAW 3
+#define SGI_PTYPE_BSD 4
+#define SGI_PTYPE_SYSV 5
+#define SGI_PTYPE_VOLUME 6
+#define SGI_PTYPE_EFS 7
+#define SGI_PTYPE_LVOL 8
+#define SGI_PTYPE_RLVOL 9
+#define SGI_PTYPE_XFS 10
+#define SGI_PTYPE_XFSLOG 11
+#define SGI_PTYPE_XLV 12
+#define SGI_PTYPE_XVM 13
+
+/* ------------------------------------------
+ * sparc
+ */
+
+#define SPARC_BOOT_BLOCK_OFFSET 512
+#define SPARC_BOOT_BLOCK_BLOCKSIZE 512
+#define SPARC_BOOT_BLOCK_MAX_SIZE (512 * 15)
+ /* Magic string -- 32 bytes long (including the NUL) */
+#define SPARC_BBINFO_MAGIC "NetBSD/sparc bootxx 20020515"
+
+
+/* ------------------------------------------
+ * sparc64
+ */
+
+#define SPARC64_BOOT_BLOCK_OFFSET 512
+#define SPARC64_BOOT_BLOCK_BLOCKSIZE 512
+#define SPARC64_BOOT_BLOCK_MAX_SIZE (512 * 15)
+
+
+/* ------------------------------------------
+ * sun68k (sun2, sun3)
+ */
+
+#define SUN68K_BOOT_BLOCK_OFFSET 512
+#define SUN68K_BOOT_BLOCK_BLOCKSIZE 512
+#define SUN68K_BOOT_BLOCK_MAX_SIZE (512 * 15)
+ /* Magic string -- 32 bytes long (including the NUL) */
+#define SUN68K_BBINFO_MAGIC "NetBSD/sun68k bootxx 20020515"
+
+
+/* ------------------------------------------
+ * vax --
+ * VAX boot block information
+ */
+
+struct vax_boot_block {
+/* Note that these don't overlap any of the pmax boot block */
+ uint8_t pad0[2];
+ uint8_t bb_id_offset; /* offset in words to id (magic1)*/
+ uint8_t bb_mbone; /* must be one */
+ uint16_t bb_lbn_hi; /* lbn (hi word) of bootstrap */
+ uint16_t bb_lbn_low; /* lbn (low word) of bootstrap */
+ uint8_t pad1[406];
+ /* disklabel offset is 64 from base, or 56 from start of pad1 */
+
+ /* The rest of these fields are identification area and describe
+ * the secondary block for uVAX VMB.
+ */
+ uint8_t bb_magic1; /* magic number */
+ uint8_t bb_mbz1; /* must be zero */
+ uint8_t bb_pad1; /* any value */
+ uint8_t bb_sum1; /* ~(magic1 + mbz1 + pad1) */
+
+ uint8_t bb_mbz2; /* must be zero */
+ uint8_t bb_volinfo; /* volinfo */
+ uint8_t bb_pad2a; /* any value */
+ uint8_t bb_pad2b; /* any value */
+
+ uint32_t bb_size; /* size in blocks of bootstrap */
+ uint32_t bb_load; /* load offset to bootstrap */
+ uint32_t bb_entry; /* byte offset in bootstrap */
+ uint32_t bb_sum3; /* sum of previous 3 fields */
+
+ /* The rest is unused.
+ */
+ uint8_t pad2[74];
+} __packed;
+
+#define VAX_BOOT_MAGIC1 0x18 /* size of BB info? */
+#define VAX_BOOT_VOLINFO_NONE 0x00 /* no special info */
+#define VAX_BOOT_VOLINFO_SS 0x01 /* single sided */
+#define VAX_BOOT_VOLINFO_DS 0x81 /* double sided */
+
+#define VAX_BOOT_SIZE 15 /* 15 blocks */
+#define VAX_BOOT_LOAD 0 /* no load offset */
+#define VAX_BOOT_ENTRY 0x200 /* one block in */
+
+#define VAX_BOOT_BLOCK_OFFSET 0
+#define VAX_BOOT_BLOCK_BLOCKSIZE 512
+
+
+/* ------------------------------------------
+ * x68k
+ */
+
+#define X68K_BOOT_BLOCK_OFFSET 0
+#define X68K_BOOT_BLOCK_BLOCKSIZE 512
+#define X68K_BOOT_BLOCK_MAX_SIZE (512 * 16)
+ /* Magic string -- 32 bytes long (including the NUL) */
+#define X68K_BBINFO_MAGIC "NetBSD/x68k bootxx 20020601"
+
+
+#endif /* !_SYS_BOOTBLOCK_H */
diff --git a/usr.sbin/makefs/sys/clock.h b/usr.sbin/makefs/sys/clock.h
new file mode 100644
index 00000000000..da61df67af9
--- /dev/null
+++ b/usr.sbin/makefs/sys/clock.h
@@ -0,0 +1,89 @@
+/* $NetBSD: clock.h,v 1.3 2014/11/17 17:11:29 christos Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _SYS_CLOCK_H_
+#define _SYS_CLOCK_H_
+
+/* Some handy constants. */
+#define SECS_PER_MINUTE 60
+#define SECS_PER_HOUR 3600
+#define SECS_PER_DAY 86400
+#define DAYS_PER_COMMON_YEAR 365
+#define DAYS_PER_LEAP_YEAR 366
+#define SECS_PER_COMMON_YEAR (SECS_PER_DAY * DAYS_PER_COMMON_YEAR)
+#define SECS_PER_LEAP_YEAR (SECS_PER_DAY * DAYS_PER_LEAP_YEAR)
+
+/* Traditional POSIX base year */
+#define POSIX_BASE_YEAR 1970
+
+/* Some handy functions */
+static inline int
+days_in_month(int m)
+{
+ switch (m) {
+ case 2:
+ return 28;
+ case 4: case 6: case 9: case 11:
+ return 30;
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ return 31;
+ default:
+ return -1;
+ }
+}
+
+/*
+ * This inline avoids some unnecessary modulo operations
+ * as compared with the usual macro:
+ * ( ((year % 4) == 0 &&
+ * (year % 100) != 0) ||
+ * ((year % 400) == 0) )
+ * It is otherwise equivalent.
+ */
+static inline int
+is_leap_year(uint64_t year)
+{
+ if ((year & 3) != 0)
+ return 0;
+
+ if (__predict_false((year % 100) != 0))
+ return 1;
+
+ return __predict_false((year % 400) == 0);
+}
+
+static inline int
+days_per_year(uint64_t year)
+{
+ return is_leap_year(year) ? DAYS_PER_LEAP_YEAR : DAYS_PER_COMMON_YEAR;
+}
+
+#endif /* _SYS_CLOCK_H_ */
diff --git a/usr.sbin/makefs/sys/quota.h b/usr.sbin/makefs/sys/quota.h
new file mode 100644
index 00000000000..d749d7fa1aa
--- /dev/null
+++ b/usr.sbin/makefs/sys/quota.h
@@ -0,0 +1,81 @@
+/* $NetBSD: quota.h,v 1.12 2012/01/30 00:56:19 dholland Exp $ */
+/*-
+ * Copyright (c) 2010 Manuel Bouyer
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _SYS_QUOTA_H_
+#define _SYS_QUOTA_H_
+
+#include <sys/types.h>
+
+/* quota id types (entities being billed) */
+#define QUOTA_IDTYPE_USER 0
+#define QUOTA_IDTYPE_GROUP 1
+
+/* quota object types (things being limited) */
+#define QUOTA_OBJTYPE_BLOCKS 0
+#define QUOTA_OBJTYPE_FILES 1
+
+/* id value for "default" */
+#define QUOTA_DEFAULTID ((id_t)-1)
+
+/* limit value for "no limit" */
+#define QUOTA_NOLIMIT ((uint64_t)0xffffffffffffffffULL)
+
+/* time value for "no time" */
+#define QUOTA_NOTIME ((time_t)-1)
+
+/*
+ * Semantic restrictions. These are hints applications can use
+ * to help produce comprehensible error diagnostics when something
+ * unsupported is attempted.
+ */
+#define QUOTA_RESTRICT_NEEDSQUOTACHECK 0x1 /* quotacheck(8) required */
+#define QUOTA_RESTRICT_UNIFORMGRACE 0x2 /* grace time is global */
+#define QUOTA_RESTRICT_32BIT 0x4 /* values limited to 2^32 */
+#define QUOTA_RESTRICT_READONLY 0x8 /* updates not supported */
+
+
+/*
+ * Structure used to describe the key part of a quota record.
+ */
+struct quotakey {
+ int qk_idtype; /* type of id (user, group, etc.) */
+ id_t qk_id; /* actual id number */
+ int qk_objtype; /* type of fs object (blocks, files, etc.) */
+};
+
+/*
+ * Structure used to describe the value part of a quota record.
+ */
+struct quotaval {
+ uint64_t qv_hardlimit; /* absolute limit */
+ uint64_t qv_softlimit; /* overflowable limit */
+ uint64_t qv_usage; /* current usage */
+ time_t qv_expiretime; /* time when softlimit grace expires */
+ time_t qv_grace; /* allowed time for overflowing soft limit */
+};
+
+#endif /* _SYS_QUOTA_H_ */
diff --git a/usr.sbin/makefs/ufs/ffs/ffs_bswap.c b/usr.sbin/makefs/ufs/ffs/ffs_bswap.c
new file mode 100644
index 00000000000..7112d72ee95
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ffs/ffs_bswap.c
@@ -0,0 +1,266 @@
+/* $NetBSD: ffs_bswap.c,v 1.39 2015/05/20 18:21:17 riastradh Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define panic(x) printf("%s\n", (x)), abort()
+
+#define bswap16 swap16
+#define bswap32 swap32
+#define bswap64 swap64
+
+void
+ffs_sb_swap(struct fs *o, struct fs *n)
+{
+ size_t i;
+ u_int32_t *o32, *n32;
+
+ /*
+ * In order to avoid a lot of lines, as the first N fields (52)
+ * of the superblock up to fs_fmod are u_int32_t, we just loop
+ * here to convert them.
+ */
+ o32 = (u_int32_t *)o;
+ n32 = (u_int32_t *)n;
+ for (i = 0; i < offsetof(struct fs, fs_fmod) / sizeof(u_int32_t); i++)
+ n32[i] = bswap32(o32[i]);
+
+ n->fs_swuid = bswap64(o->fs_swuid);
+ n->fs_cgrotor = bswap32(o->fs_cgrotor); /* Unused */
+ n->fs_old_cpc = bswap32(o->fs_old_cpc);
+
+ /* These fields overlap with a possible location for the
+ * historic FS_DYNAMICPOSTBLFMT postbl table, and with the
+ * first half of the historic FS_42POSTBLFMT postbl table.
+ */
+ n->fs_maxbsize = bswap32(o->fs_maxbsize);
+ /* XXX journal */
+ n->fs_quota_magic = bswap32(o->fs_quota_magic);
+ for (i = 0; i < MAXQUOTAS; i++)
+ n->fs_quotafile[i] = bswap64(o->fs_quotafile[i]);
+ n->fs_sblockloc = bswap64(o->fs_sblockloc);
+ ffs_csumtotal_swap(&o->fs_cstotal, &n->fs_cstotal);
+ n->fs_time = bswap64(o->fs_time);
+ n->fs_size = bswap64(o->fs_size);
+ n->fs_dsize = bswap64(o->fs_dsize);
+ n->fs_csaddr = bswap64(o->fs_csaddr);
+ n->fs_pendingblocks = bswap64(o->fs_pendingblocks);
+ n->fs_pendinginodes = bswap32(o->fs_pendinginodes);
+
+ /* These fields overlap with the second half of the
+ * historic FS_42POSTBLFMT postbl table
+ */
+ for (i = 0; i < FSMAXSNAP; i++)
+ n->fs_snapinum[i] = bswap32(o->fs_snapinum[i]);
+ n->fs_avgfilesize = bswap32(o->fs_avgfilesize);
+ n->fs_avgfpdir = bswap32(o->fs_avgfpdir);
+ /* fs_sparecon[28] - ignore for now */
+ n->fs_flags = bswap32(o->fs_flags);
+ n->fs_contigsumsize = bswap32(o->fs_contigsumsize);
+ n->fs_maxsymlinklen = bswap32(o->fs_maxsymlinklen);
+ n->fs_old_inodefmt = bswap32(o->fs_old_inodefmt);
+ n->fs_maxfilesize = bswap64(o->fs_maxfilesize);
+ n->fs_qbmask = bswap64(o->fs_qbmask);
+ n->fs_qfmask = bswap64(o->fs_qfmask);
+ n->fs_state = bswap32(o->fs_state);
+ n->fs_old_postblformat = bswap32(o->fs_old_postblformat);
+ n->fs_old_nrpos = bswap32(o->fs_old_nrpos);
+ n->fs_old_postbloff = bswap32(o->fs_old_postbloff);
+ n->fs_old_rotbloff = bswap32(o->fs_old_rotbloff);
+
+ n->fs_magic = bswap32(o->fs_magic);
+}
+
+void
+ffs_dinode1_swap(struct ufs1_dinode *o, struct ufs1_dinode *n)
+{
+
+ n->di_mode = bswap16(o->di_mode);
+ n->di_nlink = bswap16(o->di_nlink);
+ n->di_oldids[0] = bswap16(o->di_oldids[0]);
+ n->di_oldids[1] = bswap16(o->di_oldids[1]);
+ n->di_size = bswap64(o->di_size);
+ n->di_atime = bswap32(o->di_atime);
+ n->di_atimensec = bswap32(o->di_atimensec);
+ n->di_mtime = bswap32(o->di_mtime);
+ n->di_mtimensec = bswap32(o->di_mtimensec);
+ n->di_ctime = bswap32(o->di_ctime);
+ n->di_ctimensec = bswap32(o->di_ctimensec);
+ memcpy(n->di_db, o->di_db, sizeof(n->di_db));
+ memcpy(n->di_ib, o->di_ib, sizeof(n->di_ib));
+ n->di_flags = bswap32(o->di_flags);
+ n->di_blocks = bswap32(o->di_blocks);
+ n->di_gen = bswap32(o->di_gen);
+ n->di_uid = bswap32(o->di_uid);
+ n->di_gid = bswap32(o->di_gid);
+}
+
+void
+ffs_dinode2_swap(struct ufs2_dinode *o, struct ufs2_dinode *n)
+{
+ n->di_mode = bswap16(o->di_mode);
+ n->di_nlink = bswap16(o->di_nlink);
+ n->di_uid = bswap32(o->di_uid);
+ n->di_gid = bswap32(o->di_gid);
+ n->di_blksize = bswap32(o->di_blksize);
+ n->di_size = bswap64(o->di_size);
+ n->di_blocks = bswap64(o->di_blocks);
+ n->di_atime = bswap64(o->di_atime);
+ n->di_atimensec = bswap32(o->di_atimensec);
+ n->di_mtime = bswap64(o->di_mtime);
+ n->di_mtimensec = bswap32(o->di_mtimensec);
+ n->di_ctime = bswap64(o->di_ctime);
+ n->di_ctimensec = bswap32(o->di_ctimensec);
+ n->di_birthtime = bswap64(o->di_birthtime);
+ n->di_birthnsec = bswap32(o->di_birthnsec);
+ n->di_gen = bswap32(o->di_gen);
+ n->di_kernflags = bswap32(o->di_kernflags);
+ n->di_flags = bswap32(o->di_flags);
+ n->di_extsize = bswap32(o->di_extsize);
+ memcpy(n->di_extb, o->di_extb, sizeof(n->di_extb));
+ memcpy(n->di_db, o->di_db, sizeof(n->di_db));
+ memcpy(n->di_ib, o->di_ib, sizeof(n->di_ib));
+}
+
+void
+ffs_csum_swap(struct csum *o, struct csum *n, int size)
+{
+ size_t i;
+ u_int32_t *oint, *nint;
+
+ oint = (u_int32_t*)o;
+ nint = (u_int32_t*)n;
+
+ for (i = 0; i < size / sizeof(u_int32_t); i++)
+ nint[i] = bswap32(oint[i]);
+}
+
+void
+ffs_csumtotal_swap(struct csum_total *o, struct csum_total *n)
+{
+ n->cs_ndir = bswap64(o->cs_ndir);
+ n->cs_nbfree = bswap64(o->cs_nbfree);
+ n->cs_nifree = bswap64(o->cs_nifree);
+ n->cs_nffree = bswap64(o->cs_nffree);
+}
+
+/*
+ * Note that ffs_cg_swap may be called with o == n.
+ */
+void
+ffs_cg_swap(struct cg *o, struct cg *n, struct fs *fs)
+{
+ int i;
+ u_int32_t *n32, *o32;
+ u_int16_t *n16, *o16;
+ int32_t btotoff, boff, clustersumoff;
+
+ n->cg_firstfield = bswap32(o->cg_firstfield);
+ n->cg_magic = bswap32(o->cg_magic);
+ n->cg_old_time = bswap32(o->cg_old_time);
+ n->cg_cgx = bswap32(o->cg_cgx);
+ n->cg_old_ncyl = bswap16(o->cg_old_ncyl);
+ n->cg_old_niblk = bswap16(o->cg_old_niblk);
+ n->cg_ndblk = bswap32(o->cg_ndblk);
+ n->cg_cs.cs_ndir = bswap32(o->cg_cs.cs_ndir);
+ n->cg_cs.cs_nbfree = bswap32(o->cg_cs.cs_nbfree);
+ n->cg_cs.cs_nifree = bswap32(o->cg_cs.cs_nifree);
+ n->cg_cs.cs_nffree = bswap32(o->cg_cs.cs_nffree);
+ n->cg_rotor = bswap32(o->cg_rotor);
+ n->cg_frotor = bswap32(o->cg_frotor);
+ n->cg_irotor = bswap32(o->cg_irotor);
+ for (i = 0; i < MAXFRAG; i++)
+ n->cg_frsum[i] = bswap32(o->cg_frsum[i]);
+
+ if ((fs->fs_magic != FS_UFS2_MAGIC) &&
+ (fs->fs_old_postblformat == FS_42POSTBLFMT)) { /* old format */
+ struct ocg *on, *oo;
+ int j;
+ on = (struct ocg *)n;
+ oo = (struct ocg *)o;
+
+ for (i = 0; i < 32; i++) {
+ on->cg_btot[i] = bswap32(oo->cg_btot[i]);
+ for (j = 0; j < 8; j++)
+ on->cg_b[i][j] = bswap16(oo->cg_b[i][j]);
+ }
+ memmove(on->cg_iused, oo->cg_iused, 256);
+ on->cg_magic = bswap32(oo->cg_magic);
+ } else { /* new format */
+
+ n->cg_old_btotoff = bswap32(o->cg_old_btotoff);
+ n->cg_old_boff = bswap32(o->cg_old_boff);
+ n->cg_iusedoff = bswap32(o->cg_iusedoff);
+ n->cg_freeoff = bswap32(o->cg_freeoff);
+ n->cg_nextfreeoff = bswap32(o->cg_nextfreeoff);
+ n->cg_clustersumoff = bswap32(o->cg_clustersumoff);
+ n->cg_clusteroff = bswap32(o->cg_clusteroff);
+ n->cg_nclusterblks = bswap32(o->cg_nclusterblks);
+ n->cg_niblk = bswap32(o->cg_niblk);
+ n->cg_initediblk = bswap32(o->cg_initediblk);
+ n->cg_time = bswap64(o->cg_time);
+
+ if (n->cg_magic == CG_MAGIC) {
+ btotoff = n->cg_old_btotoff;
+ boff = n->cg_old_boff;
+ clustersumoff = n->cg_clustersumoff;
+ } else {
+ btotoff = bswap32(n->cg_old_btotoff);
+ boff = bswap32(n->cg_old_boff);
+ clustersumoff = bswap32(n->cg_clustersumoff);
+ }
+
+ n32 = (u_int32_t *)((u_int8_t *)n + clustersumoff);
+ o32 = (u_int32_t *)((u_int8_t *)o + clustersumoff);
+ for (i = 1; i < fs->fs_contigsumsize + 1; i++)
+ n32[i] = bswap32(o32[i]);
+
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ return;
+
+ n32 = (u_int32_t *)((u_int8_t *)n + btotoff);
+ o32 = (u_int32_t *)((u_int8_t *)o + btotoff);
+ n16 = (u_int16_t *)((u_int8_t *)n + boff);
+ o16 = (u_int16_t *)((u_int8_t *)o + boff);
+
+ for (i = 0; i < fs->fs_old_cpg; i++)
+ n32[i] = bswap32(o32[i]);
+
+ for (i = 0; i < fs->fs_old_cpg * fs->fs_old_nrpos; i++)
+ n16[i] = bswap16(o16[i]);
+ }
+}
diff --git a/usr.sbin/makefs/ufs/ffs/ffs_extern.h b/usr.sbin/makefs/ufs/ffs/ffs_extern.h
new file mode 100644
index 00000000000..db52e9e658d
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ffs/ffs_extern.h
@@ -0,0 +1,103 @@
+/* $NetBSD: ffs_extern.h,v 1.83 2016/10/01 13:15:45 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * 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. 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.
+ *
+ * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95
+ */
+
+#ifndef _UFS_FFS_FFS_EXTERN_H_
+#define _UFS_FFS_FFS_EXTERN_H_
+
+/*
+ * Sysctl values for the fast filesystem.
+ */
+#define FFS_CLUSTERREAD 1 /* cluster reading enabled */
+#define FFS_CLUSTERWRITE 2 /* cluster writing enabled */
+#define FFS_REALLOCBLKS 3 /* block reallocation enabled */
+#define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */
+#define FFS_LOG_CHANGEOPT 5 /* log optimalization strategy change */
+#define FFS_EXTATTR_AUTOCREATE 6 /* size for backing file autocreation */
+#define FFS_MAXID 7 /* number of valid ffs ids */
+
+struct buf;
+struct fid;
+struct fs;
+struct inode;
+struct ufs1_dinode;
+struct ufs2_dinode;
+struct mount;
+struct nameidata;
+struct lwp;
+struct statvfs;
+struct timeval;
+struct timespec;
+struct ufsmount;
+struct uio;
+struct vnode;
+struct mbuf;
+struct cg;
+
+
+__BEGIN_DECLS
+
+
+/* ffs_appleufs.c */
+struct appleufslabel;
+u_int16_t ffs_appleufs_cksum(const struct appleufslabel *);
+int ffs_appleufs_validate(const char*, const struct appleufslabel *,
+ struct appleufslabel *);
+void ffs_appleufs_set(struct appleufslabel *, const char *, time_t,
+ uint64_t);
+
+/* ffs_bswap.c */
+void ffs_sb_swap(struct fs*, struct fs *);
+void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *);
+void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *);
+struct csum;
+void ffs_csum_swap(struct csum *, struct csum *, int);
+struct csum_total;
+void ffs_csumtotal_swap(struct csum_total *, struct csum_total *);
+void ffs_cg_swap(struct cg *, struct cg *, struct fs *);
+
+/* ffs_subr.c */
+void ffs_fragacct(struct fs *, int, int32_t[], int, int);
+int ffs_isblock(struct fs *, u_char *, int32_t);
+int ffs_isfreeblock(struct fs *, u_char *, int32_t);
+void ffs_clrblock(struct fs *, u_char *, int32_t);
+void ffs_setblock(struct fs *, u_char *, int32_t);
+void ffs_itimes(struct inode *, const struct timespec *,
+ const struct timespec *, const struct timespec *);
+void ffs_clusteracct(struct fs *, struct cg *, int32_t, int);
+
+/* ffs_quota2.c */
+int ffs_quota2_mount(struct mount *);
+
+__END_DECLS
+
+#endif /* !_UFS_FFS_FFS_EXTERN_H_ */
diff --git a/usr.sbin/makefs/ufs/ffs/ffs_subr.c b/usr.sbin/makefs/ufs/ffs/ffs_subr.c
new file mode 100644
index 00000000000..2fe62a0ae97
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ffs/ffs_subr.c
@@ -0,0 +1,278 @@
+/* $NetBSD: ffs_subr.c,v 1.49 2016/05/07 11:59:08 maxv Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 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. 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.
+ *
+ * @(#)ffs_subr.c 8.5 (Berkeley) 3/21/95
+ */
+
+#include <sys/param.h>
+
+/* in ffs_tables.c */
+extern const int inside[], around[];
+extern const u_char * const fragtbl[];
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
+#include <ufs/ufs/ufs_bswap.h>
+
+#include <ufs/ufs/dinode.h>
+void panic(const char *, ...)
+ __attribute__((__noreturn__,__format__(__printf__,1,2)));
+
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+void
+ffs_fragacct(struct fs *fs, int fragmap, int32_t fraglist[], int cnt,
+ int needswap)
+{
+ int inblk;
+ int field, subfield;
+ int siz, pos;
+
+ inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
+ fragmap <<= 1;
+ for (siz = 1; siz < fs->fs_frag; siz++) {
+ if ((inblk & (1 << (siz + (fs->fs_frag & (NBBY - 1))))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] = ufs_rw32(
+ ufs_rw32(fraglist[siz], needswap) + cnt,
+ needswap);
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+/*
+ * block operations
+ *
+ * check if a block is available
+ * returns true if all the correponding bits in the free map are 1
+ * returns false if any corresponding bit in the free map is 0
+ */
+int
+ffs_isblock(struct fs *fs, u_char *cp, int32_t h)
+{
+ u_char mask;
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ return (cp[h] == 0xff);
+ case 2:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 1:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 0:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ panic("ffs_isblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * check if a block is completely allocated
+ * returns true if all the corresponding bits in the free map are 0
+ * returns false if any corresponding bit in the free map is 1
+ */
+int
+ffs_isfreeblock(struct fs *fs, u_char *cp, int32_t h)
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ return (cp[h] == 0);
+ case 2:
+ return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0);
+ case 1:
+ return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0);
+ case 0:
+ return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0);
+ default:
+ panic("ffs_isfreeblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+void
+ffs_clrblock(struct fs *fs, u_char *cp, int32_t h)
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ cp[h] = 0;
+ return;
+ case 2:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 1:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 0:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_clrblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * put a block into the map
+ */
+void
+ffs_setblock(struct fs *fs, u_char *cp, int32_t h)
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ cp[h] = 0xff;
+ return;
+ case 2:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 1:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 0:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_setblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * Update the cluster map because of an allocation or free.
+ *
+ * Cnt == 1 means free; cnt == -1 means allocating.
+ */
+void
+ffs_clusteracct(struct fs *fs, struct cg *cgp, int32_t blkno, int cnt)
+{
+ int32_t *sump;
+ int32_t *lp;
+ u_char *freemapp, *mapp;
+ int i, start, end, forw, back, map, bit;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ /* KASSERT(mutex_owned(&ump->um_lock)); */
+
+ if (fs->fs_contigsumsize <= 0)
+ return;
+ freemapp = cg_clustersfree(cgp, needswap);
+ sump = cg_clustersum(cgp, needswap);
+ /*
+ * Allocate or clear the actual block.
+ */
+ if (cnt > 0)
+ setbit(freemapp, blkno);
+ else
+ clrbit(freemapp, blkno);
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + fs->fs_contigsumsize;
+ if ((uint32_t)end >= ufs_rw32(cgp->cg_nclusterblks, needswap))
+ end = ufs_rw32(cgp->cg_nclusterblks, needswap);
+ mapp = &freemapp[start / NBBY];
+ map = *mapp++;
+ bit = 1 << (start % NBBY);
+ for (i = start; i < end; i++) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ forw = i - start;
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - fs->fs_contigsumsize;
+ if (end < 0)
+ end = -1;
+ mapp = &freemapp[start / NBBY];
+ map = *mapp--;
+ bit = 1 << (start % NBBY);
+ for (i = start; i > end; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << (NBBY - 1);
+ }
+ }
+ back = start - i;
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > fs->fs_contigsumsize)
+ i = fs->fs_contigsumsize;
+ ufs_add32(sump[i], cnt, needswap);
+ if (back > 0)
+ ufs_add32(sump[back], -cnt, needswap);
+ if (forw > 0)
+ ufs_add32(sump[forw], -cnt, needswap);
+
+ /*
+ * Update cluster summary information.
+ */
+ lp = &sump[fs->fs_contigsumsize];
+ for (i = fs->fs_contigsumsize; i > 0; i--)
+ if (ufs_rw32(*lp--, needswap) > 0)
+ break;
+}
diff --git a/usr.sbin/makefs/ufs/ffs/ffs_tables.c b/usr.sbin/makefs/ufs/ffs/ffs_tables.c
new file mode 100644
index 00000000000..9ed90194f77
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ffs/ffs_tables.c
@@ -0,0 +1,136 @@
+/* $NetBSD: ffs_tables.c,v 1.9 2005/12/11 12:25:25 christos Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 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. 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.
+ *
+ * @(#)ffs_tables.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/param.h>
+
+#include <ufs/ffs/fs.h>
+
+/*
+ * Bit patterns for identifying fragments in the block map
+ * used as ((map & around) == inside)
+ */
+const int around[9] = {
+ 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
+};
+const int inside[9] = {
+ 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
+};
+
+/*
+ * Given a block map bit pattern, the frag tables tell whether a
+ * particular size fragment is available.
+ *
+ * used as:
+ * if ((1 << (size - 1)) & fragtbl[fs->fs_frag][map] {
+ * at least one fragment of the indicated size is available
+ * }
+ *
+ * These tables are used by the scanc instruction on the VAX to
+ * quickly find an appropriate fragment.
+ */
+const u_char fragtbl124[256] = {
+ 0x00, 0x16, 0x16, 0x2a, 0x16, 0x16, 0x26, 0x4e,
+ 0x16, 0x16, 0x16, 0x3e, 0x2a, 0x3e, 0x4e, 0x8a,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x26, 0x36, 0x36, 0x2e, 0x36, 0x36, 0x26, 0x6e,
+ 0x36, 0x36, 0x36, 0x3e, 0x2e, 0x3e, 0x6e, 0xae,
+ 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e,
+ 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e,
+ 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe,
+ 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe,
+ 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e,
+ 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce,
+ 0x8a, 0x9e, 0x9e, 0xaa, 0x9e, 0x9e, 0xae, 0xce,
+ 0x9e, 0x9e, 0x9e, 0xbe, 0xaa, 0xbe, 0xce, 0x8a,
+};
+
+const u_char fragtbl8[256] = {
+ 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04,
+ 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04,
+ 0x08, 0x09, 0x09, 0x0a, 0x10, 0x11, 0x20, 0x40,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05,
+ 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
+ 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06,
+ 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
+ 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0a, 0x12,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04,
+ 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0c,
+ 0x08, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x0a, 0x0c,
+ 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
+};
+
+/*
+ * The actual fragtbl array.
+ */
+const u_char * const fragtbl[MAXFRAG + 1] = {
+ 0, fragtbl124, fragtbl124, 0, fragtbl124, 0, 0, 0, fragtbl8,
+};
diff --git a/usr.sbin/makefs/ufs/ffs/fs.h b/usr.sbin/makefs/ufs/ffs/fs.h
new file mode 100644
index 00000000000..a5a2ebdb8c5
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ffs/fs.h
@@ -0,0 +1,743 @@
+/* $NetBSD: fs.h,v 1.66 2015/02/14 09:06:11 maxv Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 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. 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.
+ *
+ * @(#)fs.h 8.13 (Berkeley) 3/21/95
+ */
+
+/*
+ * NOTE: COORDINATE ON-DISK FORMAT CHANGES WITH THE FREEBSD PROJECT.
+ */
+
+#ifndef _UFS_FFS_FS_H_
+#define _UFS_FFS_FS_H_
+
+/*
+ * Each disk drive contains some number of file systems.
+ * A file system consists of a number of cylinder groups.
+ * Each cylinder group has inodes and data.
+ *
+ * A file system is described by its super-block, which in turn
+ * describes the cylinder groups. The super-block is critical
+ * data and is replicated in each cylinder group to protect against
+ * catastrophic loss. This is done at `newfs' time and the critical
+ * super-block data does not change, so the copies need not be
+ * referenced further unless disaster strikes.
+ *
+ * For file system fs, the offsets of the various blocks of interest
+ * are given in the super block as:
+ * [fs->fs_sblkno] Super-block
+ * [fs->fs_cblkno] Cylinder group block
+ * [fs->fs_iblkno] Inode blocks
+ * [fs->fs_dblkno] Data blocks
+ * The beginning of cylinder group cg in fs, is given by
+ * the ``cgbase(fs, cg)'' macro.
+ *
+ * Depending on the architecture and the media, the superblock may
+ * reside in any one of four places. For tiny media where every block
+ * counts, it is placed at the very front of the partition. Historically,
+ * UFS1 placed it 8K from the front to leave room for the disk label and
+ * a small bootstrap. For UFS2 it got moved to 64K from the front to leave
+ * room for the disk label and a bigger bootstrap, and for really piggy
+ * systems we check at 256K from the front if the first three fail. In
+ * all cases the size of the superblock will be SBLOCKSIZE. All values are
+ * given in byte-offset form, so they do not imply a sector size. The
+ * SBLOCKSEARCH specifies the order in which the locations should be searched.
+ *
+ * Unfortunately the UFS2/FFSv2 change was done without adequate consideration
+ * of backward compatibility. In particular 'newfs' for a FFSv2 partition
+ * must overwrite any old FFSv1 superblock at 8k, and preferably as many
+ * of the alternates as it can find - otherwise attempting to mount on a
+ * system that only supports FFSv1 is likely to succeed!.
+ * For a small FFSv1 filesystem, an old FFSv2 superblock can be left on
+ * the disk, and a system that tries to find an FFSv2 filesystem in preference
+ * to and FFSv1 one (as NetBSD does) can mount the old FFSv2 filesystem.
+ * As a added bonus, the 'first alternate' superblock of a FFSv1 filesystem
+ * with 64k blocks is at 64k - just where the code looks first when playing
+ * 'hunt the superblock'.
+ *
+ * The ffsv2 superblock layout (which might contain an ffsv1 filesystem)
+ * can be detected by checking for sb->fs_old_flags & FS_FLAGS_UPDATED.
+ * This is the default superblock type for NetBSD since ffsv2 support was added.
+ */
+#define BBSIZE 8192
+#define BBOFF ((off_t)(0))
+#define BBLOCK ((daddr_t)(0))
+
+#define SBLOCK_FLOPPY 0
+#define SBLOCK_UFS1 8192
+#define SBLOCK_UFS2 65536
+#define SBLOCK_PIGGY 262144
+#define SBLOCKSIZE 8192
+/*
+ * NB: Do not, under any circumstances, look for an ffsv1 filesystem at
+ * SBLOCK_UFS2. Doing so will find the wrong superblock for filesystems
+ * with a 64k block size.
+ */
+#define SBLOCKSEARCH \
+ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 }
+
+/*
+ * Max number of fragments per block. This value is NOT tweakable.
+ */
+#define MAXFRAG 8
+
+
+
+/*
+ * Addresses stored in inodes are capable of addressing fragments
+ * of `blocks'. File system blocks of at most size MAXBSIZE can
+ * be optionally broken into 2, 4, or 8 pieces, each of which is
+ * addressable; these pieces may be DEV_BSIZE, or some multiple of
+ * a DEV_BSIZE unit.
+ *
+ * Large files consist of exclusively large data blocks. To avoid
+ * undue wasted disk space, the last data block of a small file may be
+ * allocated as only as many fragments of a large block as are
+ * necessary. The file system format retains only a single pointer
+ * to such a fragment, which is a piece of a single large block that
+ * has been divided. The size of such a fragment is determinable from
+ * information in the inode, using the ``ffs_blksize(fs, ip, lbn)'' macro.
+ *
+ * The file system records space availability at the fragment level;
+ * to determine block availability, aligned fragments are examined.
+ */
+
+/*
+ * MINBSIZE is the smallest allowable block size.
+ * In order to insure that it is possible to create files of size
+ * 2^32 with only two levels of indirection, MINBSIZE is set to 4096.
+ * MINBSIZE must be big enough to hold a cylinder group block,
+ * thus changes to (struct cg) must keep its size within MINBSIZE.
+ * Note that super blocks are always of size SBSIZE,
+ * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE.
+ */
+#define MINBSIZE 4096
+
+/*
+ * The path name on which the file system is mounted is maintained
+ * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in
+ * the super block for this name.
+ */
+#define MAXMNTLEN 468
+
+/*
+ * The volume name for this filesystem is maintained in fs_volname.
+ * MAXVOLLEN defines the length of the buffer allocated.
+ * This space used to be part of of fs_fsmnt.
+ */
+#define MAXVOLLEN 32
+
+/*
+ * There is a 128-byte region in the superblock reserved for in-core
+ * pointers to summary information. Originally this included an array
+ * of pointers to blocks of struct csum; now there are just four
+ * pointers and the remaining space is padded with fs_ocsp[].
+ * NOCSPTRS determines the size of this padding. One pointer (fs_csp)
+ * is taken away to point to a contiguous array of struct csum for
+ * all cylinder groups; a second (fs_maxcluster) points to an array
+ * of cluster sizes that is computed as cylinder groups are inspected;
+ * the third (fs_contigdirs) points to an array that tracks the
+ * creation of new directories; and the fourth (fs_active) is used
+ * by snapshots.
+ */
+#define NOCSPTRS ((128 / sizeof(void *)) - 4)
+
+/*
+ * A summary of contiguous blocks of various sizes is maintained
+ * in each cylinder group. Normally this is set by the initial
+ * value of fs_maxcontig. To conserve space, a maximum summary size
+ * is set by FS_MAXCONTIG.
+ */
+#define FS_MAXCONTIG 16
+
+/*
+ * The maximum number of snapshot nodes that can be associated
+ * with each filesystem. This limit affects only the number of
+ * snapshot files that can be recorded within the superblock so
+ * that they can be found when the filesystem is mounted. However,
+ * maintaining too many will slow the filesystem performance, so
+ * having this limit is a good idea.
+ */
+#define FSMAXSNAP 20
+
+/*
+ * Used to identify special blocks in snapshots:
+ *
+ * BLK_NOCOPY - A block that was unallocated at the time the snapshot
+ * was taken, hence does not need to be copied when written.
+ * BLK_SNAP - A block held by another snapshot that is not needed by this
+ * snapshot. When the other snapshot is freed, the BLK_SNAP entries
+ * are converted to BLK_NOCOPY. These are needed to allow fsck to
+ * identify blocks that are in use by other snapshots (which are
+ * expunged from this snapshot).
+ */
+#define BLK_NOCOPY ((daddr_t)(1))
+#define BLK_SNAP ((daddr_t)(2))
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 95% and 100% full; thus the minimum default
+ * value of fs_minfree is 5%. However, to get good clustering
+ * performance, 10% is a better choice. This value is used only
+ * when creating a file system and can be overridden from the
+ * command line. By default we choose to optimize for time.
+ */
+#define MINFREE 5
+#define DEFAULTOPT FS_OPTTIME
+
+/*
+ * Grigoriy Orlov <gluk@ptci.ru> has done some extensive work to fine
+ * tune the layout preferences for directories within a filesystem.
+ * His algorithm can be tuned by adjusting the following parameters
+ * which tell the system the average file size and the average number
+ * of files per directory. These defaults are well selected for typical
+ * filesystems, but may need to be tuned for odd cases like filesystems
+ * being used for squid caches or news spools.
+ */
+#define AVFILESIZ 16384 /* expected average file size */
+#define AFPDIR 64 /* expected number of files per directory */
+
+/*
+ * Per cylinder group information; summarized in blocks allocated
+ * from first cylinder group data blocks. These blocks have to be
+ * read in from fs_csaddr (size fs_cssize) in addition to the
+ * super block.
+ */
+struct csum {
+ int32_t cs_ndir; /* number of directories */
+ int32_t cs_nbfree; /* number of free blocks */
+ int32_t cs_nifree; /* number of free inodes */
+ int32_t cs_nffree; /* number of free frags */
+};
+
+struct csum_total {
+ int64_t cs_ndir; /* number of directories */
+ int64_t cs_nbfree; /* number of free blocks */
+ int64_t cs_nifree; /* number of free inodes */
+ int64_t cs_nffree; /* number of free frags */
+ int64_t cs_spare[4]; /* future expansion */
+};
+
+
+/*
+ * Super block for an FFS file system in memory.
+ */
+struct fs {
+ int32_t fs_firstfield; /* historic file system linked list, */
+ int32_t fs_unused_1; /* used for incore super blocks */
+ int32_t fs_sblkno; /* addr of super-block in filesys */
+ int32_t fs_cblkno; /* offset of cyl-block in filesys */
+ int32_t fs_iblkno; /* offset of inode-blocks in filesys */
+ int32_t fs_dblkno; /* offset of first data after cg */
+ int32_t fs_old_cgoffset; /* cylinder group offset in cylinder */
+ int32_t fs_old_cgmask; /* used to calc mod fs_ntrak */
+ int32_t fs_old_time; /* last time written */
+ int32_t fs_old_size; /* number of blocks in fs */
+ int32_t fs_old_dsize; /* number of data blocks in fs */
+ int32_t fs_ncg; /* number of cylinder groups */
+ int32_t fs_bsize; /* size of basic blocks in fs */
+ int32_t fs_fsize; /* size of frag blocks in fs */
+ int32_t fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ int32_t fs_minfree; /* minimum percentage of free blocks */
+ int32_t fs_old_rotdelay; /* num of ms for optimal next block */
+ int32_t fs_old_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */
+ int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */
+ int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */
+ int32_t fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ int32_t fs_maxcontig; /* max number of contiguous blks */
+ int32_t fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ int32_t fs_fragshift; /* block to frag shift */
+ int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ int32_t fs_sbsize; /* actual size of super block */
+ int32_t fs_spare1[2]; /* old fs_csmask */
+ /* old fs_csshift */
+ int32_t fs_nindir; /* value of FFS_NINDIR */
+ int32_t fs_inopb; /* value of FFS_INOPB */
+ int32_t fs_old_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ int32_t fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ int32_t fs_old_npsect; /* # sectors/track including spares */
+ int32_t fs_old_interleave; /* hardware sector interleave */
+ int32_t fs_old_trackskew; /* sector 0 skew, per track */
+/* fs_id takes the space of the unused fs_headswitch and fs_trkseek fields */
+ int32_t fs_id[2]; /* unique file system id */
+/* sizes determined by number of cylinder groups and their sizes */
+ int32_t fs_old_csaddr; /* blk addr of cyl grp summary area */
+ int32_t fs_cssize; /* size of cyl grp summary area */
+ int32_t fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ int32_t fs_spare2; /* old fs_ntrak */
+ int32_t fs_old_nsect; /* sectors per track */
+ int32_t fs_old_spc; /* sectors per cylinder */
+ int32_t fs_old_ncyl; /* cylinders in file system */
+ int32_t fs_old_cpg; /* cylinders per group */
+ int32_t fs_ipg; /* inodes per group */
+ int32_t fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct csum fs_old_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ int8_t fs_fmod; /* super block modified flag */
+ uint8_t fs_clean; /* file system is clean flag */
+ int8_t fs_ronly; /* mounted read-only flag */
+ uint8_t fs_old_flags; /* see FS_ flags below */
+ u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */
+ u_char fs_volname[MAXVOLLEN]; /* volume name */
+ uint64_t fs_swuid; /* system-wide uid */
+ int32_t fs_pad;
+/* these fields retain the current block allocation info */
+ int32_t fs_cgrotor; /* last cg searched (UNUSED) */
+ void *fs_ocsp[NOCSPTRS]; /* padding; was list of fs_cs buffers */
+ u_int8_t *fs_contigdirs; /* # of contiguously allocated dirs */
+ struct csum *fs_csp; /* cg summary info buffer for fs_cs */
+ int32_t *fs_maxcluster; /* max cluster in each cyl group */
+ u_char *fs_active; /* used by snapshots to track fs */
+ int32_t fs_old_cpc; /* cyl per cycle in postbl */
+/* this area is otherwise allocated unless fs_old_flags & FS_FLAGS_UPDATED */
+ int32_t fs_maxbsize; /* maximum blocking factor permitted */
+ uint8_t fs_journal_version; /* journal format version */
+ uint8_t fs_journal_location; /* journal location type */
+ uint8_t fs_journal_reserved[2];/* reserved for future use */
+ uint32_t fs_journal_flags; /* journal flags */
+ uint64_t fs_journallocs[4]; /* location info for journal */
+ uint32_t fs_quota_magic; /* see quota2.h */
+ uint8_t fs_quota_flags; /* see quota2.h */
+ uint8_t fs_quota_reserved[3];
+ uint64_t fs_quotafile[2]; /* pointer to quota inodes */
+ int64_t fs_sparecon64[9]; /* reserved for future use */
+ int64_t fs_sblockloc; /* byte offset of standard superblock */
+ struct csum_total fs_cstotal; /* cylinder summary information */
+ int64_t fs_time; /* last time written */
+ int64_t fs_size; /* number of blocks in fs */
+ int64_t fs_dsize; /* number of data blocks in fs */
+ int64_t fs_csaddr; /* blk addr of cyl grp summary area */
+ int64_t fs_pendingblocks; /* blocks in process of being freed */
+ int32_t fs_pendinginodes; /* inodes in process of being freed */
+ int32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */
+/* back to stuff that has been around a while */
+ int32_t fs_avgfilesize; /* expected average file size */
+ int32_t fs_avgfpdir; /* expected # of files per directory */
+ int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */
+ int32_t fs_sparecon32[26]; /* reserved for future constants */
+ uint32_t fs_flags; /* see FS_ flags below */
+/* back to stuff that has been around a while (again) */
+ int32_t fs_contigsumsize; /* size of cluster summary array */
+ int32_t fs_maxsymlinklen; /* max length of an internal symlink */
+ int32_t fs_old_inodefmt; /* format of on-disk inodes */
+ u_int64_t fs_maxfilesize; /* maximum representable file size */
+ int64_t fs_qbmask; /* ~fs_bmask for use with 64-bit size */
+ int64_t fs_qfmask; /* ~fs_fmask for use with 64-bit size */
+ int32_t fs_state; /* validate fs_clean field (UNUSED) */
+ int32_t fs_old_postblformat; /* format of positional layout tables */
+ int32_t fs_old_nrpos; /* number of rotational positions */
+ int32_t fs_spare5[2]; /* old fs_postbloff */
+ /* old fs_rotbloff */
+ int32_t fs_magic; /* magic number */
+};
+
+#define fs_old_postbloff fs_spare5[0]
+#define fs_old_rotbloff fs_spare5[1]
+#define fs_old_postbl_start fs_maxbsize
+#define fs_old_headswitch fs_id[0]
+#define fs_old_trkseek fs_id[1]
+#define fs_old_csmask fs_spare1[0]
+#define fs_old_csshift fs_spare1[1]
+
+#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+
+#define old_fs_postbl(fs_, cylno, opostblsave) \
+ ((((fs_)->fs_old_postblformat == FS_42POSTBLFMT) || \
+ ((fs_)->fs_old_postbloff == offsetof(struct fs, fs_old_postbl_start))) \
+ ? ((int16_t *)(opostblsave) + (cylno) * (fs_)->fs_old_nrpos) \
+ : ((int16_t *)((uint8_t *)(fs_) + \
+ (fs_)->fs_old_postbloff) + (cylno) * (fs_)->fs_old_nrpos))
+#define old_fs_rotbl(fs) \
+ (((fs)->fs_old_postblformat == FS_42POSTBLFMT) \
+ ? ((uint8_t *)(&(fs)->fs_magic+1)) \
+ : ((uint8_t *)((uint8_t *)(fs) + (fs)->fs_old_rotbloff)))
+
+/*
+ * File system identification
+ */
+#define FS_UFS1_MAGIC 0x011954 /* UFS1 fast file system magic number */
+#define FS_UFS2_MAGIC 0x19540119 /* UFS2 fast file system magic number */
+#define FS_UFS1_MAGIC_SWAPPED 0x54190100
+#define FS_UFS2_MAGIC_SWAPPED 0x19015419
+#define FS_OKAY 0x7c269d38 /* superblock checksum */
+#define FS_42INODEFMT -1 /* 4.2BSD inode format */
+#define FS_44INODEFMT 2 /* 4.4BSD inode format */
+
+/*
+ * File system clean flags
+ */
+#define FS_ISCLEAN 0x01
+#define FS_WASCLEAN 0x02
+
+/*
+ * Preference for optimization.
+ */
+#define FS_OPTTIME 0 /* minimize allocation time */
+#define FS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * File system flags
+ */
+#define FS_UNCLEAN 0x001 /* file system not clean at mount (unused) */
+#define FS_DOSOFTDEP 0x002 /* file system using soft dependencies */
+#define FS_NEEDSFSCK 0x004 /* needs sync fsck (FreeBSD compat, unused) */
+#define FS_SUJ 0x008 /* file system using journaled softupdates */
+#define FS_ACLS 0x010 /* file system has ACLs enabled */
+#define FS_MULTILABEL 0x020 /* file system is MAC multi-label */
+#define FS_GJOURNAL 0x40 /* gjournaled file system */
+#define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */
+#define FS_DOWAPBL 0x100 /* Write ahead physical block logging */
+/* FS_NFS4ACLS 0x100 file system has NFSv4 ACLs enabled (FBSD) */
+#define FS_DOQUOTA2 0x200 /* in-filesystem quotas */
+/* FS_INDEXDIRS 0x200 kernel supports indexed directories (FBSD)*/
+#define FS_TRIM 0x400 /* discard deleted blocks in storage layer */
+
+/* File system flags that are ok for NetBSD if set in fs_flags */
+#define FS_KNOWN_FLAGS (FS_DOSOFTDEP | FS_DOWAPBL | FS_DOQUOTA2)
+
+/*
+ * Macros to access bits in the fs_active array.
+ */
+#define ACTIVECG_SET(fs, cg) \
+ do { \
+ if ((fs)->fs_active != NULL) \
+ setbit((fs)->fs_active, (cg)); \
+ } while (/*CONSTCOND*/ 0)
+#define ACTIVECG_CLR(fs, cg) \
+ do { \
+ if ((fs)->fs_active != NULL) \
+ clrbit((fs)->fs_active, (cg)); \
+ } while (/*CONSTCOND*/ 0)
+#define ACTIVECG_ISSET(fs, cg) \
+ ((fs)->fs_active != NULL && isset((fs)->fs_active, (cg)))
+
+/*
+ * The size of a cylinder group is calculated by CGSIZE. The maximum size
+ * is limited by the fact that cylinder groups are at most one block.
+ * Its size is derived from the size of the maps maintained in the
+ * cylinder group and the (struct cg) size.
+ */
+#define CGSIZE_IF(fs, ipg, fpg) \
+ /* base cg */ (sizeof(struct cg) + sizeof(int32_t) + \
+ /* old btotoff */ (fs)->fs_old_cpg * sizeof(int32_t) + \
+ /* old boff */ (fs)->fs_old_cpg * sizeof(u_int16_t) + \
+ /* inode map */ howmany((ipg), NBBY) + \
+ /* block map */ howmany((fpg), NBBY) +\
+ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \
+ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(int32_t) + \
+ /* cluster map */ howmany(ffs_fragstoblks(fs, (fpg)), NBBY)))
+
+#define CGSIZE(fs) CGSIZE_IF((fs), (fs)->fs_ipg, (fs)->fs_fpg)
+
+/*
+ * The minimal number of cylinder groups that should be created.
+ */
+#define MINCYLGRPS 4
+
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ */
+#define fs_cs(fs, indx) fs_csp[indx]
+
+/*
+ * Cylinder group block for a file system.
+ */
+#define CG_MAGIC 0x090255
+struct cg {
+ int32_t cg_firstfield; /* historic cyl groups linked list */
+ int32_t cg_magic; /* magic number */
+ int32_t cg_old_time; /* time last written */
+ int32_t cg_cgx; /* we are the cgx'th cylinder group */
+ int16_t cg_old_ncyl; /* number of cyl's this cg */
+ int16_t cg_old_niblk; /* number of inode blocks this cg */
+ int32_t cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ int32_t cg_rotor; /* position of last used block */
+ int32_t cg_frotor; /* position of last used frag */
+ int32_t cg_irotor; /* position of last used inode */
+ int32_t cg_frsum[MAXFRAG]; /* counts of available frags */
+ int32_t cg_old_btotoff; /* (int32) block totals per cylinder */
+ int32_t cg_old_boff; /* (u_int16) free block positions */
+ int32_t cg_iusedoff; /* (u_int8) used inode map */
+ int32_t cg_freeoff; /* (u_int8) free block map */
+ int32_t cg_nextfreeoff; /* (u_int8) next available space */
+ int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */
+ int32_t cg_clusteroff; /* (u_int8) free cluster map */
+ int32_t cg_nclusterblks; /* number of clusters this cg */
+ int32_t cg_niblk; /* number of inode blocks this cg */
+ int32_t cg_initediblk; /* last initialized inode */
+ int32_t cg_sparecon32[3]; /* reserved for future use */
+ int64_t cg_time; /* time last written */
+ int64_t cg_sparecon64[3]; /* reserved for future use */
+ u_int8_t cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+
+/*
+ * The following structure is defined
+ * for compatibility with old file systems.
+ */
+struct ocg {
+ int32_t cg_firstfield; /* historic linked list of cyl groups */
+ int32_t cg_unused_1; /* used for incore cyl groups */
+ int32_t cg_time; /* time last written */
+ int32_t cg_cgx; /* we are the cgx'th cylinder group */
+ int16_t cg_ncyl; /* number of cyl's this cg */
+ int16_t cg_niblk; /* number of inode blocks this cg */
+ int32_t cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ int32_t cg_rotor; /* position of last used block */
+ int32_t cg_frotor; /* position of last used frag */
+ int32_t cg_irotor; /* position of last used inode */
+ int32_t cg_frsum[8]; /* counts of available frags */
+ int32_t cg_btot[32]; /* block totals per cylinder */
+ int16_t cg_b[32][8]; /* positions of free blocks */
+ u_int8_t cg_iused[256]; /* used inode map */
+ int32_t cg_magic; /* magic number */
+ u_int8_t cg_free[1]; /* free block map */
+/* actually longer */
+};
+
+
+/*
+ * Macros for access to cylinder group array structures.
+ */
+#define old_cg_blktot_old(cgp, ns) \
+ (((struct ocg *)(cgp))->cg_btot)
+#define old_cg_blks_old(fs, cgp, cylno, ns) \
+ (((struct ocg *)(cgp))->cg_b[cylno])
+
+#define old_cg_blktot_new(cgp, ns) \
+ ((int32_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_old_btotoff, (ns))))
+#define old_cg_blks_new(fs, cgp, cylno, ns) \
+ ((int16_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_old_boff, (ns))) + (cylno) * (fs)->fs_old_nrpos)
+
+#define old_cg_blktot(cgp, ns) \
+ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \
+ old_cg_blktot_old(cgp, ns) : old_cg_blktot_new(cgp, ns))
+#define old_cg_blks(fs, cgp, cylno, ns) \
+ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \
+ old_cg_blks_old(fs, cgp, cylno, ns) : old_cg_blks_new(fs, cgp, cylno, ns))
+
+#define cg_inosused_new(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_iusedoff, (ns))))
+#define cg_blksfree_new(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_freeoff, (ns))))
+#define cg_chkmagic_new(cgp, ns) \
+ (ufs_rw32((cgp)->cg_magic, (ns)) == CG_MAGIC)
+
+#define cg_inosused_old(cgp, ns) \
+ (((struct ocg *)(cgp))->cg_iused)
+#define cg_blksfree_old(cgp, ns) \
+ (((struct ocg *)(cgp))->cg_free)
+#define cg_chkmagic_old(cgp, ns) \
+ (ufs_rw32(((struct ocg *)(cgp))->cg_magic, (ns)) == CG_MAGIC)
+
+#define cg_inosused(cgp, ns) \
+ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \
+ cg_inosused_old(cgp, ns) : cg_inosused_new(cgp, ns))
+#define cg_blksfree(cgp, ns) \
+ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \
+ cg_blksfree_old(cgp, ns) : cg_blksfree_new(cgp, ns))
+#define cg_chkmagic(cgp, ns) \
+ (cg_chkmagic_new(cgp, ns) || cg_chkmagic_old(cgp, ns))
+
+#define cg_clustersfree(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_clusteroff, (ns))))
+#define cg_clustersum(cgp, ns) \
+ ((int32_t *)((u_int8_t *)(cgp) + \
+ ufs_rw32((cgp)->cg_clustersumoff, (ns))))
+
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define FFS_FSBTODB(fs, b) ((b) << (fs)->fs_fsbtodb)
+#define FFS_DBTOFSB(fs, b) ((b) >> (fs)->fs_fsbtodb)
+
+/*
+ * Cylinder group macros to locate things in cylinder groups.
+ * They calc file system addresses of cylinder group data structures.
+ */
+#define cgbase(fs, c) (((daddr_t)(fs)->fs_fpg) * (c))
+#define cgstart_ufs1(fs, c) \
+ (cgbase(fs, c) + (fs)->fs_old_cgoffset * ((c) & ~((fs)->fs_old_cgmask)))
+#define cgstart_ufs2(fs, c) cgbase((fs), (c))
+#define cgstart(fs, c) ((fs)->fs_magic == FS_UFS2_MAGIC \
+ ? cgstart_ufs2((fs), (c)) : cgstart_ufs1((fs), (c)))
+#define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */
+#define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */
+#define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */
+#define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */
+
+/*
+ * Macros for handling inode numbers:
+ * inode number to file system block offset.
+ * inode number to cylinder group number.
+ * inode number to file system block address.
+ */
+#define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg)
+#define ino_to_fsba(fs, x) \
+ ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \
+ (ffs_blkstofrags((fs), (((x) % (fs)->fs_ipg) / FFS_INOPB(fs))))))
+#define ino_to_fsbo(fs, x) ((x) % FFS_INOPB(fs))
+
+/*
+ * Give cylinder group number for a file system block.
+ * Give cylinder group block number for a file system block.
+ */
+#define dtog(fs, d) ((d) / (fs)->fs_fpg)
+#define dtogd(fs, d) ((d) % (fs)->fs_fpg)
+
+/*
+ * Extract the bits for a block from a map.
+ * Compute the cylinder and rotational position of a cyl block addr.
+ */
+#define blkmap(fs, map, loc) \
+ (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - (fs)->fs_frag)))
+#define old_cbtocylno(fs, bno) \
+ (FFS_FSBTODB(fs, bno) / (fs)->fs_old_spc)
+#define old_cbtorpos(fs, bno) \
+ ((fs)->fs_old_nrpos <= 1 ? 0 : \
+ (FFS_FSBTODB(fs, bno) % (fs)->fs_old_spc / (fs)->fs_old_nsect * (fs)->fs_old_trackskew + \
+ FFS_FSBTODB(fs, bno) % (fs)->fs_old_spc % (fs)->fs_old_nsect * (fs)->fs_old_interleave) % \
+ (fs)->fs_old_nsect * (fs)->fs_old_nrpos / (fs)->fs_old_npsect)
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define ffs_blkoff(fs, loc) /* calculates (loc % fs->fs_bsize) */ \
+ ((loc) & (fs)->fs_qbmask)
+#define ffs_fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \
+ ((loc) & (fs)->fs_qfmask)
+#define ffs_lfragtosize(fs, frag) /* calculates ((off_t)frag * fs->fs_fsize) */ \
+ (((off_t)(frag)) << (fs)->fs_fshift)
+#define ffs_lblktosize(fs, blk) /* calculates ((off_t)blk * fs->fs_bsize) */ \
+ ((uint64_t)(((off_t)(blk)) << (fs)->fs_bshift))
+#define ffs_lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \
+ ((loc) >> (fs)->fs_bshift)
+#define ffs_numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \
+ ((loc) >> (fs)->fs_fshift)
+#define ffs_blkroundup(fs, size) /* calculates roundup(size, fs->fs_bsize) */ \
+ (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask)
+#define ffs_fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \
+ (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask)
+#define ffs_fragstoblks(fs, frags) /* calculates (frags / fs->fs_frag) */ \
+ ((frags) >> (fs)->fs_fragshift)
+#define ffs_blkstofrags(fs, blks) /* calculates (blks * fs->fs_frag) */ \
+ ((blks) << (fs)->fs_fragshift)
+#define ffs_fragnum(fs, fsb) /* calculates (fsb % fs->fs_frag) */ \
+ ((fsb) & ((fs)->fs_frag - 1))
+#define ffs_blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \
+ ((fsb) &~ ((fs)->fs_frag - 1))
+
+/*
+ * Determine the number of available frags given a
+ * percentage to hold in reserve.
+ */
+#define freespace(fs, percentreserved) \
+ (ffs_blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \
+ (fs)->fs_cstotal.cs_nffree - \
+ (((off_t)((fs)->fs_dsize)) * (percentreserved) / 100))
+
+/*
+ * Determining the size of a file block in the file system.
+ */
+#define ffs_blksize(fs, ip, lbn) \
+ (((lbn) >= UFS_NDADDR || (ip)->i_size >= ffs_lblktosize(fs, (lbn) + 1)) \
+ ? (fs)->fs_bsize \
+ : ((int32_t)ffs_fragroundup(fs, ffs_blkoff(fs, (ip)->i_size))))
+
+#define ffs_sblksize(fs, size, lbn) \
+ (((lbn) >= UFS_NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : ((int32_t)ffs_fragroundup(fs, ffs_blkoff(fs, (uint64_t)(size)))))
+
+
+/*
+ * Number of inodes in a secondary storage block/fragment.
+ */
+#define FFS_INOPB(fs) ((fs)->fs_inopb)
+#define FFS_INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift)
+
+/*
+ * Number of indirects in a file system block.
+ */
+#define FFS_NINDIR(fs) ((fs)->fs_nindir)
+
+/*
+ * Apple UFS Label:
+ * We check for this to decide to use APPLEUFS_DIRBLKSIZ
+ */
+#define APPLEUFS_LABEL_MAGIC 0x4c41424c /* LABL */
+#define APPLEUFS_LABEL_SIZE 1024
+#define APPLEUFS_LABEL_OFFSET (BBSIZE - APPLEUFS_LABEL_SIZE) /* located at 7k */
+#define APPLEUFS_LABEL_VERSION 1
+#define APPLEUFS_MAX_LABEL_NAME 512
+
+struct appleufslabel {
+ u_int32_t ul_magic;
+ u_int16_t ul_checksum;
+ u_int16_t ul_unused0;
+ u_int32_t ul_version;
+ u_int32_t ul_time;
+ u_int16_t ul_namelen;
+ u_char ul_name[APPLEUFS_MAX_LABEL_NAME]; /* Warning: may not be null terminated */
+ u_int16_t ul_unused1;
+ u_int64_t ul_uuid; /* Note this is only 4 byte aligned */
+ u_char ul_reserved[24];
+ u_char ul_unused[460];
+} __packed;
+
+
+#endif /* !_UFS_FFS_FS_H_ */
diff --git a/usr.sbin/makefs/ufs/ufs/dinode.h b/usr.sbin/makefs/ufs/ufs/dinode.h
new file mode 100644
index 00000000000..172e3e337c4
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ufs/dinode.h
@@ -0,0 +1,176 @@
+/* $NetBSD: dinode.h,v 1.25 2016/01/22 23:06:10 dholland Exp $ */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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. 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.
+ *
+ * @(#)dinode.h 8.9 (Berkeley) 3/29/95
+ */
+
+/*
+ * NOTE: COORDINATE ON-DISK FORMAT CHANGES WITH THE FREEBSD PROJECT.
+ */
+
+#ifndef _UFS_UFS_DINODE_H_
+#define _UFS_UFS_DINODE_H_
+
+/*
+ * The root inode is the root of the file system. Inode 0 can't be used for
+ * normal purposes and historically bad blocks were linked to inode 1, thus
+ * the root inode is 2. (Inode 1 is no longer used for this purpose, however
+ * numerous dump tapes make this assumption, so we are stuck with it).
+ */
+#define UFS_ROOTINO ((ino_t)2)
+
+/*
+ * The Whiteout inode# is a dummy non-zero inode number which will
+ * never be allocated to a real file. It is used as a place holder
+ * in the directory entry which has been tagged as a DT_W entry.
+ * See the comments about UFS_ROOTINO above.
+ */
+#define UFS_WINO ((ino_t)1)
+
+/*
+ * A dinode contains all the meta-data associated with a UFS file.
+ * This structure defines the on-disk format of a dinode. Since
+ * this structure describes an on-disk structure, all its fields
+ * are defined by types with precise widths.
+ */
+
+#define UFS_NXADDR 2
+#define UFS_NDADDR 12 /* Direct addresses in inode. */
+#define UFS_NIADDR 3 /* Indirect addresses in inode. */
+
+struct ufs1_dinode {
+ uint16_t di_mode; /* 0: IFMT, permissions; see below. */
+ int16_t di_nlink; /* 2: File link count. */
+ uint16_t di_oldids[2]; /* 4: Ffs: old user and group ids. */
+ uint64_t di_size; /* 8: File byte count. */
+ int32_t di_atime; /* 16: Last access time. */
+ int32_t di_atimensec; /* 20: Last access time. */
+ int32_t di_mtime; /* 24: Last modified time. */
+ int32_t di_mtimensec; /* 28: Last modified time. */
+ int32_t di_ctime; /* 32: Last inode change time. */
+ int32_t di_ctimensec; /* 36: Last inode change time. */
+ int32_t di_db[UFS_NDADDR]; /* 40: Direct disk blocks. */
+ int32_t di_ib[UFS_NIADDR]; /* 88: Indirect disk blocks. */
+ uint32_t di_flags; /* 100: Status flags (chflags). */
+ uint32_t di_blocks; /* 104: Blocks actually held. */
+ int32_t di_gen; /* 108: Generation number. */
+ uint32_t di_uid; /* 112: File owner. */
+ uint32_t di_gid; /* 116: File group. */
+ uint64_t di_modrev; /* 120: i_modrev for NFSv4 */
+};
+
+struct ufs2_dinode {
+ uint16_t di_mode; /* 0: IFMT, permissions; see below. */
+ int16_t di_nlink; /* 2: File link count. */
+ uint32_t di_uid; /* 4: File owner. */
+ uint32_t di_gid; /* 8: File group. */
+ uint32_t di_blksize; /* 12: Inode blocksize. */
+ uint64_t di_size; /* 16: File byte count. */
+ uint64_t di_blocks; /* 24: Bytes actually held. */
+ int64_t di_atime; /* 32: Last access time. */
+ int64_t di_mtime; /* 40: Last modified time. */
+ int64_t di_ctime; /* 48: Last inode change time. */
+ int64_t di_birthtime; /* 56: Inode creation time. */
+ int32_t di_mtimensec; /* 64: Last modified time. */
+ int32_t di_atimensec; /* 68: Last access time. */
+ int32_t di_ctimensec; /* 72: Last inode change time. */
+ int32_t di_birthnsec; /* 76: Inode creation time. */
+ int32_t di_gen; /* 80: Generation number. */
+ uint32_t di_kernflags; /* 84: Kernel flags. */
+ uint32_t di_flags; /* 88: Status flags (chflags). */
+ int32_t di_extsize; /* 92: External attributes block. */
+ int64_t di_extb[UFS_NXADDR];/* 96: External attributes block. */
+ int64_t di_db[UFS_NDADDR]; /* 112: Direct disk blocks. */
+ int64_t di_ib[UFS_NIADDR]; /* 208: Indirect disk blocks. */
+ uint64_t di_modrev; /* 232: i_modrev for NFSv4 */
+ int64_t di_spare[2]; /* 240: Reserved; currently unused */
+};
+
+/*
+ * The di_db fields may be overlaid with other information for
+ * file types that do not have associated disk storage. Block
+ * and character devices overlay the first data block with their
+ * dev_t value. Short symbolic links place their path in the
+ * di_db area.
+ */
+#define di_ogid di_oldids[1]
+#define di_ouid di_oldids[0]
+#define di_rdev di_db[0]
+#define UFS1_MAXSYMLINKLEN ((UFS_NDADDR + UFS_NIADDR) * sizeof(int32_t))
+#define UFS2_MAXSYMLINKLEN ((UFS_NDADDR + UFS_NIADDR) * sizeof(int64_t))
+
+#define UFS_MAXSYMLINKLEN(ip) \
+ ((ip)->i_ump->um_fstype == UFS1) ? \
+ UFS1_MAXSYMLINKLEN : UFS2_MAXSYMLINKLEN
+
+/* NeXT used to keep short symlinks in the inode even when using
+ * FS_42INODEFMT. In that case fs->fs_maxsymlinklen is probably -1,
+ * but short symlinks were stored in inodes shorter than this:
+ */
+#define APPLEUFS_MAXSYMLINKLEN 60
+
+/* File permissions. */
+#define IEXEC 0000100 /* Executable. */
+#define IWRITE 0000200 /* Writable. */
+#define IREAD 0000400 /* Readable. */
+#define ISVTX 0001000 /* Sticky bit. */
+#define ISGID 0002000 /* Set-gid. */
+#define ISUID 0004000 /* Set-uid. */
+
+/* File types. */
+#define IFMT 0170000 /* Mask of file type. */
+#define IFIFO 0010000 /* Named pipe (fifo). */
+#define IFCHR 0020000 /* Character device. */
+#define IFDIR 0040000 /* Directory file. */
+#define IFBLK 0060000 /* Block device. */
+#define IFREG 0100000 /* Regular file. */
+#define IFLNK 0120000 /* Symbolic link. */
+#define IFSOCK 0140000 /* UNIX domain socket. */
+#define IFWHT 0160000 /* Whiteout. */
+
+/* Size of the on-disk inode. */
+#define DINODE1_SIZE (sizeof(struct ufs1_dinode)) /* 128 */
+#define DINODE2_SIZE (sizeof(struct ufs2_dinode))
+
+#endif /* !_UFS_UFS_DINODE_H_ */
diff --git a/usr.sbin/makefs/ufs/ufs/dir.h b/usr.sbin/makefs/ufs/ufs/dir.h
new file mode 100644
index 00000000000..4e044d87999
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ufs/dir.h
@@ -0,0 +1,204 @@
+/* $NetBSD: dir.h,v 1.25 2015/09/01 06:16:03 dholland Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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. 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.
+ *
+ * @(#)dir.h 8.5 (Berkeley) 4/27/95
+ */
+
+#ifndef _UFS_UFS_DIR_H_
+#define _UFS_UFS_DIR_H_
+
+/*
+ * Theoretically, directories can be more than 2Gb in length; however, in
+ * practice this seems unlikely. So, we define the type doff_t as a 32-bit
+ * quantity to keep down the cost of doing lookup on a 32-bit machine.
+ */
+#define doff_t int32_t
+#define UFS_MAXDIRSIZE (0x7fffffff)
+
+/*
+ * A directory consists of some number of blocks of UFS_DIRBLKSIZ
+ * bytes, where UFS_DIRBLKSIZ is chosen such that it can be transferred
+ * to disk in a single atomic operation (e.g. 512 bytes on most machines).
+ *
+ * Each UFS_DIRBLKSIZ byte block contains some number of directory entry
+ * structures, which are of variable length. Each directory entry has
+ * a struct direct at the front of it, containing its inode number,
+ * the length of the entry, and the length of the name contained in
+ * the entry. These are followed by the name padded to a 4 byte boundary.
+ * All names are guaranteed null terminated.
+ * The maximum length of a name in a directory is FFS_MAXNAMLEN.
+ *
+ * The macro UFS_DIRSIZ(fmt, dp) gives the amount of space required to represent
+ * a directory entry. Free space in a directory is represented by
+ * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All UFS_DIRBLKSIZ bytes
+ * in a directory block are claimed by the directory entries. This
+ * usually results in the last entry in a directory having a large
+ * dp->d_reclen. When entries are deleted from a directory, the
+ * space is returned to the previous entry in the same directory
+ * block by increasing its dp->d_reclen. If the first entry of
+ * a directory block is free, then its dp->d_ino is set to 0.
+ * Entries other than the first in a directory do not normally have
+ * dp->d_ino set to 0.
+ */
+#undef UFS_DIRBLKSIZ
+#define UFS_DIRBLKSIZ DEV_BSIZE
+#define FFS_MAXNAMLEN 255
+#define APPLEUFS_DIRBLKSIZ 1024
+
+#define d_ino d_fileno
+struct direct {
+ u_int32_t d_fileno; /* inode number of entry */
+ u_int16_t d_reclen; /* length of this record */
+ u_int8_t d_type; /* file type, see below */
+ u_int8_t d_namlen; /* length of string in d_name */
+ char d_name[FFS_MAXNAMLEN + 1];/* name with length <= FFS_MAXNAMLEN */
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#define DTTOIF(dirtype) ((dirtype) << 12)
+
+/*
+ * The UFS_DIRSIZ macro gives the minimum record length which will hold
+ * the directory entry. This requires the amount of space in struct direct
+ * without the d_name field, plus enough space for the name with a terminating
+ * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+ */
+#define UFS_DIRECTSIZ(namlen) \
+ ((sizeof(struct direct) - (FFS_MAXNAMLEN+1)) + (((namlen)+1 + 3) &~ 3))
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define UFS_DIRSIZ(oldfmt, dp, needswap) \
+ (((oldfmt) && !(needswap)) ? \
+ UFS_DIRECTSIZ((dp)->d_type) : UFS_DIRECTSIZ((dp)->d_namlen))
+#else
+#define UFS_DIRSIZ(oldfmt, dp, needswap) \
+ (((oldfmt) && (needswap)) ? \
+ UFS_DIRECTSIZ((dp)->d_type) : UFS_DIRECTSIZ((dp)->d_namlen))
+#endif
+
+/*
+ * UFS_OLDDIRFMT and UFS_NEWDIRFMT are code numbers for a directory
+ * format change that happened in ffs a long time ago. (Back in the
+ * 80s, if I'm not mistaken.)
+ *
+ * These code numbers do not appear on disk. They're generated from
+ * runtime logic that is cued by other things, which is why
+ * UFS_OLDDIRFMT is confusingly 1 and UFS_NEWDIRFMT is confusingly 0.
+ *
+ * Relatedly, the FFS_EI byte swapping logic for directories is a
+ * horrible mess. For example, to access the namlen field, one
+ * currently does the following:
+ *
+ * #if (BYTE_ORDER == LITTLE_ENDIAN)
+ * swap = (UFS_IPNEEDSWAP(VTOI(vp)) == 0);
+ * #else
+ * swap = (UFS_IPNEEDSWAP(VTOI(vp)) != 0);
+ * #endif
+ * return ((FSFMT(vp) && swap) ? dp->d_type : dp->d_namlen);
+ *
+ * UFS_IPNEEDSWAP() returns true if the volume is opposite-endian. This
+ * horrible "swap" logic is cutpasted all over everywhere but amounts
+ * to the following:
+ *
+ * running code volume lfs_dobyteswap "swap"
+ * ----------------------------------------------------------
+ * LITTLE_ENDIAN LITTLE_ENDIAN false true
+ * LITTLE_ENDIAN BIG_ENDIAN true false
+ * BIG_ENDIAN LITTLE_ENDIAN true true
+ * BIG_ENDIAN BIG_ENDIAN false false
+ *
+ * which you'll note boils down to "volume is little-endian".
+ *
+ * Meanwhile, FSFMT(vp) yields UFS_OLDDIRFMT or UFS_NEWDIRFMT via
+ * perverted logic of its own. Since UFS_OLDDIRFMT is 1 (contrary to
+ * what one might expect approaching this cold) what this mess means
+ * is: on OLDDIRFMT volumes that are little-endian, we read the
+ * namlen value out of the type field. This is because on OLDDIRFMT
+ * volumes there is no d_type field, just a 16-bit d_namlen; so if
+ * the 16-bit d_namlen is little-endian, the useful part of it is
+ * in the first byte, which in the NEWDIRFMT structure is the d_type
+ * field.
+ */
+
+#define UFS_OLDDIRFMT 1
+#define UFS_NEWDIRFMT 0
+
+/*
+ * Template for manipulating directories. Should use struct direct's,
+ * but the name field is FFS_MAXNAMLEN - 1, and this just won't do.
+ */
+struct dirtemplate {
+ u_int32_t dot_ino;
+ int16_t dot_reclen;
+ u_int8_t dot_type;
+ u_int8_t dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_int32_t dotdot_ino;
+ int16_t dotdot_reclen;
+ u_int8_t dotdot_type;
+ u_int8_t dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+
+/*
+ * This is the old format of directories, sans type element.
+ */
+struct odirtemplate {
+ u_int32_t dot_ino;
+ int16_t dot_reclen;
+ u_int16_t dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_int32_t dotdot_ino;
+ int16_t dotdot_reclen;
+ u_int16_t dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+#endif /* !_UFS_UFS_DIR_H_ */
diff --git a/usr.sbin/makefs/ufs/ufs/quota.h b/usr.sbin/makefs/ufs/ufs/quota.h
new file mode 100644
index 00000000000..b88411a70a1
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ufs/quota.h
@@ -0,0 +1,92 @@
+/* $NetBSD: quota.h,v 1.30 2012/08/26 02:32:14 dholland Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * 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. 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.
+ *
+ * @(#)quota.h 8.3 (Berkeley) 8/19/94
+ */
+
+#ifndef _UFS_UFS_QUOTA_H_
+#define _UFS_UFS_QUOTA_H_
+
+/*
+ * These definitions are common to the original disk quota implementation
+ * (quota1) and the newer implementation (quota2)
+ */
+
+/*
+ * The following constants define the usage of the quota file array in the
+ * ufsmount structure and dquot array in the inode structure. The semantics
+ * of the elements of these arrays are defined in the routine getinoquota;
+ * the remainder of the quota code treats them generically and need not be
+ * inspected when changing the size of the array.
+ */
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+/*
+ * Initializer for the strings corresponding to the quota ID types.
+ * (in quota1 these are also the default names of the quota files)
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+}
+
+#include <sys/quota.h>
+__inline static int __unused
+quota_idtype_to_ufs(int idtype)
+{
+ switch (idtype) {
+ case QUOTA_IDTYPE_USER:
+ return USRQUOTA;
+ case QUOTA_IDTYPE_GROUP:
+ return GRPQUOTA;
+ default:
+ return -1;
+ }
+}
+
+static __inline int __unused
+quota_idtype_from_ufs(int ufstype)
+{
+ switch (ufstype) {
+ case USRQUOTA:
+ return QUOTA_IDTYPE_USER;
+ case GRPQUOTA:
+ return QUOTA_IDTYPE_GROUP;
+ default:
+ return -1;
+ }
+}
+
+#endif /* !_UFS_UFS_QUOTA_H_ */
diff --git a/usr.sbin/makefs/ufs/ufs/ufs_bswap.h b/usr.sbin/makefs/ufs/ufs/ufs_bswap.h
new file mode 100644
index 00000000000..816cff82570
--- /dev/null
+++ b/usr.sbin/makefs/ufs/ufs/ufs_bswap.h
@@ -0,0 +1,64 @@
+/* $NetBSD: ufs_bswap.h,v 1.21 2016/04/29 03:05:04 christos Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _UFS_UFS_BSWAP_H_
+#define _UFS_UFS_BSWAP_H_
+
+#include <sys/endian.h>
+
+/* Macros to access UFS flags */
+#define UFS_MPNEEDSWAP(ump) ((void)(ump), 0)
+#define UFS_FSNEEDSWAP(fs) ((void)(fs), 0)
+#define UFS_IPNEEDSWAP(ip) ((void)(ip), 0)
+
+/* inlines for access to swapped data */
+static inline u_int16_t
+ufs_rw16(uint16_t a, int ns)
+{
+ return ((ns) ? swap16(a) : (a));
+}
+
+static inline u_int32_t
+ufs_rw32(uint32_t a, int ns)
+{
+ return ((ns) ? swap32(a) : (a));
+}
+
+static inline u_int64_t
+ufs_rw64(uint64_t a, int ns)
+{
+ return ((ns) ? swap64(a) : (a));
+}
+
+#define ufs_add16(a, b, ns) \
+ (a) = ufs_rw16(ufs_rw16((a), (ns)) + (b), (ns))
+#define ufs_add32(a, b, ns) \
+ (a) = ufs_rw32(ufs_rw32((a), (ns)) + (b), (ns))
+#define ufs_add64(a, b, ns) \
+ (a) = ufs_rw64(ufs_rw64((a), (ns)) + (b), (ns))
+
+#endif /* !_UFS_UFS_BSWAP_H_ */
diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c
new file mode 100644
index 00000000000..7ac77530afc
--- /dev/null
+++ b/usr.sbin/makefs/walk.c
@@ -0,0 +1,447 @@
+/* $NetBSD: walk.c,v 1.29 2015/11/25 00:48:49 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+
+static fsnode *create_fsnode(const char *, const char *, const char *,
+ struct stat *);
+static fsinode *link_check(fsinode *);
+
+
+/*
+ * walk_dir --
+ * build a tree of fsnodes from `root' and `dir', with a parent
+ * fsnode of `parent' (which may be NULL for the root of the tree).
+ * append the tree to a fsnode of `join' if it is not NULL.
+ * each "level" is a directory, with the "." entry guaranteed to be
+ * at the start of the list, and without ".." entries.
+ */
+fsnode *
+walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join,
+ int replace)
+{
+ fsnode *first, *cur, *prev, *last;
+ DIR *dirp;
+ struct dirent *dent;
+ char path[MAXPATHLEN + 1];
+ struct stat stbuf;
+ char *name, *rp;
+ int dot, len;
+
+ assert(root != NULL);
+ assert(dir != NULL);
+
+ len = snprintf(path, sizeof(path), "%s/%s", root, dir);
+ if (len >= (int)sizeof(path))
+ errx(1, "Pathname too long.");
+ if (debug & DEBUG_WALK_DIR)
+ printf("walk_dir: %s %p\n", path, parent);
+ if ((dirp = opendir(path)) == NULL)
+ err(1, "Can't opendir `%s'", path);
+ rp = path + strlen(root) + 1;
+ if (join != NULL) {
+ first = cur = join;
+ while (cur->next != NULL)
+ cur = cur->next;
+ prev = last = cur;
+ } else
+ last = first = prev = NULL;
+ while ((dent = readdir(dirp)) != NULL) {
+ name = dent->d_name;
+ dot = 0;
+ if (name[0] == '.')
+ switch (name[1]) {
+ case '\0': /* "." */
+ if (join != NULL)
+ continue;
+ dot = 1;
+ break;
+ case '.': /* ".." */
+ if (name[2] == '\0')
+ continue;
+ /* FALLTHROUGH */
+ default:
+ dot = 0;
+ }
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf("scanning %s/%s/%s\n", root, dir, name);
+ if (snprintf(path + len, sizeof(path) - len, "/%s", name) >=
+ (int)sizeof(path) - len)
+ errx(1, "Pathname too long.");
+ if (lstat(path, &stbuf) == -1)
+ err(1, "Can't lstat `%s'", path);
+#ifdef S_ISSOCK
+ if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf(" skipping socket %s\n", path);
+ continue;
+ }
+#endif
+
+ if (join != NULL) {
+ cur = join->next;
+ for (;;) {
+ if (cur == NULL || strcmp(cur->name, name) == 0)
+ break;
+ if (cur == last) {
+ cur = NULL;
+ break;
+ }
+ cur = cur->next;
+ }
+ if (cur != NULL) {
+ if (S_ISDIR(cur->type) &&
+ S_ISDIR(stbuf.st_mode)) {
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf("merging %s with %p\n",
+ path, cur->child);
+ cur->child = walk_dir(root, rp, cur,
+ cur->child, replace);
+ continue;
+ }
+ if (!replace)
+ errx(1, "Can't merge %s `%s' with "
+ "existing %s",
+ inode_type(stbuf.st_mode), path,
+ inode_type(cur->type));
+ else {
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf("replacing %s %s\n",
+ inode_type(stbuf.st_mode),
+ path);
+ if (cur == join->next)
+ join->next = cur->next;
+ else {
+ fsnode *p;
+ for (p = join->next;
+ p->next != cur; p = p->next)
+ continue;
+ p->next = cur->next;
+ }
+ free(cur);
+ }
+ }
+ }
+
+ cur = create_fsnode(root, dir, name, &stbuf);
+ cur->parent = parent;
+ if (dot) {
+ /* ensure "." is at the start of the list */
+ cur->next = first;
+ first = cur;
+ if (! prev)
+ prev = cur;
+ cur->first = first;
+ } else { /* not "." */
+ if (prev)
+ prev->next = cur;
+ prev = cur;
+ if (!first)
+ first = cur;
+ cur->first = first;
+ if (S_ISDIR(cur->type)) {
+ cur->child = walk_dir(root, rp, cur, NULL,
+ replace);
+ continue;
+ }
+ }
+ if (stbuf.st_nlink > 1) {
+ fsinode *curino;
+
+ curino = link_check(cur->inode);
+ if (curino != NULL) {
+ free(cur->inode);
+ cur->inode = curino;
+ cur->inode->nlink++;
+ if (debug & DEBUG_WALK_DIR_LINKCHECK)
+ printf("link_check: found [%llu, %llu]\n",
+ (unsigned long long)curino->st.st_dev,
+ (unsigned long long)curino->st.st_ino);
+ }
+ }
+ if (S_ISLNK(cur->type)) {
+ char slink[PATH_MAX+1];
+ int llen;
+
+ llen = readlink(path, slink, sizeof(slink) - 1);
+ if (llen == -1)
+ err(1, "Readlink `%s'", path);
+ slink[llen] = '\0';
+ cur->symlink = estrdup(slink);
+ }
+ }
+ assert(first != NULL);
+ if (join == NULL)
+ for (cur = first->next; cur != NULL; cur = cur->next)
+ cur->first = first;
+ if (closedir(dirp) == -1)
+ err(1, "Can't closedir `%s/%s'", root, dir);
+ return (first);
+}
+
+static fsnode *
+create_fsnode(const char *root, const char *path, const char *name,
+ struct stat *stbuf)
+{
+ fsnode *cur;
+
+ cur = ecalloc(1, sizeof(*cur));
+ cur->path = estrdup(path);
+ cur->name = estrdup(name);
+ cur->inode = ecalloc(1, sizeof(*cur->inode));
+ cur->root = root;
+ cur->type = stbuf->st_mode & S_IFMT;
+ cur->inode->nlink = 1;
+ cur->inode->st = *stbuf;
+ if (stampst.st_ino) {
+ cur->inode->st.st_atime = stampst.st_atime;
+ cur->inode->st.st_mtime = stampst.st_mtime;
+ cur->inode->st.st_ctime = stampst.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ cur->inode->st.st_atimensec = stampst.st_atimensec;
+ cur->inode->st.st_mtimensec = stampst.st_mtimensec;
+ cur->inode->st.st_ctimensec = stampst.st_ctimensec;
+#endif
+ }
+ return (cur);
+}
+
+/*
+ * free_fsnodes --
+ * Removes node from tree and frees it and all of
+ * its decendents.
+ */
+void
+free_fsnodes(fsnode *node)
+{
+ fsnode *cur, *next;
+
+ assert(node != NULL);
+
+ /* for ".", start with actual parent node */
+ if (node->first == node) {
+ assert(node->name[0] == '.' && node->name[1] == '\0');
+ if (node->parent) {
+ assert(node->parent->child == node);
+ node = node->parent;
+ }
+ }
+
+ /* Find ourselves in our sibling list and unlink */
+ if (node->first != node) {
+ for (cur = node->first; cur->next; cur = cur->next) {
+ if (cur->next == node) {
+ cur->next = node->next;
+ node->next = NULL;
+ break;
+ }
+ }
+ }
+
+ for (cur = node; cur != NULL; cur = next) {
+ next = cur->next;
+ if (cur->child) {
+ cur->child->parent = NULL;
+ free_fsnodes(cur->child);
+ }
+ if (cur->inode->nlink-- == 1)
+ free(cur->inode);
+ if (cur->symlink)
+ free(cur->symlink);
+ free(cur->path);
+ free(cur->name);
+ free(cur);
+ }
+}
+
+
+/*
+ * dump_fsnodes --
+ * dump the fsnodes from `cur'
+ */
+void
+dump_fsnodes(fsnode *root)
+{
+ fsnode *cur;
+ char path[MAXPATHLEN + 1];
+
+ printf("dump_fsnodes: %s %p\n", root->path, root);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (snprintf(path, sizeof(path), "%s/%s", cur->path,
+ cur->name) >= (int)sizeof(path))
+ errx(1, "Pathname too long.");
+
+ if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
+ printf("cur=%8p parent=%8p first=%8p ",
+ cur, cur->parent, cur->first);
+ printf("%7s: %s", inode_type(cur->type), path);
+ if (S_ISLNK(cur->type)) {
+ assert(cur->symlink != NULL);
+ printf(" -> %s", cur->symlink);
+ } else {
+ assert (cur->symlink == NULL);
+ }
+ if (cur->inode->nlink > 1)
+ printf(", nlinks=%d", cur->inode->nlink);
+ putchar('\n');
+
+ if (cur->child) {
+ assert (cur->type == S_IFDIR);
+ dump_fsnodes(cur->child);
+ }
+ }
+ printf("dump_fsnodes: finished %s/%s\n", root->path, root->name);
+}
+
+
+/*
+ * inode_type --
+ * for a given inode type `mode', return a descriptive string.
+ * for most cases, uses inotype() from mtree/misc.c
+ */
+const char *
+inode_type(mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFBLK:
+ return ("block");
+ case S_IFCHR:
+ return ("char");
+ case S_IFDIR:
+ return ("dir");
+ case S_IFIFO:
+ return ("fifo");
+ case S_IFREG:
+ return ("file");
+ case S_IFLNK:
+ return ("symlink");
+ case S_IFSOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+
+/*
+ * link_check --
+ * return pointer to fsinode matching `entry's st_ino & st_dev if it exists,
+ * otherwise add `entry' to table and return NULL
+ */
+/* This was borrowed from du.c and tweaked to keep an fsnode
+ * pointer instead. -- dbj@netbsd.org
+ */
+static fsinode *
+link_check(fsinode *entry)
+{
+ static struct entry {
+ fsinode *data;
+ } *htable;
+ static int htshift; /* log(allocated size) */
+ static int htmask; /* allocated size - 1 */
+ static int htused; /* 2*number of insertions */
+ int h, h2;
+ uint64_t tmp;
+ /* this constant is (1<<64)/((1+sqrt(5))/2)
+ * aka (word size)/(golden ratio)
+ */
+ const uint64_t HTCONST = 11400714819323198485ULL;
+ const int HTBITS = 64;
+
+ /* Never store zero in hashtable */
+ assert(entry);
+
+ /* Extend hash table if necessary, keep load under 0.5 */
+ if (htused<<1 >= htmask) {
+ struct entry *ohtable;
+
+ if (!htable)
+ htshift = 10; /* starting hashtable size */
+ else
+ htshift++; /* exponential hashtable growth */
+
+ htmask = (1 << htshift) - 1;
+ htused = 0;
+
+ ohtable = htable;
+ htable = ecalloc(htmask+1, sizeof(*htable));
+ /* populate newly allocated hashtable */
+ if (ohtable) {
+ int i;
+ for (i = 0; i <= htmask>>1; i++)
+ if (ohtable[i].data)
+ link_check(ohtable[i].data);
+ free(ohtable);
+ }
+ }
+
+ /* multiplicative hashing */
+ tmp = entry->st.st_dev;
+ tmp <<= HTBITS>>1;
+ tmp |= entry->st.st_ino;
+ tmp *= HTCONST;
+ h = tmp >> (HTBITS - htshift);
+ h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
+
+ /* open address hashtable search with double hash probing */
+ while (htable[h].data) {
+ if ((htable[h].data->st.st_ino == entry->st.st_ino) &&
+ (htable[h].data->st.st_dev == entry->st.st_dev)) {
+ return htable[h].data;
+ }
+ h = (h + h2) & htmask;
+ }
+
+ /* Insert the current entry into hashtable */
+ htable[h].data = entry;
+ htused++;
+ return NULL;
+}
diff --git a/usr.sbin/makefs/xmalloc.c b/usr.sbin/makefs/xmalloc.c
new file mode 100644
index 00000000000..1a0e22d2094
--- /dev/null
+++ b/usr.sbin/makefs/xmalloc.c
@@ -0,0 +1,43 @@
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *
+emalloc(size_t size)
+{
+ void *v;
+
+ if ((v = malloc(size)) == NULL)
+ err(1, "malloc");
+ return v;
+}
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *v;
+
+ if ((v = calloc(nmemb, size)) == NULL)
+ err(1, "calloc");
+ return v;
+}
+
+void *
+erealloc(void *ptr, size_t size)
+{
+ void *v;
+
+ if ((v = realloc(ptr, size)) == NULL)
+ err(1, "realloc");
+ return v;
+}
+
+char *
+estrdup(const char *s)
+{
+ char *s2;
+
+ if ((s2 = strdup(s)) == NULL)
+ err(1, "strdup");
+ return s2;
+}