diff options
author | Martin Natano <natano@cvs.openbsd.org> | 2016-10-16 17:08:54 +0000 |
---|---|---|
committer | Martin Natano <natano@cvs.openbsd.org> | 2016-10-16 17:08:54 +0000 |
commit | 27dd9826b6ad14ee3599a81db61506458f2b8438 (patch) | |
tree | fc159319113b6ca6798aa78f6c9648e1f507dc55 /usr.sbin | |
parent | 4289d4423235fe954dcd700a8d0dec601ce2ec7f (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')
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; +} |