From 432ccfe53432911745bfeec30f7d1ba1952da015 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Fri, 11 Sep 2015 10:52:26 +0200 Subject: [PATCH 1/4] libutil: add getmaxpartitions() implementation --- distrib/sets/lists/minix/mi | 1 + lib/libutil/Makefile | 6 +++++- minix/lib/libutil/Makefile.inc | 3 +++ minix/lib/libutil/getmaxpartitions.c | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 minix/lib/libutil/Makefile.inc create mode 100644 minix/lib/libutil/getmaxpartitions.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 1c74b3f64..05771c69c 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -3718,6 +3718,7 @@ ./usr/man/man3/getlastlogx.3 minix-sys ./usr/man/man3/getline.3 minix-sys ./usr/man/man3/getloadavg.3 minix-sys +./usr/man/man3/getmaxpartitions.3 minix-sys ./usr/man/man3/getmaxx.3 minix-sys ./usr/man/man3/getmaxy.3 minix-sys ./usr/man/man3/getmaxyx.3 minix-sys diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 7ee6eb2fa..9524703ed 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -25,7 +25,7 @@ SRCS+= efun.c \ MAN= efun.3 \ getmntopts.3 \ - getfsspecname.3 \ + getmaxpartitions.3 getfsspecname.3 \ login.3 login_cap.3 loginx.3 \ opendisk.3 openpty.3 parsedate.3 pidfile.3 pidlock.3 \ pw_getconf.3 pw_init.3 pw_lock.3 secure_path.3 \ @@ -33,6 +33,10 @@ MAN= efun.3 \ snprintb.3 sockaddr_snprintf.3 stat_flags.3 strpct.3 ttyaction.3 \ ttymsg.3 util.3 +.if defined(__MINIX) +.include "${NETBSDSRCDIR}/minix/lib/libutil/Makefile.inc" +.endif + YPREFIX=__pd .PATH: ${NETBSDSRCDIR}/lib/libc/gen diff --git a/minix/lib/libutil/Makefile.inc b/minix/lib/libutil/Makefile.inc new file mode 100644 index 000000000..9de835068 --- /dev/null +++ b/minix/lib/libutil/Makefile.inc @@ -0,0 +1,3 @@ +.PATH: ${NETBSDSRCDIR}/minix/lib/libutil + +SRCS+= getmaxpartitions.c \ No newline at end of file diff --git a/minix/lib/libutil/getmaxpartitions.c b/minix/lib/libutil/getmaxpartitions.c new file mode 100644 index 000000000..d15420423 --- /dev/null +++ b/minix/lib/libutil/getmaxpartitions.c @@ -0,0 +1,6 @@ +#include + +int getmaxpartitions(void) +{ + return NR_PARTITIONS; +} From 8178fc518c5066561141682397efd395b83a6c9c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Thu, 10 Sep 2015 23:45:24 +0200 Subject: [PATCH 2/4] Import makefs from NetBSD --- distrib/sets/lists/minix/mi | 14 + sbin/Makefile | 2 +- sbin/mount/fattr.c | 89 + sbin/mount/mountprog.h | 37 + sbin/newfs_msdos/Makefile | 25 + sbin/newfs_msdos/mkfs_msdos.c | 976 +++ sbin/newfs_msdos/mkfs_msdos.h | 71 + sbin/newfs_msdos/newfs_msdos.8 | 241 + sbin/newfs_msdos/newfs_msdos.c | 253 + sbin/newfs_udf/Makefile | 17 + sbin/newfs_udf/newfs_udf.8 | 191 + sbin/newfs_udf/newfs_udf.c | 896 +++ sbin/newfs_udf/newfs_udf.h | 67 + sbin/newfs_udf/udf_create.c | 2392 +++++++ sbin/newfs_udf/udf_create.h | 283 + sbin/newfs_udf/udf_write.c | 906 +++ sbin/newfs_udf/udf_write.h | 54 + sbin/newfs_udf/unicode.h | 161 + sbin/newfs_v7fs/Makefile | 21 + sbin/newfs_v7fs/main.c | 316 + sbin/newfs_v7fs/newfs_v7fs.8 | 130 + sbin/newfs_v7fs/newfs_v7fs.c | 240 + sbin/newfs_v7fs/newfs_v7fs.h | 48 + sys/fs/Makefile | 2 +- sys/fs/udf/Makefile | 7 + sys/fs/udf/ecma167-udf.h | 835 +++ sys/fs/udf/files.udf | 16 + sys/fs/udf/udf.h | 426 ++ sys/fs/udf/udf_allocation.c | 3215 +++++++++ sys/fs/udf/udf_bswap.h | 76 + sys/fs/udf/udf_mount.h | 70 + sys/fs/udf/udf_osta.c | 514 ++ sys/fs/udf/udf_osta.h | 44 + sys/fs/udf/udf_readwrite.c | 723 +++ sys/fs/udf/udf_rename.c | 669 ++ sys/fs/udf/udf_strat_bootstrap.c | 145 + sys/fs/udf/udf_strat_direct.c | 454 ++ sys/fs/udf/udf_strat_rmw.c | 1507 +++++ sys/fs/udf/udf_strat_sequential.c | 687 ++ sys/fs/udf/udf_subr.c | 6817 ++++++++++++++++++++ sys/fs/udf/udf_subr.h | 229 + sys/fs/udf/udf_vfsops.c | 949 +++ sys/fs/udf/udf_vnops.c | 2183 +++++++ sys/fs/v7fs/Makefile | 7 + sys/fs/v7fs/files.v7fs | 22 + sys/fs/v7fs/v7fs.h | 180 + sys/fs/v7fs/v7fs_args.h | 40 + sys/fs/v7fs/v7fs_datablock.c | 729 +++ sys/fs/v7fs/v7fs_datablock.h | 52 + sys/fs/v7fs/v7fs_dirent.c | 89 + sys/fs/v7fs/v7fs_dirent.h | 38 + sys/fs/v7fs/v7fs_endian.c | 231 + sys/fs/v7fs/v7fs_endian.h | 52 + sys/fs/v7fs/v7fs_extern.c | 262 + sys/fs/v7fs/v7fs_extern.h | 116 + sys/fs/v7fs/v7fs_file.c | 422 ++ sys/fs/v7fs/v7fs_file.h | 65 + sys/fs/v7fs/v7fs_file_util.c | 344 + sys/fs/v7fs/v7fs_impl.h | 154 + sys/fs/v7fs/v7fs_inode.c | 301 + sys/fs/v7fs/v7fs_inode.h | 91 + sys/fs/v7fs/v7fs_inode_util.c | 128 + sys/fs/v7fs/v7fs_io.c | 141 + sys/fs/v7fs/v7fs_io_kern.c | 241 + sys/fs/v7fs/v7fs_io_user.c | 172 + sys/fs/v7fs/v7fs_superblock.c | 263 + sys/fs/v7fs/v7fs_superblock.h | 48 + sys/fs/v7fs/v7fs_superblock_util.c | 101 + sys/fs/v7fs/v7fs_vfsops.c | 617 ++ sys/fs/v7fs/v7fs_vnops.c | 1328 ++++ usr.sbin/Makefile | 2 +- usr.sbin/makefs/Makefile | 37 + usr.sbin/makefs/README | 129 + usr.sbin/makefs/TODO | 41 + usr.sbin/makefs/cd9660.c | 2148 ++++++ usr.sbin/makefs/cd9660.h | 357 + usr.sbin/makefs/cd9660/Makefile.inc | 15 + usr.sbin/makefs/cd9660/cd9660_archimedes.c | 130 + usr.sbin/makefs/cd9660/cd9660_archimedes.h | 48 + usr.sbin/makefs/cd9660/cd9660_conversion.c | 203 + usr.sbin/makefs/cd9660/cd9660_debug.c | 498 ++ usr.sbin/makefs/cd9660/cd9660_eltorito.c | 698 ++ usr.sbin/makefs/cd9660/cd9660_eltorito.h | 162 + usr.sbin/makefs/cd9660/cd9660_strings.c | 127 + usr.sbin/makefs/cd9660/cd9660_write.c | 509 ++ usr.sbin/makefs/cd9660/iso9660_rrip.c | 838 +++ usr.sbin/makefs/cd9660/iso9660_rrip.h | 290 + usr.sbin/makefs/chfs.c | 222 + usr.sbin/makefs/chfs/Makefile.inc | 11 + usr.sbin/makefs/chfs/chfs_mkfs.c | 307 + usr.sbin/makefs/chfs/chfs_mkfs.h | 44 + usr.sbin/makefs/chfs_makefs.h | 48 + usr.sbin/makefs/ffs.c | 1155 ++++ usr.sbin/makefs/ffs.h | 68 + usr.sbin/makefs/ffs/Makefile.inc | 7 + usr.sbin/makefs/ffs/buf.c | 220 + usr.sbin/makefs/ffs/buf.h | 117 + usr.sbin/makefs/ffs/ffs_alloc.c | 597 ++ usr.sbin/makefs/ffs/ffs_balloc.c | 584 ++ usr.sbin/makefs/ffs/ffs_extern.h | 76 + usr.sbin/makefs/ffs/mkfs.c | 845 +++ usr.sbin/makefs/ffs/newfs_extern.h | 34 + usr.sbin/makefs/ffs/ufs_bmap.c | 145 + usr.sbin/makefs/ffs/ufs_inode.h | 112 + usr.sbin/makefs/makefs.8 | 460 ++ usr.sbin/makefs/makefs.c | 437 ++ usr.sbin/makefs/makefs.h | 267 + usr.sbin/makefs/msdos.c | 257 + usr.sbin/makefs/msdos.h | 42 + usr.sbin/makefs/msdos/Makefile.inc | 15 + usr.sbin/makefs/msdos/msdosfs_denode.c | 363 ++ usr.sbin/makefs/msdos/msdosfs_vfsops.c | 425 ++ usr.sbin/makefs/msdos/msdosfs_vnops.c | 639 ++ usr.sbin/makefs/udf.c | 1377 ++++ usr.sbin/makefs/udf/Makefile.inc | 13 + usr.sbin/makefs/udf/cdio_mmc_structs.h | 163 + usr.sbin/makefs/v7fs.c | 191 + usr.sbin/makefs/v7fs/Makefile.inc | 20 + usr.sbin/makefs/v7fs/v7fs_estimate.c | 275 + usr.sbin/makefs/v7fs/v7fs_populate.c | 298 + usr.sbin/makefs/v7fs_makefs.h | 49 + usr.sbin/makefs/walk.c | 691 ++ 122 files changed, 50035 insertions(+), 3 deletions(-) create mode 100644 sbin/mount/fattr.c create mode 100644 sbin/mount/mountprog.h create mode 100644 sbin/newfs_msdos/Makefile create mode 100644 sbin/newfs_msdos/mkfs_msdos.c create mode 100644 sbin/newfs_msdos/mkfs_msdos.h create mode 100644 sbin/newfs_msdos/newfs_msdos.8 create mode 100644 sbin/newfs_msdos/newfs_msdos.c create mode 100644 sbin/newfs_udf/Makefile create mode 100644 sbin/newfs_udf/newfs_udf.8 create mode 100644 sbin/newfs_udf/newfs_udf.c create mode 100644 sbin/newfs_udf/newfs_udf.h create mode 100644 sbin/newfs_udf/udf_create.c create mode 100644 sbin/newfs_udf/udf_create.h create mode 100644 sbin/newfs_udf/udf_write.c create mode 100644 sbin/newfs_udf/udf_write.h create mode 100644 sbin/newfs_udf/unicode.h create mode 100644 sbin/newfs_v7fs/Makefile create mode 100644 sbin/newfs_v7fs/main.c create mode 100644 sbin/newfs_v7fs/newfs_v7fs.8 create mode 100644 sbin/newfs_v7fs/newfs_v7fs.c create mode 100644 sbin/newfs_v7fs/newfs_v7fs.h create mode 100644 sys/fs/udf/Makefile create mode 100644 sys/fs/udf/ecma167-udf.h create mode 100644 sys/fs/udf/files.udf create mode 100644 sys/fs/udf/udf.h create mode 100644 sys/fs/udf/udf_allocation.c create mode 100644 sys/fs/udf/udf_bswap.h create mode 100644 sys/fs/udf/udf_mount.h create mode 100644 sys/fs/udf/udf_osta.c create mode 100644 sys/fs/udf/udf_osta.h create mode 100644 sys/fs/udf/udf_readwrite.c create mode 100644 sys/fs/udf/udf_rename.c create mode 100644 sys/fs/udf/udf_strat_bootstrap.c create mode 100644 sys/fs/udf/udf_strat_direct.c create mode 100644 sys/fs/udf/udf_strat_rmw.c create mode 100644 sys/fs/udf/udf_strat_sequential.c create mode 100644 sys/fs/udf/udf_subr.c create mode 100644 sys/fs/udf/udf_subr.h create mode 100644 sys/fs/udf/udf_vfsops.c create mode 100644 sys/fs/udf/udf_vnops.c create mode 100644 sys/fs/v7fs/Makefile create mode 100644 sys/fs/v7fs/files.v7fs create mode 100644 sys/fs/v7fs/v7fs.h create mode 100644 sys/fs/v7fs/v7fs_args.h create mode 100644 sys/fs/v7fs/v7fs_datablock.c create mode 100644 sys/fs/v7fs/v7fs_datablock.h create mode 100644 sys/fs/v7fs/v7fs_dirent.c create mode 100644 sys/fs/v7fs/v7fs_dirent.h create mode 100644 sys/fs/v7fs/v7fs_endian.c create mode 100644 sys/fs/v7fs/v7fs_endian.h create mode 100644 sys/fs/v7fs/v7fs_extern.c create mode 100644 sys/fs/v7fs/v7fs_extern.h create mode 100644 sys/fs/v7fs/v7fs_file.c create mode 100644 sys/fs/v7fs/v7fs_file.h create mode 100644 sys/fs/v7fs/v7fs_file_util.c create mode 100644 sys/fs/v7fs/v7fs_impl.h create mode 100644 sys/fs/v7fs/v7fs_inode.c create mode 100644 sys/fs/v7fs/v7fs_inode.h create mode 100644 sys/fs/v7fs/v7fs_inode_util.c create mode 100644 sys/fs/v7fs/v7fs_io.c create mode 100644 sys/fs/v7fs/v7fs_io_kern.c create mode 100644 sys/fs/v7fs/v7fs_io_user.c create mode 100644 sys/fs/v7fs/v7fs_superblock.c create mode 100644 sys/fs/v7fs/v7fs_superblock.h create mode 100644 sys/fs/v7fs/v7fs_superblock_util.c create mode 100644 sys/fs/v7fs/v7fs_vfsops.c create mode 100644 sys/fs/v7fs/v7fs_vnops.c create mode 100644 usr.sbin/makefs/Makefile create mode 100644 usr.sbin/makefs/README create mode 100644 usr.sbin/makefs/TODO create mode 100644 usr.sbin/makefs/cd9660.c create mode 100644 usr.sbin/makefs/cd9660.h create mode 100644 usr.sbin/makefs/cd9660/Makefile.inc create mode 100644 usr.sbin/makefs/cd9660/cd9660_archimedes.c create mode 100644 usr.sbin/makefs/cd9660/cd9660_archimedes.h create mode 100644 usr.sbin/makefs/cd9660/cd9660_conversion.c create mode 100644 usr.sbin/makefs/cd9660/cd9660_debug.c create mode 100644 usr.sbin/makefs/cd9660/cd9660_eltorito.c create mode 100644 usr.sbin/makefs/cd9660/cd9660_eltorito.h create mode 100644 usr.sbin/makefs/cd9660/cd9660_strings.c create mode 100644 usr.sbin/makefs/cd9660/cd9660_write.c create mode 100644 usr.sbin/makefs/cd9660/iso9660_rrip.c create mode 100644 usr.sbin/makefs/cd9660/iso9660_rrip.h create mode 100644 usr.sbin/makefs/chfs.c create mode 100644 usr.sbin/makefs/chfs/Makefile.inc create mode 100644 usr.sbin/makefs/chfs/chfs_mkfs.c create mode 100644 usr.sbin/makefs/chfs/chfs_mkfs.h create mode 100644 usr.sbin/makefs/chfs_makefs.h create mode 100644 usr.sbin/makefs/ffs.c create mode 100644 usr.sbin/makefs/ffs.h create mode 100644 usr.sbin/makefs/ffs/Makefile.inc create mode 100644 usr.sbin/makefs/ffs/buf.c create mode 100644 usr.sbin/makefs/ffs/buf.h create mode 100644 usr.sbin/makefs/ffs/ffs_alloc.c create mode 100644 usr.sbin/makefs/ffs/ffs_balloc.c create mode 100644 usr.sbin/makefs/ffs/ffs_extern.h create mode 100644 usr.sbin/makefs/ffs/mkfs.c create mode 100644 usr.sbin/makefs/ffs/newfs_extern.h create mode 100644 usr.sbin/makefs/ffs/ufs_bmap.c create mode 100644 usr.sbin/makefs/ffs/ufs_inode.h create mode 100644 usr.sbin/makefs/makefs.8 create mode 100644 usr.sbin/makefs/makefs.c create mode 100644 usr.sbin/makefs/makefs.h create mode 100644 usr.sbin/makefs/msdos.c create mode 100644 usr.sbin/makefs/msdos.h create mode 100644 usr.sbin/makefs/msdos/Makefile.inc create mode 100644 usr.sbin/makefs/msdos/msdosfs_denode.c create mode 100644 usr.sbin/makefs/msdos/msdosfs_vfsops.c create mode 100644 usr.sbin/makefs/msdos/msdosfs_vnops.c create mode 100644 usr.sbin/makefs/udf.c create mode 100644 usr.sbin/makefs/udf/Makefile.inc create mode 100644 usr.sbin/makefs/udf/cdio_mmc_structs.h create mode 100644 usr.sbin/makefs/v7fs.c create mode 100644 usr.sbin/makefs/v7fs/Makefile.inc create mode 100644 usr.sbin/makefs/v7fs/v7fs_estimate.c create mode 100644 usr.sbin/makefs/v7fs/v7fs_populate.c create mode 100644 usr.sbin/makefs/v7fs_makefs.h create mode 100644 usr.sbin/makefs/walk.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 05771c69c..63d78d958 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -181,6 +181,9 @@ ./sbin/mkfs.mfs minix-sys ./sbin/mknod minix-sys ./sbin/newfs_ext2fs minix-sys +./sbin/newfs_msdos minix-sys +./sbin/newfs_udf minix-sys +./sbin/newfs_v7fs minix-sys ./sbin/nologin minix-sys ./sbin/ping minix-sys ./sbin/poweroff minix-sys @@ -906,6 +909,12 @@ ./usr/include/fs minix-sys ./usr/include/fs/puffs minix-sys ./usr/include/fs/puffs/puffs_msgif.h minix-sys +./usr/include/fs/udf minix-sys +./usr/include/fs/udf/ecma167-udf.h minix-sys +./usr/include/fs/udf/udf_mount.h minix-sys +./usr/include/fs/v7fs minix-sys +./usr/include/fs/v7fs/v7fs_args.h minix-sys +./usr/include/fs/v7fs/v7fs.h minix-sys ./usr/include/fstab.h minix-sys ./usr/include/fts.h minix-sys ./usr/include/ftw.h minix-sys @@ -5251,11 +5260,15 @@ ./usr/man/man8/link.8 minix-sys ./usr/man/man8/loadramdisk.8 minix-sys ./usr/man/man8/MAKEDEV.8 minix-sys +./usr/man/man8/makefs.8 minix-sys ./usr/man/man8/makewhatis.8 minix-sys ./usr/man/man8/mknod.8 minix-sys ./usr/man/man8/mtree.8 minix-sys ./usr/man/man8/netconf.8 minix-sys ./usr/man/man8/newfs_ext2fs.8 minix-sys +./usr/man/man8/newfs_msdos.8 minix-sys +./usr/man/man8/newfs_udf.8 minix-sys +./usr/man/man8/newfs_v7fs.8 minix-sys ./usr/man/man8/newroot.8 minix-sys obsolete ./usr/man/man8/nologin.8 minix-sys ./usr/man/man8/nonamed.8 minix-sys @@ -5340,6 +5353,7 @@ ./usr/sbin/installboot_nbsd minix-sys ./usr/sbin/kernel minix-sys ./usr/sbin/link minix-sys +./usr/sbin/makefs minix-sys ./usr/sbin/mkfs.mfsv3 minix-sys ./usr/sbin/mkproto minix-sys ./usr/sbin/mtree minix-sys diff --git a/sbin/Makefile b/sbin/Makefile index ed2ae4306..9af12675d 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -15,7 +15,7 @@ SUBDIR= \ shutdown \ # support for various file systems -SUBDIR+= newfs_ext2fs fsck_ext2fs +SUBDIR+= newfs_ext2fs fsck_ext2fs newfs_msdos newfs_udf newfs_v7fs .if !defined(__MINIX) SUBDIR+= newfs fsck_ffs fsdb dump restore clri tunefs resize_ffs SUBDIR+= newfs_lfs fsck_lfs dump_lfs resize_lfs diff --git a/sbin/mount/fattr.c b/sbin/mount/fattr.c new file mode 100644 index 000000000..aabb5605e --- /dev/null +++ b/sbin/mount/fattr.c @@ -0,0 +1,89 @@ +/* $NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $ */ + +/*- + * Copyright (c) 2000 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. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mountprog.h" + +int +a_num(const char *s, const char *id_type) +{ + int id; + char *ep; + + id = strtol(s, &ep, 0); + if (*ep || s == ep || id < 0) + errx(1, "unknown %s id: %s", id_type, s); + return id; +} + +gid_t +a_gid(const char *s) +{ + struct group *gr; + + if ((gr = getgrnam(s)) != NULL) + return gr->gr_gid; + return a_num(s, "group"); +} + +uid_t +a_uid(const char *s) +{ + struct passwd *pw; + + if ((pw = getpwnam(s)) != NULL) + return pw->pw_uid; + return a_num(s, "user"); +} + +mode_t +a_mask(const char *s) +{ + int rv; + char *ep; + + rv = strtol(s, &ep, 8); + if (s == ep || *ep || rv < 0) + errx(1, "invalid file mode: %s", s); + return rv; +} diff --git a/sbin/mount/mountprog.h b/sbin/mount/mountprog.h new file mode 100644 index 000000000..2c7327430 --- /dev/null +++ b/sbin/mount/mountprog.h @@ -0,0 +1,37 @@ +/* $NetBSD: mountprog.h,v 1.1 2008/08/05 20:57:45 pooka Exp $ */ + +/*- + * Copyright (c) 2000, 2008 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. + */ + +int a_num(const char *, const char *); +gid_t a_gid(const char *); +uid_t a_uid(const char *); +mode_t a_mask(const char *); + +int checkvfsname(const char *, const char **); +const char ** makevfslist(const char *); + +void pathadj(const char *, char *); diff --git a/sbin/newfs_msdos/Makefile b/sbin/newfs_msdos/Makefile new file mode 100644 index 000000000..1a6dfbff7 --- /dev/null +++ b/sbin/newfs_msdos/Makefile @@ -0,0 +1,25 @@ +# $NetBSD: Makefile,v 1.6 2013/01/21 20:28:38 christos Exp $ +# From: $FreeBSD: src/sbin/newfs_msdos/Makefile,v 1.5 2001/03/26 14:33:18 ru Exp $ + +.include + +PROG= newfs_msdos +MAN= newfs_msdos.8 +SRCS= newfs_msdos.c partutil.c mkfs_msdos.c + +LDADD+= -lutil +DPADD+= ${LIBUTIL} + +LDADD+=-lprop +DPADD+=${LIBPROP} + +FSCK=${NETBSDSRCDIR}/sbin/fsck +CPPFLAGS+=-I${.CURDIR} -I${FSCK} +.PATH: ${FSCK} + + +.if ${MACHINE} == "pc98" +CFLAGS+= -DPC98 +.endif + +.include diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c new file mode 100644 index 000000000..dc7dde316 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -0,0 +1,976 @@ +/* $NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static const char rcsid[] = + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $"; +#else +__RCSID("$NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#ifndef MAKEFS +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MAKEFS +#include "partutil.h" +#endif +#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 */ + +#ifndef MAKEFS +static int check_mounted(const char *, mode_t); +#endif +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; + } +#ifndef MAKEFS + if (!o.no_create) + if (check_mounted(fname, sb.st_mode) == -1) + return -1; +#endif + 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 */ + if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */ + bpb.spc = 1; + else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */ + bpb.spc = 8; + else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ + bpb.spc = 16; + else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */ + bpb.spc = 32; + else + bpb.spc = 64; /* otherwise 32k */ + } + } + + 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; +#ifdef SIGINFO + signal(SIGINFO, infohandler); +#endif + 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, bpb.rdcl); + 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; +} + +#ifndef MAKEFS +/* + * return -1 with error if file system is mounted. + */ +static int +check_mounted(const char *fname, mode_t mode) +{ + struct statvfs *mp; + const char *s1, *s2; + size_t len; + int n, r; + + if (!(n = getmntinfo(&mp, MNT_NOWAIT))) { + warn("getmntinfo"); + return -1; + } + len = strlen(_PATH_DEV); + s1 = fname; + if (!strncmp(s1, _PATH_DEV, len)) + s1 += len; + r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; + for (; n--; mp++) { + s2 = mp->f_mntfromname; + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || + !strcmp(s1, s2)) { + warnx("%s is mounted on %s", fname, mp->f_mntonname); + return -1; + } + } + return 0; +} +#endif + +/* + * 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; + +#ifndef MAKEFS + int maxpartitions = getmaxpartitions(); + + // XXX: Does not work with wedges + if (s2 && *s2 >= 'a' && *s2 <= 'a' + maxpartitions - 1) { + part = *s2++ - 'a'; + } +#endif + 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; +#ifndef MAKEFS + struct disk_geom geo; + struct dkwedge_info dkw; + + if (!create && getdiskinfo(fname, fd, NULL, &geo, &dkw) != -1) { + sector_size = geo.dg_secsize = 512; + nsectors = geo.dg_nsectors = 63; + ntracks = geo.dg_ntracks = 255; + size = dkw.dkw_size; + } else +#endif + { + 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/sbin/newfs_msdos/mkfs_msdos.h b/sbin/newfs_msdos/mkfs_msdos.h new file mode 100644 index 000000000..1987636d9 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.h @@ -0,0 +1,71 @@ +/* $NetBSD: mkfs_msdos.h,v 1.2 2013/01/23 15:29:15 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. + * 3. Neither the name of The NetBSD Foundation 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 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 +#include +#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/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8 new file mode 100644 index 000000000..930874a85 --- /dev/null +++ b/sbin/newfs_msdos/newfs_msdos.8 @@ -0,0 +1,241 @@ +.\" $NetBSD: newfs_msdos.8,v 1.18 2013/07/20 21:39:58 wiz 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. +.\" +.\" From: $FreeBSD: src/sbin/newfs_msdos/newfs_msdos.8,v 1.13 2001/08/14 10:01:48 ru Exp $ +.\" +.Dd March 26, 2009 +.Dt NEWFS_MSDOS 8 +.Os +.Sh NAME +.Nm newfs_msdos +.Nd construct a new MS-DOS (FAT) file system +.Sh SYNOPSIS +.Nm +.Op Fl N +.Op Fl @ Ar offset +.Op Fl B Ar boot +.Op Fl C Ar create-size +.Op Fl F Ar FAT-type +.Op Fl I Ar volid +.Op Fl L Ar label +.Op Fl O Ar OEM +.Op Fl S Ar sector-size +.Op Fl a Ar FAT-size +.Op Fl b Ar block-size +.Op Fl c Ar cluster-size +.Op Fl e Ar dirents +.Op Fl f Ar format +.Op Fl h Ar heads +.Op Fl i Ar info +.Op Fl k Ar backup +.Op Fl m Ar media +.Op Fl n Ar FATs +.Op Fl o Ar hidden +.Op Fl r Ar reserved +.Op Fl s Ar total +.Op Fl u Ar track-size +.Ar special +.Op Ar disktype +.Sh DESCRIPTION +The +.Nm +utility creates a FAT12, FAT16, or FAT32 file system on device or file named +.Ar special , +using +.Xr disktab 5 +entry +.Ar disktype +to determine geometry, if required. +.Pp +The options are as follow: +.Bl -tag -width indent +.It Fl N +Don't create a file system: just print out parameters. +.It Fl @ Ar offset +Build the filesystem at the specified offset in bytes in the device or file. +A suffix s, k, m, g (lower or upper case) +appended to the offset specifies that the +number is in sectors, kilobytes, megabytes or gigabytes, respectively. +.It Fl B Ar boot +Get bootstrap from file. +.It Fl C Ar create-size +Create the image file with the specified size. +A suffix character appended to the size is interpreted as for the +.Fl @ +option. +The file is created by truncating any existing file with the +same name, seeking just before the required size and writing +a single 0 byte. +As a consequence, the space occupied on disk +may be smaller than the size specified as a parameter. +.It Fl F Ar FAT-type +FAT type (one of 12, 16, or 32). +.It Fl I Ar volid +Volume ID. +.It Fl L Ar label +Volume label (up to 11 characters). +The label should consist of only those characters permitted +in regular DOS (8+3) filenames. +The default is +.Qq Li "NO_NAME" . +.It Fl O Ar OEM +OEM string (up to 8 characters). +The default is +.Qq Li "NetBSD" . +.It Fl S Ar sector-size +Number of bytes per sector. +Acceptable values are powers of 2 in the range 512 through 32768. +.It Fl a Ar FAT-size +Number of sectors per FAT. +.It Fl b Ar block-size +File system block size (bytes per cluster). +This should resolve to an acceptable number of sectors +per cluster (see below). +.It Fl c Ar cluster-size +Sectors per cluster. +Acceptable values are powers of 2 in the range 1 through 128. +If the block or cluster size are not specified, the code +uses a cluster between 512 bytes and 32K depending on +the filesystem size. +.It Fl e Ar dirents +Number of root directory entries (FAT12 and FAT16 only). +.It Fl f Ar format +Specify a standard (floppy disk) format. +The standard formats are (capacities in kilobytes): +160, 180, 320, 360, 640, 720, 1200, 1232, 1440, 2880. +.It Fl h Ar heads +Number of drive heads. +.It Fl i Ar info +Location of the file system info sector (FAT32 only). +A value of 0xffff signifies no info sector. +.It Fl k Ar backup +Location of the backup boot sector (FAT32 only). +A value of 0xffff signifies no backup sector. +.It Fl m Ar media +Media descriptor (acceptable range 0xf0 to 0xff). +.It Fl n Ar FATs +Number of FATs. +Acceptable values are 1 to 16 inclusive. +The default is 2. +.It Fl o Ar hidden +Number of hidden sectors. +.It Fl r Ar reserved +Number of reserved sectors. +.It Fl s Ar total +File system size. +.It Fl u Ar track-size +Number of sectors per track. +.El +.Pp +If +.Nm +receives a +.Dv SIGINFO +signal +(see the +.Sy status +argument for +.Xr stty 1 ) , +a line will be written to the standard error output indicating +the name of the device currently being formatted, the sector +number being written, and the total number of sectors to be written. +.Sh NOTES +If some parameters (e.g. size, number of sectors, etc.) are not specified +through options or disktype, the program tries to generate them +automatically. +In particular, the size is determined as the +device or file size minus the offset specified with the +.Fl @ +option. +When the geometry is not available, it is assumed to be +63 sectors, 255 heads. +The size is then rounded to become +a multiple of the track size and avoid complaints by some filesystem code. +.Pp +FAT file system parameters occupy a "Boot Sector BPB (BIOS Parameter +Block)" in the first of the "reserved" sectors which precede the actual +file system. +For reference purposes, this structure is presented below. +.Bd -literal +struct bsbpb { + u_int16_t bps; /* [-S] bytes per sector */ + u_int8_t spc; /* [-c] sectors per cluster */ + u_int16_t res; /* [-r] reserved sectors */ + u_int8_t nft; /* [-n] number of FATs */ + u_int16_t rde; /* [-e] root directory entries */ + u_int16_t sec; /* [-s] total sectors */ + u_int8_t mid; /* [-m] media descriptor */ + u_int16_t spf; /* [-a] sectors per FAT */ + u_int16_t spt; /* [-u] sectors per track */ + u_int16_t hds; /* [-h] drive heads */ + u_int32_t hid; /* [-o] hidden sectors */ + u_int32_t bsec; /* [-s] big total sectors */ +}; +/* FAT32 extensions */ +struct bsxbpb { + u_int32_t bspf; /* [-a] big sectors per FAT */ + u_int16_t xflg; /* control flags */ + u_int16_t vers; /* file system version */ + u_int32_t rdcl; /* root directory start cluster */ + u_int16_t infs; /* [-i] file system info sector */ + u_int16_t bkbs; /* [-k] backup boot sector */ +}; +.Ed +.Sh EXAMPLES +.Bd -literal -offset indent +newfs_msdos /dev/rwd1a +.Ed +.Pp +Create a file system, using default parameters, on +.Pa /dev/rwd1a . +.Bd -literal -offset indent +newfs_msdos -f 1440 -L foo /dev/rfd0a +.Ed +.Pp +Create a standard 1.44M file system, with volume label +.Ar foo , +on +.Pa /dev/rfd0a . +Create a 30MB image file, with the FAT partition starting +63 sectors within the image file: +.Bd -literal -offset indent +newfs_msdos -C 30M -@63s ./somefile +.Ed +.Sh DIAGNOSTICS +Exit status is 0 on success and 1 on error. +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr fdisk 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Nx 1.3 . +.Sh AUTHORS +.An Robert Nordier Aq Mt rnordier@FreeBSD.org . diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c new file mode 100644 index 000000000..42b61d196 --- /dev/null +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -0,0 +1,253 @@ +/* $NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos 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 +#ifndef lint +#if 0 +static const char rcsid[] = + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $"; +#else +__RCSID("$NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mkfs_msdos.h" + +#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) +#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) +#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) +#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) + +__dead static void usage(void); +static u_int argtou(const char *, u_int, u_int, const char *); +static off_t argtooff(const char *, const char *); + +/* + * Construct a FAT12, FAT16, or FAT32 file system. + */ +int +main(int argc, char *argv[]) +{ + static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; + struct msdos_options o; + char *fname, *dtype; + char buf[MAXPATHLEN]; + int ch; + + memset(&o, 0, sizeof(o)); + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case '@': + o.offset = argtooff(optarg, "offset"); + break; + case 'N': + o.no_create = 1; + break; + case 'B': + o.bootstrap = optarg; + break; + case 'C': + o.create_size = argtooff(optarg, "create size"); + break; + case 'F': + o.fat_type = atoi(optarg); + break; + case 'I': + o.volume_id = argto4(optarg, 0, "volume ID"); + o.volume_id_set = 1; + break; + case 'L': + o.volume_label = optarg; + break; + case 'O': + o.OEM_string = optarg; + break; + case 'S': + o.bytes_per_sector = argto2(optarg, 1, "bytes/sector"); + break; + case 'a': + o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT"); + break; + case 'b': + o.block_size = argtox(optarg, 1, "block size"); + break; + case 'c': + o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster"); + break; + case 'e': + o.directory_entries = argto2(optarg, 1, "directory entries"); + break; + case 'f': + o.floppy = optarg; + break; + case 'h': + o.drive_heads = argto2(optarg, 1, "drive heads"); + break; + case 'i': + o.info_sector = argto2(optarg, 1, "info sector"); + break; + case 'k': + o.backup_sector = argto2(optarg, 1, "backup sector"); + break; + case 'm': + o.media_descriptor = argto1(optarg, 0, "media descriptor"); + o.media_descriptor_set = 1; + break; + case 'n': + o.num_FAT = argto1(optarg, 1, "number of FATs"); + break; + case 'o': + o.hidden_sectors = argto4(optarg, 0, "hidden sectors"); + o.hidden_sectors_set = 1; + break; + case 'r': + o.reserved_sectors = argto2(optarg, 1, "reserved sectors"); + break; + case 's': + o.size = argto4(optarg, 1, "file system size"); + break; + case 'u': + o.sectors_per_track = argto2(optarg, 1, "sectors/track"); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) + usage(); + fname = *argv++; + if (!strchr(fname, '/') && !o.create_size) { + snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + err(1, NULL); + } + dtype = *argv; + return mkfs_msdos(fname, dtype, &o); +} + +/* + * Convert and check a numeric option argument. + */ +static u_int +argtou(const char *arg, u_int lo, u_int hi, const char *msg) +{ + char *s; + u_long x; + + errno = 0; + x = strtoul(arg, &s, 0); + if (errno || !*arg || *s || x < lo || x > hi) + errx(1, "%s: bad %s", arg, msg); + return x; +} + +/* + * Same for off_t, with optional skmgpP suffix + */ +static off_t +argtooff(const char *arg, const char *msg) +{ + char *s; + off_t x; + + errno = 0; + x = strtoll(arg, &s, 0); + /* allow at most one extra char */ + if (errno || x < 0 || (s[0] && s[1]) ) + errx(1, "%s: bad %s", arg, msg); + if (*s) { /* the extra char is the multiplier */ + switch (*s) { + default: + errx(1, "%s: bad %s", arg, msg); + /* notreached */ + + case 's': /* sector */ + case 'S': + x <<= 9; /* times 512 */ + break; + + case 'k': /* kilobyte */ + case 'K': + x <<= 10; /* times 1024 */ + break; + + case 'm': /* megabyte */ + case 'M': + x <<= 20; /* times 1024*1024 */ + break; + + case 'g': /* gigabyte */ + case 'G': + x <<= 30; /* times 1024*1024*1024 */ + break; + + case 'p': /* partition start */ + case 'P': /* partition start */ + case 'l': /* partition length */ + case 'L': /* partition length */ + errx(1, "%s: not supported yet %s", arg, msg); + return -1; + /* notreached */ + } + } + return x; +} + +/* + * Print usage message. + */ +static void +usage(void) +{ + fprintf(stderr, + "usage: %s [ -options ] special [disktype]\n", getprogname()); + fprintf(stderr, "where the options are:\n"); +static struct { + char o; + const char *h; +} opts[] = { +#define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc }, +ALLOPTS +#undef AOPT +}; + for (size_t i = 0; i < __arraycount(opts); i++) + fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h); + exit(1); +} diff --git a/sbin/newfs_udf/Makefile b/sbin/newfs_udf/Makefile new file mode 100644 index 000000000..008587e8a --- /dev/null +++ b/sbin/newfs_udf/Makefile @@ -0,0 +1,17 @@ +# $NetBSD: Makefile,v 1.4 2013/07/18 12:44:21 reinoud Exp $ + +.include + +PROG= newfs_udf +MAN= newfs_udf.8 +SRCS= newfs_udf.c udf_create.c udf_write.c udf_osta.c fattr.c + +MOUNT= ${NETBSDSRCDIR}/sbin/mount +KUDF= ${NETBSDSRCDIR}/sys/fs/udf +CPPFLAGS+= -I${MOUNT} -I${KUDF} -I${NETBSDSRCDIR}/sys +.PATH: ${MOUNT} ${KUDF} + +DPADD+=${LIBUTIL} +LDADD+=-lutil + +.include diff --git a/sbin/newfs_udf/newfs_udf.8 b/sbin/newfs_udf/newfs_udf.8 new file mode 100644 index 000000000..2660fb4e7 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.8 @@ -0,0 +1,191 @@ +.\" $NetBSD: newfs_udf.8,v 1.18 2013/08/06 12:15:20 wiz Exp $ +.\" +.\" Copyright (c) 2008 Reinoud Zandijk +.\" 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. +.\" +.\" +.Dd August 2, 2013 +.Dt NEWFS_UDF 8 +.Os +.Sh NAME +.Nm newfs_udf +.Nd construct a new UDF file system +.Sh SYNOPSIS +.Nm +.Op Fl cFM +.Op Fl B Ar blockingsize +.Op Fl L Ar loglabel +.Op Fl P Ar discid +.Op Fl p Ar percentage +.Op Fl S Ar sectorsize +.Op Fl s Ar size +.Op Fl t Ar gmtoff +.Op Fl V Ar max_udf +.Op Fl v Ar min_udf +.Ar special +.Sh DESCRIPTION +The +.Nm +utility creates an UDF file system on device +.Ar special +suitable for the media currently inserted. +.Pp +The options are as follow: +.Bl -tag -width indent +.It Fl B Ar blockingsize +When creating image files, specify the blocking size or packetsize of the media +to +.Ar blockingsize . +.It Fl c +Perform a crude surface check first to weed out disc faults on rewritable +media. +.It Fl F +Force file system construction on non-empty recordable media or create an +image file. +.It Fl L Ar loglabel +Set the disc logical label to the specified +.Ar loglabel . +.It Fl M +Disable metadata partition creation when selected UDF version or media dictates +this. +For strict conformance and interchange, don't disable this unless +its causing problems. +.It Fl P Ar discid +Set the physical disc label to the specified +.Ar discid . +.Pp +Prepend +.Ar discid +with volsetname separated with a ':' if wanted. +For strict conformance and interchange, don't set this manually unless it has +a unique hex number in the first 8 character positions. +.It Fl p Ar percentage +Percentage of partition to be initially reserved for metadata on the Metadata +partition. +It defaults to 20 %. +.It Fl S Ar sectorsize +Set the sectorsize for image files. +For strict conformance and interchange, don't set this manually. +.It Fl s Ar size +For image files, set the file size to the humanized size +.Ar size . +.It Fl t Ar gmtoff +Use the specified +.Ar gmtoff +as gmt time offset for recording times on the disc. +.It Fl V Ar max_udf +Select +.Ar max_udf +as the maximum UDF version to be supported. +For UDF version 2.50, use +.Dq 0x250 +or +.Dq 2.50 . +.It Fl v Ar min_udf +Select +.Ar min_udf +as the minimum UDF version to be supported. +For UDF version 2.01, use +.Dq 0x201 +or +.Dq 2.01 . +.El +.Sh NOTES +The UDF file system is defined for the entire optical medium. +It can only function on the entire CD/DVD/BD so the raw partition +has to be specified for read/write actions. +For +.Nm +this means specifying the raw device with the raw partition, i.e. +.Pa /dev/rcd0d +or +.Pa /dev/rcd0c . +.Pp +Some rewritable optical media needs to be formatted first before it can be +used by UDF. +This can be done using +.Xr mmcformat 8 . +.Pp +The default UDF version is version 2.01. +.Sh EXAMPLES +Create a file system, using the specified names on the device +.Pa /dev/rcd0d +with the default UDF version : +.Bd -literal -offset indent +newfs_udf -P "Encyclopedia:copy-nr-1" -L "volume 2" /dev/rcd0d +.Ed +.Pp +Create a 4.8 GiB sparse file and configure it using +.Xr vnconfig 8 +to be a 2048 sector size disc and create a new UDF file system on +.Pa /dev/rvnd0d +: +.Bd -literal -offset indent +dd if=/dev/zero of=bigdisk.2048.udf seek=9999999 count=1 +vnconfig -c vnd0 bigdisk.2048.udf 2048/1/1/1 +newfs_udf -L bigdisk /dev/rvnd0d +.Ed +.Pp +Create a 2 GiB file and create a new UDF file system on it using the default +512 byte sector size : +.Bd -literal -offset indent +newfs_udf -L bigdisk2 -F -s 2G bigdisk2.iso +.Ed +.Pp +Create a 200 MiB file and create a new UDF file system on it using a sector size +of 2048 : +.Bd -literal -offset indent +newfs_udf -L bigdisk2 -F -s 200M -S 2048 bigdisk3.iso +.Ed +.Pp +Create a new UDF file system on the inserted USB stick using its +native sectorsize of 512 : +.Bd -literal -offset indent +newfs_udf -L "My USB stick" /dev/rsd0d +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr mmcformat 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Nx 5.0 . +.Sh AUTHORS +.An Reinoud Zandijk Aq Mt reinoud@NetBSD.org +.Sh BUGS +The +.Ar P +and the +.Ar S +arguments have changed meaning. +The meaning of +.Ar S +has been merged into +.Ar P +since +.Nx 6.1 . diff --git a/sbin/newfs_udf/newfs_udf.c b/sbin/newfs_udf/newfs_udf.c new file mode 100644 index 000000000..eaab2b144 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.c @@ -0,0 +1,896 @@ +/* $NetBSD: newfs_udf.c,v 1.18 2013/08/09 15:11:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ + +/* + * TODO + * - implement metadata formatting for BD-R + * - implement support for a read-only companion partition? + */ + +#define _EXPOSE_MMC +#if 0 +# define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mountprog.h" +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + +/* prototypes */ +int newfs_udf(int argc, char **argv); +static void usage(void) __attribute__((__noreturn__)); + + +/* queue for temporary storage of sectors to be written out */ +struct wrsect { + uint64_t sectornr; + uint8_t *sector_data; + TAILQ_ENTRY(wrsect) next; +}; + +/* write queue and track blocking skew */ +TAILQ_HEAD(wrsect_list, wrsect) write_queue; + + +/* global variables describing disc and format requests */ +int fd; /* device: file descriptor */ +char *dev; /* device: name */ +struct mmc_discinfo mmc_discinfo; /* device: disc info */ + +char *format_str; /* format: string representation */ +int format_flags; /* format: attribute flags */ +int media_accesstype; /* derived from current mmc cap */ +int check_surface; /* for rewritables */ +int imagefile_secsize; /* for files */ +int emul_packetsize; /* for discs and files */ + +int wrtrack_skew; +int meta_perc = UDF_META_PERC; +float meta_fract = (float) UDF_META_PERC / 100.0; + + +/* --------------------------------------------------------------------- */ + +/* + * write queue implementation + */ + +int +udf_write_sector(void *sector, uint64_t location) +{ + struct wrsect *pos, *seekpos; + + + /* search location */ + TAILQ_FOREACH_REVERSE(seekpos, &write_queue, wrsect_list, next) { + if (seekpos->sectornr <= location) + break; + } + if ((seekpos == NULL) || (seekpos->sectornr != location)) { + pos = calloc(1, sizeof(struct wrsect)); + if (pos == NULL) + return ENOMEM; + /* allocate space for copy of sector data */ + pos->sector_data = calloc(1, context.sector_size); + if (pos->sector_data == NULL) + return ENOMEM; + pos->sectornr = location; + + if (seekpos) { + TAILQ_INSERT_AFTER(&write_queue, seekpos, pos, next); + } else { + TAILQ_INSERT_HEAD(&write_queue, pos, next); + } + } else { + pos = seekpos; + } + memcpy(pos->sector_data, sector, context.sector_size); + + return 0; +} + + +/* + * Now all write requests are queued in the TAILQ, write them out to the + * disc/file image. Special care needs to be taken for devices that are only + * strict overwritable i.e. only in packet size chunks + * + * XXX support for growing vnd? + */ + +int +writeout_write_queue(void) +{ + struct wrsect *pos; + uint64_t offset; + uint64_t line_start, new_line_start; + uint32_t line_len, line_offset, relpos; + uint32_t blockingnr; + uint8_t *linebuf, *adr; + + blockingnr = layout.blockingnr; + line_len = blockingnr * context.sector_size; + line_offset = wrtrack_skew * context.sector_size; + + linebuf = malloc(line_len); + if (linebuf == NULL) + return ENOMEM; + + pos = TAILQ_FIRST(&write_queue); + bzero(linebuf, line_len); + + /* + * Always writing out in whole lines now; this is slightly wastefull + * on logical overwrite volumes but it reduces complexity and the loss + * is near zero compared to disc size. + */ + line_start = (pos->sectornr - wrtrack_skew) / blockingnr; + TAILQ_FOREACH(pos, &write_queue, next) { + new_line_start = (pos->sectornr - wrtrack_skew) / blockingnr; + if (new_line_start != line_start) { + /* write out */ + offset = (uint64_t) line_start * line_len + line_offset; +#ifdef DEBUG + printf("WRITEOUT %08"PRIu64" + %02d -- " + "[%08"PRIu64"..%08"PRIu64"]\n", + offset / context.sector_size, blockingnr, + offset / context.sector_size, + offset / context.sector_size + blockingnr-1); +#endif + if (pwrite(fd, linebuf, line_len, offset) < 0) { + perror("Writing failed"); + return errno; + } + line_start = new_line_start; + bzero(linebuf, line_len); + } + + relpos = (pos->sectornr - wrtrack_skew) % blockingnr; + adr = linebuf + relpos * context.sector_size; + memcpy(adr, pos->sector_data, context.sector_size); + } + /* writeout last chunk */ + offset = (uint64_t) line_start * line_len + line_offset; +#ifdef DEBUG + printf("WRITEOUT %08"PRIu64" + %02d -- [%08"PRIu64"..%08"PRIu64"]\n", + offset / context.sector_size, blockingnr, + offset / context.sector_size, + offset / context.sector_size + blockingnr-1); +#endif + if (pwrite(fd, linebuf, line_len, offset) < 0) { + perror("Writing failed"); + return errno; + } + + /* success */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main + * code in sys/fs/udf/ + */ + +#ifdef DEBUG +static void +udf_dump_discinfo(struct mmc_discinfo *di) +{ + char bits[128]; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, (uint64_t) di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); + printf("\n"); + printf("\tlast_possible_lba %d\n", di->last_possible_lba); + printf("\n"); +} +#else +#define udf_dump_discinfo(a); +#endif + +/* --------------------------------------------------------------------- */ + +static int +udf_update_discinfo(struct mmc_discinfo *di) +{ + struct stat st; + struct disklabel disklab; + struct partition *dp; + off_t size, sectors, secsize; + int partnr, error; + + memset(di, 0, sizeof(struct mmc_discinfo)); + + /* check if we're on a MMC capable device, i.e. CD/DVD */ + error = ioctl(fd, MMCGETDISCINFO, di); + if (error == 0) + return 0; + + /* (re)fstat the file */ + fstat(fd, &st); + + if (S_ISREG(st.st_mode)) { + /* file support; we pick the minimum sector size allowed */ + size = st.st_size; + secsize = imagefile_secsize; + sectors = size / secsize; + } else { + /* + * disc partition support; note we can't use DIOCGPART in + * userland so get disc label and use the stat info to get the + * partition number. + */ + if (ioctl(fd, DIOCGDINFO, &disklab) == -1) { + /* failed to get disclabel! */ + perror("disklabel"); + return errno; + } + + /* get disk partition it refers to */ + fstat(fd, &st); + partnr = DISKPART(st.st_rdev); + dp = &disklab.d_partitions[partnr]; + + /* TODO problem with last_possible_lba on resizable VND */ + if (dp->p_size == 0) { + perror("faulty disklabel partition returned, " + "check label\n"); + return EIO; + } + + sectors = dp->p_size; + secsize = disklab.d_secsize; + } + + /* set up a disc info profile for partitions */ + di->mmc_profile = 0x01; /* disc type */ + di->mmc_class = MMC_CLASS_DISC; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE | + MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE; + di->mmc_cap = di->mmc_cur; + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + di->last_possible_lba = sectors - 1; + di->sector_size = secsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + return 0; +} + + +int +udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti) +{ + int error, class; + + class = di->mmc_class; + if (class != MMC_CLASS_DISC) { + /* tracknr specified in struct ti */ + error = ioctl(fd, MMCGETTRACKINFO, ti); + return error; + } + + /* discs partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti (TODO check for resized vnds) */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = emul_packetsize; + + /* TODO support for resizable vnd */ + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + + +static int +udf_setup_writeparams(struct mmc_discinfo *di) +{ + struct mmc_writeparams mmc_writeparams; + int error; + + if (di->mmc_class == MMC_CLASS_DISC) + return 0; + + /* + * only CD burning normally needs setting up, but other disc types + * might need other settings to be made. The MMC framework will set up + * the nessisary recording parameters according to the disc + * characteristics read in. Modifications can be made in the discinfo + * structure passed to change the nature of the disc. + */ + memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams)); + mmc_writeparams.mmc_class = di->mmc_class; + mmc_writeparams.mmc_cur = di->mmc_cur; + + /* + * UDF dictates first track to determine track mode for the whole + * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1] + * To prevent problems with a `reserved' track in front we start with + * the 2nd track and if that is not valid, go for the 1st. + */ + mmc_writeparams.tracknr = 2; + mmc_writeparams.data_mode = MMC_DATAMODE_DEFAULT; /* XA disc */ + mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT; /* data */ + + error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + if (error) { + mmc_writeparams.tracknr = 1; + error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + } + return error; +} + + +static void +udf_synchronise_caches(void) +{ + struct mmc_op mmc_op; + + bzero(&mmc_op, sizeof(struct mmc_op)); + mmc_op.operation = MMC_OP_SYNCHRONISECACHE; + + /* this device might not know this ioct, so just be ignorant */ + (void) ioctl(fd, MMCOP, &mmc_op); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_prepare_disc(void) +{ + struct mmc_trackinfo ti; + struct mmc_op op; + int tracknr, error; + + /* If the last track is damaged, repair it */ + ti.tracknr = mmc_discinfo.last_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + if (ti.flags & MMC_TRACKINFO_DAMAGED) { + /* + * Need to repair last track before anything can be done. + * this is an optional command, so ignore its error but report + * warning. + */ + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_REPAIRTRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = ti.tracknr; + error = ioctl(fd, MMCOP, &op); + + if (error) + (void)printf("Drive can't explicitly repair last " + "damaged track, but it might autorepair\n"); + } + /* last track (if any) might not be damaged now, operations are ok now */ + + /* setup write parameters from discinfo */ + error = udf_setup_writeparams(&mmc_discinfo); + if (error) + return error; + + /* if the drive is not sequential, we're done */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) == 0) + return 0; + +#ifdef notyet + /* if last track is not the reserved but an empty track, unreserve it */ + if (ti.flags & MMC_TRACKINFO_BLANK) { + if (ti.flags & MMC_TRACKINFO_RESERVED == 0) { + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_UNRESERVETRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = ti.tracknr; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + + /* update discinfo since it changed by the operation */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) + return error; + } + } +#endif + + /* close the last session if its still open */ + if (mmc_discinfo.last_session_state == MMC_STATE_INCOMPLETE) { + printf("Closing last open session if present\n"); + /* close all associated tracks */ + tracknr = mmc_discinfo.first_track_last_session; + while (tracknr <= mmc_discinfo.last_track_last_session) { + ti.tracknr = tracknr; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + printf("\tClosing open track %d\n", tracknr); + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_CLOSETRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = tracknr; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + tracknr ++; + } + printf("Closing session\n"); + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_CLOSESESSION; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.sessionnr = mmc_discinfo.num_sessions; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + + /* update discinfo since it changed by the operations */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) + return error; + } + + if (format_flags & FORMAT_TRACK512) { + /* get last track again */ + ti.tracknr = mmc_discinfo.last_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + /* Split up the space at 512 for iso cd9660 hooking */ + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_RESERVETRACK_NWA; /* UPTO nwa */ + op.mmc_profile = mmc_discinfo.mmc_profile; + op.extent = 512; /* size */ + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_surface_check(void) +{ + uint32_t loc, block_bytes; + uint32_t sector_size, blockingnr, bpos; + uint8_t *buffer; + int error, num_errors; + + sector_size = context.sector_size; + blockingnr = layout.blockingnr; + + block_bytes = layout.blockingnr * sector_size; + if ((buffer = malloc(block_bytes)) == NULL) + return ENOMEM; + + /* set all one to not kill Flash memory? */ + for (bpos = 0; bpos < block_bytes; bpos++) + buffer[bpos] = 0x00; + + printf("\nChecking disc surface : phase 1 - writing\n"); + num_errors = 0; + loc = layout.first_lba; + while (loc <= layout.last_lba) { + /* write blockingnr sectors */ + error = pwrite(fd, buffer, block_bytes, loc*sector_size); + printf(" %08d + %d (%02d %%)\r", loc, blockingnr, + (int)((100.0 * loc)/layout.last_lba)); + fflush(stdout); + if (error == -1) { + /* block is bad */ + printf("BAD block at %08d + %d \n", + loc, layout.blockingnr); + if ((error = udf_register_bad_block(loc))) { + free(buffer); + return error; + } + num_errors ++; + } + loc += layout.blockingnr; + } + + printf("\nChecking disc surface : phase 2 - reading\n"); + num_errors = 0; + loc = layout.first_lba; + while (loc <= layout.last_lba) { + /* read blockingnr sectors */ + error = pread(fd, buffer, block_bytes, loc*sector_size); + printf(" %08d + %d (%02d %%)\r", loc, blockingnr, + (int)((100.0 * loc)/layout.last_lba)); + fflush(stdout); + if (error == -1) { + /* block is bad */ + printf("BAD block at %08d + %d \n", + loc, layout.blockingnr); + if ((error = udf_register_bad_block(loc))) { + free(buffer); + return error; + } + num_errors ++; + } + loc += layout.blockingnr; + } + printf("Scan complete : %d bad blocks found\n", num_errors); + free(buffer); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_do_newfs(void) +{ + int error; + + error = udf_do_newfs_prefix(); + if (error) + return error; + error = udf_do_rootdir(); + if (error) + return error; + error = udf_do_newfs_postfix(); + + return error; +} + + + +/* --------------------------------------------------------------------- */ + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [-cFM] [-L loglabel] " + "[-P discid] [-S sectorsize] [-s size] [-p perc] " + "[-t gmtoff] [-v min_udf] [-V max_udf] special\n", getprogname()); + exit(EXIT_FAILURE); +} + + +int +main(int argc, char **argv) +{ + struct tm *tm; + struct stat st; + time_t now; + off_t setsize; + char scrap[255], *colon; + int ch, req_enable, req_disable, force; + int error; + + setprogname(argv[0]); + + /* initialise */ + format_str = strdup(""); + req_enable = req_disable = 0; + format_flags = FORMAT_INVALID; + force = 0; + check_surface = 0; + setsize = 0; + imagefile_secsize = 512; /* minimum allowed sector size */ + emul_packetsize = 32; /* reasonable default */ + + srandom((unsigned long) time(NULL)); + udf_init_create_context(); + context.app_name = "*NetBSD newfs"; + context.app_version_main = APP_VERSION_MAIN; + context.app_version_sub = APP_VERSION_SUB; + context.impl_name = IMPL_NAME; + + /* minimum and maximum UDF versions we advise */ + context.min_udf = 0x201; + context.max_udf = 0x201; + + /* use user's time zone as default */ + (void)time(&now); + tm = localtime(&now); + context.gmtoff = tm->tm_gmtoff; + + /* process options */ + while ((ch = getopt(argc, argv, "cFL:Mp:P:s:S:B:t:v:V:")) != -1) { + switch (ch) { + case 'c' : + check_surface = 1; + break; + case 'F' : + force = 1; + break; + case 'L' : + if (context.logvol_name) free(context.logvol_name); + context.logvol_name = strdup(optarg); + break; + case 'M' : + req_disable |= FORMAT_META; + break; + case 'p' : + meta_perc = a_num(optarg, "meta_perc"); + /* limit to `sensible` values */ + meta_perc = MIN(meta_perc, 99); + meta_perc = MAX(meta_perc, 1); + meta_fract = (float) meta_perc/100.0; + break; + case 'v' : + context.min_udf = a_udf_version(optarg, "min_udf"); + if (context.min_udf > context.max_udf) + context.max_udf = context.min_udf; + break; + case 'V' : + context.max_udf = a_udf_version(optarg, "max_udf"); + if (context.min_udf > context.max_udf) + context.min_udf = context.max_udf; + break; + case 'P' : + /* check if there is a ':' in the name */ + if ((colon = strstr(optarg, ":"))) { + if (context.volset_name) + free(context.volset_name); + *colon = 0; + context.volset_name = strdup(optarg); + optarg = colon+1; + } + if (context.primary_name) + free(context.primary_name); + if ((strstr(optarg, ":"))) { + perror("primary name can't have ':' in its name"); + return EXIT_FAILURE; + } + context.primary_name = strdup(optarg); + break; + case 's' : + /* support for files, set file size */ + /* XXX support for formatting recordables on vnd/file? */ + if (dehumanize_number(optarg, &setsize) < 0) { + perror("can't parse size argument"); + return EXIT_FAILURE; + } + setsize = MAX(0, setsize); + break; + case 'S' : + imagefile_secsize = a_num(optarg, "secsize"); + imagefile_secsize = MAX(512, imagefile_secsize); + break; + case 'B' : + emul_packetsize = a_num(optarg, + "blockingnr, packetsize"); + emul_packetsize = MAX(emul_packetsize, 1); + emul_packetsize = MIN(emul_packetsize, 32); + break; + case 't' : + /* time zone overide */ + context.gmtoff = a_num(optarg, "gmtoff"); + break; + default : + usage(); + /* NOTREACHED */ + } + } + + if (optind + 1 != argc) + usage(); + + /* get device and directory specifier */ + dev = argv[optind]; + + /* open device */ + if ((fd = open(dev, O_RDWR, 0)) == -1) { + /* check if we need to create a file */ + fd = open(dev, O_RDONLY, 0); + if (fd > 0) { + perror("device is there but can't be opened for read/write"); + return EXIT_FAILURE; + } + if (!force) { + perror("can't open device"); + return EXIT_FAILURE; + } + if (setsize == 0) { + perror("need to create image file but no size specified"); + return EXIT_FAILURE; + } + /* need to create a file */ + fd = open(dev, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd == -1) { + perror("can't create image file"); + return EXIT_FAILURE; + } + } + + /* stat the device */ + if (fstat(fd, &st) != 0) { + perror("can't stat the device"); + close(fd); + return EXIT_FAILURE; + } + + if (S_ISREG(st.st_mode)) { + if (setsize == 0) + setsize = st.st_size; + /* sanitise arguments */ + imagefile_secsize &= ~511; + setsize &= ~(imagefile_secsize-1); + + if (ftruncate(fd, setsize)) { + perror("can't resize file"); + return EXIT_FAILURE; + } + } + + /* formatting can only be done on raw devices */ + if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) { + printf("%s is not a raw device\n", dev); + close(fd); + return EXIT_FAILURE; + } + + /* just in case something went wrong, synchronise the drive's cache */ + udf_synchronise_caches(); + + /* get 'disc' information */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) { + perror("can't retrieve discinfo"); + close(fd); + return EXIT_FAILURE; + } + + /* derive disc identifiers when not specified and check given */ + error = udf_proces_names(); + if (error) { + /* error message has been printed */ + close(fd); + return EXIT_FAILURE; + } + + /* derive newfs disc format from disc profile */ + error = udf_derive_format(req_enable, req_disable, force); + if (error) { + /* error message has been printed */ + close(fd); + return EXIT_FAILURE; + } + + udf_dump_discinfo(&mmc_discinfo); + printf("Formatting disc compatible with UDF version %x to %x\n\n", + context.min_udf, context.max_udf); + (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS, + (uint64_t) format_flags); + printf("UDF properties %s\n", scrap); + printf("Volume set `%s'\n", context.volset_name); + printf("Primary volume `%s`\n", context.primary_name); + printf("Logical volume `%s`\n", context.logvol_name); + if (format_flags & FORMAT_META) + printf("Metadata percentage %d %%\n", meta_perc); + printf("\n"); + + /* prepare disc if nessisary (recordables mainly) */ + error = udf_prepare_disc(); + if (error) { + perror("preparing disc failed"); + close(fd); + return EXIT_FAILURE; + }; + + /* setup sector writeout queue's */ + TAILQ_INIT(&write_queue); + + /* perform the newfs itself */ + error = udf_do_newfs(); + if (!error) { + /* write out sectors */ + error = writeout_write_queue(); + } + + /* in any case, synchronise the drive's cache to prevent lockups */ + udf_synchronise_caches(); + + close(fd); + if (error) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ + diff --git a/sbin/newfs_udf/newfs_udf.h b/sbin/newfs_udf/newfs_udf.h new file mode 100644 index 000000000..1c21a0b48 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ + +#ifndef _FS_UDF_NEWFS_UDF_H_ +#define _FS_UDF_NEWFS_UDF_H_ + +/* general settings */ +#define UDF_512_TRACK 0 /* NOT recommended */ +#define UDF_META_PERC 20 /* picked */ + +/* Identifying myself */ +#define APP_VERSION_MAIN 0 +#define APP_VERSION_SUB 5 +#define IMPL_NAME "*NetBSD userland UDF" + + +/* global variables describing disc and format requests */ +extern int fd; /* device: file descriptor */ +extern char *dev; /* device: name */ +extern struct mmc_discinfo mmc_discinfo;/* device: disc info */ + +extern char *format_str; /* format: string representation */ +extern int format_flags; /* format: attribute flags */ +extern int media_accesstype; /* derived from current mmc cap */ +extern int check_surface; /* for rewritables */ + +extern int wrtrack_skew; +extern int meta_perc; +extern float meta_fract; + + +/* shared structure between udf_create.c users */ +struct udf_create_context context; +struct udf_disclayout layout; + +/* prototypes */ +int udf_write_sector(void *sector, uint64_t location); +int udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti); + +/* tmp */ +int writeout_write_queue(void); +int udf_surface_check(void); + +#endif /* _FS_UDF_UDF_WRITE_H_ */ diff --git a/sbin/newfs_udf/udf_create.c b/sbin/newfs_udf/udf_create.c new file mode 100644 index 000000000..e40c70868 --- /dev/null +++ b/sbin/newfs_udf/udf_create.c @@ -0,0 +1,2392 @@ +/* $NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unicode.h" +#include "udf_create.h" + + +#if 0 +# ifndef DEBUG +# define DEBUG +# endif +#endif + +/* + * NOTE that there is some overlap between this code and the udf kernel fs. + * This is intentially though it might better be factored out one day. + */ + +void +udf_init_create_context(void) +{ + /* clear */ + memset(&context, 0, sizeof(struct udf_create_context)); + + /* fill with defaults currently known */ + context.dscrver = 3; + context.min_udf = 0x0102; + context.max_udf = 0x0260; + context.serialnum = 1; /* default */ + + context.gmtoff = 0; + context.sector_size = 512; /* minimum for UDF */ + + context.logvol_name = NULL; + context.primary_name = NULL; + context.volset_name = NULL; + context.fileset_name = NULL; + + /* most basic identification */ + context.app_name = "*NetBSD"; + context.app_version_main = 0; + context.app_version_sub = 0; + context.impl_name = "*NetBSD"; + + context.vds_seq = 0; /* first one starts with zero */ + + /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ + context.unique_id = 0x10; + + context.num_files = 0; + context.num_directories = 0; + + context.data_part = 0; + context.metadata_part = 0; + context.metadata_alloc_pos = 0; + context.data_alloc_pos = 0; +} + + +/* version can be specified as 0xabc or a.bc */ +static int +parse_udfversion(const char *pos, uint32_t *version) { + int hex = 0; + char c1, c2, c3, c4; + + *version = 0; + if (*pos == '0') { + pos++; + /* expect hex format */ + hex = 1; + if (*pos++ != 'x') + return 1; + } + + c1 = *pos++; + if (c1 < '0' || c1 > '9') + return 1; + c1 -= '0'; + + c2 = *pos++; + if (!hex) { + if (c2 != '.') + return 1; + c2 = *pos++; + } + if (c2 < '0' || c2 > '9') + return 1; + c2 -= '0'; + + c3 = *pos++; + if (c3 < '0' || c3 > '9') + return 1; + c3 -= '0'; + + c4 = *pos++; + if (c4 != 0) + return 1; + + *version = c1 * 0x100 + c2 * 0x10 + c3; + return 0; +} + + +/* parse a given string for an udf version */ +int +a_udf_version(const char *s, const char *id_type) +{ + uint32_t version; + + if (parse_udfversion(s, &version)) + errx(1, "unknown %s id %s; specify as hex or float", id_type, s); + return version; +} + + +static uint32_t +udf_space_bitmap_len(uint32_t part_size) +{ + return sizeof(struct space_bitmap_desc)-1 + + part_size/8; +} + + +static uint32_t +udf_bytes_to_sectors(uint64_t bytes) +{ + uint32_t sector_size = layout.sector_size; + return (bytes + sector_size -1) / sector_size; +} + + +int +udf_calculate_disc_layout(int format_flags, int min_udf, + uint32_t wrtrack_skew, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr, + uint32_t sparable_blocks, float meta_fract) +{ + uint64_t kbsize, bytes; + uint32_t sparable_blockingnr; + uint32_t align_blockingnr; + uint32_t pos, mpos; + + /* clear */ + memset(&layout, 0, sizeof(layout)); + + /* fill with parameters */ + layout.wrtrack_skew = wrtrack_skew; + layout.first_lba = first_lba; + layout.last_lba = last_lba; + layout.sector_size = sector_size; + layout.blockingnr = blockingnr; + layout.sparable_blocks = sparable_blocks; + + /* start disc layouting */ + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum `sector size' 2048 + */ + layout.iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + first_lba; + + /* anchor starts at specified offset in sectors */ + layout.anchors[0] = first_lba + 256; + if (format_flags & FORMAT_TRACK512) + layout.anchors[0] = first_lba + 512; + layout.anchors[1] = last_lba - 256; + layout.anchors[2] = last_lba; + + /* update workable space */ + first_lba = layout.anchors[0] + blockingnr; + last_lba = layout.anchors[1] - 1; + + /* XXX rest of anchor packet can be added to unallocated space descr */ + + /* reserve space for VRS and VRS copy and associated tables */ + layout.vds_size = MAX(16, blockingnr); /* UDF 2.2.3.1+2 */ + layout.vds1 = first_lba; + first_lba += layout.vds_size; /* next packet */ + + if (format_flags & FORMAT_SEQUENTIAL) { + /* for sequential, append them ASAP */ + layout.vds2 = first_lba; + first_lba += layout.vds_size; + } else { + layout.vds2 = layout.anchors[1] - layout.vds_size; + last_lba = layout.vds2 - 1; /* XXX -1 ?? */ + } + + /* reserve space for logvol integrity sequence */ + layout.lvis_size = MAX(8192/sector_size, 2 * blockingnr); + if (format_flags & FORMAT_VAT) + layout.lvis_size = 2; + if (format_flags & FORMAT_WORM) + layout.lvis_size = 64 * blockingnr; + + /* TODO skip bad blocks in LVID sequence; for now use f.e. */ +//first_lba+=128; + layout.lvis = first_lba; + first_lba += layout.lvis_size; + + /* initial guess of UDF partition size */ + layout.part_start_lba = first_lba; + layout.part_size_lba = last_lba - layout.part_start_lba; + + /* all non sequential media needs an unallocated space bitmap */ + layout.alloc_bitmap_dscr_size = 0; + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + bytes = udf_space_bitmap_len(layout.part_size_lba); + layout.alloc_bitmap_dscr_size = udf_bytes_to_sectors(bytes); + + /* XXX freed space map when applicable */ + } + + /* + * Note that for (bug) compatibility with version UDF 2.00 (fixed in + * 2.01 and higher) the blocking size needs to be 32 sectors otherwise + * the drive's blockingnr. + */ + + sparable_blockingnr = blockingnr; + if (min_udf <= 0x200) + sparable_blockingnr = 32; + + align_blockingnr = blockingnr; + if (format_flags & (FORMAT_SPARABLE | FORMAT_META)) + align_blockingnr = sparable_blockingnr; + + layout.align_blockingnr = align_blockingnr; + layout.sparable_blockingnr = sparable_blockingnr; + + /* + * Align partition LBA space to blocking granularity. Not strickly + * nessisary for non sparables but safer for the VRS data since it is + * not updated sporadically + */ + + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { +#ifdef DEBUG + printf("Lost %d slack sectors at start\n", UDF_ROUNDUP( + first_lba - wrtrack_skew, align_blockingnr) - + (first_lba - wrtrack_skew)); + printf("Lost %d slack sectors at end\n", + (first_lba - wrtrack_skew) - UDF_ROUNDDOWN( + first_lba - wrtrack_skew, align_blockingnr)); +#endif + + first_lba = UDF_ROUNDUP( first_lba - wrtrack_skew, + align_blockingnr); + last_lba = UDF_ROUNDDOWN(last_lba - wrtrack_skew, + align_blockingnr); + } + + if ((format_flags & FORMAT_SPARABLE) == 0) + layout.sparable_blocks = 0; + + if (format_flags & FORMAT_SPARABLE) { + layout.sparable_area_size = + layout.sparable_blocks * sparable_blockingnr; + + /* a sparing table descriptor is a whole blockingnr sectors */ + layout.sparing_table_dscr_lbas = sparable_blockingnr; + + /* place the descriptors at the start and end of the area */ + layout.spt_1 = first_lba; + first_lba += layout.sparing_table_dscr_lbas; + + layout.spt_2 = last_lba - layout.sparing_table_dscr_lbas; + last_lba -= layout.sparing_table_dscr_lbas; + + /* allocate sparable section */ + layout.sparable_area = first_lba; + first_lba += layout.sparable_area_size; + } + + /* update guess of UDF partition size */ + layout.part_start_lba = first_lba; + layout.part_size_lba = last_lba - layout.part_start_lba; + + /* determine partition selection for data and metadata */ + context.data_part = 0; + context.metadata_part = context.data_part; + if ((format_flags & FORMAT_VAT) || (format_flags & FORMAT_META)) + context.metadata_part = context.data_part + 1; + + /* + * Pick fixed logical space sector numbers for main FSD, rootdir and + * unallocated space. The reason for this pre-allocation is that they + * are referenced in the volume descriptor sequence and hence can't be + * allocated later. + */ + pos = 0; + layout.unalloc_space = pos; + pos += layout.alloc_bitmap_dscr_size; + + /* claim metadata descriptors and partition space [UDF 2.2.10] */ + if (format_flags & FORMAT_META) { + /* note: all in backing partition space */ + layout.meta_file = pos++; + layout.meta_bitmap = pos++;; + layout.meta_mirror = layout.part_size_lba-1; + layout.meta_alignment = MAX(blockingnr, sparable_blockingnr); + layout.meta_blockingnr = MAX(layout.meta_alignment, 32); + + /* calculate our partition length and store in sectors */ + layout.meta_part_size_lba = layout.part_size_lba * meta_fract; + layout.meta_part_size_lba = MAX(layout.meta_part_size_lba, 32); + layout.meta_part_size_lba = + UDF_ROUNDDOWN(layout.meta_part_size_lba, layout.meta_blockingnr); + + /* calculate positions */ + bytes = udf_space_bitmap_len(layout.meta_part_size_lba); + layout.meta_bitmap_dscr_size = udf_bytes_to_sectors(bytes); + + layout.meta_bitmap_space = pos; + pos += layout.meta_bitmap_dscr_size; + + layout.meta_part_start_lba = UDF_ROUNDUP(pos, layout.meta_alignment); + } + + mpos = (context.metadata_part == context.data_part) ? pos : 0; + layout.fsd = mpos; mpos += 1; + layout.rootdir = mpos; mpos += 1; + layout.vat = mpos; mpos += 1; /* if present */ + +#if 0 + printf("Summary so far\n"); + printf("\tiso9660_vrs\t\t%d\n", layout.iso9660_vrs); + printf("\tanchor0\t\t\t%d\n", layout.anchors[0]); + printf("\tanchor1\t\t\t%d\n", layout.anchors[1]); + printf("\tanchor2\t\t\t%d\n", layout.anchors[2]); + printf("\tvds_size\t\t%d\n", layout.vds_size); + printf("\tvds1\t\t\t%d\n", layout.vds1); + printf("\tvds2\t\t\t%d\n", layout.vds2); + printf("\tlvis_size\t\t%d\n", layout.lvis_size); + printf("\tlvis\t\t\t%d\n", layout.lvis); + if (format_flags & FORMAT_SPARABLE) { + printf("\tsparable size\t\t%d\n", layout.sparable_area_size); + printf("\tsparable\t\t%d\n", layout.sparable_area); + } + printf("\tpartition start lba\t%d\n", layout.part_start_lba); + printf("\tpartition size\t\t%d KiB, %d MiB\n", + (layout.part_size_lba * sector_size) / 1024, + (layout.part_size_lba * sector_size) / (1024*1024)); + if ((format_flags & FORMAT_SEQUENTIAL) == 0) { + printf("\tpart bitmap start\t%d\n", layout.unalloc_space); + printf("\t\tfor %d lba\n", layout.alloc_bitmap_dscr_size); + } + if (format_flags & FORMAT_META) { + printf("\tmeta blockingnr\t\t%d\n", layout.meta_blockingnr); + printf("\tmeta alignment\t\t%d\n", layout.meta_alignment); + printf("\tmeta size\t\t%d KiB, %d MiB\n", + (layout.meta_part_size_lba * sector_size) / 1024, + (layout.meta_part_size_lba * sector_size) / (1024*1024)); + printf("\tmeta file\t\t%d\n", layout.meta_file); + printf("\tmeta mirror\t\t%d\n", layout.meta_mirror); + printf("\tmeta bitmap\t\t%d\n", layout.meta_bitmap); + printf("\tmeta bitmap start\t%d\n", layout.meta_bitmap_space); + printf("\t\tfor %d lba\n", layout.meta_bitmap_dscr_size); + printf("\tmeta space start\t%d\n", layout.meta_part_start_lba); + printf("\t\tfor %d lba\n", layout.meta_part_size_lba); + } + printf("\n"); +#endif + + kbsize = (uint64_t) last_lba * sector_size; + printf("Total space on this medium approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n", + kbsize/1024, kbsize/(1024*1024)); + kbsize = (uint64_t)(layout.part_size_lba - layout.alloc_bitmap_dscr_size + - layout.meta_bitmap_dscr_size) * sector_size; + printf("Free space on this volume approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n\n", + kbsize/1024, kbsize/(1024*1024)); + + return 0; +} + + +int +udf_validate_tag_sum(union dscrptr *dscr) +{ + struct desc_tag *tag = &dscr->tag; + uint8_t *pos, sum, cnt; + + /* calculate TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) sum += *pos; + pos++; + }; + tag->cksum = sum; /* 8 bit */ + + return 0; +} + + +/* assumes sector number of descriptor to be allready present */ +int +udf_validate_tag_and_crc_sums(union dscrptr *dscr) +{ + struct desc_tag *tag = &dscr->tag; + uint16_t crc; + + /* check payload CRC if applicable */ + if (udf_rw16(tag->desc_crc_len) > 0) { + crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, + udf_rw16(tag->desc_crc_len)); + tag->desc_crc = udf_rw16(crc); + }; + + /* calculate TAG header checksum */ + return udf_validate_tag_sum(dscr); +} + + +void +udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc) +{ + tag->id = udf_rw16(tagid); + tag->descriptor_ver = udf_rw16(context.dscrver); + tag->cksum = 0; + tag->reserved = 0; + tag->serial_num = udf_rw16(context.serialnum); + tag->tag_loc = udf_rw32(loc); +} + + +int +udf_create_anchor(int num) +{ + struct anchor_vdp *avdp; + uint32_t vds_extent_len = layout.vds_size * context.sector_size; + + if ((avdp = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + udf_inittag(&avdp->tag, TAGID_ANCHOR, layout.anchors[num]); + + avdp->main_vds_ex.loc = udf_rw32(layout.vds1); + avdp->main_vds_ex.len = udf_rw32(vds_extent_len); + + avdp->reserve_vds_ex.loc = udf_rw32(layout.vds2); + avdp->reserve_vds_ex.len = udf_rw32(vds_extent_len); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + avdp->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); + + context.anchors[num] = avdp; + return 0; +} + + +void +udf_create_terminator(union dscrptr *dscr, uint32_t loc) +{ + memset(dscr, 0, context.sector_size); + udf_inittag(&dscr->tag, TAGID_TERM, loc); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); +} + + +void +udf_osta_charset(struct charspec *charspec) +{ + memset(charspec, 0, sizeof(*charspec)); + charspec->type = 0; + strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); +} + + +void +udf_encode_osta_id(char *osta_id, uint16_t len, char *text) +{ + uint16_t u16_name[1024]; + uint8_t *pos; + uint16_t *pos16; + + memset(osta_id, 0, len); + if (!text || (strlen(text) == 0)) return; + + memset(u16_name, 0, sizeof(uint16_t) * 1023); + + /* convert ascii to 16 bits unicode */ + pos = (uint8_t *) text; + pos16 = u16_name; + while (*pos) { + *pos16 = *pos; + pos++; pos16++; + }; + *pos16 = 0; + + udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id); + + /* Ecma 167/7.2.13 states that length is recorded in the last byte */ + osta_id[len-1] = strlen(text)+1; +} + + +/* first call udf_set_regid and then the suffix */ +void +udf_set_regid(struct regid *regid, char const *name) +{ + memset(regid, 0, sizeof(*regid)); + regid->flags = 0; /* not dirty and not protected */ + strcpy((char *) regid->id, name); +} + + +void +udf_add_domain_regid(struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = udf_rw16(context.min_udf); +} + + +void +udf_add_udf_regid(struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = udf_rw16(context.min_udf); + + regid->id_suffix[2] = 4; /* unix */ + regid->id_suffix[3] = 8; /* NetBSD */ +} + + +void +udf_add_impl_regid(struct regid *regid) +{ + regid->id_suffix[0] = 4; /* unix */ + regid->id_suffix[1] = 8; /* NetBSD */ +} + + +void +udf_add_app_regid(struct regid *regid) +{ + regid->id_suffix[0] = context.app_version_main; + regid->id_suffix[1] = context.app_version_sub; +} + + +/* + * Fill in timestamp structure based on clock_gettime(). Time is reported back + * as a time_t accompanied with a nano second field. + * + * The husec, usec and csec could be relaxed in type. + */ +static void +udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) +{ + struct tm tm; + uint64_t husec, usec, csec; + + memset(timestamp, 0, sizeof(*timestamp)); + gmtime_r(×pec->tv_sec, &tm); + + /* + * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1. + * + * Lower 12 bits are two complement signed timezone offset if bit 12 + * (method 1) is clear. Otherwise if bit 12 is set, specify timezone + * offset to -2047 i.e. unsigned `zero' + */ + + /* set method 1 for CUT/GMT */ + timestamp->type_tz = udf_rw16((1<<12) + 0); + timestamp->year = udf_rw16(tm.tm_year + 1900); + timestamp->month = tm.tm_mon + 1; /* `tm' uses 0..11 for months */ + timestamp->day = tm.tm_mday; + timestamp->hour = tm.tm_hour; + timestamp->minute = tm.tm_min; + timestamp->second = tm.tm_sec; + + usec = (timespec->tv_nsec + 500) / 1000; /* round */ + husec = usec / 100; + usec -= husec * 100; /* only 0-99 in usec */ + csec = husec / 100; /* only 0-99 in csec */ + husec -= csec * 100; /* only 0-99 in husec */ + + /* in rare cases there is overflow in csec */ + csec = MIN(99, csec); + husec = MIN(99, husec); + usec = MIN(99, usec); + + timestamp->centisec = csec; + timestamp->hund_usec = husec; + timestamp->usec = usec; +} + + +void +udf_set_timestamp_now(struct timestamp *timestamp) +{ + struct timespec now; + +#ifdef CLOCK_REALTIME + (void)clock_gettime(CLOCK_REALTIME, &now); +#else + struct timeval time_of_day; + + (void)gettimeofday(&time_of_day, NULL); + now.tv_sec = time_of_day.tv_sec; + now.tv_nsec = time_of_day.tv_usec * 1000; +#endif + udf_timespec_to_timestamp(&now, timestamp); +} + + +/* some code copied from sys/fs/udf */ + +static void +udf_set_timestamp(struct timestamp *timestamp, time_t value) +{ + struct timespec t; + + memset(&t, 0, sizeof(struct timespec)); + t.tv_sec = value; + t.tv_nsec = 0; + udf_timespec_to_timestamp(&t, timestamp); +} + + +static uint32_t +unix_mode_to_udf_perm(mode_t mode) +{ + uint32_t perm; + + perm = ((mode & S_IRWXO) ); + perm |= ((mode & S_IRWXG) << 2); + perm |= ((mode & S_IRWXU) << 4); + perm |= ((mode & S_IWOTH) << 3); + perm |= ((mode & S_IWGRP) << 5); + perm |= ((mode & S_IWUSR) << 7); + + return perm; +} + +/* end of copied code */ + + +int +udf_create_primaryd(void) +{ + struct pri_vol_desc *pri; + uint16_t crclen; + + pri = calloc(1, context.sector_size); + if (pri == NULL) + return ENOMEM; + + memset(pri, 0, context.sector_size); + udf_inittag(&pri->tag, TAGID_PRI_VOL, /* loc */ 0); + pri->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + pri->pvd_num = udf_rw32(0); /* default serial */ + udf_encode_osta_id(pri->vol_id, 32, context.primary_name); + + /* set defaults for single disc volumes as UDF prescribes */ + pri->vds_num = udf_rw16(1); + pri->max_vol_seq = udf_rw16(1); + pri->ichg_lvl = udf_rw16(2); + pri->max_ichg_lvl = udf_rw16(3); + pri->flags = udf_rw16(0); + + pri->charset_list = udf_rw32(1); /* only CS0 */ + pri->max_charset_list = udf_rw32(1); /* only CS0 */ + + udf_encode_osta_id(pri->volset_id, 128, context.volset_name); + udf_osta_charset(&pri->desc_charset); + udf_osta_charset(&pri->explanatory_charset); + + udf_set_regid(&pri->app_id, context.app_name); + udf_add_app_regid(&pri->app_id); + + udf_set_regid(&pri->imp_id, context.impl_name); + udf_add_impl_regid(&pri->imp_id); + + udf_set_timestamp_now(&pri->time); + + crclen = sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH; + pri->tag.desc_crc_len = udf_rw16(crclen); + + context.primary_vol = pri; + + return 0; +} + + +/* XXX no support for unallocated or freed space tables yet (!) */ +int +udf_create_partitiond(int part_num, int part_accesstype) +{ + struct part_desc *pd; + struct part_hdr_desc *phd; + uint32_t sector_size, bitmap_bytes; + uint16_t crclen; + + sector_size = context.sector_size; + bitmap_bytes = layout.alloc_bitmap_dscr_size * sector_size; + + if (context.partitions[part_num]) { + printf("Internal error: partition %d allready defined\n", + part_num); + return EINVAL; + } + + pd = calloc(1, context.sector_size); + if (pd == NULL) + return ENOMEM; + phd = &pd->_impl_use.part_hdr; + + udf_inittag(&pd->tag, TAGID_PARTITION, /* loc */ 0); + pd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + pd->flags = udf_rw16(1); /* allocated */ + pd->part_num = udf_rw16(part_num); /* only one physical partition */ + + if (context.dscrver == 2) { + udf_set_regid(&pd->contents, "+NSR02"); + } else { + udf_set_regid(&pd->contents, "+NSR03"); + } + udf_add_app_regid(&pd->contents); + + phd->unalloc_space_bitmap.len = udf_rw32(bitmap_bytes); + phd->unalloc_space_bitmap.lb_num = udf_rw32(layout.unalloc_space); + + if (layout.freed_space) { + phd->freed_space_bitmap.len = udf_rw32(bitmap_bytes); + phd->freed_space_bitmap.lb_num = udf_rw32(layout.freed_space); + } + + pd->access_type = udf_rw32(part_accesstype); + pd->start_loc = udf_rw32(layout.part_start_lba); + pd->part_len = udf_rw32(layout.part_size_lba); + + udf_set_regid(&pd->imp_id, context.impl_name); + udf_add_impl_regid(&pd->imp_id); + + crclen = sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH; + pd->tag.desc_crc_len = udf_rw16(crclen); + + context.partitions[part_num] = pd; + + return 0; +} + + +int +udf_create_unalloc_spaced(void) +{ + struct unalloc_sp_desc *usd; + uint16_t crclen; + + usd = calloc(1, context.sector_size); + if (usd == NULL) + return ENOMEM; + + udf_inittag(&usd->tag, TAGID_UNALLOC_SPACE, /* loc */ 0); + usd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + /* no default entries */ + usd->alloc_desc_num = udf_rw32(0); /* no entries */ + + crclen = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); + crclen -= UDF_DESC_TAG_LENGTH; + usd->tag.desc_crc_len = udf_rw16(crclen); + + context.unallocated = usd; + + return 0; +} + + +static int +udf_create_base_logical_dscr(void) +{ + struct logvol_desc *lvd; + uint32_t sector_size; + uint16_t crclen; + + sector_size = context.sector_size; + + lvd = calloc(1, sector_size); + if (lvd == NULL) + return ENOMEM; + + udf_inittag(&lvd->tag, TAGID_LOGVOL, /* loc */ 0); + lvd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + udf_osta_charset(&lvd->desc_charset); + udf_encode_osta_id(lvd->logvol_id, 128, context.logvol_name); + lvd->lb_size = udf_rw32(context.sector_size); + + udf_set_regid(&lvd->domain_id, "*OSTA UDF Compliant"); + udf_add_domain_regid(&lvd->domain_id); + + /* no partition mappings/entries yet */ + lvd->mt_l = udf_rw32(0); + lvd->n_pm = udf_rw32(0); + + udf_set_regid(&lvd->imp_id, context.impl_name); + udf_add_impl_regid(&lvd->imp_id); + + lvd->integrity_seq_loc.loc = udf_rw32(layout.lvis); + lvd->integrity_seq_loc.len = udf_rw32(layout.lvis_size * sector_size); + + /* just one fsd for now */ + lvd->lv_fsd_loc.len = udf_rw32(sector_size); + lvd->lv_fsd_loc.loc.part_num = udf_rw32(context.metadata_part); + lvd->lv_fsd_loc.loc.lb_num = udf_rw32(layout.fsd); + + crclen = sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH; + lvd->tag.desc_crc_len = udf_rw16(crclen); + + context.logical_vol = lvd; + context.vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW; + context.vtop_offset[UDF_VTOP_RAWPART] = 0; + + return 0; +} + + +static void +udf_add_logvol_part_physical(uint16_t phys_part) +{ + struct logvol_desc *logvol = context.logical_vol; + union udf_pmap *pmap; + uint8_t *pmap_pos; + uint16_t crclen; + uint32_t pmap1_size, log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmap1_size = sizeof(struct part_map_1); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pm1.type = 1; + pmap->pm1.len = sizeof(struct part_map_1); + pmap->pm1.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pm1.part_num = udf_rw16(phys_part); + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_PHYS; + context.vtop_offset[log_part] = layout.part_start_lba; + context.part_size[log_part] = layout.part_size_lba; + context.part_free[log_part] = layout.part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmap1_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmap1_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +static void +udf_add_logvol_part_virtual(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint8_t *pmap_pos; + uint16_t crclen; + uint32_t pmapv_size, log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmapv_size = sizeof(struct part_map_2); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pmv.type = 2; + pmap->pmv.len = pmapv_size; + + udf_set_regid(&pmap->pmv.id, "*UDF Virtual Partition"); + udf_add_udf_regid(&pmap->pmv.id); + + pmap->pmv.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pmv.part_num = udf_rw16(phys_part); + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_VIRT; + context.vtop_offset[log_part] = context.vtop_offset[phys_part]; + context.part_size[log_part] = 0xffffffff; + context.part_free[log_part] = 0xffffffff; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +/* sparing table size is in bytes */ +static void +udf_add_logvol_part_sparable(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint32_t *st_pos, sparable_bytes, pmaps_size; + uint8_t *pmap_pos, num; + uint16_t crclen; + uint32_t log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmaps_size = sizeof(struct part_map_2); + sparable_bytes = layout.sparable_area_size * context.sector_size; + + pmap = (union udf_pmap *) pmap_pos; + pmap->pms.type = 2; + pmap->pms.len = pmaps_size; + + udf_set_regid(&pmap->pmv.id, "*UDF Sparable Partition"); + udf_add_udf_regid(&pmap->pmv.id); + + pmap->pms.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pms.part_num = udf_rw16(phys_part); + + pmap->pms.packet_len = udf_rw16(layout.sparable_blockingnr); + pmap->pms.st_size = udf_rw32(sparable_bytes); + + /* enter spare tables */ + st_pos = &pmap->pms.st_loc[0]; + *st_pos++ = udf_rw32(layout.spt_1); + *st_pos++ = udf_rw32(layout.spt_2); + + num = 2; + if (layout.spt_2 == 0) num--; + if (layout.spt_1 == 0) num--; + pmap->pms.n_st = num; /* 8 bit */ + + /* the vtop_offset needs to explicitly set since there is no phys. */ + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_SPARABLE; + context.vtop_offset[log_part] = layout.part_start_lba; + context.part_size[log_part] = layout.part_size_lba; + context.part_free[log_part] = layout.part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmaps_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmaps_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +int +udf_create_sparing_tabled(void) +{ + struct udf_sparing_table *spt; + struct spare_map_entry *sme; + uint32_t loc, cnt; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + + spt = calloc(context.sector_size, layout.sparing_table_dscr_lbas); + if (spt == NULL) + return ENOMEM; + + /* a sparing table descriptor is a whole sparable_blockingnr sectors */ + udf_inittag(&spt->tag, TAGID_SPARING_TABLE, /* loc */ 0); + + udf_set_regid(&spt->id, "*UDF Sparing Table"); + udf_add_udf_regid(&spt->id); + + spt->rt_l = udf_rw16(layout.sparable_blocks); + spt->seq_num = udf_rw32(0); /* first generation */ + + for (cnt = 0; cnt < layout.sparable_blocks; cnt++) { + sme = &spt->entries[cnt]; + loc = layout.sparable_area + cnt * layout.sparable_blockingnr; + sme->org = udf_rw32(0xffffffff); /* open for reloc */ + sme->map = udf_rw32(loc); + } + + /* calculate crc len for actual size */ + crclen = sizeof(struct udf_sparing_table) - UDF_DESC_TAG_LENGTH; + crclen += (layout.sparable_blocks-1) * sizeof(struct spare_map_entry); +/* XXX ensure crclen doesn't exceed UINT16_MAX ? */ + spt->tag.desc_crc_len = udf_rw16((uint16_t)crclen); + + context.sparing_table = spt; + + return 0; +} + + +static void +udf_add_logvol_part_meta(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint8_t *pmap_pos; + uint32_t pmapv_size, log_part; + uint16_t crclen; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmapv_size = sizeof(struct part_map_2); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pmm.type = 2; + pmap->pmm.len = pmapv_size; + + udf_set_regid(&pmap->pmm.id, "*UDF Metadata Partition"); + udf_add_udf_regid(&pmap->pmm.id); + + pmap->pmm.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pmm.part_num = udf_rw16(phys_part); + + /* fill in meta data file(s) and alloc/alignment unit sizes */ + pmap->pmm.meta_file_lbn = udf_rw32(layout.meta_file); + pmap->pmm.meta_mirror_file_lbn = udf_rw32(layout.meta_mirror); + pmap->pmm.meta_bitmap_file_lbn = udf_rw32(layout.meta_bitmap); + pmap->pmm.alloc_unit_size = udf_rw32(layout.meta_blockingnr); + pmap->pmm.alignment_unit_size = udf_rw16(layout.meta_alignment); + pmap->pmm.flags = 0; /* METADATA_DUPLICATED */ + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_META; + context.vtop_offset[log_part] = + context.vtop_offset[phys_part] + layout.meta_part_start_lba; + context.part_size[log_part] = layout.meta_part_size_lba; + context.part_free[log_part] = layout.meta_part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +int +udf_create_logical_dscr(int format_flags) +{ + int error; + + if ((error = udf_create_base_logical_dscr())) + return error; + + /* we pass data_part for there might be a read-only part one day */ + if (format_flags & FORMAT_SPARABLE) { + /* sparable partition mapping has no physical mapping */ + udf_add_logvol_part_sparable(context.data_part); + } else { + udf_add_logvol_part_physical(context.data_part); + } + + if (format_flags & FORMAT_VAT) { + /* add VAT virtual mapping; reflects on datapart */ + udf_add_logvol_part_virtual(context.data_part); + } + if (format_flags & FORMAT_META) { + /* add META data mapping; reflects on datapart */ + udf_add_logvol_part_meta(context.data_part); + } + + return 0; +} + + +int +udf_create_impvold(char *field1, char *field2, char *field3) +{ + struct impvol_desc *ivd; + struct udf_lv_info *lvi; + uint16_t crclen; + + ivd = calloc(1, context.sector_size); + if (ivd == NULL) + return ENOMEM; + lvi = &ivd->_impl_use.lv_info; + + udf_inittag(&ivd->tag, TAGID_IMP_VOL, /* loc */ 0); + ivd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + udf_set_regid(&ivd->impl_id, "*UDF LV Info"); + udf_add_udf_regid(&ivd->impl_id); + + /* fill in UDF specific part */ + udf_osta_charset(&lvi->lvi_charset); + udf_encode_osta_id(lvi->logvol_id, 128, context.logvol_name); + + udf_encode_osta_id(lvi->lvinfo1, 36, field1); + udf_encode_osta_id(lvi->lvinfo2, 36, field2); + udf_encode_osta_id(lvi->lvinfo3, 36, field3); + + udf_set_regid(&lvi->impl_id, context.impl_name); + udf_add_impl_regid(&lvi->impl_id); + + crclen = sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH; + ivd->tag.desc_crc_len = udf_rw16(crclen); + + context.implementation = ivd; + + return 0; +} + + +/* XXX might need to be sanitised a bit later */ +void +udf_update_lvintd(int type) +{ + struct logvol_int_desc *lvid; + struct udf_logvol_info *lvinfo; + struct logvol_desc *logvol; + uint32_t *pos; + uint32_t cnt, l_iu, num_partmappings; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + + lvid = context.logvol_integrity; + logvol = context.logical_vol; + + assert(lvid); + assert(logvol); + + lvid->integrity_type = udf_rw32(type); + + num_partmappings = udf_rw32(logvol->n_pm); + + udf_set_timestamp_now(&lvid->time); + + lvinfo = (struct udf_logvol_info *) + (lvid->tables + num_partmappings * 2); + udf_set_regid(&lvinfo->impl_id, context.impl_name); + udf_add_impl_regid(&lvinfo->impl_id); + + lvinfo->num_files = udf_rw32(context.num_files); + lvinfo->num_directories = udf_rw32(context.num_directories); + + lvid->lvint_next_unique_id = udf_rw64(context.unique_id); + + /* XXX sane enough ? */ + lvinfo->min_udf_readver = udf_rw16(context.min_udf); + lvinfo->min_udf_writever = udf_rw16(context.min_udf); + lvinfo->max_udf_writever = udf_rw16(context.max_udf); + + lvid->num_part = udf_rw32(num_partmappings); + + /* no impl. use needed */ + l_iu = sizeof(struct udf_logvol_info); + lvid->l_iu = udf_rw32(l_iu); + + pos = &lvid->tables[0]; + for (cnt = 0; cnt < num_partmappings; cnt++) { + *pos++ = udf_rw32(context.part_free[cnt]); + } + for (cnt = 0; cnt < num_partmappings; cnt++) { + *pos++ = udf_rw32(context.part_size[cnt]); + } + + crclen = sizeof(struct logvol_int_desc) -4 -UDF_DESC_TAG_LENGTH + l_iu; + crclen += num_partmappings * 2 * 4; +/* XXX ensure crclen doesn't exceed UINT16_MAX ? */ + lvid->tag.desc_crc_len = udf_rw16(crclen); + + context.logvol_info = lvinfo; +} + + +int +udf_create_lvintd(int type) +{ + struct logvol_int_desc *lvid; + + lvid = calloc(1, context.sector_size); + if (lvid == NULL) + return ENOMEM; + + udf_inittag(&lvid->tag, TAGID_LOGVOL_INTEGRITY, /* loc */ 0); + + context.logvol_integrity = lvid; + + udf_update_lvintd(type); + + return 0; +} + + +int +udf_create_fsd(void) +{ + struct fileset_desc *fsd; + uint16_t crclen; + + fsd = calloc(1, context.sector_size); + if (fsd == NULL) + return ENOMEM; + + udf_inittag(&fsd->tag, TAGID_FSD, /* loc */ 0); + + udf_set_timestamp_now(&fsd->time); + fsd->ichg_lvl = udf_rw16(3); /* UDF 2.3.2.1 */ + fsd->max_ichg_lvl = udf_rw16(3); /* UDF 2.3.2.2 */ + + fsd->charset_list = udf_rw32(1); /* only CS0 */ + fsd->max_charset_list = udf_rw32(1); /* only CS0 */ + + fsd->fileset_num = udf_rw32(0); /* only one fsd */ + fsd->fileset_desc_num = udf_rw32(0); /* origional */ + + udf_osta_charset(&fsd->logvol_id_charset); + udf_encode_osta_id(fsd->logvol_id, 128, context.logvol_name); + + udf_osta_charset(&fsd->fileset_charset); + udf_encode_osta_id(fsd->fileset_id, 32, context.fileset_name); + + /* copyright file and abstract file names obmitted */ + + fsd->rootdir_icb.len = udf_rw32(context.sector_size); + fsd->rootdir_icb.loc.lb_num = udf_rw32(layout.rootdir); + fsd->rootdir_icb.loc.part_num = udf_rw16(context.metadata_part); + + udf_set_regid(&fsd->domain_id, "*OSTA UDF Compliant"); + udf_add_domain_regid(&fsd->domain_id); + + /* next_ex stays zero */ + /* no system streamdirs yet */ + + crclen = sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH; + fsd->tag.desc_crc_len = udf_rw16(crclen); + + context.fileset_desc = fsd; + + return 0; +} + + +int +udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, + struct space_bitmap_desc **sbdp) +{ + struct space_bitmap_desc *sbd; + uint32_t cnt; + uint16_t crclen; + + *sbdp = NULL; + sbd = calloc(context.sector_size, dscr_size); + if (sbd == NULL) + return ENOMEM; + + udf_inittag(&sbd->tag, TAGID_SPACE_BITMAP, /* loc */ 0); + + sbd->num_bits = udf_rw32(part_size_lba); + sbd->num_bytes = udf_rw32((part_size_lba + 7)/8); + + /* fill space with 0xff to indicate free */ + for (cnt = 0; cnt < udf_rw32(sbd->num_bytes); cnt++) + sbd->data[cnt] = 0xff; + + /* set crc to only cover the header (UDF 2.3.1.2, 2.3.8.1) */ + crclen = sizeof(struct space_bitmap_desc) -1 - UDF_DESC_TAG_LENGTH; + sbd->tag.desc_crc_len = udf_rw16(crclen); + + *sbdp = sbd; + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_register_bad_block(uint32_t location) +{ + struct udf_sparing_table *spt; + struct spare_map_entry *sme, *free_sme; + uint32_t cnt; + + spt = context.sparing_table; + if (spt == NULL) { + printf("internal error: adding bad block to non sparable\n"); + return EINVAL; + } + + /* find us a free spare map entry */ + free_sme = NULL; + for (cnt = 0; cnt < layout.sparable_blocks; cnt++) { + sme = &spt->entries[cnt]; + /* if we are allready in it, bail out */ + if (udf_rw32(sme->org) == location) + return 0; + if (udf_rw32(sme->org) == 0xffffffff) { + free_sme = sme; + break; + } + } + if (free_sme == NULL) { + printf("Disc relocation blocks full; disc too damanged\n"); + return EINVAL; + } + free_sme->org = udf_rw32(location); + + return 0; +} + + +void +udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks) +{ + union dscrptr *dscr; + uint8_t *bpos; + uint32_t cnt, bit; + + /* account for space used on underlying partition */ + context.part_free[partnr] -= blocks; +#ifdef DEBUG + printf("mark allocated : partnr %d, start_lb %d for %d blocks\n", + partnr, start_lb, blocks); +#endif + + switch (context.vtop_tp[partnr]) { + case UDF_VTOP_TYPE_VIRT: + /* nothing */ + break; + case UDF_VTOP_TYPE_PHYS: + case UDF_VTOP_TYPE_SPARABLE: + case UDF_VTOP_TYPE_META: + if (context.part_unalloc_bits[context.vtop[partnr]] == NULL) { + context.part_free[partnr] = 0; + break; + } +#ifdef DEBUG + printf("Marking %d+%d as used\n", start_lb, blocks); +#endif + dscr = (union dscrptr *) (context.part_unalloc_bits[partnr]); + for (cnt = start_lb; cnt < start_lb + blocks; cnt++) { + bpos = &dscr->sbd.data[cnt / 8]; + bit = cnt % 8; + *bpos &= ~(1<< bit); + } + break; + default: + printf("internal error: reality check in mapping type %d\n", + context.vtop_tp[partnr]); + exit(EXIT_FAILURE); + } +} + + +void +udf_advance_uniqueid(void) +{ + /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ + context.unique_id++; + if (context.unique_id < 0x10) + context.unique_id = 0x10; +} + +/* --------------------------------------------------------------------- */ + +static void +unix_to_udf_name(char *result, uint8_t *result_len, + char const *name, int name_len, struct charspec *chsp) +{ + uint16_t *raw_name; + uint16_t *outchp; + const char *inchp; + const char *osta_id = "OSTA Compressed Unicode"; + int udf_chars, is_osta_typ0, bits; + size_t cnt; + + /* allocate temporary unicode-16 buffer */ + raw_name = malloc(1024); + assert(raw_name); + + /* convert utf8 to unicode-16 */ + *raw_name = 0; + inchp = name; + outchp = raw_name; + bits = 8; + for (cnt = name_len, udf_chars = 0; cnt;) { + *outchp = wget_utf8(&inchp, &cnt); + if (*outchp > 0xff) + bits=16; + outchp++; + udf_chars++; + } + /* null terminate just in case */ + *outchp++ = 0; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + udf_chars = udf_CompressUnicode(udf_chars, bits, + (unicode_t *) raw_name, + (byte *) result); + } else { + printf("unix to udf name: no CHSP0 ?\n"); + /* XXX assume 8bit char length byte latin-1 */ + *result++ = 8; udf_chars = 1; + strncpy(result, name + 1, name_len); + udf_chars += name_len; + } + *result_len = udf_chars; + free(raw_name); +} + + +#define UDF_SYMLINKBUFLEN (64*1024) /* picked */ +int +udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target) +{ + struct charspec osta_charspec; + struct pathcomp pathcomp; + char *pathbuf, *pathpos, *compnamepos; +// char *mntonname; +// int mntonnamelen; + int pathlen, len, compnamelen; + int error; + + /* process `target' to an UDF structure */ + pathbuf = malloc(UDF_SYMLINKBUFLEN); + assert(pathbuf); + + *pathbufp = NULL; + *pathlenp = 0; + + pathpos = pathbuf; + pathlen = 0; + udf_osta_charset(&osta_charspec); + + if (*target == '/') { + /* symlink starts from the root */ + len = UDF_PATH_COMP_SIZE; + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_ROOT; + +#if 0 + /* XXX how to check for in makefs? */ + /* check if its mount-point relative! */ + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + if (strlen(target) >= mntonnamelen) { + if (strncmp(target, mntonname, mntonnamelen) == 0) { + pathcomp.type = UDF_PATH_COMP_MOUNTROOT; + target += mntonnamelen; + } + } else { + target++; + } +#else + target++; +#endif + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + error = 0; + while (*target) { + /* ignore multiple '/' */ + while (*target == '/') { + target++; + } + if (!*target) + break; + + /* extract component name */ + compnamelen = 0; + compnamepos = target; + while ((*target) && (*target != '/')) { + target++; + compnamelen++; + } + + /* just trunc if too long ?? (security issue) */ + if (compnamelen >= 127) { + error = ENAMETOOLONG; + break; + } + + /* convert unix name to UDF name */ + len = sizeof(struct pathcomp); + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_NAME; + len = UDF_PATH_COMP_SIZE; + + if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0)) + pathcomp.type = UDF_PATH_COMP_PARENTDIR; + if ((compnamelen == 1) && (*compnamepos == '.')) + pathcomp.type = UDF_PATH_COMP_CURDIR; + + if (pathcomp.type == UDF_PATH_COMP_NAME) { + unix_to_udf_name( + (char *) &pathcomp.ident, &pathcomp.l_ci, + compnamepos, compnamelen, + &osta_charspec); + len = UDF_PATH_COMP_SIZE + pathcomp.l_ci; + } + + if (pathlen + len >= UDF_SYMLINKBUFLEN) { + error = ENAMETOOLONG; + break; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + if (error) { + /* aparently too big */ + free(pathbuf); + return error; + } + + /* return status of symlink contents writeout */ + *pathbufp = (uint8_t *) pathbuf; + *pathlenp = pathlen; + + return 0; + +} +#undef UDF_SYMLINKBUFLEN + + +int +udf_fidsize(struct fileid_desc *fid) +{ + uint32_t size; + + if (udf_rw16(fid->tag.id) != TAGID_FID) + errx(EINVAL, "got udf_fidsize on non FID\n"); + + size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); + size = (size + 3) & ~3; + + return size; +} + + +int +udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent) +{ + /* the size of an empty FID is 38 but needs to be a multiple of 4 */ + int fidsize = 40; + + udf_inittag(&fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR; + fid->icb = *parent; + fid->icb.longad_uniqueid = parent->longad_uniqueid; + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + + /* we have to do the fid here explicitly for simplicity */ + udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + return fidsize; +} + + +void +udf_create_fid(uint32_t diroff, struct fileid_desc *fid, char *name, + int file_char, struct long_ad *ref) +{ + struct charspec osta_charspec; + uint32_t endfid; + uint32_t fidsize, lb_rest; + + memset(fid, 0, sizeof(*fid)); + udf_inittag(&fid->tag, TAGID_FID, udf_rw32(ref->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = file_char; + fid->l_iu = udf_rw16(0); + fid->icb = *ref; + fid->icb.longad_uniqueid = ref->longad_uniqueid; + + udf_osta_charset(&osta_charspec); + unix_to_udf_name((char *) fid->data, &fid->l_fi, name, strlen(name), + &osta_charspec); + + /* + * OK, tricky part: we need to pad so the next descriptor header won't + * cross the sector boundary + */ + endfid = diroff + udf_fidsize(fid); + lb_rest = context.sector_size - (endfid % context.sector_size); + if (lb_rest < sizeof(struct desc_tag)) { + /* add at least 32 */ + fid->l_iu = udf_rw16(32); + udf_set_regid((struct regid *) fid->data, context.impl_name); + udf_add_impl_regid((struct regid *) fid->data); + + unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu), + &fid->l_fi, name, strlen(name), &osta_charspec); + } + + fidsize = udf_fidsize(fid); + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums((union dscrptr *)fid); +} + + +static void +udf_append_parentfid(union dscrptr *dscr, struct long_ad *parent_icb) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + uint32_t l_ea; + uint32_t fidsize, crclen; + uint8_t *bpos, *data; + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = udf_rw32(fe->l_ea); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = udf_rw32(efe->l_ea); + } else { + errx(1, "Bad tag passed to udf_append_parentfid"); + } + + /* create '..' */ + bpos = data + l_ea; + fid = (struct fileid_desc *) bpos; + fidsize = udf_create_parentfid(fid, parent_icb); + + /* record fidlength information */ + if (fe) { + fe->inf_len = udf_rw64(fidsize); + fe->l_ad = udf_rw32(fidsize); + fe->logblks_rec = udf_rw64(0); /* intern */ + crclen = sizeof(struct file_entry); + } else { + efe->inf_len = udf_rw64(fidsize); + efe->obj_size = udf_rw64(fidsize); + efe->l_ad = udf_rw32(fidsize); + efe->logblks_rec = udf_rw64(0); /* intern */ + crclen = sizeof(struct extfile_entry); + } + crclen -= 1 + UDF_DESC_TAG_LENGTH; + crclen += l_ea + fidsize; + dscr->tag.desc_crc_len = udf_rw16(crclen); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums(dscr); +} + + + +/* + * Order of extended attributes : + * ECMA 167 EAs + * Non block aligned Implementation Use EAs + * Block aligned Implementation Use EAs (not in newfs_udf) + * Application Use EAs (not in newfs_udf) + * + * no checks for doubles, must be called in-order + */ +static void +udf_extattr_append_internal(union dscrptr *dscr, struct extattr_entry *extattr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + struct impl_extattr_entry *implext; + uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len; + uint32_t *l_eap, l_ad; + uint16_t *spos; + uint8_t *bpos, *data; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_eap = &fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_eap = &efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + } else { + errx(1, "Bad tag passed to udf_extattr_append_internal"); + } + + /* should have a header! */ + extattrhdr = (struct extattrhdr_desc *) data; + l_ea = udf_rw32(*l_eap); + if (l_ea == 0) { + assert(l_ad == 0); + /* create empty extended attribute header */ + exthdr_len = sizeof(struct extattrhdr_desc); + + udf_inittag(&extattrhdr->tag, TAGID_EXTATTR_HDR, /* loc */ 0); + extattrhdr->impl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->appl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->tag.desc_crc_len = udf_rw16(8); + + /* record extended attribute header length */ + l_ea = exthdr_len; + *l_eap = udf_rw32(l_ea); + } + + /* extract locations */ + impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc); + appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc); + if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + impl_attr_loc = l_ea; + if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + appl_attr_loc = l_ea; + + /* Ecma 167 EAs */ + if (udf_rw32(extattr->type) < 2048) { + assert(impl_attr_loc == l_ea); + assert(appl_attr_loc == l_ea); + } + + /* implementation use extended attributes */ + if (udf_rw32(extattr->type) == 2048) { + assert(appl_attr_loc == l_ea); + + /* calculate and write extended attribute header checksum */ + implext = (struct impl_extattr_entry *) extattr; + assert(udf_rw32(implext->iu_l) == 4); /* [UDF 3.3.4.5] */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } + + /* application use extended attributes */ + assert(udf_rw32(extattr->type) != 65536); + assert(appl_attr_loc == l_ea); + + /* append the attribute at the end of the current space */ + bpos = data + udf_rw32(*l_eap); + a_l = udf_rw32(extattr->a_l); + + /* update impl. attribute locations */ + if (udf_rw32(extattr->type) < 2048) { + impl_attr_loc = l_ea + a_l; + appl_attr_loc = l_ea + a_l; + } + if (udf_rw32(extattr->type) == 2048) { + appl_attr_loc = l_ea + a_l; + } + + /* copy and advance */ + memcpy(bpos, extattr, a_l); + l_ea += a_l; + *l_eap = udf_rw32(l_ea); + + /* do the `dance` again backwards */ + if (context.dscrver != 2) { + if (impl_attr_loc == l_ea) + impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT; + if (appl_attr_loc == l_ea) + appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT; + } + + /* store offsets */ + extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc); + extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); +} + + +int +udf_create_new_fe(struct file_entry **fep, int file_type, struct stat *st) +{ + struct file_entry *fe; + struct icb_tag *icb; + struct timestamp birthtime; + struct filetimes_extattr_entry *ft_extattr; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + uint16_t icbflags; + + *fep = NULL; + fe = calloc(1, context.sector_size); + if (fe == NULL) + return ENOMEM; + + udf_inittag(&fe->tag, TAGID_FENTRY, /* loc */ 0); + icb = &fe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + fe->perm = udf_rw32(0x7fff); /* all is allowed */ + fe->link_cnt = udf_rw16(0); /* explicit setting */ + + fe->ckpoint = udf_rw32(1); /* user supplied file version */ + + udf_set_timestamp_now(&birthtime); + udf_set_timestamp_now(&fe->atime); + udf_set_timestamp_now(&fe->attrtime); + udf_set_timestamp_now(&fe->mtime); + + /* set attributes */ + if (st) { +#if !HAVE_NBTOOL_CONFIG_H + udf_set_timestamp(&birthtime, st->st_birthtime); +#else + udf_set_timestamp(&birthtime, 0); +#endif + udf_set_timestamp(&fe->atime, st->st_atime); + udf_set_timestamp(&fe->attrtime, st->st_ctime); + udf_set_timestamp(&fe->mtime, st->st_mtime); + fe->uid = udf_rw32(st->st_uid); + fe->gid = udf_rw32(st->st_gid); + + fe->perm = unix_mode_to_udf_perm(st->st_mode); + + icbflags = udf_rw16(fe->icbtag.flags); + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (st->st_mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (st->st_mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (st->st_mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + fe->icbtag.flags = udf_rw16(icbflags); + } + + udf_set_regid(&fe->imp_id, context.impl_name); + udf_add_impl_regid(&fe->imp_id); + fe->unique_id = udf_rw64(context.unique_id); + udf_advance_uniqueid(); + + fe->l_ea = udf_rw32(0); + + /* create extended attribute to record our creation time */ + ft_extattr = calloc(1, UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO); + ft_extattr->hdr.subtype = 1; /* [4/48.10.5] */ + ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->d_l = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */ + ft_extattr->existence = UDF_FILETIMES_FILE_CREATION; + ft_extattr->times[0] = birthtime; + + udf_extattr_append_internal((union dscrptr *) fe, + (struct extattr_entry *) ft_extattr); + free(ft_extattr); + + /* record fidlength information */ + fe->inf_len = udf_rw64(0); + fe->l_ad = udf_rw32(0); + fe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(fe->l_ea); + + /* make sure the header sums stays correct */ + fe->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums((union dscrptr *) fe); + + *fep = fe; + return 0; +} + + +int +udf_create_new_efe(struct extfile_entry **efep, int file_type, struct stat *st) +{ + struct extfile_entry *efe; + struct icb_tag *icb; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + uint16_t icbflags; + + *efep = NULL; + efe = calloc(1, context.sector_size); + if (efe == NULL) + return ENOMEM; + + udf_inittag(&efe->tag, TAGID_EXTFENTRY, /* loc */ 0); + icb = &efe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + efe->perm = udf_rw32(0x7fff); /* all is allowed */ + efe->link_cnt = udf_rw16(0); /* explicit setting */ + + efe->ckpoint = udf_rw32(1); /* user supplied file version */ + + udf_set_timestamp_now(&efe->ctime); + udf_set_timestamp_now(&efe->atime); + udf_set_timestamp_now(&efe->attrtime); + udf_set_timestamp_now(&efe->mtime); + + /* set attributes */ + if (st) { +#if !HAVE_NBTOOL_CONFIG_H + udf_set_timestamp(&efe->ctime, st->st_birthtime); +#else + udf_set_timestamp(&efe->ctime, 0); +#endif + udf_set_timestamp(&efe->atime, st->st_atime); + udf_set_timestamp(&efe->attrtime, st->st_ctime); + udf_set_timestamp(&efe->mtime, st->st_mtime); + efe->uid = udf_rw32(st->st_uid); + efe->gid = udf_rw32(st->st_gid); + + efe->perm = unix_mode_to_udf_perm(st->st_mode); + + icbflags = udf_rw16(efe->icbtag.flags); + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (st->st_mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (st->st_mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (st->st_mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + efe->icbtag.flags = udf_rw16(icbflags); + } + + udf_set_regid(&efe->imp_id, context.impl_name); + udf_add_impl_regid(&efe->imp_id); + + efe->unique_id = udf_rw64(context.unique_id); + udf_advance_uniqueid(); + + /* record fidlength information */ + efe->inf_len = udf_rw64(0); + efe->obj_size = udf_rw64(0); + efe->l_ad = udf_rw32(0); + efe->logblks_rec = udf_rw64(0); + + crclen = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH; + + /* make sure the header sums stays correct */ + efe->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums((union dscrptr *) efe); + + *efep = efe; + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* for METADATA file appending only */ +static void +udf_append_meta_mapping_part_to_efe(struct extfile_entry *efe, + struct short_ad *mapping) +{ + struct icb_tag *icb; + uint64_t inf_len, obj_size, logblks_rec; + uint32_t l_ad, l_ea; + uint16_t crclen; + uint8_t *bpos; + + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + logblks_rec = udf_rw64(efe->logblks_rec); + l_ad = udf_rw32(efe->l_ad); + l_ea = udf_rw32(efe->l_ea); + crclen = udf_rw16(efe->tag.desc_crc_len); + icb = &efe->icbtag; + + /* set our allocation to shorts if not already done */ + icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); + + /* append short_ad */ + bpos = (uint8_t *) efe->data + l_ea + l_ad; + memcpy(bpos, mapping, sizeof(struct short_ad)); + + l_ad += sizeof(struct short_ad); + crclen += sizeof(struct short_ad); + inf_len += UDF_EXT_LEN(udf_rw32(mapping->len)); + obj_size += UDF_EXT_LEN(udf_rw32(mapping->len)); + logblks_rec = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(obj_size); + efe->logblks_rec = udf_rw64(logblks_rec); + efe->tag.desc_crc_len = udf_rw16(crclen); +} + + +/* for METADATA file appending only */ +static void +udf_append_meta_mapping_to_efe(struct extfile_entry *efe, + uint16_t partnr, uint32_t lb_num, + uint64_t len) +{ + struct short_ad mapping; + uint64_t max_len, part_len; + + /* calculate max length meta allocation sizes */ + max_len = UDF_EXT_MAXLEN / context.sector_size; /* in sectors */ + max_len = (max_len / layout.meta_blockingnr) * layout.meta_blockingnr; + max_len = max_len * context.sector_size; + + memset(&mapping, 0, sizeof(mapping)); + while (len) { + part_len = MIN(len, max_len); + mapping.lb_num = udf_rw32(lb_num); + mapping.len = udf_rw32(part_len); + + udf_append_meta_mapping_part_to_efe(efe, &mapping); + + lb_num += part_len / context.sector_size; + len -= part_len; + } +} + + +int +udf_create_meta_files(void) +{ + struct extfile_entry *efe; + struct long_ad meta_icb; + uint64_t bytes; + uint32_t sector_size; + int filetype, error; + + sector_size = context.sector_size; + + memset(&meta_icb, 0, sizeof(meta_icb)); + meta_icb.len = udf_rw32(sector_size); + meta_icb.loc.part_num = udf_rw16(context.data_part); + + /* create metadata file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_file); + filetype = UDF_ICB_FILETYPE_META_MAIN; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_file = efe; + + /* create metadata mirror file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_mirror); + filetype = UDF_ICB_FILETYPE_META_MIRROR; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_mirror = efe; + + /* create metadata bitmap file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_bitmap); + filetype = UDF_ICB_FILETYPE_META_BITMAP; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_bitmap = efe; + + /* patch up files */ + context.meta_file->unique_id = udf_rw64(0); + context.meta_mirror->unique_id = udf_rw64(0); + context.meta_bitmap->unique_id = udf_rw64(0); + + /* restart unique id */ + context.unique_id = 0x10; + + /* XXX no support for metadata mirroring yet */ + /* insert extents */ + efe = context.meta_file; + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_part_start_lba, + (uint64_t) layout.meta_part_size_lba * sector_size); + + efe = context.meta_mirror; + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_part_start_lba, + (uint64_t) layout.meta_part_size_lba * sector_size); + + efe = context.meta_bitmap; + bytes = udf_space_bitmap_len(layout.meta_part_size_lba); + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_bitmap_space, bytes); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_create_new_rootdir(union dscrptr **dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct long_ad root_icb; + int filetype, error; + + memset(&root_icb, 0, sizeof(root_icb)); + root_icb.len = udf_rw32(context.sector_size); + root_icb.loc.lb_num = udf_rw32(layout.rootdir); + root_icb.loc.part_num = udf_rw16(context.metadata_part); + + filetype = UDF_ICB_FILETYPE_DIRECTORY; + if (context.dscrver == 2) { + error = udf_create_new_fe(&fe, filetype, NULL); + *dscr = (union dscrptr *) fe; + } else { + error = udf_create_new_efe(&efe, filetype, NULL); + *dscr = (union dscrptr *) efe; + } + if (error) + return error; + + /* append '..' */ + udf_append_parentfid(*dscr, &root_icb); + + /* rootdir has explicit only one link on creation; '..' is no link */ + if (context.dscrver == 2) { + fe->link_cnt = udf_rw16(1); + } else { + efe->link_cnt = udf_rw16(1); + } + + context.num_directories++; + assert(context.num_directories == 1); + + return 0; +} + + +void +udf_prepend_VAT_file(void) +{ + /* old style VAT has no prepend */ + if (context.dscrver == 2) { + context.vat_start = 0; + context.vat_size = 0; + return; + } + + context.vat_start = offsetof(struct udf_vat, data); + context.vat_size = offsetof(struct udf_vat, data); +} + + +void +udf_vat_update(uint32_t virt, uint32_t phys) +{ + uint32_t *vatpos; + uint32_t new_size; + + if (context.vtop_tp[context.metadata_part] != UDF_VTOP_TYPE_VIRT) + return; + + new_size = MAX(context.vat_size, + (context.vat_start + (virt+1)*sizeof(uint32_t))); + + if (new_size > context.vat_allocated) { + context.vat_allocated = + UDF_ROUNDUP(new_size, context.sector_size); + context.vat_contents = realloc(context.vat_contents, + context.vat_allocated); + assert(context.vat_contents); + /* XXX could also report error */ + } + vatpos = (uint32_t *) (context.vat_contents + context.vat_start); + vatpos[virt] = udf_rw32(phys); + + context.vat_size = MAX(context.vat_size, + (context.vat_start + (virt+1)*sizeof(uint32_t))); +} + + +int +udf_append_VAT_file(void) +{ + struct udf_oldvat_tail *oldvat_tail; + struct udf_vat *vathdr; + int32_t len_diff; + + /* new style VAT has VAT LVInt analog in front */ + if (context.dscrver == 3) { + /* set up VATv2 descriptor */ + vathdr = (struct udf_vat *) context.vat_contents; + vathdr->header_len = udf_rw16(sizeof(struct udf_vat) - 1); + vathdr->impl_use_len = udf_rw16(0); + memcpy(vathdr->logvol_id, context.logical_vol->logvol_id, 128); + vathdr->prev_vat = udf_rw32(UDF_NO_PREV_VAT); + vathdr->num_files = udf_rw32(context.num_files); + vathdr->num_directories = udf_rw32(context.num_directories); + + vathdr->min_udf_readver = udf_rw16(context.min_udf); + vathdr->min_udf_writever = udf_rw16(context.min_udf); + vathdr->max_udf_writever = udf_rw16(context.max_udf); + + return 0; + } + + /* old style VAT has identifier appended */ + + /* append "*UDF Virtual Alloc Tbl" id and prev. VAT location */ + len_diff = context.vat_allocated - context.vat_size; + assert(len_diff >= 0); + if (len_diff < (int32_t) sizeof(struct udf_oldvat_tail)) { + context.vat_allocated += context.sector_size; + context.vat_contents = realloc(context.vat_contents, + context.vat_allocated); + assert(context.vat_contents); + /* XXX could also report error */ + } + + oldvat_tail = (struct udf_oldvat_tail *) (context.vat_contents + + context.vat_size); + + udf_set_regid(&oldvat_tail->id, "*UDF Virtual Alloc Tbl"); + udf_add_udf_regid(&oldvat_tail->id); + oldvat_tail->prev_vat = udf_rw32(UDF_NO_PREV_VAT); + + context.vat_size += sizeof(struct udf_oldvat_tail); + + return 0; +} + + +int +udf_create_VAT(union dscrptr **vat_dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry *vatlvext; + struct long_ad dataloc, *allocpos; + uint8_t *bpos, *extattr; + uint32_t ea_len, inf_len, vat_len, blks; + int filetype; + int error; + + assert((layout.rootdir < 2) && (layout.fsd < 2)); + + memset(&dataloc, 0, sizeof(dataloc)); + dataloc.len = udf_rw32(context.vat_size); + dataloc.loc.part_num = udf_rw16(context.data_part); + dataloc.loc.lb_num = udf_rw32(layout.vat); + + if (context.dscrver == 2) { + /* old style VAT */ + filetype = UDF_ICB_FILETYPE_UNKNOWN; + error = udf_create_new_fe(&fe, filetype, NULL); + if (error) + return error; + + /* append VAT LVExtension attribute */ + ea_len = sizeof(struct impl_extattr_entry) - 1 + + sizeof(struct vatlvext_extattr_entry) + 4; + + extattr = calloc(1, ea_len); + + implext = (struct impl_extattr_entry *) extattr; + implext->hdr.type = udf_rw32(2048); /* [4/48.10.8] */ + implext->hdr.subtype = 1; /* [4/48.10.8.2] */ + implext->hdr.a_l = udf_rw32(ea_len); /* VAT LVext EA size */ + /* use 4 bytes of imp use for UDF checksum [UDF 3.3.4.5] */ + implext->iu_l = udf_rw32(4); + udf_set_regid(&implext->imp_id, "*UDF VAT LVExtension"); + udf_add_udf_regid(&implext->imp_id); + + /* VAT LVExtension data follows UDF IU space */ + bpos = ((uint8_t *) implext->data) + 4; + vatlvext = (struct vatlvext_extattr_entry *) bpos; + + vatlvext->unique_id_chk = udf_rw64(fe->unique_id); + vatlvext->num_files = udf_rw32(context.num_files); + vatlvext->num_directories = udf_rw32(context.num_directories); + memcpy(vatlvext->logvol_id, context.logical_vol->logvol_id,128); + + udf_extattr_append_internal((union dscrptr *) fe, + (struct extattr_entry *) extattr); + + free(extattr); + + fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); + + allocpos = (struct long_ad *) (fe->data + udf_rw32(fe->l_ea)); + *allocpos = dataloc; + + /* set length */ + inf_len = context.vat_size; + fe->inf_len = udf_rw64(inf_len); + fe->l_ad = udf_rw32(sizeof(struct long_ad)); + blks = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + fe->logblks_rec = udf_rw32(blks); + + /* update vat descriptor's CRC length */ + vat_len = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + vat_len += udf_rw32(fe->l_ad) + udf_rw32(fe->l_ea); + fe->tag.desc_crc_len = udf_rw16(vat_len); + + *vat_dscr = (union dscrptr *) fe; + } else { + /* new style VAT */ + filetype = UDF_ICB_FILETYPE_VAT; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + + efe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); + + allocpos = (struct long_ad *) efe->data; + *allocpos = dataloc; + + /* set length */ + inf_len = context.vat_size; + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + efe->l_ad = udf_rw32(sizeof(struct long_ad)); + blks = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + efe->logblks_rec = udf_rw32(blks); + + vat_len = sizeof(struct extfile_entry)-1 - UDF_DESC_TAG_LENGTH; + vat_len += udf_rw32(efe->l_ad); + efe->tag.desc_crc_len = udf_rw16(vat_len); + + *vat_dscr = (union dscrptr *) efe; + } + + return 0; +} + diff --git a/sbin/newfs_udf/udf_create.h b/sbin/newfs_udf/udf_create.h new file mode 100644 index 000000000..001d64eec --- /dev/null +++ b/sbin/newfs_udf/udf_create.h @@ -0,0 +1,283 @@ +/* $NetBSD: udf_create.h,v 1.7 2013/08/09 15:11:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#ifndef _FS_UDF_UDF_CREATE_H_ +#define _FS_UDF_UDF_CREATE_H_ + +#include +#include +#if !HAVE_NBTOOL_CONFIG_H +#include +#else +#include "../../sys/fs/udf/ecma167-udf.h" +#endif +#include "udf_bswap.h" +#include "udf_osta.h" + + +/* format flags indicating properties of disc to create */ +#define FORMAT_WRITEONCE 0x00001 +#define FORMAT_SEQUENTIAL 0x00002 +#define FORMAT_REWRITABLE 0x00004 +#define FORMAT_SPARABLE 0x00008 +#define FORMAT_META 0x00010 +#define FORMAT_LOW 0x00020 +#define FORMAT_VAT 0x00040 +#define FORMAT_WORM 0x00080 +#define FORMAT_TRACK512 0x00100 +#define FORMAT_INVALID 0x00200 +#define FORMAT_READONLY 0x00400 +#define FORMAT_FLAGBITS \ + "\10\1WRITEONCE\2SEQUENTIAL\3REWRITABLE\4SPARABLE\5META\6LOW" \ + "\7VAT\10WORM\11TRACK512\12INVALID\13READONLY" + + +/* structure space */ +#define UDF_ANCHORS 4 /* 256, 512, N-256, N */ +#define UDF_PARTITIONS 4 /* overkill */ +#define UDF_PMAPS 4 /* overkill */ + +/* misc constants */ +#define UDF_MAX_NAMELEN 255 /* as per SPEC */ + +/* translation constants */ +#define UDF_VTOP_RAWPART UDF_PMAPS /* [0..UDF_PMAPS> are normal */ + +/* virtual to physical mapping types */ +#define UDF_VTOP_TYPE_RAW 0 +#define UDF_VTOP_TYPE_UNKNOWN 0 +#define UDF_VTOP_TYPE_PHYS 1 +#define UDF_VTOP_TYPE_VIRT 2 +#define UDF_VTOP_TYPE_SPARABLE 3 +#define UDF_VTOP_TYPE_META 4 + +#define UDF_TRANS_ZERO ((uint64_t) -1) +#define UDF_TRANS_UNMAPPED ((uint64_t) -2) +#define UDF_TRANS_INTERN ((uint64_t) -3) +#define UDF_MAX_SECTOR ((uint64_t) -10) /* high water mark */ + +/* handys */ +#define UDF_ROUNDUP(val, gran) \ + ((uint64_t) (gran) * (((uint64_t)(val) + (gran)-1) / (gran))) + +#define UDF_ROUNDDOWN(val, gran) \ + ((uint64_t) (gran) * (((uint64_t)(val)) / (gran))) + + +/* disc offsets for various structures and their sizes */ +struct udf_disclayout { + uint32_t wrtrack_skew; + + uint32_t iso9660_vrs; + uint32_t anchors[UDF_ANCHORS]; + uint32_t vds_size, vds1, vds2; + uint32_t lvis_size, lvis; + + uint32_t first_lba, last_lba; + uint32_t sector_size; + uint32_t blockingnr, align_blockingnr, sparable_blockingnr; + uint32_t meta_blockingnr, meta_alignment; + + /* sparables */ + uint32_t sparable_blocks; + uint32_t sparable_area, sparable_area_size; + uint32_t sparing_table_dscr_lbas; + uint32_t spt_1, spt_2; + + /* bitmaps */ + uint32_t alloc_bitmap_dscr_size; + uint32_t unalloc_space, freed_space; + + uint32_t meta_bitmap_dscr_size; + uint32_t meta_bitmap_space; + + /* metadata partition */ + uint32_t meta_file, meta_mirror, meta_bitmap; + uint32_t meta_part_start_lba, meta_part_size_lba; + + /* main partition */ + uint32_t part_start_lba, part_size_lba; + + uint32_t fsd, rootdir, vat; + +}; + + +/* all info about discs and descriptors building */ +struct udf_create_context { + /* descriptors */ + int dscrver; /* 2 or 3 */ + int min_udf; /* hex */ + int max_udf; /* hex */ + int serialnum; /* format serialno */ + + int gmtoff; /* in minutes */ + + /* XXX to layout? */ + uint32_t sector_size; + + /* identification */ + char *logvol_name; + char *primary_name; + char *volset_name; + char *fileset_name; + + char const *app_name; + char const *impl_name; + int app_version_main; + int app_version_sub; + + /* building */ + int vds_seq; /* for building functions */ + int unique_id; /* only first few are used */ + + /* constructed structures */ + struct anchor_vdp *anchors[UDF_ANCHORS]; /* anchors to VDS */ + struct pri_vol_desc *primary_vol; /* identification */ + struct logvol_desc *logical_vol; /* main mapping v->p */ + struct unalloc_sp_desc *unallocated; /* free UDF space */ + struct impvol_desc *implementation; /* likely reduntant */ + struct logvol_int_desc *logvol_integrity; /* current integrity */ + struct part_desc *partitions[UDF_PARTITIONS]; /* partitions */ + + /* XXX to layout? */ + int data_part; + int metadata_part; + + /* block numbers as offset in partition */ + uint32_t metadata_alloc_pos; + uint32_t data_alloc_pos; + + /* derived; points *into* other structures */ + struct udf_logvol_info *logvol_info; /* inside integrity */ + + /* fileset and root directories */ + struct fileset_desc *fileset_desc; /* normally one */ + + /* logical to physical translations */ + int vtop[UDF_PMAPS+1]; /* vpartnr trans */ + int vtop_tp[UDF_PMAPS+1]; /* type of trans */ + uint64_t vtop_offset[UDF_PMAPS+1]; /* offset in lb */ + + /* sparable */ + struct udf_sparing_table*sparing_table; /* replacements */ + + /* VAT file */ + uint32_t vat_size; /* length */ + uint32_t vat_allocated; /* allocated length */ + uint32_t vat_start; /* offset 1st entry */ + uint8_t *vat_contents; /* the VAT */ + + /* meta data partition */ + struct extfile_entry *meta_file; + struct extfile_entry *meta_mirror; + struct extfile_entry *meta_bitmap; + + /* lvint */ + int num_files; + int num_directories; + uint32_t part_size[UDF_PARTITIONS]; + uint32_t part_free[UDF_PARTITIONS]; + + struct space_bitmap_desc*part_unalloc_bits[UDF_PARTITIONS]; + struct space_bitmap_desc*part_freed_bits [UDF_PARTITIONS]; +}; + + +/* globals */ + +extern struct udf_create_context context; +extern struct udf_disclayout layout; + +/* prototypes */ +void udf_init_create_context(void); +int a_udf_version(const char *s, const char *id_type); + +int udf_calculate_disc_layout(int format_flags, int min_udf, + uint32_t wrtrack_skew, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr, + uint32_t sparable_blocks, + float meta_fract); + +void udf_osta_charset(struct charspec *charspec); +void udf_encode_osta_id(char *osta_id, uint16_t len, char *text); + +void udf_set_regid(struct regid *regid, char const *name); +void udf_add_domain_regid(struct regid *regid); +void udf_add_udf_regid(struct regid *regid); +void udf_add_impl_regid(struct regid *regid); +void udf_add_app_regid(struct regid *regid); + +int udf_validate_tag_sum(union dscrptr *dscr); +int udf_validate_tag_and_crc_sums(union dscrptr *dscr); + +void udf_set_timestamp_now(struct timestamp *timestamp); + +void udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc); +int udf_create_anchor(int num); + +void udf_create_terminator(union dscrptr *dscr, uint32_t loc); +int udf_create_primaryd(void); +int udf_create_partitiond(int part_num, int part_accesstype); +int udf_create_unalloc_spaced(void); +int udf_create_sparing_tabled(void); +int udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, + struct space_bitmap_desc **sbdp); +int udf_create_logical_dscr(int format_flags); +int udf_create_impvold(char *field1, char *field2, char *field3); +int udf_create_fsd(void); +int udf_create_lvintd(int type); +void udf_update_lvintd(int type); + +int udf_register_bad_block(uint32_t location); +void udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks); + +int udf_create_new_fe(struct file_entry **fep, int file_type, + struct stat *st); +int udf_create_new_efe(struct extfile_entry **efep, int file_type, + struct stat *st); + +int udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target); + +void udf_advance_uniqueid(void); +int udf_fidsize(struct fileid_desc *fid); +void udf_create_fid(uint32_t diroff, struct fileid_desc *fid, + char *name, int namelen, struct long_ad *ref); +int udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent); + +int udf_create_meta_files(void); +int udf_create_new_rootdir(union dscrptr **dscr); + +int udf_create_VAT(union dscrptr **vat_dscr); +void udf_prepend_VAT_file(void); +void udf_vat_update(uint32_t virt, uint32_t phys); +int udf_append_VAT_file(void); + +#endif /* _FS_UDF_UDF_CREATE_H_ */ + diff --git a/sbin/newfs_udf/udf_write.c b/sbin/newfs_udf/udf_write.c new file mode 100644 index 000000000..c4fb4b57f --- /dev/null +++ b/sbin/newfs_udf/udf_write.c @@ -0,0 +1,906 @@ +/* $NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + + +union dscrptr *terminator_dscr; + +static int +udf_write_phys(void *blob, uint32_t location, uint32_t sects) +{ + uint32_t phys, cnt; + uint8_t *bpos; + int error; + + for (cnt = 0; cnt < sects; cnt++) { + bpos = (uint8_t *) blob; + bpos += context.sector_size * cnt; + + phys = location + cnt; + error = udf_write_sector(bpos, phys); + if (error) + return error; + } + return 0; +} + + +static int +udf_write_dscr_phys(union dscrptr *dscr, uint32_t location, + uint32_t sects) +{ + dscr->tag.tag_loc = udf_rw32(location); + (void) udf_validate_tag_and_crc_sums(dscr); + + return udf_write_phys(dscr, location, sects); +} + + +int +udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart, + uint32_t sects) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + uint32_t phys; + + extattrhdr = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = (struct file_entry *) dscr; + if (udf_rw32(fe->l_ea) > 0) + extattrhdr = (struct extattrhdr_desc *) fe->data; + } + if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = (struct extfile_entry *) dscr; + if (udf_rw32(efe->l_ea) > 0) + extattrhdr = (struct extattrhdr_desc *) efe->data; + } + if (extattrhdr) { + extattrhdr->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); + } + + dscr->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums(dscr); + + /* determine physical location */ + phys = context.vtop_offset[vpart]; + if (context.vtop_tp[vpart] == UDF_VTOP_TYPE_VIRT) { + udf_vat_update(location, context.data_alloc_pos); + phys += context.data_alloc_pos++; + } else { + phys += location; + } + + return udf_write_phys(dscr, phys, sects); +} + + +void +udf_metadata_alloc(int nblk, struct long_ad *pos) +{ + memset(pos, 0, sizeof(*pos)); + pos->len = udf_rw32(nblk * context.sector_size); + pos->loc.lb_num = udf_rw32(context.metadata_alloc_pos); + pos->loc.part_num = udf_rw16(context.metadata_part); + + udf_mark_allocated(context.metadata_alloc_pos, context.metadata_part, + nblk); + + context.metadata_alloc_pos += nblk; + if (context.metadata_part == context.data_part) + context.data_alloc_pos = context.metadata_alloc_pos; +} + + +void +udf_data_alloc(int nblk, struct long_ad *pos) +{ + memset(pos, 0, sizeof(*pos)); + pos->len = udf_rw32(nblk * context.sector_size); + pos->loc.lb_num = udf_rw32(context.data_alloc_pos); + pos->loc.part_num = udf_rw16(context.data_part); + + udf_mark_allocated(context.data_alloc_pos, context.data_part, nblk); + context.data_alloc_pos += nblk; + if (context.metadata_part == context.data_part) + context.metadata_alloc_pos = context.data_alloc_pos; +} + + + +/* --------------------------------------------------------------------- */ + +/* + * udf_derive_format derives the format_flags from the disc's mmc_discinfo. + * The resulting flags uniquely define a disc format. Note there are at least + * 7 distinct format types defined in UDF. + */ + +#define UDF_VERSION(a) \ + (((a) == 0x100) || ((a) == 0x102) || ((a) == 0x150) || ((a) == 0x200) || \ + ((a) == 0x201) || ((a) == 0x250) || ((a) == 0x260)) + +int +udf_derive_format(int req_enable, int req_disable, int force) +{ + /* disc writability, formatted, appendable */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { + (void)printf("Can't newfs readonly device\n"); + return EROFS; + } + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* sequentials need sessions appended */ + if (mmc_discinfo.disc_state == MMC_STATE_CLOSED) { + (void)printf("Can't append session to a closed disc\n"); + return EROFS; + } + if ((mmc_discinfo.disc_state != MMC_STATE_EMPTY) && !force) { + (void)printf("Disc not empty! Use -F to force " + "initialisation\n"); + return EROFS; + } + } else { + /* check if disc (being) formatted or has been started on */ + if (mmc_discinfo.disc_state == MMC_STATE_EMPTY) { + (void)printf("Disc is not formatted\n"); + return EROFS; + } + } + + /* determine UDF format */ + format_flags = 0; + if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { + /* all rewritable media */ + format_flags |= FORMAT_REWRITABLE; + if (context.min_udf >= 0x0250) { + /* standard dictates meta as default */ + format_flags |= FORMAT_META; + } + + if ((mmc_discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) == 0) { + /* sparables for defect management */ + if (context.min_udf >= 0x150) + format_flags |= FORMAT_SPARABLE; + } + } else { + /* all once recordable media */ + format_flags |= FORMAT_WRITEONCE; + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + format_flags |= FORMAT_SEQUENTIAL; + + if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) { + /* logical overwritable */ + format_flags |= FORMAT_LOW; + } else { + /* have to use VAT for overwriting */ + format_flags |= FORMAT_VAT; + } + } else { + /* rare WORM devices, but BluRay has one, strat4096 */ + format_flags |= FORMAT_WORM; + } + } + + /* enable/disable requests */ + if (req_disable & FORMAT_META) { + format_flags &= ~(FORMAT_META | FORMAT_LOW); + req_disable &= ~FORMAT_META; + } + if ((format_flags & FORMAT_VAT) & UDF_512_TRACK) + format_flags |= FORMAT_TRACK512; + + if (req_enable & FORMAT_READONLY) { + format_flags |= FORMAT_READONLY; + } + + /* determine partition/media access type */ + media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED; + if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { + media_accesstype = UDF_ACCESSTYPE_OVERWRITABLE; + if (mmc_discinfo.mmc_cur & MMC_CAP_ERASABLE) + media_accesstype = UDF_ACCESSTYPE_REWRITEABLE; + } else { + /* all once recordable media */ + media_accesstype = UDF_ACCESSTYPE_WRITE_ONCE; + } + if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) + media_accesstype = UDF_ACCESSTYPE_PSEUDO_OVERWITE; + + /* patch up media accesstype */ + if (req_enable & FORMAT_READONLY) { + /* better now */ + media_accesstype = UDF_ACCESSTYPE_READ_ONLY; + } + + /* adjust minimum version limits */ + if (format_flags & FORMAT_VAT) + context.min_udf = MAX(context.min_udf, 0x0150); + if (format_flags & FORMAT_SPARABLE) + context.min_udf = MAX(context.min_udf, 0x0150); + if (format_flags & FORMAT_META) + context.min_udf = MAX(context.min_udf, 0x0250); + if (format_flags & FORMAT_LOW) + context.min_udf = MAX(context.min_udf, 0x0260); + + /* adjust maximum version limits not to tease or break things */ + if (!(format_flags & (FORMAT_META | FORMAT_LOW)) && + (context.max_udf > 0x200)) + context.max_udf = 0x201; + + if ((format_flags & (FORMAT_VAT | FORMAT_SPARABLE)) == 0) + if (context.max_udf <= 0x150) + context.min_udf = 0x102; + + /* limit Ecma 167 descriptor if possible/needed */ + context.dscrver = 3; + if ((context.min_udf < 0x200) || (context.max_udf < 0x200)) { + context.dscrver = 2; + context.max_udf = 0x150; /* last version < 0x200 */ + } + + /* is it possible ? */ + if (context.min_udf > context.max_udf) { + (void)printf("Initialisation prohibited by specified maximum " + "UDF version 0x%04x. Minimum version required 0x%04x\n", + context.max_udf, context.min_udf); + return EPERM; + } + + if (!UDF_VERSION(context.min_udf) || !UDF_VERSION(context.max_udf)) { + printf("Choose UDF version numbers from " + "0x102, 0x150, 0x200, 0x201, 0x250 and 0x260\n"); + printf("Default version is 0x201\n"); + return EPERM; + } + + return 0; +} + +#undef UDF_VERSION + + +/* --------------------------------------------------------------------- */ + +int +udf_proces_names(void) +{ + uint32_t primary_nr; + uint64_t volset_nr; + + if (context.logvol_name == NULL) + context.logvol_name = strdup("anonymous"); + if (context.primary_name == NULL) { + if (mmc_discinfo.disc_flags & MMC_DFLAGS_DISCIDVALID) { + primary_nr = mmc_discinfo.disc_id; + } else { + primary_nr = (uint32_t) random(); + } + context.primary_name = calloc(32, 1); + sprintf(context.primary_name, "%08"PRIx32, primary_nr); + } + if (context.volset_name == NULL) { + if (mmc_discinfo.disc_flags & MMC_DFLAGS_BARCODEVALID) { + volset_nr = mmc_discinfo.disc_barcode; + } else { + volset_nr = (uint32_t) random(); + volset_nr |= ((uint64_t) random()) << 32; + } + context.volset_name = calloc(128,1); + sprintf(context.volset_name, "%016"PRIx64, volset_nr); + } + if (context.fileset_name == NULL) + context.fileset_name = strdup("anonymous"); + + /* check passed/created identifiers */ + if (strlen(context.logvol_name) > 128) { + (void)printf("Logical volume name too long\n"); + return EINVAL; + } + if (strlen(context.primary_name) > 32) { + (void)printf("Primary volume name too long\n"); + return EINVAL; + } + if (strlen(context.volset_name) > 128) { + (void)printf("Volume set name too long\n"); + return EINVAL; + } + if (strlen(context.fileset_name) > 32) { + (void)printf("Fileset name too long\n"); + return EINVAL; + } + + /* signal all OK */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_write_iso9660_vrs(void) +{ + struct vrs_desc *iso9660_vrs_desc; + uint32_t pos; + int error, cnt, dpos; + + /* create ISO/Ecma-167 identification descriptors */ + if ((iso9660_vrs_desc = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + /* + * All UDF formats should have their ISO/Ecma-167 descriptors written + * except when not possible due to track reservation in the case of + * VAT + */ + if ((format_flags & FORMAT_TRACK512) == 0) { + dpos = (2048 + context.sector_size - 1) / context.sector_size; + + /* wipe at least 6 times 2048 byte `sectors' */ + for (cnt = 0; cnt < 6 *dpos; cnt++) { + pos = layout.iso9660_vrs + cnt; + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + } + + /* common VRS fields in all written out ISO descriptors */ + iso9660_vrs_desc->struct_type = 0; + iso9660_vrs_desc->version = 1; + pos = layout.iso9660_vrs; + + /* BEA01, NSR[23], TEA01 */ + memcpy(iso9660_vrs_desc->identifier, "BEA01", 5); + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + pos += dpos; + + if (context.dscrver == 2) + memcpy(iso9660_vrs_desc->identifier, "NSR02", 5); + else + memcpy(iso9660_vrs_desc->identifier, "NSR03", 5); + ; + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + pos += dpos; + + memcpy(iso9660_vrs_desc->identifier, "TEA01", 5); + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + } + + free(iso9660_vrs_desc); + /* return success */ + return 0; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Main function that creates and writes out disc contents based on the + * format_flags's that uniquely define the type of disc to create. + */ + +int +udf_do_newfs_prefix(void) +{ + union dscrptr *zero_dscr; + union dscrptr *dscr; + struct mmc_trackinfo ti; + uint32_t sparable_blocks; + uint32_t sector_size, blockingnr; + uint32_t cnt, loc, len; + int sectcopy; + int error, integrity_type; + int data_part, metadata_part; + + /* init */ + sector_size = mmc_discinfo.sector_size; + + /* determine span/size */ + ti.tracknr = mmc_discinfo.first_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + if (mmc_discinfo.sector_size < context.sector_size) { + fprintf(stderr, "Impossible to format: sectorsize too small\n"); + return EIO; + } + context.sector_size = sector_size; + + /* determine blockingnr */ + blockingnr = ti.packet_size; + if (blockingnr <= 1) { + /* paranoia on blockingnr */ + switch (mmc_discinfo.mmc_profile) { + case 0x08 : /* CDROM */ + case 0x09 : /* CD-R */ + case 0x0a : /* CD-RW */ + blockingnr = 32; /* UDF requirement */ + break; + case 0x10 : /* DVDROM */ + case 0x11 : /* DVD-R (DL) */ + case 0x12 : /* DVD-RAM */ + case 0x1b : /* DVD+R */ + case 0x2b : /* DVD+R Dual layer */ + case 0x13 : /* DVD-RW restricted overwrite */ + case 0x14 : /* DVD-RW sequential */ + blockingnr = 16; /* SCSI definition */ + break; + case 0x40 : /* BDROM */ + case 0x41 : /* BD-R Sequential recording (SRM) */ + case 0x42 : /* BD-R Random recording (RRM) */ + case 0x43 : /* BD-RE */ + case 0x51 : /* HD DVD-R */ + case 0x52 : /* HD DVD-RW */ + blockingnr = 32; /* SCSI definition */ + break; + default: + break; + } + } + if (blockingnr <= 0) { + printf("Can't fixup blockingnumber for device " + "type %d\n", mmc_discinfo.mmc_profile); + + printf("Device is not returning valid blocking" + " number and media type is unknown.\n"); + + return EINVAL; + } + wrtrack_skew = ti.track_start % blockingnr; + + if (mmc_discinfo.mmc_class == MMC_CLASS_CD) { + /* not too much for CD-RW, still 20MiB */ + sparable_blocks = 32; + } else { + /* take a value for DVD*RW mainly, BD is `defect free' */ + sparable_blocks = 512; + } + + /* get layout */ + error = udf_calculate_disc_layout(format_flags, context.min_udf, + wrtrack_skew, + ti.track_start, mmc_discinfo.last_possible_lba, + context.sector_size, blockingnr, sparable_blocks, + meta_fract); + + /* cache partition for we need it often */ + data_part = context.data_part; + metadata_part = context.metadata_part; + + /* Create sparing table descriptor if applicable */ + if (format_flags & FORMAT_SPARABLE) { + if ((error = udf_create_sparing_tabled())) + return error; + + if (check_surface) { + if ((error = udf_surface_check())) + return error; + } + } + + /* Create a generic terminator descriptor (later reused) */ + terminator_dscr = calloc(1, sector_size); + if (terminator_dscr == NULL) + return ENOMEM; + udf_create_terminator(terminator_dscr, 0); + + /* + * Start with wipeout of VRS1 upto start of partition. This allows + * formatting for sequentials with the track reservation and it + * cleans old rubbish on rewritables. For sequentuals without the + * track reservation all is wiped from track start. + */ + if ((zero_dscr = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + loc = (format_flags & FORMAT_TRACK512) ? layout.vds1 : ti.track_start; + for (; loc < layout.part_start_lba; loc++) { + if ((error = udf_write_sector(zero_dscr, loc))) { + free(zero_dscr); + return error; + } + } + free(zero_dscr); + + /* Create anchors */ + for (cnt = 0; cnt < 3; cnt++) { + if ((error = udf_create_anchor(cnt))) { + return error; + } + } + + /* + * Create the two Volume Descriptor Sets (VDS) each containing the + * following descriptors : primary volume, partition space, + * unallocated space, logical volume, implementation use and the + * terminator + */ + + /* start of volume recognision sequence building */ + context.vds_seq = 0; + + /* Create primary volume descriptor */ + if ((error = udf_create_primaryd())) + return error; + + /* Create partition descriptor */ + if ((error = udf_create_partitiond(context.data_part, media_accesstype))) + return error; + + /* Create unallocated space descriptor */ + if ((error = udf_create_unalloc_spaced())) + return error; + + /* Create logical volume descriptor */ + if ((error = udf_create_logical_dscr(format_flags))) + return error; + + /* Create implementation use descriptor */ + /* TODO input of fields 1,2,3 and passing them */ + if ((error = udf_create_impvold(NULL, NULL, NULL))) + return error; + + /* write out what we've created so far */ + + /* writeout iso9660 vrs */ + if ((error = udf_write_iso9660_vrs())) + return error; + + /* Writeout anchors */ + for (cnt = 0; cnt < 3; cnt++) { + dscr = (union dscrptr *) context.anchors[cnt]; + loc = layout.anchors[cnt]; + if ((error = udf_write_dscr_phys(dscr, loc, 1))) + return error; + + /* sequential media has only one anchor */ + if (format_flags & FORMAT_SEQUENTIAL) + break; + } + + /* write out main and secondary VRS */ + for (sectcopy = 1; sectcopy <= 2; sectcopy++) { + loc = (sectcopy == 1) ? layout.vds1 : layout.vds2; + + /* primary volume descriptor */ + dscr = (union dscrptr *) context.primary_vol; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* partition descriptor(s) */ + for (cnt = 0; cnt < UDF_PARTITIONS; cnt++) { + dscr = (union dscrptr *) context.partitions[cnt]; + if (dscr) { + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + } + } + + /* unallocated space descriptor */ + dscr = (union dscrptr *) context.unallocated; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* logical volume descriptor */ + dscr = (union dscrptr *) context.logical_vol; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* implementation use descriptor */ + dscr = (union dscrptr *) context.implementation; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* terminator descriptor */ + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + loc++; + } + + /* writeout the two sparable table descriptors (if needed) */ + if (format_flags & FORMAT_SPARABLE) { + for (sectcopy = 1; sectcopy <= 2; sectcopy++) { + loc = (sectcopy == 1) ? layout.spt_1 : layout.spt_2; + dscr = (union dscrptr *) context.sparing_table; + len = layout.sparing_table_dscr_lbas; + + /* writeout */ + error = udf_write_dscr_phys(dscr, loc, len); + if (error) + return error; + } + } + + /* + * Create unallocated space bitmap descriptor. Sequential recorded + * media report their own free/used space; no free/used space tables + * should be recorded for these. + */ + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + error = udf_create_space_bitmap( + layout.alloc_bitmap_dscr_size, + layout.part_size_lba, + &context.part_unalloc_bits[data_part]); + if (error) + return error; + /* TODO: freed space bitmap if applicable */ + + /* mark space allocated for the unallocated space bitmap */ + udf_mark_allocated(layout.unalloc_space, data_part, + layout.alloc_bitmap_dscr_size); + } + + /* + * Create metadata partition file entries and allocate and init their + * space and free space maps. + */ + if (format_flags & FORMAT_META) { + error = udf_create_space_bitmap( + layout.meta_bitmap_dscr_size, + layout.meta_part_size_lba, + &context.part_unalloc_bits[metadata_part]); + if (error) + return error; + + error = udf_create_meta_files(); + if (error) + return error; + + /* mark space allocated for meta partition and its bitmap */ + udf_mark_allocated(layout.meta_file, data_part, 1); + udf_mark_allocated(layout.meta_mirror, data_part, 1); + udf_mark_allocated(layout.meta_bitmap, data_part, 1); + udf_mark_allocated(layout.meta_part_start_lba, data_part, + layout.meta_part_size_lba); + + /* mark space allocated for the unallocated space bitmap */ + udf_mark_allocated(layout.meta_bitmap_space, data_part, + layout.meta_bitmap_dscr_size); + } + + /* create logical volume integrity descriptor */ + context.num_files = 0; + context.num_directories = 0; + integrity_type = UDF_INTEGRITY_OPEN; + if ((error = udf_create_lvintd(integrity_type))) + return error; + + /* writeout initial open integrity sequence + terminator */ + loc = layout.lvis; + dscr = (union dscrptr *) context.logvol_integrity; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + + /* create VAT if needed */ + if (format_flags & FORMAT_VAT) { + context.vat_allocated = context.sector_size; + context.vat_contents = malloc(context.vat_allocated); + assert(context.vat_contents); + + udf_prepend_VAT_file(); + } + + /* create FSD and writeout */ + if ((error = udf_create_fsd())) + return error; + udf_mark_allocated(layout.fsd, metadata_part, 1); + + dscr = (union dscrptr *) context.fileset_desc; + error = udf_write_dscr_virt(dscr, layout.fsd, metadata_part, 1); + + return error; +} + + +/* specific routine for newfs to create empty rootdirectory */ +int +udf_do_rootdir(void) { + union dscrptr *root_dscr; + int error; + + /* create root directory and write out */ + assert(context.unique_id == 0x10); + context.unique_id = 0; + if ((error = udf_create_new_rootdir(&root_dscr))) + return error; + udf_mark_allocated(layout.rootdir, context.metadata_part, 1); + + error = udf_write_dscr_virt(root_dscr, + layout.rootdir, context.metadata_part, 1); + + free(root_dscr); + + return error; +} + + +int +udf_do_newfs_postfix(void) +{ + union dscrptr *vat_dscr; + union dscrptr *dscr; + struct long_ad vatdata_pos; + uint32_t loc, len, phys, sects; + int data_part, metadata_part; + int error; + + /* cache partition for we need it often */ + data_part = context.data_part; + metadata_part = context.metadata_part; + + if ((format_flags & FORMAT_SEQUENTIAL) == 0) { + /* update lvint and mark it closed */ + udf_update_lvintd(UDF_INTEGRITY_CLOSED); + + /* overwrite initial terminator */ + loc = layout.lvis+1; + dscr = (union dscrptr *) context.logvol_integrity; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* mark end of integrity desciptor sequence again */ + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + } + + /* write out unallocated space bitmap on non sequential media */ + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + /* writeout unallocated space bitmap */ + loc = layout.unalloc_space; + dscr = (union dscrptr *) (context.part_unalloc_bits[data_part]); + len = layout.alloc_bitmap_dscr_size; + error = udf_write_dscr_virt(dscr, loc, data_part, len); + if (error) + return error; + } + + if (format_flags & FORMAT_META) { + loc = layout.meta_file; + dscr = (union dscrptr *) context.meta_file; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + loc = layout.meta_mirror; + dscr = (union dscrptr *) context.meta_mirror; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + loc = layout.meta_bitmap; + dscr = (union dscrptr *) context.meta_bitmap; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + /* writeout unallocated space bitmap */ + loc = layout.meta_bitmap_space; + dscr = (union dscrptr *) + (context.part_unalloc_bits[metadata_part]); + len = layout.meta_bitmap_dscr_size; + error = udf_write_dscr_virt(dscr, loc, data_part, len); + if (error) + return error; + } + + /* create a VAT and account for FSD+root */ + vat_dscr = NULL; + if (format_flags & FORMAT_VAT) { + /* update lvint to reflect the newest values (no writeout) */ + udf_update_lvintd(UDF_INTEGRITY_CLOSED); + + error = udf_append_VAT_file(); + if (error) + return error; + + /* write out VAT data */ + sects = UDF_ROUNDUP(context.vat_size, context.sector_size) / + context.sector_size; + layout.vat = context.data_alloc_pos; + udf_data_alloc(sects, &vatdata_pos); + + loc = udf_rw32(vatdata_pos.loc.lb_num); + phys = context.vtop_offset[context.data_part] + loc; + + error = udf_write_phys(context.vat_contents, phys, sects); + if (error) + return error; + loc += sects; + + /* create new VAT descriptor */ + error = udf_create_VAT(&vat_dscr); + if (error) + return error; + context.data_alloc_pos++; + loc++; + + error = udf_write_dscr_virt(vat_dscr, loc, metadata_part, 1); + free(vat_dscr); + if (error) + return error; + } + + /* done */ + return 0; +} diff --git a/sbin/newfs_udf/udf_write.h b/sbin/newfs_udf/udf_write.h new file mode 100644 index 000000000..d2fd8ac0c --- /dev/null +++ b/sbin/newfs_udf/udf_write.h @@ -0,0 +1,54 @@ +/* $NetBSD: udf_write.h,v 1.4 2013/08/05 20:52:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ + +#ifndef _FS_UDF_UDF_WRITE_H_ +#define _FS_UDF_UDF_WRITE_H_ + +#include "udf_create.h" +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +/* prototypes */ + +int udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart, + uint32_t sects); +void udf_metadata_alloc(int nblk, struct long_ad *pos); +void udf_data_alloc(int nblk, struct long_ad *pos); + +int udf_derive_format(int req_enable, int req_disable, int force); +int udf_proces_names(void); + +int udf_do_newfs_prefix(void); +int udf_do_rootdir(void); +int udf_do_newfs_postfix(void); + +#endif /* _FS_UDF_UDF_WRITE_H_ */ diff --git a/sbin/newfs_udf/unicode.h b/sbin/newfs_udf/unicode.h new file mode 100644 index 000000000..f54f32076 --- /dev/null +++ b/sbin/newfs_udf/unicode.h @@ -0,0 +1,161 @@ +/* $NetBSD: unicode.h,v 1.1 2013/08/05 14:11:30 reinoud Exp $ */ + +/*- + * Copyright (c) 2001, 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 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Borman at Krystal Technologies. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +/* + * Routines for handling Unicode encoded in UTF-8 form, code derived from + * src/lib/libc/locale/utf2.c. + */ +static u_int16_t wget_utf8(const char **, size_t *) __unused; +static int wput_utf8(char *, size_t, u_int16_t) __unused; + +/* + * Read one UTF8-encoded character off the string, shift the string pointer + * and return the character. + */ +static u_int16_t +wget_utf8(const char **str, size_t *sz) +{ + unsigned int c; + u_int16_t rune = 0; + const char *s = *str; + static const int _utf_count[16] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 2, 2, 3, 0, + }; + + /* must be called with at least one byte remaining */ + assert(*sz > 0); + + c = _utf_count[(s[0] & 0xf0) >> 4]; + if (c == 0 || c > *sz) { + decoding_error: + /* + * The first character is in range 128-255 and doesn't + * mark valid a valid UTF-8 sequence. There is not much + * we can do with this, so handle by returning + * the first character as if it would be a correctly + * encoded ISO-8859-1 character. + */ + c = 1; + } + + switch (c) { + case 1: + rune = s[0] & 0xff; + break; + case 2: + if ((s[1] & 0xc0) != 0x80) + goto decoding_error; + rune = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F); + break; + case 3: + if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80) + goto decoding_error; + rune = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) + | (s[2] & 0x3F); + break; + } + + *str += c; + *sz -= c; + return rune; +} + +/* + * Encode wide character and write it to the string. 'n' specifies + * how much buffer space remains in 's'. Returns number of bytes written + * to the target string 's'. + */ +static int +wput_utf8(char *s, size_t n, u_int16_t wc) +{ + if (wc & 0xf800) { + if (n < 3) { + /* bound check failure */ + return 0; + } + + s[0] = 0xE0 | (wc >> 12); + s[1] = 0x80 | ((wc >> 6) & 0x3F); + s[2] = 0x80 | ((wc) & 0x3F); + return 3; + } else if (wc & 0x0780) { + if (n < 2) { + /* bound check failure */ + return 0; + } + + s[0] = 0xC0 | (wc >> 6); + s[1] = 0x80 | ((wc) & 0x3F); + return 2; + } else { + if (n < 1) { + /* bound check failure */ + return 0; + } + + s[0] = wc; + return 1; + } +} diff --git a/sbin/newfs_v7fs/Makefile b/sbin/newfs_v7fs/Makefile new file mode 100644 index 000000000..961a05c66 --- /dev/null +++ b/sbin/newfs_v7fs/Makefile @@ -0,0 +1,21 @@ +# $NetBSD: Makefile,v 1.4 2012/09/05 23:01:42 riz Exp $ + +.include + +V7FS = ${NETBSDSRCDIR}/sys/fs/v7fs +PROG= newfs_v7fs +MAN= newfs_v7fs.8 +SRCS= newfs_v7fs.c main.c v7fs_endian.c v7fs_superblock.c v7fs_inode.c \ +v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_io_user.c progress.c + +# use progress meter. +FSCK= ${NETBSDSRCDIR}/sbin/fsck + +DPADD+= ${LIBUTIL} +LDADD+= -lutil +CPPFLAGS+=-DV7FS_EI -I${V7FS} -I${FSCK} +.PATH: ${V7FS} ${FSCK} + +COPTS.newfs_v7fs.c+= -Wno-pointer-sign + +.include diff --git a/sbin/newfs_v7fs/main.c b/sbin/newfs_v7fs/main.c new file mode 100644 index 000000000..a31ebdeb5 --- /dev/null +++ b/sbin/newfs_v7fs/main.c @@ -0,0 +1,316 @@ +/* $NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +__RCSID("$NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" /*v7fs_datablock_expand/last */ +#include "newfs_v7fs.h" +#include "progress.h" /*../sbin/fsck */ + +#define VPRINTF(lv, fmt, args...) { if (v7fs_newfs_verbose >= lv) \ + printf(fmt, ##args); } + +static v7fs_daddr_t +determine_ilist_size(v7fs_daddr_t volume_size, int32_t files) +{ + v7fs_daddr_t ilist_size; + + if (files) + ilist_size = howmany(files, V7FS_INODE_PER_BLOCK); + else + ilist_size = volume_size / 25; /* 4% */ + if (ilist_size > (v7fs_daddr_t)V7FS_ILISTBLK_MAX) + ilist_size = V7FS_ILISTBLK_MAX; + + return ilist_size; +} + +static int +partition_check(struct v7fs_self *fs) +{ + struct v7fs_superblock *sb = &fs->superblock; + int error; + + if ((error = v7fs_superblock_load(fs))) { + if (error != EINVAL) { + /* Invalid superblock information is OK. */ + warnx("Can't read superblock sector."); + } + } + sb->modified = 1; + if ((error = v7fs_superblock_writeback(fs))) { + if (errno == EROFS) { + warnx("Overwriting disk label? "); + } + warnx("Can't write superblock sector."); + } + + return error; +} + +static int +make_root(struct v7fs_self *fs) +{ + struct v7fs_inode inode; + struct v7fs_dirent *dir; + int error; + + /* INO 1 badblk (don't used) */ + memset(&inode, 0, sizeof(inode)); + inode.inode_number = 1; + inode.mode = V7FS_IFREG; /* V7 manner */ + v7fs_inode_writeback(fs, &inode); + + /* INO 2 root */ + v7fs_ino_t ino; + if ((error = v7fs_inode_allocate(fs, &ino))) { + errno = error; + warn("Can't allocate / inode"); + return error; + } + + memset(&inode, 0, sizeof(inode)); + inode.inode_number = ino; + inode.mode = 0777 | V7FS_IFDIR; + inode.uid = 0; + inode.gid = 0; + inode.nlink = 2; /* . + .. */ + inode.atime = inode.mtime = inode.ctime = time(0); + + /* root dirent. */ + v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2); + v7fs_daddr_t blk = inode.addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + v7fs_inode_deallocate(fs, ino); + errno = error = EIO; + warn("Can't read / dirent."); + return error; + } + dir = (struct v7fs_dirent *)buf; /*disk endian */ + + strcpy(dir[0].name, "."); + dir[0].inode_number = V7FS_VAL16(fs, ino); + strcpy(dir[1].name, ".."); + dir[1].inode_number = V7FS_VAL16(fs, ino); + if (!fs->io.write(fs->io.cookie, buf, blk)) {/*writeback */ + scratch_free(fs, buf); + errno = error = EIO; + warn("Can't write / dirent."); + return error; + } + scratch_free(fs, buf); + v7fs_inode_writeback(fs, &inode); + if ((error = v7fs_superblock_writeback(fs))) { + errno = error; + warnx("Can't write superblock."); + } + + return error; +} + +static v7fs_daddr_t +make_freeblocklist(struct v7fs_self *fs, v7fs_daddr_t listblk, uint8_t *buf) +{ + uint32_t (*val32)(uint32_t) = fs->val.conv32; + uint16_t (*val16)(uint16_t) = fs->val.conv16; + struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf; + int i, j, k; + + memset(buf, 0, V7FS_BSIZE); + + for (i = V7FS_MAX_FREEBLOCK - 1, j = listblk + 1, k = 0; i >= 0; + i--, j++, k++) { + progress(0); + if (j == (int32_t)fs->superblock.volume_size) + { + VPRINTF(4, "\nlast freeblock #%d\n", + (*val32)(fb->freeblock[i + 1])); + + memmove(fb->freeblock + 1, fb->freeblock + i + 1, k * + sizeof(v7fs_daddr_t)); + fb->freeblock[0] = 0; /* Terminate link; */ + fb->nfreeblock = (*val16)(k + 1); + VPRINTF(4, "last freeblock contains #%d\n", + (*val16)(fb->nfreeblock)); + fs->io.write(fs->io.cookie, buf, listblk); + return 0; + } + fb->freeblock[i] = (*val32)(j); + } + fb->nfreeblock = (*val16)(k); + + if (!fs->io.write(fs->io.cookie, buf, listblk)) { + errno = EIO; + warn("blk=%ld", (long)listblk); + return 0; + } + + /* Return next link block */ + return (*val32)(fb->freeblock[0]); +} + +static int +make_filesystem(struct v7fs_self *fs, v7fs_daddr_t volume_size, + v7fs_daddr_t ilist_size) +{ + struct v7fs_superblock *sb; + v7fs_daddr_t blk; + uint8_t buf[V7FS_BSIZE]; + int error = 0; + int32_t i, j; + + /* Setup ilist. (ilist must be zero filled. becuase of they are free) */ + VPRINTF(4, "Zero clear ilist.\n"); + progress(&(struct progress_arg){ .label = "zero ilist", .tick = + ilist_size / PROGRESS_BAR_GRANULE }); + memset(buf, 0, sizeof buf); + for (i = V7FS_ILIST_SECTOR; i < (int32_t)ilist_size; i++) { + fs->io.write(fs->io.cookie, buf, i); + progress(0); + } +#ifndef HAVE_NBTOOL_CONFIG_H + progress_done(); +#endif + VPRINTF(4, "\n"); + + /* Construct superblock */ + sb = &fs->superblock; + sb->volume_size = volume_size; + sb->datablock_start_sector = ilist_size + V7FS_ILIST_SECTOR; + sb->update_time = time(NULL); + + /* fill free inode cache. */ + VPRINTF(4, "Setup inode cache.\n"); + sb->nfreeinode = V7FS_MAX_FREEINODE; + for (i = V7FS_MAX_FREEINODE - 1, j = V7FS_ROOT_INODE; i >= 0; i--, j++) + sb->freeinode[i] = j; + sb->total_freeinode = ilist_size * V7FS_INODE_PER_BLOCK - 1; + + /* fill free block cache. */ + VPRINTF(4, "Setup free block cache.\n"); + sb->nfreeblock = V7FS_MAX_FREEBLOCK; + for (i = V7FS_MAX_FREEBLOCK - 1, j = sb->datablock_start_sector; i >= 0; + i--, j++) + sb->freeblock[i] = j; + + sb->total_freeblock = volume_size - sb->datablock_start_sector; + + /* Write superblock. */ + sb->modified = 1; + if ((error = v7fs_superblock_writeback(fs))) { + errno = error; + warn("Can't write back superblock."); + return error; + } + + /* Construct freeblock list */ + VPRINTF(4, "Setup whole freeblock list.\n"); + progress(&(struct progress_arg){ .label = "freeblock list", .tick = + (volume_size - sb->datablock_start_sector) / PROGRESS_BAR_GRANULE}); + blk = sb->freeblock[0]; + while ((blk = make_freeblocklist(fs, blk, buf))) + continue; +#ifndef HAVE_NBTOOL_CONFIG_H + progress_done(); +#endif + + VPRINTF(4, "done.\n"); + + return 0; +} + +int +v7fs_newfs(const struct v7fs_mount_device *mount, int32_t maxfile) +{ + struct v7fs_self *fs; + v7fs_daddr_t ilist_size; + int error; + v7fs_daddr_t volume_size = mount->sectors; + + /* Check and determine ilistblock, datablock size. */ + if (volume_size > V7FS_DADDR_MAX + 1) { + warnx("volume size %d over v7fs limit %d. truncated.", + volume_size, V7FS_DADDR_MAX + 1); + volume_size = V7FS_DADDR_MAX + 1; + } + + ilist_size = determine_ilist_size(volume_size, maxfile); + + VPRINTF(1, "volume size=%d, ilist size=%d, endian=%d, NAME_MAX=%d\n", + volume_size, ilist_size, mount->endian, V7FS_NAME_MAX); + + /* Setup I/O ops. */ + if ((error = v7fs_io_init(&fs, mount, V7FS_BSIZE))) { + errno = error; + warn("I/O setup failed."); + return error; + } + fs->endian = mount->endian; + v7fs_endian_init(fs); + + if ((error = partition_check(fs))) { + return error; + } + + /* Construct filesystem. */ + if ((error = make_filesystem(fs, volume_size, ilist_size))) { + return error; + } + + /* Setup root. */ + if ((error = make_root(fs))) { + return error; + } + + v7fs_io_fini(fs); + + return 0; +} diff --git a/sbin/newfs_v7fs/newfs_v7fs.8 b/sbin/newfs_v7fs/newfs_v7fs.8 new file mode 100644 index 000000000..7dd3669d2 --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.8 @@ -0,0 +1,130 @@ +.\" $NetBSD: newfs_v7fs.8,v 1.3 2011/08/10 11:31:49 uch Exp $ +.\" +.\" Copyright (c) 2011 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by UCHIYAMA Yasushi. +.\" +.\" 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 +.\" 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. +.\" +.\" @(#)newlfs.8 8.1 (Berkeley) 6/19/93 +.\" +.Dd April 29, 2011 +.Dt NEWFS_V7FS 8 +.Os +.Sh NAME +.Nm newfs_v7fs +.Nd construct a new 7th Edition(V7) File System +.Sh SYNOPSIS +.Nm +.Op Fl FZ +.Op Fl B Ar byte-order +.Op Fl n Ar inodes +.Op Fl s Ar sectors +.Op Fl V Ar verbose +.Ar special +.Sh DESCRIPTION +.Nm +builds a 7th Edition(V7) file system on the specified +.Ar special . +If it is a device, the size information will be taken from the disk label and +before running +.Nm +the disk must be labeled using +.Xr disklabel 8 ; +the proper fstype is +.Dq Version 7 . +Otherwise, the size must be specified on the command line. +V7 filesystem's block size and sector size are 512 byte. +Disk address limits are 24 bit. +.Pp +The following arguments are supported: +.Bl -tag -width XBXbyteXorderXX +.It Fl B Ar byte-order +Specify the metadata byte order of the file system to be created. +Valid byte orders are +.Sq be , +.Sq le , +and +.Sq pdp . +If no byte order is specified, the file system is created in host +byte order. +.It Fl F +Create file system to a regular file (needs the +.Fl s +option). +.It Fl n Ar inodes +This specifies the number of inodes for the filesystem. +If the number of inodes exceeds 65536, it is reduced to 65536. +.It Fl s Ar sectors +Create file system with specified number of disk sectors. +.It Fl V Ar verbose +This controls the amount of information written to stdout: +.Bl -tag -width 3n -offset indent -compact +.It 0 +No output. +.It 1 +Overall size, ilist size, endian and filename length. +.It 2 +A progress bar. +.It 3 +.It 4 +More verbose message. +.El +The default is 3. +.It Fl Z +Fill file with zeroes instead of creating a sparse file. +.El +.Sh SEE ALSO +.Xr disklabel 5 , +.Xr disktab 5 , +.\" .Xr fs 5 , +.Xr disklabel 8 , +.Xr diskpart 8 diff --git a/sbin/newfs_v7fs/newfs_v7fs.c b/sbin/newfs_v7fs/newfs_v7fs.c new file mode 100644 index 000000000..565b5fdcb --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.c @@ -0,0 +1,240 @@ +/* $NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 +#ifndef lint +__RCSID("$NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "v7fs_impl.h" +#include "progress.h" +#include "newfs_v7fs.h" + +static void usage(void) __dead; +static bool progress_bar_enable = false; +int v7fs_newfs_verbose = 3; /* newfs compatible */ + +int +main(int argc, char **argv) +{ + const char *device; + struct disklabel d; + struct partition *p; + struct stat st; + uint32_t partsize; + int Fflag, Zflag; + int part; + int fd, ch; + int endian = _BYTE_ORDER; + int32_t maxfile = 0; + + if (argc < 2) + usage(); + + Fflag = Zflag = partsize = 0; + while ((ch = getopt(argc, argv, "Fs:Zs:n:B:V:")) != -1) { + switch (ch) { + case 'V': + v7fs_newfs_verbose = atoi(optarg); + break; + case 'F': + Fflag = 1; + break; + case 's': + partsize = atoi(optarg); + break; + case 'n': + maxfile = atoi(optarg); + break; + case 'Z': + Zflag = 1; + break; + case 'B': + switch (optarg[0]) { + case 'l': + endian = _LITTLE_ENDIAN; + break; + case 'b': + endian = _BIG_ENDIAN; + break; + case 'p': + endian = _PDP_ENDIAN; + break; + } + break; + default: + usage(); + /*NOTREACHED*/ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + device = argv[0]; + + progress_bar_enable = v7fs_newfs_verbose > 1; + + + if (progress_bar_enable) { + progress_switch(progress_bar_enable); + progress_init(); + progress(&(struct progress_arg){ .cdev = device }); + } + + if (!Fflag) { + if ((fd = open(device, O_RDWR)) == -1) { + err(EXIT_FAILURE, "%s", device); + } + if (fstat(fd, &st) != 0) { + goto err_exit; + } + if (!S_ISCHR(st.st_mode)) { + warnx("not a raw device.\n"); + } + + part = DISKPART(st.st_rdev); + + if (ioctl(fd, DIOCGDINFO, &d) == -1) { + goto err_exit; + } + p = &d.d_partitions[part]; + if (v7fs_newfs_verbose) { + printf("partition=%d size=%d offset=%d fstype=%d" + " secsize=%d\n", part, p->p_size, p->p_offset, + p->p_fstype, d.d_secsize); + } + if (p->p_fstype != FS_V7) { + warnx("not a Version 7 partition."); + goto err_exit; + } + partsize = p->p_size; + } else { + off_t filesize; + uint8_t zbuf[8192] = {0, }; + + if (partsize == 0) { + errx(EXIT_FAILURE, "-F requires -s"); + } + + filesize = partsize << V7FS_BSHIFT; + + fd = open(device, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fd == -1) { + err(EXIT_FAILURE, "%s", device); + } + + if (Zflag) { + while (filesize > 0) { + size_t writenow = MIN(filesize, + (off_t)sizeof(zbuf)); + + if ((size_t)write(fd, zbuf, writenow) != + writenow) { + err(EXIT_FAILURE, NULL); + } + filesize -= writenow; + } + } else { + if (lseek(fd, filesize - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, zbuf, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + } + } + + if (v7fs_newfs(&(struct v7fs_mount_device) + { .device.fd = fd, .endian = endian, .sectors = partsize }, + maxfile) != 0) + goto err_exit; + + close(fd); + + return EXIT_SUCCESS; + err_exit: + close(fd); + err(EXIT_FAILURE, NULL); +} + +void +progress(const struct progress_arg *p) +{ + static struct progress_arg Progress; + static char cdev[32]; + static char label[32]; + + if (!progress_bar_enable) + return; + + if (p) { + Progress = *p; + if (p->cdev) + strcpy(cdev, p->cdev); + if (p->label) + strcpy(label, p->label); + } + + if (!Progress.tick) + return; + if (++Progress.cnt > Progress.tick) { + Progress.cnt = 0; + Progress.total++; + progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE); + } +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: \n%s [-FZ] [-B byte-order]" + " [-n inodes] [-s sectors] [-V verbose] special\n", getprogname()); + + exit(EXIT_FAILURE); +} diff --git a/sbin/newfs_v7fs/newfs_v7fs.h b/sbin/newfs_v7fs/newfs_v7fs.h new file mode 100644 index 000000000..52ddcea11 --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.h @@ -0,0 +1,48 @@ +/* $NetBSD: newfs_v7fs.h,v 1.2 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ +#define _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ + +#define PROGRESS_BAR_GRANULE 100 +struct progress_arg { + const char *cdev; + const char *label; + off_t tick; + off_t cnt; + off_t total; +}; +__BEGIN_DECLS +void progress(const struct progress_arg *); +int v7fs_newfs(const struct v7fs_mount_device *, int32_t); +extern int v7fs_newfs_verbose; +__END_DECLS +#endif /* !_SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ */ diff --git a/sys/fs/Makefile b/sys/fs/Makefile index 42a39fb7f..1cb94a587 100644 --- a/sys/fs/Makefile +++ b/sys/fs/Makefile @@ -1,6 +1,6 @@ # $NetBSD: Makefile,v 1.20 2011/06/27 11:52:24 uch Exp $ SUBDIR= cd9660 msdosfs \ - puffs + puffs udf v7fs .include diff --git a/sys/fs/udf/Makefile b/sys/fs/udf/Makefile new file mode 100644 index 000000000..02c65a12b --- /dev/null +++ b/sys/fs/udf/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2006/02/02 15:19:15 reinoud Exp $ + +INCSDIR= /usr/include/fs/udf + +INCS= ecma167-udf.h udf_mount.h + +.include diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h new file mode 100644 index 000000000..c52b99de8 --- /dev/null +++ b/sys/fs/udf/ecma167-udf.h @@ -0,0 +1,835 @@ +/* $NetBSD: ecma167-udf.h,v 1.14 2011/07/07 17:45:38 reinoud Exp $ */ + +/*- + * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009 + * Reinoud Zandijk * + * Copyright (c) 2001, 2002 Scott Long + * 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 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 AUTHOR 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. + * + * + * Extended and adapted for UDFv2.50+ bij Reinoud Zandijk based on the + * original by Scott Long. + * + * 20030508 Made some small typo and explanatory comments + * 20030510 Added UDF 2.01 structures + * 20030519 Added/correct comments on multi-partitioned logical volume space + * 20050616 Added pseudo overwrite + * 20050624 Added the missing extended attribute types and `magic values'. + * 20051106 Reworked some implementation use parts + * + */ + + +#ifndef _FS_UDF_ECMA167_UDF_H_ +#define _FS_UDF_ECMA167_UDF_H_ + + +/* + * in case of an older gcc versions, define the __packed as explicit + * attribute + */ + +/* + * You may specify the `aligned' and `transparent_union' attributes either in + * a `typedef' declaration or just past the closing curly brace of a complete + * enum, struct or union type _definition_ and the `packed' attribute only + * past the closing brace of a definition. You may also specify attributes + * between the enum, struct or union tag and the name of the type rather than + * after the closing brace. +*/ + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + + +/* ecma167-udf.h */ + +/* Volume recognition sequence ECMA 167 rev. 3 16.1 */ +struct vrs_desc { + uint8_t struct_type; + uint8_t identifier[5]; + uint8_t version; + uint8_t data[2041]; +} __packed; + + +#define VRS_NSR02 "NSR02" +#define VRS_NSR03 "NSR03" +#define VRS_BEA01 "BEA01" +#define VRS_TEA01 "TEA01" +#define VRS_CD001 "CD001" +#define VRS_CDW02 "CDW02" + + +/* Structure/definitions/constants a la ECMA 167 rev. 3 */ + + +#define MAX_TAGID_VOLUMES 9 +/* Tag identifiers */ +enum { + TAGID_SPARING_TABLE = 0, + TAGID_PRI_VOL = 1, + TAGID_ANCHOR = 2, + TAGID_VOL = 3, + TAGID_IMP_VOL = 4, + TAGID_PARTITION = 5, + TAGID_LOGVOL = 6, + TAGID_UNALLOC_SPACE = 7, + TAGID_TERM = 8, + TAGID_LOGVOL_INTEGRITY= 9, + TAGID_FSD = 256, + TAGID_FID = 257, + TAGID_ALLOCEXTENT = 258, + TAGID_INDIRECTENTRY = 259, + TAGID_ICB_TERM = 260, + TAGID_FENTRY = 261, + TAGID_EXTATTR_HDR = 262, + TAGID_UNALL_SP_ENTRY = 263, + TAGID_SPACE_BITMAP = 264, + TAGID_PART_INTEGRITY = 265, + TAGID_EXTFENTRY = 266, + TAGID_MAX = 266 +}; + + +enum { + UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT = 1, + UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT = 2 +}; + + +enum { + UDF_ACCESSTYPE_NOT_SPECIFIED = 0, /* unknown */ + UDF_ACCESSTYPE_PSEUDO_OVERWITE = 0, /* pseudo overwritable, e.g. BD-R's LOW */ + UDF_ACCESSTYPE_READ_ONLY = 1, /* really only readable */ + UDF_ACCESSTYPE_WRITE_ONCE = 2, /* write once and you're done */ + UDF_ACCESSTYPE_REWRITEABLE = 3, /* may need extra work to rewrite */ + UDF_ACCESSTYPE_OVERWRITABLE = 4 /* no limits on rewriting; e.g. harddisc*/ +}; + + +/* Descriptor tag [3/7.2] */ +struct desc_tag { + uint16_t id; + uint16_t descriptor_ver; + uint8_t cksum; + uint8_t reserved; + uint16_t serial_num; + uint16_t desc_crc; + uint16_t desc_crc_len; + uint32_t tag_loc; +} __packed; +#define UDF_DESC_TAG_LENGTH 16 + + +/* Recorded Address [4/7.1] */ +struct lb_addr { /* within partition space */ + uint32_t lb_num; + uint16_t part_num; +} __packed; + + +/* Extent Descriptor [3/7.1] */ +struct extent_ad { + uint32_t len; + uint32_t loc; +} __packed; + + +/* Short Allocation Descriptor [4/14.14.1] */ +struct short_ad { + uint32_t len; + uint32_t lb_num; +} __packed; + + +/* Long Allocation Descriptor [4/14.14.2] */ +struct UDF_ADImp_use { + uint16_t flags; + uint32_t unique_id; +} __packed; +#define UDF_ADIMP_FLAGS_EXTENT_ERASED 1 + + +struct long_ad { + uint32_t len; + struct lb_addr loc; /* within a logical volume mapped partition space !! */ + union { + uint8_t bytes[6]; + struct UDF_ADImp_use im_used; + } impl; +} __packed; +#define longad_uniqueid impl.im_used.unique_id + + +/* Extended Allocation Descriptor [4/14.14.3] ; identifies an extent of allocation descriptors ; also in UDF ? */ +struct ext_ad { + uint32_t ex_len; + uint32_t rec_len; + uint32_t inf_len; + struct lb_addr ex_loc; + uint8_t reserved[2]; +} __packed; + + +/* ICB : Information Control Block; positioning */ +union icb { + struct short_ad s_ad; + struct long_ad l_ad; + struct ext_ad e_ad; +}; + + +/* short/long/ext extent have flags encoded in length */ +#define UDF_EXT_ALLOCATED (0<<30) +#define UDF_EXT_FREED (1<<30) +#define UDF_EXT_ALLOCATED_BUT_NOT_USED (1<<30) +#define UDF_EXT_FREE (2<<30) +#define UDF_EXT_REDIRECT (3<<30) +#define UDF_EXT_FLAGS(len) ((len) & (3<<30)) +#define UDF_EXT_LEN(len) ((len) & ((1<<30)-1)) +#define UDF_EXT_MAXLEN ((1<<30)-1) + + +/* Character set spec [1/7.2.1] */ +struct charspec { + uint8_t type; + uint8_t inf[63]; +} __packed; + + +struct pathcomp { + uint8_t type; + uint8_t l_ci; + uint16_t comp_filever; + uint8_t ident[256]; +} __packed; +#define UDF_PATH_COMP_SIZE 4 +#define UDF_PATH_COMP_RESERVED 0 +#define UDF_PATH_COMP_ROOT 1 +#define UDF_PATH_COMP_MOUNTROOT 2 +#define UDF_PATH_COMP_PARENTDIR 3 +#define UDF_PATH_COMP_CURDIR 4 +#define UDF_PATH_COMP_NAME 5 + + +/* Timestamp [1/7.3] */ +struct timestamp { + uint16_t type_tz; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t centisec; + uint8_t hund_usec; + uint8_t usec; +} __packed; +#define UDF_TIMESTAMP_SIZE 12 + + +/* Entity Identifier [1/7.4] */ +#define UDF_REGID_ID_SIZE 23 +struct regid { + uint8_t flags; + uint8_t id[UDF_REGID_ID_SIZE]; + uint8_t id_suffix[8]; +} __packed; + + +/* ICB Tag [4/14.6] */ +struct icb_tag { + uint32_t prev_num_dirs; + uint16_t strat_type; + uint8_t strat_param[2]; + uint16_t max_num_entries; + uint8_t reserved; + uint8_t file_type; + struct lb_addr parent_icb; + uint16_t flags; +} __packed; +#define UDF_ICB_TAG_FLAGS_ALLOC_MASK 0x03 +#define UDF_ICB_SHORT_ALLOC 0x00 +#define UDF_ICB_LONG_ALLOC 0x01 +#define UDF_ICB_EXT_ALLOC 0x02 +#define UDF_ICB_INTERN_ALLOC 0x03 + +#define UDF_ICB_TAG_FLAGS_DIRORDERED (1<< 3) +#define UDF_ICB_TAG_FLAGS_NONRELOC (1<< 4) +#define UDF_ICB_TAG_FLAGS_CONTIGUES (1<< 9) +#define UDF_ICB_TAG_FLAGS_MULTIPLEVERS (1<<12) + +#define UDF_ICB_TAG_FLAGS_SETUID (1<< 6) +#define UDF_ICB_TAG_FLAGS_SETGID (1<< 7) +#define UDF_ICB_TAG_FLAGS_STICKY (1<< 8) + +#define UDF_ICB_FILETYPE_UNKNOWN 0 +#define UDF_ICB_FILETYPE_UNALLOCSPACE 1 +#define UDF_ICB_FILETYPE_PARTINTEGRITY 2 +#define UDF_ICB_FILETYPE_INDIRECTENTRY 3 +#define UDF_ICB_FILETYPE_DIRECTORY 4 +#define UDF_ICB_FILETYPE_RANDOMACCESS 5 +#define UDF_ICB_FILETYPE_BLOCKDEVICE 6 +#define UDF_ICB_FILETYPE_CHARDEVICE 7 +#define UDF_ICB_FILETYPE_EXTATTRREC 8 +#define UDF_ICB_FILETYPE_FIFO 9 +#define UDF_ICB_FILETYPE_SOCKET 10 +#define UDF_ICB_FILETYPE_TERM 11 +#define UDF_ICB_FILETYPE_SYMLINK 12 +#define UDF_ICB_FILETYPE_STREAMDIR 13 +#define UDF_ICB_FILETYPE_VAT 248 +#define UDF_ICB_FILETYPE_REALTIME 249 +#define UDF_ICB_FILETYPE_META_MAIN 250 +#define UDF_ICB_FILETYPE_META_MIRROR 251 +#define UDF_ICB_FILETYPE_META_BITMAP 252 + + +/* Anchor Volume Descriptor Pointer [3/10.2] */ +struct anchor_vdp { + struct desc_tag tag; + struct extent_ad main_vds_ex; /* to main volume descriptor set ; 16 sectors min */ + struct extent_ad reserve_vds_ex; /* copy of main volume descriptor set ; 16 sectors min */ +} __packed; + + +/* Volume Descriptor Pointer [3/10.3] */ +struct vol_desc_ptr { + struct desc_tag tag; /* use for extending the volume descriptor space */ + uint32_t vds_number; + struct extent_ad next_vds_ex; /* points to the next block for volume descriptor space */ +} __packed; + + +/* Primary Volume Descriptor [3/10.1] */ +struct pri_vol_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevail */ + uint32_t pvd_num; /* assigned by author; 0 is special as in it may only occur once */ + char vol_id[32]; /* KEY ; main identifier of this disc */ + uint16_t vds_num; /* volume descriptor number; i.e. what volume number is it */ + uint16_t max_vol_seq; /* maximum volume descriptor number known */ + uint16_t ichg_lvl; + uint16_t max_ichg_lvl; + uint32_t charset_list; + uint32_t max_charset_list; + char volset_id[128]; /* KEY ; if part of a multi-disc set or a band of volumes */ + struct charspec desc_charset; /* KEY according to ECMA 167 */ + struct charspec explanatory_charset; + struct extent_ad vol_abstract; + struct extent_ad vol_copyright; + struct regid app_id; + struct timestamp time; + struct regid imp_id; + uint8_t imp_use[64]; + uint32_t prev_vds_loc; /* location of predecessor _lov ? */ + uint16_t flags; /* bit 0 : if set indicates volume set name is meaningful */ + uint8_t reserved[22]; +} __packed; + + +/* UDF specific implementation use part of the implementation use volume descriptor */ +struct udf_lv_info { + struct charspec lvi_charset; + char logvol_id[128]; + + char lvinfo1[36]; + char lvinfo2[36]; + char lvinfo3[36]; + + struct regid impl_id; + uint8_t impl_use[128]; +} __packed; + + +/* Implementation use Volume Descriptor */ +struct impvol_desc { + struct desc_tag tag; + uint32_t seq_num; + struct regid impl_id; + union { + struct udf_lv_info lv_info; + char impl_use[460]; + } _impl_use; +} __packed; + + +/* Logical Volume Descriptor [3/10.6] */ +struct logvol_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevail */ + struct charspec desc_charset; /* KEY */ + char logvol_id[128]; /* KEY */ + uint32_t lb_size; + struct regid domain_id; + union { + struct long_ad fsd_loc; /* to fileset descriptor SEQUENCE */ + uint8_t logvol_content_use[16]; + } _lvd_use; + uint32_t mt_l; /* Partition map length */ + uint32_t n_pm; /* Number of partition maps */ + struct regid imp_id; + uint8_t imp_use[128]; + struct extent_ad integrity_seq_loc; + uint8_t maps[1]; +} __packed; +#define lv_fsd_loc _lvd_use.fsd_loc + +#define UDF_INTEGRITY_OPEN 0 +#define UDF_INTEGRITY_CLOSED 1 + + +#define UDF_PMAP_SIZE 64 + +/* Type 1 Partition Map [3/10.7.2] */ +struct part_map_1 { + uint8_t type; + uint8_t len; + uint16_t vol_seq_num; + uint16_t part_num; +} __packed; + + +/* Type 2 Partition Map [3/10.7.3] */ +struct part_map_2 { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid part_id; + uint16_t vol_seq_num; + uint16_t part_num; + uint8_t reserved2[24]; +} __packed; + + +/* Virtual Partition Map [UDF 2.01/2.2.8] */ +struct part_map_virt { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint8_t reserved1[24]; +} __packed; + + +/* Sparable Partition Map [UDF 2.01/2.2.9] */ +struct part_map_spare { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint16_t packet_len; + uint8_t n_st; /* Number of redundant sparing tables range 1-4 */ + uint8_t reserved1; + uint32_t st_size; /* size of EACH sparing table */ + uint32_t st_loc[1]; /* locations of sparing tables */ +} __packed; + + +/* Metadata Partition Map [UDF 2.50/2.2.10] */ +struct part_map_meta { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint32_t meta_file_lbn; /* logical block number for file entry within part_num */ + uint32_t meta_mirror_file_lbn; + uint32_t meta_bitmap_file_lbn; + uint32_t alloc_unit_size; /* allocation unit size in blocks */ + uint16_t alignment_unit_size; /* alignment necessary in blocks */ + uint8_t flags; + uint8_t reserved1[5]; +} __packed; +#define METADATA_DUPLICATED 1 + + +union udf_pmap { + uint8_t data[UDF_PMAP_SIZE]; + struct part_map_1 pm1; + struct part_map_2 pm2; + struct part_map_virt pmv; + struct part_map_spare pms; + struct part_map_meta pmm; +}; + + +/* Sparing Map Entry [UDF 2.01/2.2.11] */ +struct spare_map_entry { + uint32_t org; /* partition relative address */ + uint32_t map; /* absolute disc address (!) can be in partition, but doesn't have to be */ +} __packed; + + +/* Sparing Table [UDF 2.01/2.2.11] */ +struct udf_sparing_table { + struct desc_tag tag; + struct regid id; + uint16_t rt_l; /* Relocation Table len */ + uint8_t reserved[2]; + uint32_t seq_num; + struct spare_map_entry entries[1]; +} __packed; + + +#define UDF_NO_PREV_VAT 0xffffffff +/* UDF 1.50 VAT suffix [UDF 2.2.10 (UDF 1.50 spec)] */ +struct udf_oldvat_tail { + struct regid id; /* "*UDF Virtual Alloc Tbl" */ + uint32_t prev_vat; +} __packed; + + +/* VAT table [UDF 2.0.1/2.2.10] */ +struct udf_vat { + uint16_t header_len; + uint16_t impl_use_len; + char logvol_id[128]; /* newer version of the LVD one */ + uint32_t prev_vat; + uint32_t num_files; + uint32_t num_directories; + uint16_t min_udf_readver; + uint16_t min_udf_writever; + uint16_t max_udf_writever; + uint16_t reserved; + uint8_t data[1]; /* impl.use followed by VAT entries (uint32_t) */ +} __packed; + + +/* Space bitmap descriptor as found in the partition header descriptor */ +struct space_bitmap_desc { + struct desc_tag tag; /* TagId 264 */ + uint32_t num_bits; /* number of bits */ + uint32_t num_bytes; /* bytes that contain it */ + uint8_t data[1]; +} __packed; + + +/* Unalloc space entry as found in the partition header descriptor */ +struct space_entry_desc { + struct desc_tag tag; /* TagId 263 */ + struct icb_tag icbtag; /* type 1 */ + uint32_t l_ad; /* in bytes */ + uint8_t entry[1]; +} __packed; + + +/* Partition header descriptor; in the contents_use of part_desc */ +struct part_hdr_desc { + struct short_ad unalloc_space_table; + struct short_ad unalloc_space_bitmap; + struct short_ad part_integrity_table; /* has to be ZERO for UDF */ + struct short_ad freed_space_table; + struct short_ad freed_space_bitmap; + uint8_t reserved[88]; +} __packed; + + +/* Partition Descriptor [3/10.5] */ +struct part_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevailing */ + uint16_t flags; /* bit 0 : if set the space is allocated */ + uint16_t part_num; /* KEY */ + struct regid contents; + union { + struct part_hdr_desc part_hdr; + uint8_t contents_use[128]; + } _impl_use; + uint32_t access_type; /* R/W, WORM etc. */ + uint32_t start_loc; /* start of partition with given length */ + uint32_t part_len; + struct regid imp_id; + uint8_t imp_use[128]; + uint8_t reserved[156]; +} __packed; +#define pd_part_hdr _impl_use.part_hdr +#define UDF_PART_FLAG_ALLOCATED 1 + + +/* Unallocated Space Descriptor (UDF 2.01/2.2.5) */ +struct unalloc_sp_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevailing */ + uint32_t alloc_desc_num; + struct extent_ad alloc_desc[1]; +} __packed; + + +/* Logical Volume Integrity Descriptor [3/30.10] */ +struct logvolhdr { + uint64_t next_unique_id; + /* rest reserved */ +} __packed; + + +struct udf_logvol_info { + struct regid impl_id; + uint32_t num_files; + uint32_t num_directories; + uint16_t min_udf_readver; + uint16_t min_udf_writever; + uint16_t max_udf_writever; +} __packed; + + +struct logvol_int_desc { + struct desc_tag tag; + struct timestamp time; + uint32_t integrity_type; + struct extent_ad next_extent; + union { + struct logvolhdr logvolhdr; + int8_t reserved[32]; + } _impl_use; + uint32_t num_part; + uint32_t l_iu; + uint32_t tables[1]; /* Freespace table, Sizetable, Implementation use */ +} __packed; +#define lvint_next_unique_id _impl_use.logvolhdr.next_unique_id + + +/* File Set Descriptor [4/14.1] */ +struct fileset_desc { + struct desc_tag tag; + struct timestamp time; + uint16_t ichg_lvl; + uint16_t max_ichg_lvl; + uint32_t charset_list; + uint32_t max_charset_list; + uint32_t fileset_num; /* key! */ + uint32_t fileset_desc_num; + struct charspec logvol_id_charset; + char logvol_id[128]; /* for recovery */ + struct charspec fileset_charset; + char fileset_id[32]; /* Mountpoint !! */ + char copyright_file_id[32]; + char abstract_file_id[32]; + struct long_ad rootdir_icb; /* to rootdir; icb->virtual ? */ + struct regid domain_id; + struct long_ad next_ex; /* to the next fileset_desc extent */ + struct long_ad streamdir_icb; /* streamdir; needed? */ + uint8_t reserved[32]; +} __packed; + + +/* File Identifier Descriptor [4/14.4] */ +struct fileid_desc { + struct desc_tag tag; + uint16_t file_version_num; + uint8_t file_char; + uint8_t l_fi; /* Length of file identifier area */ + struct long_ad icb; + uint16_t l_iu; /* Length of implementation use area */ + uint8_t data[0]; +} __packed; +#define UDF_FID_SIZE 38 +#define UDF_FILE_CHAR_VIS (1 << 0) /* Invisible */ +#define UDF_FILE_CHAR_DIR (1 << 1) /* Directory */ +#define UDF_FILE_CHAR_DEL (1 << 2) /* Deleted */ +#define UDF_FILE_CHAR_PAR (1 << 3) /* Parent Directory */ +#define UDF_FILE_CHAR_META (1 << 4) /* Stream metadata */ + + +/* Extended attributes [4/14.10.1] */ +struct extattrhdr_desc { + struct desc_tag tag; + uint32_t impl_attr_loc; /* offsets within this descriptor */ + uint32_t appl_attr_loc; /* ditto */ +} __packed; +#define UDF_IMPL_ATTR_LOC_NOT_PRESENT 0xffffffff +#define UDF_APPL_ATTR_LOC_NOT_PRESENT 0xffffffff + + +/* Extended attribute entry [4/48.10.2] */ +struct extattr_entry { + uint32_t type; + uint8_t subtype; + uint8_t reserved[3]; + uint32_t a_l; +} __packed; + + +/* Extended attribute entry; type 2048 [4/48.10.8] */ +struct impl_extattr_entry { + struct extattr_entry hdr; + uint32_t iu_l; + struct regid imp_id; + uint8_t data[1]; +} __packed; + + +/* Extended attribute entry; type 65 536 [4/48.10.9] */ +struct appl_extattr_entry { + struct extattr_entry hdr; + uint32_t au_l; + struct regid appl_id; + uint8_t data[1]; +} __packed; + + +/* File Times attribute entry; type 5 or type 6 [4/48.10.5], [4/48.10.6] */ +struct filetimes_extattr_entry { + struct extattr_entry hdr; + uint32_t d_l; /* length of times[] data following */ + uint32_t existence; /* bitmask */ + struct timestamp times[1]; /* in order of ascending bits */ +} __packed; +#define UDF_FILETIMES_ATTR_NO 5 +#define UDF_FILETIMES_FILE_CREATION 1 +#define UDF_FILETIMES_FILE_DELETION 4 +#define UDF_FILETIMES_FILE_EFFECTIVE 8 +#define UDF_FILETIMES_FILE_BACKUPED 16 +#define UDF_FILETIMES_ATTR_SIZE(no) (20 + (no)*sizeof(struct timestamp)) + + +/* Device Specification Extended Attribute [4/4.10.7] */ +struct device_extattr_entry { + struct extattr_entry hdr; + uint32_t iu_l; /* length of implementation use */ + uint32_t major; + uint32_t minor; + uint8_t data[1]; /* UDF: if nonzero length, contain developer ID regid */ +} __packed; +#define UDF_DEVICESPEC_ATTR_NO 12 + + +/* VAT LV extension Extended Attribute [UDF 3.3.4.5.1.3] 1.50 errata */ +struct vatlvext_extattr_entry { + uint64_t unique_id_chk; /* needs to be copy of ICB's */ + uint32_t num_files; + uint32_t num_directories; + char logvol_id[128]; /* replaces logvol name */ +} __packed; + + +/* File Entry [4/14.9] */ +struct file_entry { + struct desc_tag tag; + struct icb_tag icbtag; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint16_t link_cnt; + uint8_t rec_format; + uint8_t rec_disp_attr; + uint32_t rec_len; + uint64_t inf_len; + uint64_t logblks_rec; + struct timestamp atime; + struct timestamp mtime; + struct timestamp attrtime; + uint32_t ckpoint; + struct long_ad ex_attr_icb; + struct regid imp_id; + uint64_t unique_id; + uint32_t l_ea; /* Length of extended attribute area */ + uint32_t l_ad; /* Length of allocation descriptors */ + uint8_t data[1]; +} __packed; +#define UDF_FENTRY_SIZE 176 +#define UDF_FENTRY_PERM_USER_MASK 0x07 +#define UDF_FENTRY_PERM_GRP_MASK 0xE0 +#define UDF_FENTRY_PERM_OWNER_MASK 0x1C00 + + +/* Extended File Entry [4/48.17] */ +struct extfile_entry { + struct desc_tag tag; + struct icb_tag icbtag; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint16_t link_cnt; + uint8_t rec_format; + uint8_t rec_disp_attr; + uint32_t rec_len; + uint64_t inf_len; + uint64_t obj_size; + uint64_t logblks_rec; + struct timestamp atime; + struct timestamp mtime; + struct timestamp ctime; + struct timestamp attrtime; + uint32_t ckpoint; + uint32_t reserved1; + struct long_ad ex_attr_icb; + struct long_ad streamdir_icb; + struct regid imp_id; + uint64_t unique_id; + uint32_t l_ea; /* Length of extended attribute area */ + uint32_t l_ad; /* Length of allocation descriptors */ + uint8_t data[1]; +} __packed; +#define UDF_EXTFENTRY_SIZE 216 + + +/* Indirect entry [ecma 48.7] */ +struct indirect_entry { + struct desc_tag tag; + struct icb_tag icbtag; + struct long_ad indirect_icb; +} __packed; + + +/* Allocation extent descriptor [ecma 48.5] */ +struct alloc_ext_entry { + struct desc_tag tag; + uint32_t prev_entry; + uint32_t l_ad; + uint8_t data[1]; +} __packed; + + +union dscrptr { + struct desc_tag tag; + struct anchor_vdp avdp; + struct vol_desc_ptr vdp; + struct pri_vol_desc pvd; + struct logvol_desc lvd; + struct unalloc_sp_desc usd; + struct logvol_int_desc lvid; + struct impvol_desc ivd; + struct part_desc pd; + struct fileset_desc fsd; + struct fileid_desc fid; + struct file_entry fe; + struct extfile_entry efe; + struct extattrhdr_desc eahd; + struct indirect_entry inde; + struct alloc_ext_entry aee; + struct udf_sparing_table spt; + struct space_bitmap_desc sbd; + struct space_entry_desc sed; +}; + + +#endif /* !_FS_UDF_ECMA167_UDF_H_ */ + diff --git a/sys/fs/udf/files.udf b/sys/fs/udf/files.udf new file mode 100644 index 000000000..87fb6676d --- /dev/null +++ b/sys/fs/udf/files.udf @@ -0,0 +1,16 @@ +# $NetBSD: files.udf,v 1.5 2013/07/10 15:10:56 reinoud Exp $ + +deffs UDF + +file fs/udf/udf_osta.c udf +file fs/udf/udf_vfsops.c udf +file fs/udf/udf_vnops.c udf +file fs/udf/udf_subr.c udf +file fs/udf/udf_readwrite.c udf +file fs/udf/udf_strat_bootstrap.c udf +file fs/udf/udf_strat_sequential.c udf +file fs/udf/udf_strat_direct.c udf +file fs/udf/udf_strat_rmw.c udf +file fs/udf/udf_allocation.c udf +file fs/udf/udf_rename.c udf + diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h new file mode 100644 index 000000000..6848203c7 --- /dev/null +++ b/sys/fs/udf/udf.h @@ -0,0 +1,426 @@ +/* $NetBSD: udf.h,v 1.46 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#ifndef _FS_UDF_UDF_H_ +#define _FS_UDF_UDF_H_ + +#include +#include +#include +#include + +#include "udf_osta.h" +#include "ecma167-udf.h" +#include +#include +#include +#include +#include + +/* debug section */ +extern int udf_verbose; + +/* undefine UDF_COMPLETE_DELETE to need `purge'; but purge is not implemented */ +#define UDF_COMPLETE_DELETE + +/* debug categories */ +#define UDF_DEBUG_VOLUMES 0x0000001 +#define UDF_DEBUG_LOCKING 0x0000002 +#define UDF_DEBUG_NODE 0x0000004 +#define UDF_DEBUG_LOOKUP 0x0000008 +#define UDF_DEBUG_READDIR 0x0000010 +#define UDF_DEBUG_FIDS 0x0000020 +#define UDF_DEBUG_DESCRIPTOR 0x0000040 +#define UDF_DEBUG_TRANSLATE 0x0000080 +#define UDF_DEBUG_STRATEGY 0x0000100 +#define UDF_DEBUG_READ 0x0000200 +#define UDF_DEBUG_WRITE 0x0000400 +#define UDF_DEBUG_CALL 0x0000800 +#define UDF_DEBUG_ATTR 0x0001000 +#define UDF_DEBUG_EXTATTR 0x0002000 +#define UDF_DEBUG_ALLOC 0x0004000 +#define UDF_DEBUG_ADWLK 0x0008000 +#define UDF_DEBUG_DIRHASH 0x0010000 +#define UDF_DEBUG_NOTIMPL 0x0020000 +#define UDF_DEBUG_SHEDULE 0x0040000 +#define UDF_DEBUG_ECCLINE 0x0080000 +#define UDF_DEBUG_SYNC 0x0100000 +#define UDF_DEBUG_PARANOIA 0x0200000 +#define UDF_DEBUG_PARANOIDADWLK 0x0400000 +#define UDF_DEBUG_NODEDUMP 0x0800000 +#define UDF_DEBUG_RESERVE 0x1000000 + +/* initial value of udf_verbose */ +#define UDF_DEBUGGING 0 + +#ifdef UDF_DEBUG +#define DPRINTF(name, arg) { \ + if (udf_verbose & UDF_DEBUG_##name) {\ + printf arg;\ + };\ + } +#define DPRINTFIF(name, cond, arg) { \ + if (udf_verbose & UDF_DEBUG_##name) { \ + if (cond) printf arg;\ + };\ + } +#else +#define DPRINTF(name, arg) {} +#define DPRINTFIF(name, cond, arg) {} +#endif + + +/* constants to identify what kind of identifier we are dealing with */ +#define UDF_REGID_DOMAIN 1 +#define UDF_REGID_UDF 2 +#define UDF_REGID_IMPLEMENTATION 3 +#define UDF_REGID_APPLICATION 4 +#define UDF_REGID_NAME 99 + + +/* DON'T change these: they identify 13thmonkey's UDF implementation */ +#define APP_NAME "*NetBSD UDF" +#define APP_VERSION_MAIN 0 +#define APP_VERSION_SUB 5 +#define IMPL_NAME "*NetBSD kernel UDF" + + +/* Configuration values */ +#define UDF_INODE_HASHBITS 10 +#define UDF_INODE_HASHSIZE (1< are normal */ + +#define UDF_VTOP_TYPE_RAW 0 +#define UDF_VTOP_TYPE_UNKNOWN 0 +#define UDF_VTOP_TYPE_PHYS 1 +#define UDF_VTOP_TYPE_VIRT 2 +#define UDF_VTOP_TYPE_SPARABLE 3 +#define UDF_VTOP_TYPE_META 4 + + +/* allocation strategies */ +#define UDF_ALLOC_INVALID 0 +#define UDF_ALLOC_SEQUENTIAL 1 /* linear on NWA */ +#define UDF_ALLOC_VAT 2 /* VAT handling */ +#define UDF_ALLOC_SPACEMAP 3 /* spacemaps */ +#define UDF_ALLOC_METABITMAP 4 /* metadata bitmap */ +#define UDF_ALLOC_METASEQUENTIAL 5 /* in chunks seq., nodes not seq */ +#define UDF_ALLOC_RELAXEDSEQUENTIAL 6 /* only nodes not seq. */ + + +/* logical volume open/close actions */ +#define UDF_OPEN_SESSION 0x01 /* if needed writeout VRS + VDS */ +#define UDF_CLOSE_SESSION 0x02 /* close session after writing VAT */ +#define UDF_FINALISE_DISC 0x04 /* close session after writing VAT */ +#define UDF_WRITE_VAT 0x08 /* sequential VAT filesystem */ +#define UDF_WRITE_LVINT 0x10 /* write out open lvint */ +#define UDF_WRITE_PART_BITMAPS 0x20 /* write out partition space bitmaps */ +#define UDF_APPENDONLY_LVINT 0x40 /* no shifting, only appending */ +#define UDF_WRITE_METAPART_NODES 0x80 /* write out metadata partition nodes*/ +#define UDFLOGVOL_BITS "\20\1OPEN_SESSION\2CLOSE_SESSION\3FINALISE_DISC" \ + "\4WRITE_VAT\5WRITE_LVINT\6WRITE_PART_BITMAPS" \ + "\7APPENDONLY_LVINT\10WRITE_METAPART_NODES" + +/* logical volume error handling actions */ +#define UDF_UPDATE_TRACKINFO 0x01 /* update trackinfo and re-shedule */ +#define UDF_REMAP_BLOCK 0x02 /* remap the failing block length */ +#define UDFONERROR_BITS "\20\1UPDATE_TRACKINFO\2REMAP_BLOCK" + + +/* readdir cookies */ +#define UDF_DIRCOOKIE_DOT 1 + + +/* malloc pools */ +MALLOC_DECLARE(M_UDFMNT); +MALLOC_DECLARE(M_UDFVOLD); +MALLOC_DECLARE(M_UDFTEMP); + +extern struct pool udf_node_pool; +struct udf_node; +struct udf_strategy; + + +struct udf_lvintq { + uint32_t start; + uint32_t end; + uint32_t pos; + uint32_t wpos; +}; + + +struct udf_bitmap { + uint8_t *blob; /* allocated */ + uint8_t *bits; /* bits themselves */ + uint8_t *pages; /* dirty pages */ + uint32_t max_offset; /* in bits */ + uint32_t data_pos; /* position in data */ + uint32_t metadata_pos; /* .. in metadata */ +}; + + +struct udf_strat_args { + struct udf_mount *ump; + struct udf_node *udf_node; + struct long_ad *icb; + union dscrptr *dscr; + struct buf *nestbuf; + kauth_cred_t cred; + int waitfor; +}; + +struct udf_strategy { + int (*create_logvol_dscr) (struct udf_strat_args *args); + void (*free_logvol_dscr) (struct udf_strat_args *args); + int (*read_logvol_dscr) (struct udf_strat_args *args); + int (*write_logvol_dscr) (struct udf_strat_args *args); + void (*queuebuf) (struct udf_strat_args *args); + void (*discstrat_init) (struct udf_strat_args *args); + void (*discstrat_finish) (struct udf_strat_args *args); +}; + +extern struct udf_strategy udf_strat_bootstrap; +extern struct udf_strategy udf_strat_sequential; +extern struct udf_strategy udf_strat_direct; +extern struct udf_strategy udf_strat_rmw; + + +/* pre cleanup */ +struct udf_mount { + struct mount *vfs_mountp; + struct vnode *devvp; + struct mmc_discinfo discinfo; + struct udf_args mount_args; + + /* format descriptors */ + kmutex_t logvol_mutex; + struct anchor_vdp *anchors[UDF_ANCHORS]; /* anchors to VDS */ + struct pri_vol_desc *primary_vol; /* identification */ + struct logvol_desc *logical_vol; /* main mapping v->p */ + struct unalloc_sp_desc *unallocated; /* free UDF space */ + struct impvol_desc *implementation; /* likely reduntant */ + struct logvol_int_desc *logvol_integrity; /* current integrity */ + struct part_desc *partitions[UDF_PARTITIONS]; /* partitions */ + /* logvol_info is derived; points *into* other structures */ + struct udf_logvol_info *logvol_info; /* integrity descr. */ + + /* fileset and root directories */ + struct fileset_desc *fileset_desc; /* normally one */ + + /* tracing logvol integrity history */ + struct udf_lvintq lvint_trace[UDF_LVDINT_SEGMENTS]; + int lvopen; /* logvol actions */ + int lvclose; /* logvol actions */ + + /* logical to physical translations */ + int vtop[UDF_PMAPS+1]; /* vpartnr trans */ + int vtop_tp[UDF_PMAPS+1]; /* type of trans */ + + /* disc allocation / writing method */ + kmutex_t allocate_mutex; + int lvreadwrite; /* error handling */ + int vtop_alloc[UDF_PMAPS+1]; /* alloc scheme */ + int data_part; + int node_part; + int fids_part; + + /* sequential track info */ + struct mmc_trackinfo data_track; + struct mmc_trackinfo metadata_track; + + /* VAT */ + uint32_t first_possible_vat_location; + uint32_t last_possible_vat_location; + uint32_t vat_entries; + uint32_t vat_offset; /* offset in table */ + uint32_t vat_last_free_lb; /* last free lb_num */ + uint32_t vat_table_len; + uint32_t vat_table_alloc_len; + uint8_t *vat_table; + uint8_t *vat_pages; /* TODO */ + struct udf_node *vat_node; /* system node */ + + /* space bitmaps for physical partitions */ + struct space_bitmap_desc*part_unalloc_dscr[UDF_PARTITIONS]; + struct space_bitmap_desc*part_freed_dscr [UDF_PARTITIONS]; + struct udf_bitmap part_unalloc_bits[UDF_PARTITIONS]; + struct udf_bitmap part_freed_bits [UDF_PARTITIONS]; + + /* sparable */ + uint32_t sparable_packet_size; + uint32_t packet_size; + struct udf_sparing_table*sparing_table; + + /* meta */ + struct udf_node *metadata_node; /* system node */ + struct udf_node *metadatamirror_node; /* system node */ + struct udf_node *metadatabitmap_node; /* system node */ + struct space_bitmap_desc*metadata_unalloc_dscr; + struct udf_bitmap metadata_unalloc_bits; + uint32_t metadata_alloc_unit_size; + uint16_t metadata_alignment_unit_size; + uint8_t metadata_flags; + + /* rb tree for lookup icb to udf_node and sorted list for sync */ + kmutex_t ihash_lock; + kmutex_t get_node_lock; + struct rb_tree udf_node_tree; + + /* syncing */ + int syncing; /* are we syncing? */ + kcondvar_t dirtynodes_cv; /* sleeping on sync */ + + /* late allocation */ + int32_t uncommitted_lbs[UDF_PARTITIONS]; + struct long_ad *la_node_ad_cpy; /* issue buf */ + uint64_t *la_lmapping, *la_pmapping; /* issue buf */ + + /* lists */ + STAILQ_HEAD(udfmntpts, udf_mount) all_udf_mntpnts; + + /* device strategy */ + struct udf_strategy *strategy; + void *strategy_private; +}; + +/* + * UDF node describing a file/directory. + * + * BUGALERT claim node_mutex before reading/writing to prevent inconsistencies ! + */ +struct udf_node { + struct genfs_node i_gnode; /* has to be first */ + struct vnode *vnode; /* vnode associated */ + struct udf_mount *ump; + + kmutex_t node_mutex; + kcondvar_t node_lock; /* sleeping lock */ + char const *lock_fname; + int lock_lineno; + + /* rb_node for fast lookup and fast sequential visiting */ + struct rb_node rbnode; + + /* one of `fe' or `efe' can be set, not both (UDF file entry dscr.) */ + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext[UDF_MAX_ALLOC_EXTENTS]; + int num_extensions; + + /* location found, recording location & hints */ + struct long_ad loc; /* FID/hash loc. */ + struct long_ad write_loc; /* strat 4096 loc */ + int needs_indirect; /* has missing indr. */ + struct long_ad ext_loc[UDF_MAX_ALLOC_EXTENTS]; + + struct dirhash *dir_hash; + + /* misc */ + uint32_t i_flags; /* associated flags */ + struct lockf *lockf; /* lock list */ + uint32_t outstanding_bufs; /* file data */ + uint32_t outstanding_nodedscr; /* node dscr */ + int32_t uncommitted_lbs; /* in UBC */ + + /* references to associated nodes */ + struct udf_node *extattr; + struct udf_node *streamdir; + struct udf_node *my_parent; /* if extended attr. */ +}; + + +/* misc. flags stored in i_flags (XXX needs cleaning up) */ +#define IN_ACCESS 0x0001 /* Inode access time update request */ +#define IN_CHANGE 0x0002 /* Inode change time update request */ +#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/ +#define IN_MODIFY 0x0008 /* Modification time update request */ +#define IN_MODIFIED 0x0010 /* node has been modified */ +#define IN_ACCESSED 0x0020 /* node has been accessed */ +#define IN_RENAME 0x0040 /* node is being renamed. XXX ?? */ +#define IN_DELETED 0x0080 /* node is unlinked, no FID reference*/ +#define IN_LOCKED 0x0100 /* node is locked by condvar */ +#define IN_SYNCED 0x0200 /* node is being used by sync */ +#define IN_CALLBACK_ULK 0x0400 /* node will be unlocked by callback */ +#define IN_NODE_REBUILD 0x0800 /* node is rebuild */ + + +#define IN_FLAGBITS \ + "\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFY\5IN_MODIFIED" \ + "\6IN_ACCESSED\7IN_RENAME\10IN_DELETED\11IN_LOCKED\12IN_SYNCED" \ + "\13IN_CALLBACK_ULK\14IN_NODE_REBUILD" + +#endif /* !_FS_UDF_UDF_H_ */ diff --git a/sys/fs/udf/udf_allocation.c b/sys/fs/udf/udf_allocation.c new file mode 100644 index 000000000..002116932 --- /dev/null +++ b/sys/fs/udf/udf_allocation.c @@ -0,0 +1,3215 @@ +/* $NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +/* TODO strip */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +static void udf_record_allocation_in_node(struct udf_mount *ump, + struct buf *buf, uint16_t vpart_num, uint64_t *mapping, + struct long_ad *node_ad_cpy); + +static void udf_collect_free_space_for_vpart(struct udf_mount *ump, + uint16_t vpart_num, uint32_t num_lb); + +static int udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2); +static void udf_wipe_adslots(struct udf_node *udf_node); +static void udf_count_alloc_exts(struct udf_node *udf_node); + + +/* --------------------------------------------------------------------- */ + +#if 0 +#if 1 +static void +udf_node_dump(struct udf_node *udf_node) { + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad; + uint64_t inflen; + uint32_t icbflags, addr_type; + uint32_t len, lb_num; + uint32_t flags; + int part_num; + int lb_size, eof, slot; + + if ((udf_verbose & UDF_DEBUG_NODEDUMP) == 0) + return; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + printf("udf_node_dump %p :\n", udf_node); + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + printf("\tIntern alloc, len = %"PRIu64"\n", inflen); + return; + } + + printf("\tInflen = %"PRIu64"\n", inflen); + printf("\t\t"); + + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + part_num = udf_rw16(s_ad.loc.part_num); + lb_num = udf_rw32(s_ad.loc.lb_num); + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + printf("["); + if (part_num >= 0) + printf("part %d, ", part_num); + printf("lb_num %d, len %d", lb_num, len); + if (flags) + printf(", flags %d", flags>>30); + printf("] "); + + if (flags == UDF_EXT_REDIRECT) { + printf("\n\textent END\n\tallocation extent\n\t\t"); + } + + slot++; + } + printf("\n\tl_ad END\n\n"); +} +#else +#define udf_node_dump(a) +#endif + + +static void +udf_assert_allocated(struct udf_mount *ump, uint16_t vpart_num, + uint32_t lb_num, uint32_t num_lb) +{ + struct udf_bitmap *bitmap; + struct part_desc *pdesc; + uint32_t ptov; + uint32_t bitval; + uint8_t *bpos; + int bit; + int phys_part; + int ok; + + DPRINTF(PARANOIA, ("udf_assert_allocated: check virt lbnum %d " + "part %d + %d sect\n", lb_num, vpart_num, num_lb)); + + /* get partition backing up this vpart_num */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + + switch (ump->vtop_tp[vpart_num]) { + case UDF_VTOP_TYPE_PHYS : + case UDF_VTOP_TYPE_SPARABLE : + /* free space to freed or unallocated space bitmap */ + ptov = udf_rw32(pdesc->start_loc); + phys_part = ump->vtop[vpart_num]; + + /* use unallocated bitmap */ + bitmap = &ump->part_unalloc_bits[phys_part]; + + /* if no bitmaps are defined, bail out */ + if (bitmap->bits == NULL) + break; + + /* check bits */ + KASSERT(bitmap->bits); + ok = 1; + bpos = bitmap->bits + lb_num/8; + bit = lb_num % 8; + while (num_lb > 0) { + bitval = (1 << bit); + DPRINTF(PARANOIA, ("XXX : check %d, %p, bit %d\n", + lb_num, bpos, bit)); + KASSERT(bitmap->bits + lb_num/8 == bpos); + if (*bpos & bitval) { + printf("\tlb_num %d is NOT marked busy\n", + lb_num); + ok = 0; + } + lb_num++; num_lb--; + bit = (bit + 1) % 8; + if (bit == 0) + bpos++; + } + if (!ok) { + /* KASSERT(0); */ + } + + break; + case UDF_VTOP_TYPE_VIRT : + /* TODO check space */ + KASSERT(num_lb == 1); + break; + case UDF_VTOP_TYPE_META : + /* TODO check space in the metadata bitmap */ + default: + /* not implemented */ + break; + } +} + + +static void +udf_node_sanity_check(struct udf_node *udf_node, + uint64_t *cnt_inflen, uint64_t *cnt_logblksrec) +{ + union dscrptr *dscr; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad; + uint64_t inflen, logblksrec; + uint32_t icbflags, addr_type; + uint32_t len, lb_num, l_ea, l_ad, max_l_ad; + uint16_t part_num; + uint8_t *data_pos; + int dscr_size, lb_size, flags, whole_lb; + int i, slot, eof; + +// KASSERT(mutex_owned(&udf_node->ump->allocate_mutex)); + + if (1) + udf_node_dump(udf_node); + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + dscr = (union dscrptr *) fe; + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + dscr_size = sizeof(struct file_entry) -1; + logblksrec = udf_rw64(fe->logblks_rec); + l_ad = udf_rw32(fe->l_ad); + l_ea = udf_rw32(fe->l_ea); + } else { + dscr = (union dscrptr *) efe; + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + dscr_size = sizeof(struct extfile_entry) -1; + logblksrec = udf_rw64(efe->logblks_rec); + l_ad = udf_rw32(efe->l_ad); + l_ea = udf_rw32(efe->l_ea); + } + data_pos = (uint8_t *) dscr + dscr_size + l_ea; + max_l_ad = lb_size - dscr_size - l_ea; + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* check if tail is zero */ + DPRINTF(PARANOIA, ("Sanity check blank tail\n")); + for (i = l_ad; i < max_l_ad; i++) { + if (data_pos[i] != 0) + printf( "sanity_check: violation: node byte %d " + "has value %d\n", i, data_pos[i]); + } + + /* reset counters */ + *cnt_inflen = 0; + *cnt_logblksrec = 0; + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + KASSERT(l_ad <= max_l_ad); + KASSERT(l_ad == inflen); + *cnt_inflen = inflen; + return; + } + + /* start counting */ + whole_lb = 1; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + KASSERT(whole_lb == 1); + + part_num = udf_rw16(s_ad.loc.part_num); + lb_num = udf_rw32(s_ad.loc.lb_num); + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags != UDF_EXT_REDIRECT) { + *cnt_inflen += len; + if (flags == UDF_EXT_ALLOCATED) { + *cnt_logblksrec += (len + lb_size -1) / lb_size; + } + } else { + KASSERT(len == lb_size); + } + /* check allocation */ + if (flags == UDF_EXT_ALLOCATED) + udf_assert_allocated(udf_node->ump, part_num, lb_num, + (len + lb_size - 1) / lb_size); + + /* check whole lb */ + whole_lb = ((len % lb_size) == 0); + + slot++; + } + /* rest should be zero (ad_off > l_ad < max_l_ad - adlen) */ + + KASSERT(*cnt_inflen == inflen); + KASSERT(*cnt_logblksrec == logblksrec); + +// KASSERT(mutex_owned(&udf_node->ump->allocate_mutex)); +} +#else +static void +udf_node_sanity_check(struct udf_node *udf_node, + uint64_t *cnt_inflen, uint64_t *cnt_logblksrec) { + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t inflen, logblksrec; + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + inflen = udf_rw64(fe->inf_len); + logblksrec = udf_rw64(fe->logblks_rec); + } else { + inflen = udf_rw64(efe->inf_len); + logblksrec = udf_rw64(efe->logblks_rec); + } + *cnt_logblksrec = logblksrec; + *cnt_inflen = inflen; +} +#endif + +/* --------------------------------------------------------------------- */ + +void +udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks) +{ + struct logvol_int_desc *lvid; + uint32_t *pos1, *pos2; + int vpart, num_vpart; + + lvid = ump->logvol_integrity; + *freeblks = *sizeblks = 0; + + /* + * Sequentials media report free space directly (CD/DVD/BD-R), for the + * other media we need the logical volume integrity. + * + * We sum all free space up here regardless of type. + */ + + KASSERT(lvid); + num_vpart = udf_rw32(lvid->num_part); + + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* use track info directly summing if there are 2 open */ + /* XXX assumption at most two tracks open */ + *freeblks = ump->data_track.free_blocks; + if (ump->data_track.tracknr != ump->metadata_track.tracknr) + *freeblks += ump->metadata_track.free_blocks; + *sizeblks = ump->discinfo.last_possible_lba; + } else { + /* free and used space for mountpoint based on logvol integrity */ + for (vpart = 0; vpart < num_vpart; vpart++) { + pos1 = &lvid->tables[0] + vpart; + pos2 = &lvid->tables[0] + num_vpart + vpart; + if (udf_rw32(*pos1) != (uint32_t) -1) { + *freeblks += udf_rw32(*pos1); + *sizeblks += udf_rw32(*pos2); + } + } + } + /* adjust for accounted uncommitted blocks */ + for (vpart = 0; vpart < num_vpart; vpart++) + *freeblks -= ump->uncommitted_lbs[vpart]; + + if (*freeblks > UDF_DISC_SLACK) { + *freeblks -= UDF_DISC_SLACK; + } else { + *freeblks = 0; + } +} + + +static void +udf_calc_vpart_freespace(struct udf_mount *ump, uint16_t vpart_num, uint64_t *freeblks) +{ + struct logvol_int_desc *lvid; + uint32_t *pos1; + + lvid = ump->logvol_integrity; + *freeblks = 0; + + /* + * Sequentials media report free space directly (CD/DVD/BD-R), for the + * other media we need the logical volume integrity. + * + * We sum all free space up here regardless of type. + */ + + KASSERT(lvid); + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* XXX assumption at most two tracks open */ + if (vpart_num == ump->data_part) { + *freeblks = ump->data_track.free_blocks; + } else { + *freeblks = ump->metadata_track.free_blocks; + } + } else { + /* free and used space for mountpoint based on logvol integrity */ + pos1 = &lvid->tables[0] + vpart_num; + if (udf_rw32(*pos1) != (uint32_t) -1) + *freeblks += udf_rw32(*pos1); + } + + /* adjust for accounted uncommitted blocks */ + if (*freeblks > ump->uncommitted_lbs[vpart_num]) { + *freeblks -= ump->uncommitted_lbs[vpart_num]; + } else { + *freeblks = 0; + } +} + +/* --------------------------------------------------------------------- */ + +int +udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc, + uint32_t *lb_numres, uint32_t *extres) +{ + struct part_desc *pdesc; + struct spare_map_entry *sme; + struct long_ad s_icb_loc; + uint64_t foffset, end_foffset; + uint32_t lb_size, len; + uint32_t lb_num, lb_rel, lb_packet; + uint32_t udf_rw32_lbmap, ext_offset; + uint16_t vpart; + int rel, part, error, eof, slot, flags; + + assert(ump && icb_loc && lb_numres); + + vpart = udf_rw16(icb_loc->loc.part_num); + lb_num = udf_rw32(icb_loc->loc.lb_num); + if (vpart > UDF_VTOP_RAWPART) + return EINVAL; + +translate_again: + part = ump->vtop[vpart]; + pdesc = ump->partitions[part]; + + switch (ump->vtop_tp[vpart]) { + case UDF_VTOP_TYPE_RAW : + /* 1:1 to the end of the device */ + *lb_numres = lb_num; + *extres = INT_MAX; + return 0; + case UDF_VTOP_TYPE_PHYS : + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* extent from here to the end of the partition */ + *extres = udf_rw32(pdesc->part_len) - lb_num; + return 0; + case UDF_VTOP_TYPE_VIRT : + /* only maps one logical block, lookup in VAT */ + if (lb_num >= ump->vat_entries) /* XXX > or >= ? */ + return EINVAL; + + /* lookup in virtual allocation table file */ + mutex_enter(&ump->allocate_mutex); + error = udf_vat_read(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + mutex_exit(&ump->allocate_mutex); + + if (error) + return error; + + lb_num = udf_rw32(udf_rw32_lbmap); + + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* just one logical block */ + *extres = 1; + return 0; + case UDF_VTOP_TYPE_SPARABLE : + /* check if the packet containing the lb_num is remapped */ + lb_packet = lb_num / ump->sparable_packet_size; + lb_rel = lb_num % ump->sparable_packet_size; + + for (rel = 0; rel < udf_rw16(ump->sparing_table->rt_l); rel++) { + sme = &ump->sparing_table->entries[rel]; + if (lb_packet == udf_rw32(sme->org)) { + /* NOTE maps to absolute disc logical block! */ + *lb_numres = udf_rw32(sme->map) + lb_rel; + *extres = ump->sparable_packet_size - lb_rel; + return 0; + } + } + + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* rest of block */ + *extres = ump->sparable_packet_size - lb_rel; + return 0; + case UDF_VTOP_TYPE_META : + /* we have to look into the file's allocation descriptors */ + + /* use metadatafile allocation mutex */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + + UDF_LOCK_NODE(ump->metadata_node, 0); + + /* get first overlapping extent */ + foffset = 0; + slot = 0; + for (;;) { + udf_get_adslot(ump->metadata_node, + slot, &s_icb_loc, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, " + "len = %d, lb_num = %d, part = %d\n", + slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_icb_loc.len)), + UDF_EXT_LEN(udf_rw32(s_icb_loc.len)), + udf_rw32(s_icb_loc.loc.lb_num), + udf_rw16(s_icb_loc.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Meta partition translation " + "failed: can't seek location\n")); + UDF_UNLOCK_NODE(ump->metadata_node, 0); + return EINVAL; + } + len = udf_rw32(s_icb_loc.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + + if (end_foffset > (uint64_t) lb_num * lb_size) + break; /* found */ + foffset = end_foffset; + slot++; + } + /* found overlapping slot */ + ext_offset = lb_num * lb_size - foffset; + + /* process extent offset */ + lb_num = udf_rw32(s_icb_loc.loc.lb_num); + vpart = udf_rw16(s_icb_loc.loc.part_num); + lb_num += (ext_offset + lb_size -1) / lb_size; + ext_offset = 0; + + UDF_UNLOCK_NODE(ump->metadata_node, 0); + if (flags != UDF_EXT_ALLOCATED) { + DPRINTF(TRANSLATE, ("Metadata partition translation " + "failed: not allocated\n")); + return EINVAL; + } + + /* + * vpart and lb_num are updated, translate again since we + * might be mapped on sparable media + */ + goto translate_again; + default: + printf("UDF vtop translation scheme %d unimplemented yet\n", + ump->vtop_tp[vpart]); + } + + return EINVAL; +} + + +/* XXX provisional primitive braindead version */ +/* TODO use ext_res */ +void +udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors, + uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping) +{ + struct long_ad loc; + uint32_t lb_numres, ext_res; + int sector; + + for (sector = 0; sector < sectors; sector++) { + memset(&loc, 0, sizeof(struct long_ad)); + loc.loc.part_num = udf_rw16(vpart_num); + loc.loc.lb_num = udf_rw32(*lmapping); + udf_translate_vtop(ump, &loc, &lb_numres, &ext_res); + *pmapping = lb_numres; + lmapping++; pmapping++; + } +} + + +/* --------------------------------------------------------------------- */ + +/* + * Translate an extent (in logical_blocks) into logical block numbers; used + * for read and write operations. DOESNT't check extents. + */ + +int +udf_translate_file_extent(struct udf_node *udf_node, + uint32_t from, uint32_t num_lb, + uint64_t *map) +{ + struct udf_mount *ump; + struct icb_tag *icbtag; + struct long_ad t_ad, s_ad; + uint64_t transsec; + uint64_t foffset, end_foffset; + uint32_t transsec32; + uint32_t lb_size; + uint32_t ext_offset; + uint32_t lb_num, len; + uint32_t overlap, translen; + uint16_t vpart_num; + int eof, error, flags; + int slot, addr_type, icbflags; + + if (!udf_node) + return ENOENT; + + KASSERT(num_lb > 0); + + UDF_LOCK_NODE(udf_node, 0); + + /* initialise derivative vars */ + ump = udf_node->ump; + lb_size = udf_rw32(ump->logical_vol->lb_size); + + if (udf_node->fe) { + icbtag = &udf_node->fe->icbtag; + } else { + icbtag = &udf_node->efe->icbtag; + } + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* do the work */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + *map = UDF_TRANS_INTERN; + UDF_UNLOCK_NODE(udf_node, 0); + return 0; + } + + /* find first overlapping extent */ + foffset = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_ad.len)), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + udf_rw32(s_ad.loc.lb_num), + udf_rw16(s_ad.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: can't seek location\n")); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + lb_num = udf_rw32(s_ad.loc.lb_num); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + + if (end_foffset > (uint64_t) from * lb_size) + break; /* found */ + foffset = end_foffset; + slot++; + } + /* found overlapping slot */ + ext_offset = (uint64_t) from * lb_size - foffset; + + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_ad.len)), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + udf_rw32(s_ad.loc.lb_num), + udf_rw16(s_ad.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: past eof\n")); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + + end_foffset = foffset + len; + + /* process extent, don't forget to advance on ext_offset! */ + lb_num += (ext_offset + lb_size -1) / lb_size; + overlap = (len - ext_offset + lb_size -1) / lb_size; + ext_offset = 0; + + /* + * note that the while(){} is nessisary for the extent that + * the udf_translate_vtop() returns doens't have to span the + * whole extent. + */ + + overlap = MIN(overlap, num_lb); + while (overlap && (flags != UDF_EXT_REDIRECT)) { + switch (flags) { + case UDF_EXT_FREE : + case UDF_EXT_ALLOCATED_BUT_NOT_USED : + transsec = UDF_TRANS_ZERO; + translen = overlap; + while (overlap && num_lb && translen) { + *map++ = transsec; + lb_num++; + overlap--; num_lb--; translen--; + } + break; + case UDF_EXT_ALLOCATED : + t_ad.loc.lb_num = udf_rw32(lb_num); + t_ad.loc.part_num = udf_rw16(vpart_num); + error = udf_translate_vtop(ump, + &t_ad, &transsec32, &translen); + transsec = transsec32; + if (error) { + UDF_UNLOCK_NODE(udf_node, 0); + return error; + } + while (overlap && num_lb && translen) { + *map++ = transsec; + lb_num++; transsec++; + overlap--; num_lb--; translen--; + } + break; + default: + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: bad flags %x\n", flags)); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + } + if (num_lb == 0) + break; + + if (flags != UDF_EXT_REDIRECT) + foffset = end_foffset; + slot++; + } + UDF_UNLOCK_NODE(udf_node, 0); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_search_free_vatloc(struct udf_mount *ump, uint32_t *lbnumres) +{ + uint32_t lb_size, lb_num, lb_map, udf_rw32_lbmap; + uint8_t *blob; + int entry, chunk, found, error; + + KASSERT(ump); + KASSERT(ump->logical_vol); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blob = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + /* TODO static allocation of search chunk */ + + lb_num = MIN(ump->vat_entries, ump->vat_last_free_lb); + found = 0; + error = 0; + entry = 0; + do { + chunk = MIN(lb_size, (ump->vat_entries - lb_num) * 4); + if (chunk <= 0) + break; + /* load in chunk */ + error = udf_vat_read(ump->vat_node, blob, chunk, + ump->vat_offset + lb_num * 4); + + if (error) + break; + + /* search this chunk */ + for (entry=0; entry < chunk /4; entry++, lb_num++) { + udf_rw32_lbmap = *((uint32_t *) (blob + entry * 4)); + lb_map = udf_rw32(udf_rw32_lbmap); + if (lb_map == 0xffffffff) { + found = 1; + break; + } + } + } while (!found); + if (error) { + printf("udf_search_free_vatloc: error reading in vat chunk " + "(lb %d, size %d)\n", lb_num, chunk); + } + + if (!found) { + /* extend VAT */ + DPRINTF(WRITE, ("udf_search_free_vatloc: extending\n")); + lb_num = ump->vat_entries; + ump->vat_entries++; + } + + /* mark entry with initialiser just in case */ + lb_map = udf_rw32(0xfffffffe); + udf_vat_write(ump->vat_node, (uint8_t *) &lb_map, 4, + ump->vat_offset + lb_num *4); + ump->vat_last_free_lb = lb_num; + + free(blob, M_UDFTEMP); + *lbnumres = lb_num; + return 0; +} + + +static void +udf_bitmap_allocate(struct udf_bitmap *bitmap, int ismetadata, + uint32_t *num_lb, uint64_t *lmappos) +{ + uint32_t offset, lb_num, bit; + int32_t diff; + uint8_t *bpos; + int pass; + + if (!ismetadata) { + /* heuristic to keep the two pointers not too close */ + diff = bitmap->data_pos - bitmap->metadata_pos; + if ((diff >= 0) && (diff < 1024)) + bitmap->data_pos = bitmap->metadata_pos + 1024; + } + offset = ismetadata ? bitmap->metadata_pos : bitmap->data_pos; + offset &= ~7; + for (pass = 0; pass < 2; pass++) { + if (offset >= bitmap->max_offset) + offset = 0; + + while (offset < bitmap->max_offset) { + if (*num_lb == 0) + break; + + /* use first bit not set */ + bpos = bitmap->bits + offset/8; + bit = ffs(*bpos); /* returns 0 or 1..8 */ + if (bit == 0) { + offset += 8; + continue; + } + + /* check for ffs overshoot */ + if (offset + bit-1 >= bitmap->max_offset) { + offset = bitmap->max_offset; + break; + } + + DPRINTF(PARANOIA, ("XXX : allocate %d, %p, bit %d\n", + offset + bit -1, bpos, bit-1)); + *bpos &= ~(1 << (bit-1)); + lb_num = offset + bit-1; + *lmappos++ = lb_num; + *num_lb = *num_lb - 1; + // offset = (offset & ~7); + } + } + + if (ismetadata) { + bitmap->metadata_pos = offset; + } else { + bitmap->data_pos = offset; + } +} + + +static void +udf_bitmap_free(struct udf_bitmap *bitmap, uint32_t lb_num, uint32_t num_lb) +{ + uint32_t offset; + uint32_t bit, bitval; + uint8_t *bpos; + + offset = lb_num; + + /* starter bits */ + bpos = bitmap->bits + offset/8; + bit = offset % 8; + while ((bit != 0) && (num_lb > 0)) { + bitval = (1 << bit); + KASSERT((*bpos & bitval) == 0); + DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n", + offset, bpos, bit)); + *bpos |= bitval; + offset++; num_lb--; + bit = (bit + 1) % 8; + } + if (num_lb == 0) + return; + + /* whole bytes */ + KASSERT(bit == 0); + bpos = bitmap->bits + offset / 8; + while (num_lb >= 8) { + KASSERT((*bpos == 0)); + DPRINTF(PARANOIA, ("XXX : free %d + 8, %p\n", offset, bpos)); + *bpos = 255; + offset += 8; num_lb -= 8; + bpos++; + } + + /* stop bits */ + KASSERT(num_lb < 8); + bit = 0; + while (num_lb > 0) { + bitval = (1 << bit); + KASSERT((*bpos & bitval) == 0); + DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n", + offset, bpos, bit)); + *bpos |= bitval; + offset++; num_lb--; + bit = (bit + 1) % 8; + } +} + + +static uint32_t +udf_bitmap_check_trunc_free(struct udf_bitmap *bitmap, uint32_t to_trunc) +{ + uint32_t seq_free, offset; + uint8_t *bpos; + uint8_t bit, bitval; + + DPRINTF(RESERVE, ("\ttrying to trunc %d bits from bitmap\n", to_trunc)); + offset = bitmap->max_offset - to_trunc; + + /* starter bits (if any) */ + bpos = bitmap->bits + offset/8; + bit = offset % 8; + seq_free = 0; + while (to_trunc > 0) { + seq_free++; + bitval = (1 << bit); + if (!(*bpos & bitval)) + seq_free = 0; + offset++; to_trunc--; + bit++; + if (bit == 8) { + bpos++; + bit = 0; + } + } + + DPRINTF(RESERVE, ("\tfound %d sequential free bits in bitmap\n", seq_free)); + return seq_free; +} + +/* --------------------------------------------------------------------- */ + +/* + * We check for overall disc space with a margin to prevent critical + * conditions. If disc space is low we try to force a sync() to improve our + * estimates. When confronted with meta-data partition size shortage we know + * we have to check if it can be extended and we need to extend it when + * needed. + * + * A 2nd strategy we could use when disc space is getting low on a disc + * formatted with a meta-data partition is to see if there are sparse areas in + * the meta-data partition and free blocks there for extra data. + */ + +void +udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, + uint16_t vpart_num, uint32_t num_lb) +{ + ump->uncommitted_lbs[vpart_num] += num_lb; + if (udf_node) + udf_node->uncommitted_lbs += num_lb; +} + + +void +udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node, + uint16_t vpart_num, uint32_t num_lb) +{ + ump->uncommitted_lbs[vpart_num] -= num_lb; + if (ump->uncommitted_lbs[vpart_num] < 0) { + DPRINTF(RESERVE, ("UDF: underflow on partition reservation, " + "part %d: %d\n", vpart_num, + ump->uncommitted_lbs[vpart_num])); + ump->uncommitted_lbs[vpart_num] = 0; + } + if (udf_node) { + udf_node->uncommitted_lbs -= num_lb; + if (udf_node->uncommitted_lbs < 0) { + DPRINTF(RESERVE, ("UDF: underflow of node " + "reservation : %d\n", + udf_node->uncommitted_lbs)); + udf_node->uncommitted_lbs = 0; + } + } +} + + +int +udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, + int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail) +{ + uint64_t freeblks; + uint64_t slack; + int i, error; + + slack = 0; + if (can_fail) + slack = UDF_DISC_SLACK; + + error = 0; + mutex_enter(&ump->allocate_mutex); + + /* check if there is enough space available */ + for (i = 0; i < 3; i++) { /* XXX arbitrary number */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack < freeblks) + break; + /* issue SYNC */ + DPRINTF(RESERVE, ("udf_reserve_space: issuing sync\n")); + mutex_exit(&ump->allocate_mutex); + udf_do_sync(ump, FSCRED, 0); + mutex_enter(&mntvnode_lock); + /* 1/8 second wait */ + cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock, + hz/8); + mutex_exit(&mntvnode_lock); + mutex_enter(&ump->allocate_mutex); + } + + /* check if there is enough space available now */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack >= freeblks) { + DPRINTF(RESERVE, ("udf_reserve_space: try to redistribute " + "partition space\n")); + DPRINTF(RESERVE, ("\tvpart %d, type %d is full\n", + vpart_num, ump->vtop_alloc[vpart_num])); + /* Try to redistribute space if possible */ + udf_collect_free_space_for_vpart(ump, vpart_num, num_lb + slack); + } + + /* check if there is enough space available now */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack <= freeblks) { + udf_do_reserve_space(ump, udf_node, vpart_num, num_lb); + } else { + DPRINTF(RESERVE, ("udf_reserve_space: out of disc space\n")); + error = ENOSPC; + } + + mutex_exit(&ump->allocate_mutex); + return error; +} + + +void +udf_cleanup_reservation(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + int vpart_num; + + mutex_enter(&ump->allocate_mutex); + + /* compensate for overlapping blocks */ + DPRINTF(RESERVE, ("UDF: overlapped %d blocks in count\n", udf_node->uncommitted_lbs)); + + vpart_num = udf_get_record_vpart(ump, udf_get_c_type(udf_node)); + udf_do_unreserve_space(ump, udf_node, vpart_num, udf_node->uncommitted_lbs); + + DPRINTF(RESERVE, ("\ttotal now %d\n", ump->uncommitted_lbs[vpart_num])); + + /* sanity */ + if (ump->uncommitted_lbs[vpart_num] < 0) + ump->uncommitted_lbs[vpart_num] = 0; + + mutex_exit(&ump->allocate_mutex); +} + +/* --------------------------------------------------------------------- */ + +/* + * Allocate an extent of given length on given virt. partition. It doesn't + * have to be one stretch. + */ + +int +udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node, + int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping) +{ + struct mmc_trackinfo *alloc_track, *other_track; + struct udf_bitmap *bitmap; + struct part_desc *pdesc; + struct logvol_int_desc *lvid; + uint64_t *lmappos; + uint32_t ptov, lb_num, *freepos, free_lbs; + int lb_size __diagused, alloc_num_lb; + int alloc_type, error; + int is_node; + + DPRINTF(CALL, ("udf_allocate_space(ctype %d, vpart %d, num_lb %d\n", + udf_c_type, vpart_num, num_lb)); + mutex_enter(&ump->allocate_mutex); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + KASSERT(lb_size == ump->discinfo.sector_size); + + alloc_type = ump->vtop_alloc[vpart_num]; + is_node = (udf_c_type == UDF_C_NODE); + + lmappos = lmapping; + error = 0; + switch (alloc_type) { + case UDF_ALLOC_VAT : + /* search empty slot in VAT file */ + KASSERT(num_lb == 1); + error = udf_search_free_vatloc(ump, &lb_num); + if (!error) { + *lmappos = lb_num; + + /* reserve on the backing sequential partition since + * that partition is credited back later */ + udf_do_reserve_space(ump, udf_node, + ump->vtop[vpart_num], num_lb); + } + break; + case UDF_ALLOC_SEQUENTIAL : + /* sequential allocation on recordable media */ + /* get partition backing up this vpart_num_num */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + + /* calculate offset from physical base partition */ + ptov = udf_rw32(pdesc->start_loc); + + /* get our track descriptors */ + if (vpart_num == ump->node_part) { + alloc_track = &ump->metadata_track; + other_track = &ump->data_track; + } else { + alloc_track = &ump->data_track; + other_track = &ump->metadata_track; + } + + /* allocate */ + for (lb_num = 0; lb_num < num_lb; lb_num++) { + *lmappos++ = alloc_track->next_writable - ptov; + alloc_track->next_writable++; + alloc_track->free_blocks--; + } + + /* keep other track up-to-date */ + if (alloc_track->tracknr == other_track->tracknr) + memcpy(other_track, alloc_track, + sizeof(struct mmc_trackinfo)); + break; + case UDF_ALLOC_SPACEMAP : + /* try to allocate on unallocated bits */ + alloc_num_lb = num_lb; + bitmap = &ump->part_unalloc_bits[vpart_num]; + udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* have we allocated all? */ + if (alloc_num_lb) { + /* TODO convert freed to unalloc and try again */ + /* free allocated piece for now */ + lmappos = lmapping; + for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) { + udf_bitmap_free(bitmap, *lmappos++, 1); + } + error = ENOSPC; + } + if (!error) { + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs - num_lb); + } + break; + case UDF_ALLOC_METABITMAP : /* UDF 2.50, 2.60 BluRay-RE */ + /* allocate on metadata unallocated bits */ + alloc_num_lb = num_lb; + bitmap = &ump->metadata_unalloc_bits; + udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* have we allocated all? */ + if (alloc_num_lb) { + /* YIKES! TODO we need to extend the metadata partition */ + /* free allocated piece for now */ + lmappos = lmapping; + for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) { + udf_bitmap_free(bitmap, *lmappos++, 1); + } + error = ENOSPC; + } + if (!error) { + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs - num_lb); + } + break; + case UDF_ALLOC_METASEQUENTIAL : /* UDF 2.60 BluRay-R */ + case UDF_ALLOC_RELAXEDSEQUENTIAL : /* UDF 2.50/~meta BluRay-R */ + printf("ALERT: udf_allocate_space : allocation %d " + "not implemented yet!\n", alloc_type); + /* TODO implement, doesn't have to be contiguous */ + error = ENOSPC; + break; + } + + if (!error) { + /* credit our partition since we have committed the space */ + udf_do_unreserve_space(ump, udf_node, vpart_num, num_lb); + } + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_ALLOC) { + lmappos = lmapping; + printf("udf_allocate_space, allocated logical lba :\n"); + for (lb_num = 0; lb_num < num_lb; lb_num++) { + printf("%s %"PRIu64, (lb_num > 0)?",":"", + *lmappos++); + } + printf("\n"); + } +#endif + mutex_exit(&ump->allocate_mutex); + + return error; +} + +/* --------------------------------------------------------------------- */ + +void +udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num, + uint16_t vpart_num, uint32_t num_lb) +{ + struct udf_bitmap *bitmap; + struct logvol_int_desc *lvid; + uint32_t lb_map, udf_rw32_lbmap; + uint32_t *freepos, free_lbs; + int phys_part; + int error __diagused; + + DPRINTF(ALLOC, ("udf_free_allocated_space: freeing virt lbnum %d " + "part %d + %d sect\n", lb_num, vpart_num, num_lb)); + + /* no use freeing zero length */ + if (num_lb == 0) + return; + + mutex_enter(&ump->allocate_mutex); + + switch (ump->vtop_tp[vpart_num]) { + case UDF_VTOP_TYPE_PHYS : + case UDF_VTOP_TYPE_SPARABLE : + /* free space to freed or unallocated space bitmap */ + phys_part = ump->vtop[vpart_num]; + + /* first try freed space bitmap */ + bitmap = &ump->part_freed_bits[phys_part]; + + /* if not defined, use unallocated bitmap */ + if (bitmap->bits == NULL) + bitmap = &ump->part_unalloc_bits[phys_part]; + + /* if no bitmaps are defined, bail out; XXX OK? */ + if (bitmap->bits == NULL) + break; + + /* free bits if its defined */ + KASSERT(bitmap->bits); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + udf_bitmap_free(bitmap, lb_num, num_lb); + + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs + num_lb); + break; + case UDF_VTOP_TYPE_VIRT : + /* free this VAT entry */ + KASSERT(num_lb == 1); + + lb_map = 0xffffffff; + udf_rw32_lbmap = udf_rw32(lb_map); + error = udf_vat_write(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + KASSERT(error == 0); + ump->vat_last_free_lb = MIN(ump->vat_last_free_lb, lb_num); + break; + case UDF_VTOP_TYPE_META : + /* free space in the metadata bitmap */ + bitmap = &ump->metadata_unalloc_bits; + KASSERT(bitmap->bits); + + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + udf_bitmap_free(bitmap, lb_num, num_lb); + + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs + num_lb); + break; + default: + printf("ALERT: udf_free_allocated_space : allocation %d " + "not implemented yet!\n", ump->vtop_tp[vpart_num]); + break; + } + + mutex_exit(&ump->allocate_mutex); +} + +/* --------------------------------------------------------------------- */ + +/* + * Special function to synchronise the metadatamirror file when they change on + * resizing. When the metadatafile is actually duplicated, this action is a + * no-op since they describe different extents on the disc. + */ + +void +udf_synchronise_metadatamirror_node(struct udf_mount *ump) +{ + struct udf_node *meta_node, *metamirror_node; + struct long_ad s_ad; + uint32_t len, flags; + int slot, cpy_slot; + int error, eof; + + if (ump->metadata_flags & METADATA_DUPLICATED) + return; + + meta_node = ump->metadata_node; + metamirror_node = ump->metadatamirror_node; + + /* 1) wipe mirror node */ + udf_wipe_adslots(metamirror_node); + + /* 2) copy all node descriptors from the meta_node */ + slot = 0; + cpy_slot = 0; + for (;;) { + udf_get_adslot(meta_node, slot, &s_ad, &eof); + if (eof) + break; + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + error = udf_append_adslot(metamirror_node, &cpy_slot, &s_ad); + if (error) { + /* WTF, this shouldn't happen, what to do now? */ + panic("udf_synchronise_metadatamirror_node failed!"); + } + cpy_slot++; + slot++; + } + + /* 3) adjust metamirror_node size */ + if (meta_node->fe) { + KASSERT(metamirror_node->fe); + metamirror_node->fe->inf_len = meta_node->fe->inf_len; + } else { + KASSERT(meta_node->efe); + KASSERT(metamirror_node->efe); + metamirror_node->efe->inf_len = meta_node->efe->inf_len; + metamirror_node->efe->obj_size = meta_node->efe->obj_size; + } + + /* for sanity */ + udf_count_alloc_exts(metamirror_node); +} + +/* --------------------------------------------------------------------- */ + +/* + * When faced with an out of space but there is still space available on other + * partitions, try to redistribute the space. This is only defined for media + * using Metadata partitions. + * + * There are two formats to deal with. Either its a `normal' metadata + * partition and we can move blocks between a metadata bitmap and its + * companion data spacemap OR its a UDF 2.60 formatted BluRay-R disc with POW + * and a metadata partition. + */ + +/* implementation limit: ump->datapart is the companion partition */ +static uint32_t +udf_trunc_metadatapart(struct udf_mount *ump, uint32_t num_lb) +{ + struct udf_node *bitmap_node; + struct udf_bitmap *bitmap; + struct space_bitmap_desc *sbd, *new_sbd; + struct logvol_int_desc *lvid; + uint64_t inf_len; + uint64_t meta_free_lbs, data_free_lbs, to_trunc; + uint32_t *freepos, *sizepos; + uint32_t unit, lb_size; + uint16_t meta_vpart_num, data_vpart_num, num_vpart; + int err __diagused; + + unit = ump->metadata_alloc_unit_size; + lb_size = udf_rw32(ump->logical_vol->lb_size); + lvid = ump->logvol_integrity; + + /* XXX + * + * the following checks will fail for BD-R UDF 2.60! but they are + * read-only for now anyway! Its even doubtfull if it is to be allowed + * for these discs. + */ + + /* lookup vpart for metadata partition */ + meta_vpart_num = ump->node_part; + KASSERT(ump->vtop_alloc[meta_vpart_num] == UDF_ALLOC_METABITMAP); + + /* lookup vpart for data partition */ + data_vpart_num = ump->data_part; + KASSERT(ump->vtop_alloc[data_vpart_num] == UDF_ALLOC_SPACEMAP); + + udf_calc_vpart_freespace(ump, data_vpart_num, &data_free_lbs); + udf_calc_vpart_freespace(ump, meta_vpart_num, &meta_free_lbs); + + DPRINTF(RESERVE, ("\tfree space on data partition %"PRIu64" blks\n", data_free_lbs)); + DPRINTF(RESERVE, ("\tfree space on metadata partition %"PRIu64" blks\n", meta_free_lbs)); + + /* give away some of the free meta space, in unit block sizes */ + to_trunc = meta_free_lbs/4; /* give out a quarter */ + to_trunc = MAX(to_trunc, num_lb); + to_trunc = unit * ((to_trunc + unit-1) / unit); /* round up */ + + /* scale down if needed and bail out when out of space */ + if (to_trunc >= meta_free_lbs) + return num_lb; + + /* check extent of bits marked free at the end of the map */ + bitmap = &ump->metadata_unalloc_bits; + to_trunc = udf_bitmap_check_trunc_free(bitmap, to_trunc); + to_trunc = unit * (to_trunc / unit); /* round down again */ + if (to_trunc == 0) + return num_lb; + + DPRINTF(RESERVE, ("\ttruncating %"PRIu64" lbs from the metadata bitmap\n", + to_trunc)); + + /* get length of the metadata bitmap node file */ + bitmap_node = ump->metadatabitmap_node; + if (bitmap_node->fe) { + inf_len = udf_rw64(bitmap_node->fe->inf_len); + } else { + KASSERT(bitmap_node->efe); + inf_len = udf_rw64(bitmap_node->efe->inf_len); + } + inf_len -= to_trunc/8; + + /* as per [UDF 2.60/2.2.13.6] : */ + /* 1) update the SBD in the metadata bitmap file */ + sbd = (struct space_bitmap_desc *) bitmap->blob; + sbd->num_bits = udf_rw32(udf_rw32(sbd->num_bits) - to_trunc); + sbd->num_bytes = udf_rw32(udf_rw32(sbd->num_bytes) - to_trunc/8); + bitmap->max_offset = udf_rw32(sbd->num_bits); + + num_vpart = udf_rw32(lvid->num_part); + freepos = &lvid->tables[0] + meta_vpart_num; + sizepos = &lvid->tables[0] + num_vpart + meta_vpart_num; + *freepos = udf_rw32(*freepos) - to_trunc; + *sizepos = udf_rw32(*sizepos) - to_trunc; + + /* realloc bitmap for better memory usage */ + new_sbd = realloc(sbd, inf_len, M_UDFVOLD, + M_CANFAIL | M_WAITOK); + if (new_sbd) { + /* update pointers */ + ump->metadata_unalloc_dscr = new_sbd; + bitmap->blob = (uint8_t *) new_sbd; + } + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* + * The truncated space is secured now and can't be allocated anymore. + * Release the allocate mutex so we can shrink the nodes the normal + * way. + */ + mutex_exit(&ump->allocate_mutex); + + /* 2) trunc the metadata bitmap information file, freeing blocks */ + err = udf_shrink_node(bitmap_node, inf_len); + KASSERT(err == 0); + + /* 3) trunc the metadata file and mirror file, freeing blocks */ + inf_len = (uint64_t) udf_rw32(sbd->num_bits) * lb_size; /* [4/14.12.4] */ + err = udf_shrink_node(ump->metadata_node, inf_len); + KASSERT(err == 0); + if (ump->metadatamirror_node) { + if (ump->metadata_flags & METADATA_DUPLICATED) { + err = udf_shrink_node(ump->metadatamirror_node, inf_len); + } else { + /* extents will be copied on writeout */ + } + KASSERT(err == 0); + } + ump->lvclose |= UDF_WRITE_METAPART_NODES; + + /* relock before exit */ + mutex_enter(&ump->allocate_mutex); + + if (to_trunc > num_lb) + return 0; + return num_lb - to_trunc; +} + + +static void +udf_sparsify_metadatapart(struct udf_mount *ump, uint32_t num_lb) +{ + /* NOT IMPLEMENTED, fail */ +} + + +static void +udf_collect_free_space_for_vpart(struct udf_mount *ump, + uint16_t vpart_num, uint32_t num_lb) +{ + /* allocate mutex is helt */ + + /* only defined for metadata partitions */ + if (ump->vtop_tp[ump->node_part] != UDF_VTOP_TYPE_META) { + DPRINTF(RESERVE, ("\tcan't grow/shrink; no metadata partitioning\n")); + return; + } + + /* UDF 2.60 BD-R+POW? */ + if (ump->vtop_alloc[ump->node_part] == UDF_ALLOC_METASEQUENTIAL) { + DPRINTF(RESERVE, ("\tUDF 2.60 BD-R+POW track grow not implemented yet\n")); + return; + } + + if (ump->vtop_tp[vpart_num] == UDF_VTOP_TYPE_META) { + /* try to grow the meta partition */ + DPRINTF(RESERVE, ("\ttrying to grow the meta partition\n")); + /* as per [UDF 2.60/2.2.13.5] : extend bitmap and metadata file(s) */ + DPRINTF(NOTIMPL, ("\tgrowing meta partition not implemented yet\n")); + } else { + /* try to shrink the metadata partition */ + DPRINTF(RESERVE, ("\ttrying to shrink the meta partition\n")); + /* as per [UDF 2.60/2.2.13.6] : either trunc or make sparse */ + num_lb = udf_trunc_metadatapart(ump, num_lb); + if (num_lb) + udf_sparsify_metadatapart(ump, num_lb); + } + + /* allocate mutex should still be helt */ +} + +/* --------------------------------------------------------------------- */ + +/* + * Allocate a buf on disc for direct write out. The space doesn't have to be + * contiguous as the caller takes care of this. + */ + +void +udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf, + uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_nump) +{ + struct udf_node *udf_node = VTOI(buf->b_vp); + int lb_size, udf_c_type; + int vpart_num, num_lb; + int error, s; + + /* + * for each sector in the buf, allocate a sector on disc and record + * its position in the provided mapping array. + * + * If its userdata or FIDs, record its location in its node. + */ + + lb_size = udf_rw32(ump->logical_vol->lb_size); + num_lb = (buf->b_bcount + lb_size -1) / lb_size; + udf_c_type = buf->b_udf_c_type; + + KASSERT(lb_size == ump->discinfo.sector_size); + + /* select partition to record the buffer on */ + vpart_num = *vpart_nump = udf_get_record_vpart(ump, udf_c_type); + + if (udf_c_type == UDF_C_NODE) { + /* if not VAT, its allready allocated */ + if (ump->vtop_alloc[ump->node_part] != UDF_ALLOC_VAT) + return; + + /* allocate on its backing sequential partition */ + vpart_num = ump->data_part; + } + + /* XXX can this still happen? */ + /* do allocation on the selected partition */ + error = udf_allocate_space(ump, udf_node, udf_c_type, + vpart_num, num_lb, lmapping); + if (error) { + /* + * ARGH! we haven't done our accounting right! it should + * allways succeed. + */ + panic("UDF disc allocation accounting gone wrong"); + } + + /* If its userdata or FIDs, record its allocation in its node. */ + if ((udf_c_type == UDF_C_USERDATA) || + (udf_c_type == UDF_C_FIDS) || + (udf_c_type == UDF_C_METADATA_SBM)) + { + udf_record_allocation_in_node(ump, buf, vpart_num, lmapping, + node_ad_cpy); + /* decrement our outstanding bufs counter */ + s = splbio(); + udf_node->outstanding_bufs--; + splx(s); + } +} + +/* --------------------------------------------------------------------- */ + +/* + * Try to merge a1 with the new piece a2. udf_ads_merge returns error when not + * possible (anymore); a2 returns the rest piece. + */ + +static int +udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2) +{ + uint32_t merge_len; + uint32_t a1_len, a2_len; + uint32_t a1_flags, a2_flags; + uint32_t a1_lbnum, a2_lbnum; + uint16_t a1_part, a2_part; + + a1_flags = UDF_EXT_FLAGS(udf_rw32(a1->len)); + a1_len = UDF_EXT_LEN(udf_rw32(a1->len)); + a1_lbnum = udf_rw32(a1->loc.lb_num); + a1_part = udf_rw16(a1->loc.part_num); + + a2_flags = UDF_EXT_FLAGS(udf_rw32(a2->len)); + a2_len = UDF_EXT_LEN(udf_rw32(a2->len)); + a2_lbnum = udf_rw32(a2->loc.lb_num); + a2_part = udf_rw16(a2->loc.part_num); + + /* defines same space */ + if (a1_flags != a2_flags) + return 1; + + if (a1_flags != UDF_EXT_FREE) { + /* the same partition */ + if (a1_part != a2_part) + return 1; + + /* a2 is successor of a1 */ + if (a1_lbnum * lb_size + a1_len != a2_lbnum * lb_size) + return 1; + } + + /* merge as most from a2 if possible */ + merge_len = MIN(a2_len, max_len - a1_len); + a1_len += merge_len; + a2_len -= merge_len; + a2_lbnum += merge_len/lb_size; + + a1->len = udf_rw32(a1_len | a1_flags); + a2->len = udf_rw32(a2_len | a2_flags); + a2->loc.lb_num = udf_rw32(a2_lbnum); + + if (a2_len > 0) + return 1; + + /* there is space over to merge */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_wipe_adslots(struct udf_node *udf_node) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + uint32_t lb_size, dscr_size, l_ea, max_l_ad, crclen; + uint8_t *data_pos; + int extnr; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + max_l_ad = lb_size - dscr_size - l_ea; + + /* wipe fe/efe */ + memset(data_pos, 0, max_l_ad); + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea; + if (fe) { + fe->l_ad = udf_rw32(0); + fe->logblks_rec = udf_rw64(0); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->l_ad = udf_rw32(0); + efe->logblks_rec = udf_rw64(0); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + + /* wipe all allocation extent entries */ + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + ext = udf_node->ext[extnr]; + dscr_size = sizeof(struct alloc_ext_entry) -1; + data_pos = (uint8_t *) ext->data; + max_l_ad = lb_size - dscr_size; + memset(data_pos, 0, max_l_ad); + ext->l_ad = udf_rw32(0); + + crclen = dscr_size - UDF_DESC_TAG_LENGTH; + ext->tag.desc_crc_len = udf_rw16(crclen); + } + udf_node->i_flags |= IN_NODE_REBUILD; +} + +/* --------------------------------------------------------------------- */ + +void +udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb, + int *eof) { + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + struct icb_tag *icbtag; + struct short_ad *short_ad; + struct long_ad *long_ad, l_icb; + uint32_t offset; + uint32_t dscr_size, l_ea, l_ad, flags; + uint8_t *data_pos; + int icbflags, addr_type, adlen, extnr; + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + icbtag = &efe->icbtag; + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* just in case we're called on an intern, its EOF */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + memset(icb, 0, sizeof(struct long_ad)); + *eof = 1; + return; + } + + adlen = 0; + if (addr_type == UDF_ICB_SHORT_ALLOC) { + adlen = sizeof(struct short_ad); + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + adlen = sizeof(struct long_ad); + } + + /* if offset too big, we go to the allocation extensions */ + offset = slot * adlen; + extnr = -1; + while (offset >= l_ad) { + /* check if our last entry is a redirect */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + l_ad-adlen); + l_icb.len = short_ad->len; + l_icb.loc.part_num = udf_node->loc.loc.part_num; + l_icb.loc.lb_num = short_ad->lb_num; + } else { + KASSERT(addr_type == UDF_ICB_LONG_ALLOC); + long_ad = (struct long_ad *) (data_pos + l_ad-adlen); + l_icb = *long_ad; + } + flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len)); + if (flags != UDF_EXT_REDIRECT) { + l_ad = 0; /* force EOF */ + break; + } + + /* advance to next extent */ + extnr++; + if (extnr >= udf_node->num_extensions) { + l_ad = 0; /* force EOF */ + break; + } + offset = offset - l_ad; + ext = udf_node->ext[extnr]; + dscr_size = sizeof(struct alloc_ext_entry) -1; + l_ad = udf_rw32(ext->l_ad); + data_pos = (uint8_t *) ext + dscr_size; + } + + /* XXX l_ad == 0 should be enough to check */ + *eof = (offset >= l_ad) || (l_ad == 0); + if (*eof) { + DPRINTF(PARANOIDADWLK, ("returning EOF, extnr %d, offset %d, " + "l_ad %d\n", extnr, offset, l_ad)); + memset(icb, 0, sizeof(struct long_ad)); + return; + } + + /* get the element */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + icb->len = short_ad->len; + icb->loc.part_num = udf_node->loc.loc.part_num; + icb->loc.lb_num = short_ad->lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *icb = *long_ad; + } + DPRINTF(PARANOIDADWLK, ("returning element : v %d, lb %d, len %d, " + "flags %d\n", icb->loc.part_num, icb->loc.lb_num, + UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len))); +} + +/* --------------------------------------------------------------------- */ + +int +udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb) { + struct udf_mount *ump = udf_node->ump; + union dscrptr *dscr, *extdscr; + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + struct icb_tag *icbtag; + struct short_ad *short_ad; + struct long_ad *long_ad, o_icb, l_icb; + uint64_t logblks_rec, *logblks_rec_p; + uint64_t lmapping; + uint32_t offset, rest, len, lb_num; + uint32_t lb_size, dscr_size, l_ea, l_ad, *l_ad_p, max_l_ad, crclen; + uint32_t flags; + uint16_t vpart_num; + uint8_t *data_pos; + int icbflags, addr_type, adlen, extnr; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + vpart_num = udf_rw16(udf_node->loc.loc.part_num); + + /* determine what descriptor we are in */ + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + dscr = (union dscrptr *) fe; + dscr_size = sizeof(struct file_entry) -1; + + l_ea = udf_rw32(fe->l_ea); + l_ad_p = &fe->l_ad; + logblks_rec_p = &fe->logblks_rec; + } else { + icbtag = &efe->icbtag; + dscr = (union dscrptr *) efe; + dscr_size = sizeof(struct extfile_entry) -1; + + l_ea = udf_rw32(efe->l_ea); + l_ad_p = &efe->l_ad; + logblks_rec_p = &efe->logblks_rec; + } + data_pos = (uint8_t *) dscr + dscr_size + l_ea; + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* just in case we're called on an intern, its EOF */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + panic("udf_append_adslot on UDF_ICB_INTERN_ALLOC\n"); + } + + adlen = 0; + if (addr_type == UDF_ICB_SHORT_ALLOC) { + adlen = sizeof(struct short_ad); + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + adlen = sizeof(struct long_ad); + } + + /* clean up given long_ad since it can be a synthesized one */ + flags = UDF_EXT_FLAGS(udf_rw32(icb->len)); + if (flags == UDF_EXT_FREE) { + icb->loc.part_num = udf_rw16(0); + icb->loc.lb_num = udf_rw32(0); + } + + /* if offset too big, we go to the allocation extensions */ + l_ad = udf_rw32(*l_ad_p); + offset = (*slot) * adlen; + extnr = -1; + while (offset >= l_ad) { + /* check if our last entry is a redirect */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + l_ad-adlen); + l_icb.len = short_ad->len; + l_icb.loc.part_num = udf_node->loc.loc.part_num; + l_icb.loc.lb_num = short_ad->lb_num; + } else { + KASSERT(addr_type == UDF_ICB_LONG_ALLOC); + long_ad = (struct long_ad *) (data_pos + l_ad-adlen); + l_icb = *long_ad; + } + flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len)); + if (flags != UDF_EXT_REDIRECT) { + /* only one past the last one is adressable */ + break; + } + + /* advance to next extent */ + extnr++; + KASSERT(extnr < udf_node->num_extensions); + offset = offset - l_ad; + + ext = udf_node->ext[extnr]; + dscr = (union dscrptr *) ext; + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + l_ad_p = &ext->l_ad; + l_ad = udf_rw32(*l_ad_p); + data_pos = (uint8_t *) ext + dscr_size; + } + DPRINTF(PARANOIDADWLK, ("append, ext %d, offset %d, l_ad %d\n", + extnr, offset, udf_rw32(*l_ad_p))); + KASSERT(l_ad == udf_rw32(*l_ad_p)); + + /* offset is offset within the current (E)FE/AED */ + l_ad = udf_rw32(*l_ad_p); + crclen = udf_rw16(dscr->tag.desc_crc_len); + logblks_rec = udf_rw64(*logblks_rec_p); + + /* overwriting old piece? */ + if (offset < l_ad) { + /* overwrite entry; compensate for the old element */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + o_icb.len = short_ad->len; + o_icb.loc.part_num = udf_rw16(0); /* ignore */ + o_icb.loc.lb_num = short_ad->lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + o_icb = *long_ad; + } else { + panic("Invalid address type in udf_append_adslot\n"); + } + + len = udf_rw32(o_icb.len); + if (UDF_EXT_FLAGS(len) == UDF_EXT_ALLOCATED) { + /* adjust counts */ + len = UDF_EXT_LEN(len); + logblks_rec -= (len + lb_size -1) / lb_size; + } + } + + /* check if we're not appending a redirection */ + flags = UDF_EXT_FLAGS(udf_rw32(icb->len)); + KASSERT(flags != UDF_EXT_REDIRECT); + + /* round down available space */ + rest = adlen * ((max_l_ad - offset) / adlen); + if (rest <= adlen) { + /* have to append aed, see if we already have a spare one */ + extnr++; + ext = udf_node->ext[extnr]; + l_icb = udf_node->ext_loc[extnr]; + if (ext == NULL) { + DPRINTF(ALLOC,("adding allocation extent %d\n", extnr)); + + error = udf_reserve_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, /* can fail */ false); + if (error) { + printf("UDF: couldn't reserve space for AED!\n"); + return error; + } + error = udf_allocate_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, &lmapping); + lb_num = lmapping; + if (error) + panic("UDF: couldn't allocate AED!\n"); + + /* initialise pointer to location */ + memset(&l_icb, 0, sizeof(struct long_ad)); + l_icb.len = udf_rw32(lb_size | UDF_EXT_REDIRECT); + l_icb.loc.lb_num = udf_rw32(lb_num); + l_icb.loc.part_num = udf_rw16(vpart_num); + + /* create new aed descriptor */ + udf_create_logvol_dscr(ump, udf_node, &l_icb, &extdscr); + ext = &extdscr->aee; + + udf_inittag(ump, &ext->tag, TAGID_ALLOCEXTENT, lb_num); + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + memset(ext->data, 0, max_l_ad); + ext->l_ad = udf_rw32(0); + ext->tag.desc_crc_len = + udf_rw16(dscr_size - UDF_DESC_TAG_LENGTH); + + /* declare aed */ + udf_node->num_extensions++; + udf_node->ext_loc[extnr] = l_icb; + udf_node->ext[extnr] = ext; + } + /* add redirect and adjust l_ad and crclen for old descr */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + short_ad->len = l_icb.len; + short_ad->lb_num = l_icb.loc.lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *long_ad = l_icb; + } + l_ad += adlen; + crclen += adlen; + dscr->tag.desc_crc_len = udf_rw16(crclen); + *l_ad_p = udf_rw32(l_ad); + + /* advance to the new extension */ + KASSERT(ext != NULL); + dscr = (union dscrptr *) ext; + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + data_pos = (uint8_t *) dscr + dscr_size; + + l_ad_p = &ext->l_ad; + l_ad = udf_rw32(*l_ad_p); + crclen = udf_rw16(dscr->tag.desc_crc_len); + offset = 0; + + /* adjust callees slot count for link insert */ + *slot += 1; + } + + /* write out the element */ + DPRINTF(PARANOIDADWLK, ("adding element : %p : v %d, lb %d, " + "len %d, flags %d\n", data_pos + offset, + icb->loc.part_num, icb->loc.lb_num, + UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len))); + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + short_ad->len = icb->len; + short_ad->lb_num = icb->loc.lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *long_ad = *icb; + } + + /* adjust logblks recorded count */ + len = udf_rw32(icb->len); + flags = UDF_EXT_FLAGS(len); + if (flags == UDF_EXT_ALLOCATED) + logblks_rec += (UDF_EXT_LEN(len) + lb_size -1) / lb_size; + *logblks_rec_p = udf_rw64(logblks_rec); + + /* adjust l_ad and crclen when needed */ + if (offset >= l_ad) { + l_ad += adlen; + crclen += adlen; + dscr->tag.desc_crc_len = udf_rw16(crclen); + *l_ad_p = udf_rw32(l_ad); + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_count_alloc_exts(struct udf_node *udf_node) +{ + struct long_ad s_ad; + uint32_t lb_num, len, flags; + uint16_t vpart_num; + int slot, eof; + int num_extents, extnr; + + if (udf_node->num_extensions == 0) + return; + + /* count number of allocation extents in use */ + num_extents = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + + if (flags == UDF_EXT_REDIRECT) + num_extents++; + + slot++; + } + + DPRINTF(ALLOC, ("udf_count_alloc_ext counted %d live extents\n", + num_extents)); + + /* XXX choice: we could delay freeing them on node writeout */ + /* free excess entries */ + extnr = num_extents; + for (;extnr < udf_node->num_extensions; extnr++) { + DPRINTF(ALLOC, ("freeing alloc ext %d\n", extnr)); + /* free dscriptor */ + s_ad = udf_node->ext_loc[extnr]; + udf_free_logvol_dscr(udf_node->ump, &s_ad, + udf_node->ext[extnr]); + udf_node->ext[extnr] = NULL; + + /* free disc space */ + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + udf_free_allocated_space(udf_node->ump, lb_num, vpart_num, 1); + + memset(&udf_node->ext_loc[extnr], 0, sizeof(struct long_ad)); + } + + /* set our new number of allocation extents */ + udf_node->num_extensions = num_extents; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Adjust the node's allocation descriptors to reflect the new mapping; do + * take note that we might glue to existing allocation descriptors. + * + * XXX Note there can only be one allocation being recorded/mount; maybe + * explicit allocation in shedule thread? + */ + +static void +udf_record_allocation_in_node(struct udf_mount *ump, struct buf *buf, + uint16_t vpart_num, uint64_t *mapping, struct long_ad *node_ad_cpy) +{ + struct vnode *vp = buf->b_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad, c_ad; + uint64_t inflen, from, till; + uint64_t foffset, end_foffset, restart_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t max_len; + uint32_t num_lb, len, flags, lb_num; + uint32_t run_start; + uint32_t slot_offset, replace_len, replace; + int addr_type, icbflags; +// int udf_c_type = buf->b_udf_c_type; + int lb_size, run_length, eof; + int slot, cpy_slot, cpy_slots, restart_slot; + int error; + + DPRINTF(ALLOC, ("udf_record_allocation_in_node\n")); + +#if 0 + /* XXX disable sanity check for now */ + /* sanity check ... should be panic ? */ + if ((udf_c_type != UDF_C_USERDATA) && (udf_c_type != UDF_C_FIDS)) + return; +#endif + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + max_len = ((UDF_EXT_MAXLEN / lb_size) * lb_size); + + /* do the job */ + UDF_LOCK_NODE(udf_node, 0); /* XXX can deadlock ? */ + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + } + + /* do check if `till' is not past file information length */ + from = buf->b_lblkno * lb_size; + till = MIN(inflen, from + buf->b_resid); + + num_lb = (till - from + lb_size -1) / lb_size; + + DPRINTF(ALLOC, ("record allocation from %"PRIu64" + %d\n", from, buf->b_bcount)); + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* nothing to do */ + /* XXX clean up rest of node? just in case? */ + UDF_UNLOCK_NODE(udf_node, 0); + return; + } + + slot = 0; + cpy_slot = 0; + foffset = 0; + + /* 1) copy till first overlap piece to the rewrite buffer */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) { + DPRINTF(WRITE, + ("Record allocation in node " + "failed: encountered EOF\n")); + UDF_UNLOCK_NODE(udf_node, 0); + buf->b_error = EINVAL; + return; + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + if (end_foffset > from) + break; /* found */ + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + foffset = end_foffset; + slot++; + } + restart_slot = slot; + restart_foffset = foffset; + + /* 2) trunc overlapping slot at overlap and copy it */ + slot_offset = from - foffset; + if (slot_offset > 0) { + DPRINTF(ALLOC, ("\tslot_offset = %d, flags = %d (%d)\n", + slot_offset, flags >> 30, flags)); + + s_ad.len = udf_rw32(slot_offset | flags); + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + foffset += slot_offset; + + /* 3) insert new mappings */ + memset(&s_ad, 0, sizeof(struct long_ad)); + lb_num = 0; + for (lb_num = 0; lb_num < num_lb; lb_num++) { + run_start = mapping[lb_num]; + run_length = 1; + while (lb_num < num_lb-1) { + if (mapping[lb_num+1] != mapping[lb_num]+1) + if (mapping[lb_num+1] != mapping[lb_num]) + break; + run_length++; + lb_num++; + } + /* insert slot for this mapping */ + len = run_length * lb_size; + + /* bounds checking */ + if (foffset + len > till) + len = till - foffset; + KASSERT(foffset + len <= inflen); + + s_ad.len = udf_rw32(len | UDF_EXT_ALLOCATED); + s_ad.loc.part_num = udf_rw16(vpart_num); + s_ad.loc.lb_num = udf_rw32(run_start); + + foffset += len; + + /* paranoia */ + if (len == 0) { + DPRINTF(WRITE, + ("Record allocation in node " + "failed: insert failed\n")); + UDF_UNLOCK_NODE(udf_node, 0); + buf->b_error = EINVAL; + return; + } + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t3: insert new mapping vp %d lb %d, len %d, " + "flags %d -> stack\n", + udf_rw16(s_ad.loc.part_num), udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + + /* 4) pop replaced length */ + slot = restart_slot; + foffset = restart_foffset; + + replace_len = till - foffset; /* total amount of bytes to pop */ + slot_offset = from - foffset; /* offset in first encounted slot */ + KASSERT((slot_offset % lb_size) == 0); + + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + lb_num = udf_rw32(s_ad.loc.lb_num); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + DPRINTF(ALLOC, ("\t4i: got slot %d, slot_offset %d, " + "replace_len %d, " + "vp %d, lb %d, len %d, flags %d\n", + slot, slot_offset, replace_len, + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* adjust for slot offset */ + if (slot_offset) { + DPRINTF(ALLOC, ("\t4s: skipping %d\n", slot_offset)); + lb_num += slot_offset / lb_size; + len -= slot_offset; + foffset += slot_offset; + replace_len -= slot_offset; + + /* mark adjusted */ + slot_offset = 0; + } + + /* advance for (the rest of) this slot */ + replace = MIN(len, replace_len); + DPRINTF(ALLOC, ("\t4d: replacing %d\n", replace)); + + /* advance for this slot */ + if (replace) { + /* note: dont round DOWN on num_lb since we then + * forget the last partial one */ + num_lb = (replace + lb_size - 1) / lb_size; + if (flags != UDF_EXT_FREE) { + udf_free_allocated_space(ump, lb_num, + udf_rw16(s_ad.loc.part_num), num_lb); + } + lb_num += num_lb; + len -= replace; + foffset += replace; + replace_len -= replace; + } + + /* do we have a slot tail ? */ + if (len) { + KASSERT(foffset % lb_size == 0); + + /* we arrived at our point, push remainder */ + s_ad.len = udf_rw32(len | flags); + s_ad.loc.lb_num = udf_rw32(lb_num); + if (flags == UDF_EXT_FREE) + s_ad.loc.lb_num = udf_rw32(0); + node_ad_cpy[cpy_slot++] = s_ad; + foffset += len; + slot++; + + DPRINTF(ALLOC, ("\t4: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + break; + } + + slot++; + } + + /* 5) copy remainder */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t5: insert new mapping " + "vp %d lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + slot++; + } + + /* 6) reset node descriptors */ + udf_wipe_adslots(udf_node); + + /* 7) copy back extents; merge when possible. Recounting on the fly */ + cpy_slots = cpy_slot; + + c_ad = node_ad_cpy[0]; + slot = 0; + DPRINTF(ALLOC, ("\t7s: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) { + s_ad = node_ad_cpy[cpy_slot]; + + DPRINTF(ALLOC, ("\t7i: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* see if we can merge */ + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + DPRINTF(ALLOC, ("\t7: appending vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) { + buf->b_error = error; + goto out; + } + c_ad = s_ad; + slot++; + } + } + + /* 8) push rest slot (if any) */ + if (UDF_EXT_LEN(c_ad.len) > 0) { + DPRINTF(ALLOC, ("\t8: last append vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) { + buf->b_error = error; + goto out; + } + } + +out: + udf_count_alloc_exts(udf_node); + + /* the node's descriptors should now be sane */ + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(orig_inflen == new_inflen); + KASSERT(new_lbrec >= orig_lbrec); + + return; +} + +/* --------------------------------------------------------------------- */ + +int +udf_grow_node(struct udf_node *udf_node, uint64_t new_size) +{ + struct vnode *vp = udf_node->vnode; + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad c_ad, s_ad; + uint64_t size_diff, old_size, inflen, objsize, chunk, append_len; + uint64_t foffset, end_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t lb_size, unit_size, dscr_size, crclen, lastblock_grow; + uint32_t icbflags, len, flags, max_len; + uint32_t max_l_ad, l_ad, l_ea; + uint16_t my_part, dst_part; + uint8_t *evacuated_data; + int addr_type; + int slot; + int eof, error; + + DPRINTF(ALLOC, ("udf_grow_node\n")); + + UDF_LOCK_NODE(udf_node, 0); + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* max_len in unit's IFF its a metadata node or metadata mirror node */ + unit_size = lb_size; + if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node)) + unit_size = ump->metadata_alloc_unit_size * lb_size; + max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + objsize = inflen; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + objsize = udf_rw64(efe->obj_size); + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + } + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + old_size = inflen; + size_diff = new_size - old_size; + + DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size)); + + evacuated_data = NULL; + if (addr_type == UDF_ICB_INTERN_ALLOC) { + if (l_ad + size_diff <= max_l_ad) { + /* only reflect size change directly in the node */ + inflen += size_diff; + objsize += size_diff; + l_ad += size_diff; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + error = 0; + + /* set new size for uvm */ + uvm_vnp_setwritesize(vp, new_size); + uvm_vnp_setsize(vp, new_size); + +#if 0 + /* zero append space in buffer */ + ubc_zerorange(&vp->v_uobj, old_size, + new_size - old_size, UBC_UNMAP_FLAG(vp)); +#endif + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + + /* unlock */ + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen + size_diff); + KASSERT(new_lbrec == orig_lbrec); + KASSERT(new_lbrec == 0); + return 0; + } + + DPRINTF(ALLOC, ("\tCONVERT from internal\n")); + + if (old_size > 0) { + /* allocate some space and copy in the stuff to keep */ + evacuated_data = malloc(lb_size, M_UDFTEMP, M_WAITOK); + memset(evacuated_data, 0, lb_size); + + /* node is locked, so safe to exit mutex */ + UDF_UNLOCK_NODE(udf_node, 0); + + /* read in using the `normal' vn_rdwr() */ + error = vn_rdwr(UIO_READ, udf_node->vnode, + evacuated_data, old_size, 0, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + /* enter again */ + UDF_LOCK_NODE(udf_node, 0); + } + + /* convert to a normal alloc and select type */ + my_part = udf_rw16(udf_node->loc.loc.part_num); + dst_part = udf_get_record_vpart(ump, udf_get_c_type(udf_node)); + addr_type = UDF_ICB_SHORT_ALLOC; + if (dst_part != my_part) + addr_type = UDF_ICB_LONG_ALLOC; + + icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK; + icbflags |= addr_type; + icbtag->flags = udf_rw16(icbflags); + + /* wipe old descriptor space */ + udf_wipe_adslots(udf_node); + + memset(&c_ad, 0, sizeof(struct long_ad)); + c_ad.len = udf_rw32(old_size | UDF_EXT_FREE); + c_ad.loc.part_num = udf_rw16(0); /* not relevant */ + c_ad.loc.lb_num = udf_rw32(0); /* not relevant */ + + slot = 0; + } else { + /* goto the last entry (if any) */ + slot = 0; + foffset = 0; + memset(&c_ad, 0, sizeof(struct long_ad)); + for (;;) { + udf_get_adslot(udf_node, slot, &c_ad, &eof); + if (eof) + break; + + len = udf_rw32(c_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + end_foffset = foffset + len; + if (flags != UDF_EXT_REDIRECT) + foffset = end_foffset; + + slot++; + } + /* at end of adslots */ + + /* special case if the old size was zero, then there is no last slot */ + if (old_size == 0) { + c_ad.len = udf_rw32(0 | UDF_EXT_FREE); + c_ad.loc.part_num = udf_rw16(0); /* not relevant */ + c_ad.loc.lb_num = udf_rw32(0); /* not relevant */ + } else { + /* refetch last slot */ + slot--; + udf_get_adslot(udf_node, slot, &c_ad, &eof); + } + } + + /* + * If the length of the last slot is not a multiple of lb_size, adjust + * length so that it is; don't forget to adjust `append_len'! relevant for + * extending existing files + */ + len = udf_rw32(c_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + lastblock_grow = 0; + if (len % lb_size > 0) { + lastblock_grow = lb_size - (len % lb_size); + lastblock_grow = MIN(size_diff, lastblock_grow); + len += lastblock_grow; + c_ad.len = udf_rw32(len | flags); + + /* TODO zero appened space in buffer! */ + /* using ubc_zerorange(&vp->v_uobj, old_size, */ + /* new_size - old_size, UBC_UNMAP_FLAG(vp)); ? */ + } + memset(&s_ad, 0, sizeof(struct long_ad)); + + /* size_diff can be bigger than allowed, so grow in chunks */ + append_len = size_diff - lastblock_grow; + while (append_len > 0) { + chunk = MIN(append_len, max_len); + s_ad.len = udf_rw32(chunk | UDF_EXT_FREE); + s_ad.loc.part_num = udf_rw16(0); + s_ad.loc.lb_num = udf_rw32(0); + + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; + slot++; + c_ad = s_ad; + memset(&s_ad, 0, sizeof(struct long_ad)); + } + append_len -= chunk; + } + + /* if there is a rest piece in the accumulator, append it */ + if (UDF_EXT_LEN(udf_rw32(c_ad.len)) > 0) { + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; + slot++; + } + + /* if there is a rest piece that didn't fit, append it */ + if (UDF_EXT_LEN(udf_rw32(s_ad.len)) > 0) { + error = udf_append_adslot(udf_node, &slot, &s_ad); + if (error) + goto errorout; + slot++; + } + + inflen += size_diff; + objsize += size_diff; + if (fe) { + fe->inf_len = udf_rw64(inflen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + } + error = 0; + + if (evacuated_data) { + /* set new write size for uvm */ + uvm_vnp_setwritesize(vp, old_size); + + /* write out evacuated data */ + error = vn_rdwr(UIO_WRITE, udf_node->vnode, + evacuated_data, old_size, 0, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + uvm_vnp_setsize(vp, old_size); + } + +errorout: + if (evacuated_data) + free(evacuated_data, M_UDFTEMP); + + udf_count_alloc_exts(udf_node); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen + size_diff); + KASSERT(new_lbrec == orig_lbrec); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_shrink_node(struct udf_node *udf_node, uint64_t new_size) +{ + struct vnode *vp = udf_node->vnode; + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad c_ad, s_ad, *node_ad_cpy; + uint64_t size_diff, old_size, inflen, objsize; + uint64_t foffset, end_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t lb_size, unit_size, dscr_size, crclen; + uint32_t slot_offset, slot_offset_lb; + uint32_t len, flags, max_len; + uint32_t num_lb, lb_num; + uint32_t max_l_ad, l_ad, l_ea; + uint16_t vpart_num; + uint8_t *data_pos; + int icbflags, addr_type; + int slot, cpy_slot, cpy_slots; + int eof, error; + + DPRINTF(ALLOC, ("udf_shrink_node\n")); + + UDF_LOCK_NODE(udf_node, 0); + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* max_len in unit's IFF its a metadata node or metadata mirror node */ + unit_size = lb_size; + if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node)) + unit_size = ump->metadata_alloc_unit_size * lb_size; + max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size); + + /* do the work */ + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + objsize = inflen; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + objsize = udf_rw64(efe->obj_size); + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + old_size = inflen; + size_diff = old_size - new_size; + + DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size)); + + /* shrink the node to its new size */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* only reflect size change directly in the node */ + KASSERT(new_size <= max_l_ad); + inflen -= size_diff; + objsize -= size_diff; + l_ad -= size_diff; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + error = 0; + + /* clear the space in the descriptor */ + KASSERT(old_size > new_size); + memset(data_pos + new_size, 0, old_size - new_size); + + /* TODO zero appened space in buffer! */ + /* using ubc_zerorange(&vp->v_uobj, old_size, */ + /* old_size - new_size, UBC_UNMAP_FLAG(vp)); ? */ + + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + KASSERT(new_lbrec == orig_lbrec); + KASSERT(new_lbrec == 0); + + return 0; + } + + /* setup node cleanup extents copy space */ + node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS, + M_UDFMNT, M_WAITOK); + memset(node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS); + + /* + * Shrink the node by releasing the allocations and truncate the last + * allocation to the new size. If the new size fits into the + * allocation descriptor itself, transform it into an + * UDF_ICB_INTERN_ALLOC. + */ + slot = 0; + cpy_slot = 0; + foffset = 0; + + /* 1) copy till first overlap piece to the rewrite buffer */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) { + DPRINTF(WRITE, + ("Shrink node failed: " + "encountered EOF\n")); + error = EINVAL; + goto errorout; /* panic? */ + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + if (end_foffset > new_size) + break; /* found */ + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + foffset = end_foffset; + slot++; + } + slot_offset = new_size - foffset; + + /* 2) trunc overlapping slot at overlap and copy it */ + if (slot_offset > 0) { + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + + if (flags == UDF_EXT_ALLOCATED) { + /* calculate extent in lb, and offset in lb */ + num_lb = (len + lb_size -1) / lb_size; + slot_offset_lb = (slot_offset + lb_size -1) / lb_size; + + /* adjust our slot */ + lb_num += slot_offset_lb; + num_lb -= slot_offset_lb; + + udf_free_allocated_space(ump, lb_num, vpart_num, num_lb); + } + + s_ad.len = udf_rw32(slot_offset | flags); + node_ad_cpy[cpy_slot++] = s_ad; + slot++; + + DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + + /* 3) delete remainder */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + DPRINTF(ALLOC, ("\t3: delete remainder " + "vp %d lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + if (flags == UDF_EXT_ALLOCATED) { + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + num_lb = (len + lb_size - 1) / lb_size; + + udf_free_allocated_space(ump, lb_num, vpart_num, + num_lb); + } + + slot++; + } + + /* 4) if it will fit into the descriptor then convert */ + if (new_size < max_l_ad) { + /* + * resque/evacuate old piece by reading it in, and convert it + * to internal alloc. + */ + if (new_size == 0) { + /* XXX/TODO only for zero sizing now */ + udf_wipe_adslots(udf_node); + + icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK; + icbflags |= UDF_ICB_INTERN_ALLOC; + icbtag->flags = udf_rw16(icbflags); + + inflen -= size_diff; KASSERT(inflen == 0); + objsize -= size_diff; + l_ad = new_size; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + /* eventually copy in evacuated piece */ + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + + free(node_ad_cpy, M_UDFMNT); + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + KASSERT(new_inflen == 0); + KASSERT(new_lbrec == 0); + + return 0; + } + + printf("UDF_SHRINK_NODE: could convert to internal alloc!\n"); + } + + /* 5) reset node descriptors */ + udf_wipe_adslots(udf_node); + + /* 6) copy back extents; merge when possible. Recounting on the fly */ + cpy_slots = cpy_slot; + + c_ad = node_ad_cpy[0]; + slot = 0; + for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) { + s_ad = node_ad_cpy[cpy_slot]; + + DPRINTF(ALLOC, ("\t6: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* see if we can merge */ + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + DPRINTF(ALLOC, ("\t6: appending vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; /* panic? */ + c_ad = s_ad; + slot++; + } + } + + /* 7) push rest slot (if any) */ + if (UDF_EXT_LEN(c_ad.len) > 0) { + DPRINTF(ALLOC, ("\t7: last append vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; /* panic? */ + ; + } + + inflen -= size_diff; + objsize -= size_diff; + if (fe) { + fe->inf_len = udf_rw64(inflen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + } + error = 0; + + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + +errorout: + free(node_ad_cpy, M_UDFMNT); + + udf_count_alloc_exts(udf_node); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + + return error; +} + diff --git a/sys/fs/udf/udf_bswap.h b/sys/fs/udf/udf_bswap.h new file mode 100644 index 000000000..9a481cfcc --- /dev/null +++ b/sys/fs/udf/udf_bswap.h @@ -0,0 +1,76 @@ +/* $NetBSD: udf_bswap.h,v 1.8 2009/10/22 21:50:01 bouyer 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. + * + * adapted for UDF by Reinoud Zandijk + * + */ + +#ifndef _FS_UDF_UDF_BSWAP_H_ +#define _FS_UDF_UDF_BSWAP_H_ + +#include +#include +#include + +/* rest only relevant for big endian machines */ +#if (BYTE_ORDER == BIG_ENDIAN) + +/* inlines for access to swapped data */ +static __inline uint16_t udf_rw16(uint16_t); +static __inline uint32_t udf_rw32(uint32_t); +static __inline uint64_t udf_rw64(uint64_t); + + +static __inline uint16_t +udf_rw16(uint16_t a) +{ + return bswap16(a); +} + + +static __inline uint32_t +udf_rw32(uint32_t a) +{ + return bswap32(a); +} + + +static __inline uint64_t +udf_rw64(uint64_t a) +{ + return bswap64(a); +} + +#else + +#define udf_rw16(a) ((uint16_t)(a)) +#define udf_rw32(a) ((uint32_t)(a)) +#define udf_rw64(a) ((uint64_t)(a)) + +#endif + + +#endif /* !_FS_UDF_UDF_BSWAP_H_ */ + diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h new file mode 100644 index 000000000..bf02be7dd --- /dev/null +++ b/sys/fs/udf/udf_mount.h @@ -0,0 +1,70 @@ +/* $NetBSD: udf_mount.h,v 1.3 2006/02/02 15:52:23 reinoud Exp $ */ + +/* + * Copyright (c) 2006 Reinoud Zandijk + * 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 for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 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 _FS_UDF_UDF_MOUNT_H_ +#define _FS_UDF_UDF_MOUNT_H_ + +/* + * Arguments to mount UDF filingsystem. + */ + +#define UDFMNT_VERSION 1 +struct udf_args { + uint32_t version; /* version of this structure */ + char *fspec; /* mount specifier */ + int32_t sessionnr; /* session specifier, rel of abs */ + uint32_t udfmflags; /* mount options */ + int32_t gmtoff; /* offset from UTC in seconds */ + + uid_t anon_uid; /* mapping of anonymous files uid */ + gid_t anon_gid; /* mapping of anonymous files gid */ + uid_t nobody_uid; /* nobody:nobody will map to -1:-1 */ + gid_t nobody_gid; /* nobody:nobody will map to -1:-1 */ + + uint32_t sector_size; /* for mounting dumps/files */ + + /* extendable */ + uint8_t reserved[32]; +}; + + +/* udf mount options */ + +#define UDFMNT_CLOSESESSION 0x00000001 /* close session on dismount */ +#define UDFMNT_BITS "\20\1CLOSESESSION" + +#endif /* !_FS_UDF_UDF_MOUNT_H_ */ + diff --git a/sys/fs/udf/udf_osta.c b/sys/fs/udf/udf_osta.c new file mode 100644 index 000000000..3b1a28293 --- /dev/null +++ b/sys/fs/udf/udf_osta.c @@ -0,0 +1,514 @@ +/* $NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $"); + +/* + * Various routines from the OSTA 2.01 specs. Copyrights are included with + * each code segment. Slight whitespace modifications have been made for + * formatting purposes. Typos/bugs have been fixed. + * + */ + +#include "udf_osta.h" + +#ifndef _KERNEL +#include +#endif + +/*****************************************************************************/ +/*********************************************************************** + * OSTA compliant Unicode compression, uncompression routines. + * Copyright 1995 Micro Design International, Inc. + * Written by Jason M. Rinn. + * Micro Design International gives permission for the free use of the + * following source code. + */ + +/*********************************************************************** + * Takes an OSTA CS0 compressed unicode name, and converts + * it to Unicode. + * The Unicode output will be in the byte order + * that the local compiler uses for 16-bit values. + * NOTE: This routine only performs error checking on the compID. + * It is up to the user to ensure that the unicode buffer is large + * enough, and that the compressed unicode name is correct. + * + * RETURN VALUE + * + * The number of unicode characters which were uncompressed. + * A -1 is returned if the compression ID is invalid. + */ +int +udf_UncompressUnicode( + int numberOfBytes, /* (Input) number of bytes read from media. */ + byte *UDFCompressed, /* (Input) bytes read from media. */ + unicode_t *unicode) /* (Output) uncompressed unicode characters. */ +{ + unsigned int compID; + int returnValue, unicodeIndex, byteIndex; + + /* Use UDFCompressed to store current byte being read. */ + compID = UDFCompressed[0]; + + /* First check for valid compID. */ + if (compID != 8 && compID != 16) { + returnValue = -1; + } else { + unicodeIndex = 0; + byteIndex = 1; + + /* Loop through all the bytes. */ + while (byteIndex < numberOfBytes) { + if (compID == 16) { + /* Move the first byte to the high bits of the + * unicode char. + */ + unicode[unicodeIndex] = + UDFCompressed[byteIndex++] << 8; + } else { + unicode[unicodeIndex] = 0; + } + if (byteIndex < numberOfBytes) { + /*Then the next byte to the low bits. */ + unicode[unicodeIndex] |= + UDFCompressed[byteIndex++]; + } + unicodeIndex++; + } + returnValue = unicodeIndex; + } + return(returnValue); +} + +/*********************************************************************** + * DESCRIPTION: + * Takes a string of unicode wide characters and returns an OSTA CS0 + * compressed unicode string. The unicode MUST be in the byte order of + * the compiler in order to obtain correct results. Returns an error + * if the compression ID is invalid. + * + * NOTE: This routine assumes the implementation already knows, by + * the local environment, how many bits are appropriate and + * therefore does no checking to test if the input characters fit + * into that number of bits or not. + * + * RETURN VALUE + * + * The total number of bytes in the compressed OSTA CS0 string, + * including the compression ID. + * A -1 is returned if the compression ID is invalid. + */ +int +udf_CompressUnicode( + int numberOfChars, /* (Input) number of unicode characters. */ + int compID, /* (Input) compression ID to be used. */ + unicode_t *unicode, /* (Input) unicode characters to compress. */ + byte *UDFCompressed) /* (Output) compressed string, as bytes. */ +{ + int byteIndex, unicodeIndex; + + if (compID != 8 && compID != 16) { + byteIndex = -1; /* Unsupported compression ID ! */ + } else { + /* Place compression code in first byte. */ + UDFCompressed[0] = compID; + + byteIndex = 1; + unicodeIndex = 0; + while (unicodeIndex < numberOfChars) { + if (compID == 16) { + /* First, place the high bits of the char + * into the byte stream. + */ + UDFCompressed[byteIndex++] = + (unicode[unicodeIndex] & 0xFF00) >> 8; + } + /*Then place the low bits into the stream. */ + UDFCompressed[byteIndex++] = + unicode[unicodeIndex] & 0x00FF; + unicodeIndex++; + } + } + return(byteIndex); +} + +/*****************************************************************************/ +/* + * CRC 010041 + */ +static unsigned short crc_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned short +udf_cksum(unsigned char *s, int n) +{ + unsigned short crc=0; + + while (n-- > 0) + crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8); + return crc; +} + +/* UNICODE Checksum */ +unsigned short +udf_unicode_cksum(unsigned short *s, int n) +{ + unsigned short crc=0; + + while (n-- > 0) { + /* Take high order byte first--corresponds to a big endian + * byte stream. + */ + crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8); + crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8); + } + return crc; +} + + +/* + * Calculates a 16-bit checksum of the Implementation Use + * Extended Attribute header or Application Use Extended Attribute + * header. The fields AttributeType through ImplementationIdentifier + * (or ApplicationIdentifier) inclusively represent the + * data covered by the checksum (48 bytes). + * + */ +uint16_t udf_ea_cksum(uint8_t *data) { + uint16_t checksum = 0; + int count; + + for (count = 0; count < 48; count++) { + checksum += *data++; + } + + return checksum; +} + + +#ifdef MAIN +unsigned char bytes[] = { 0x70, 0x6A, 0x77 }; + +main(void) +{ + unsigned short x; + x = cksum(bytes, sizeof bytes); + printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299); + exit(0); +} +#endif + +/*****************************************************************************/ +/* #ifdef NEEDS_ISPRINT */ +/*********************************************************************** + * OSTA UDF compliant file name translation routine for OS/2, + * Windows 95, Windows NT, Macintosh and UNIX. + * Copyright 1995 Micro Design International, Inc. + * Written by Jason M. Rinn. + * Micro Design International gives permission for the free use of the + * following source code. + */ + +/*********************************************************************** + * To use these routines with different operating systems. + * + * OS/2 + * Define OS2 + * Define MAXLEN = 254 + * + * Windows 95 + * Define WIN_95 + * Define MAXLEN = 255 + * + * Windows NT + * Define WIN_NT + * Define MAXLEN = 255 + * + * Macintosh: + * Define MAC. + * Define MAXLEN = 31. + * + * UNIX + * Define UNIX. + * Define MAXLEN as specified by unix version. + */ + +#define ILLEGAL_CHAR_MARK 0x005F +#define CRC_MARK 0x0023 +#define EXT_SIZE 5 +#define PERIOD 0x002E +#define SPACE 0x0020 + +/*** PROTOTYPES ***/ +int IsIllegal(unicode_t ch); + +/* Define a function or macro which determines if a Unicode character is + * printable under your implementation. + */ + + +/* #include */ +static int UnicodeIsPrint(unicode_t ch) { + return (ch >=' ') && (ch != 127); +} + + +int UnicodeLength(unicode_t *string) { + int length; + length = 0; + while (*string++) length++; + + return length; +} + + +#ifdef _KERNEL +static int isprint(int c) { + return (c >= ' ') && (c != 127); +} +#endif + + +/*********************************************************************** + * Translates a long file name to one using a MAXLEN and an illegal + * char set in accord with the OSTA requirements. Assumes the name has + * already been translated to Unicode. + * + * RETURN VALUE + * + * Number of unicode characters in translated name. + */ +int UDFTransName( + unicode_t *newName, /* (Output)Translated name. Must be of length + * MAXLEN */ + unicode_t *udfName, /* (Input) Name from UDF volume.*/ + int udfLen) /* (Input) Length of UDF Name. */ +{ + int Index, newIndex = 0, needsCRC = false; /* index is shadowed */ + int extIndex = 0, newExtIndex = 0, hasExt = false; +#if defined OS2 || defined WIN_95 || defined WIN_NT + int trailIndex = 0; +#endif + unsigned short valueCRC; + unicode_t current; + const char hexChar[] = "0123456789ABCDEF"; + + for (Index = 0; Index < udfLen; Index++) { + current = udfName[Index]; + + if (IsIllegal(current) || !UnicodeIsPrint(current)) { + needsCRC = true; + /* Replace Illegal and non-displayable chars with + * underscore. + */ + current = ILLEGAL_CHAR_MARK; + /* Skip any other illegal or non-displayable + * characters. + */ + while(Index+1 < udfLen && (IsIllegal(udfName[Index+1]) + || !UnicodeIsPrint(udfName[Index+1]))) { + Index++; + } + } + + /* Record position of extension, if one is found. */ + if (current == PERIOD && (udfLen - Index -1) <= EXT_SIZE) { + if (udfLen == Index + 1) { + /* A trailing period is NOT an extension. */ + hasExt = false; + } else { + hasExt = true; + extIndex = Index; + newExtIndex = newIndex; + } + } + +#if defined OS2 || defined WIN_95 || defined WIN_NT + /* Record position of last char which is NOT period or space. */ + else if (current != PERIOD && current != SPACE) { + trailIndex = newIndex; + } +#endif + + if (newIndex < MAXLEN) { + newName[newIndex++] = current; + } else { + needsCRC = true; + } + } + +#if defined OS2 || defined WIN_95 || defined WIN_NT + /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */ + if (trailIndex != newIndex - 1) { + newIndex = trailIndex + 1; + needsCRC = true; + hasExt = false; /* Trailing period does not make an + * extension. */ + } +#endif + + if (needsCRC) { + unicode_t ext[EXT_SIZE]; + int localExtIndex = 0; + if (hasExt) { + int maxFilenameLen; + /* Translate extension, and store it in ext. */ + for(Index = 0; Index maxFilenameLen) { + newIndex = maxFilenameLen; + } else { + newIndex = newExtIndex; + } + } else if (newIndex > MAXLEN - 5) { + /*If no extension, make sure to leave room for CRC. */ + newIndex = MAXLEN - 5; + } + newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */ + + /*Calculate CRC from original filename from FileIdentifier. */ + valueCRC = udf_unicode_cksum(udfName, udfLen); + /* Convert 16-bits of CRC to hex characters. */ + newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; + newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; + newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; + newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; + + /* Place a translated extension at end, if found. */ + if (hasExt) { + newName[newIndex++] = PERIOD; + for (Index = 0;Index < localExtIndex ;Index++ ) { + newName[newIndex++] = ext[Index]; + } + } + } + return(newIndex); +} + +#if defined OS2 || defined WIN_95 || defined WIN_NT +/*********************************************************************** + * Decides if a Unicode character matches one of a list + * of ASCII characters. + * Used by OS2 version of IsIllegal for readability, since all of the + * illegal characters above 0x0020 are in the ASCII subset of Unicode. + * Works very similarly to the standard C function strchr(). + * + * RETURN VALUE + * + * Non-zero if the Unicode character is in the given ASCII string. + */ +int UnicodeInString( + unsigned char *string, /* (Input) String to search through. */ + unicode_t ch) /* (Input) Unicode char to search for. */ +{ + int found = false; + while (*string != '\0' && found == false) { + /* These types should compare, since both are unsigned + * numbers. */ + if (*string == ch) { + found = true; + } + string++; + } + return(found); +} +#endif /* OS2 */ + +/*********************************************************************** + * Decides whether the given character is illegal for a given OS. + * + * RETURN VALUE + * + * Non-zero if char is illegal. + */ +int IsIllegal(unicode_t ch) +{ +#ifdef MAC + /* Only illegal character on the MAC is the colon. */ + if (ch == 0x003A) { + return(1); + } else { + return(0); + } + +#elif defined UNIX + /* Illegal UNIX characters are NULL and slash. */ + if (ch == 0x0000 || ch == 0x002F) { + return(1); + } else { + return(0); + } + +#elif defined OS2 || defined WIN_95 || defined WIN_NT + /* Illegal char's for OS/2 according to WARP toolkit. */ + if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) { + return(1); + } else { + return(0); + } +#endif +} +/* #endif*/ /* NEEDS_ISPRINT */ + diff --git a/sys/fs/udf/udf_osta.h b/sys/fs/udf/udf_osta.h new file mode 100644 index 000000000..4d166b74f --- /dev/null +++ b/sys/fs/udf/udf_osta.h @@ -0,0 +1,44 @@ +/* $NetBSD: udf_osta.h,v 1.4 2008/05/14 16:49:48 reinoud Exp $ */ + +/* + * Prototypes for the OSTA functions + */ + + +#ifndef _FS_UDF_OSTA_H_ +#define _FS_UDF_OSTA_H_ + +#include + +#ifndef _KERNEL +#include +#endif + +#ifndef UNIX +#define UNIX +#endif + +#ifndef MAXLEN +#define MAXLEN 255 +#endif + + +/*********************************************************************** + * The following two typedef's are to remove compiler dependancies. + * byte needs to be unsigned 8-bit, and unicode_t needs to be + * unsigned 16-bit. + */ +typedef uint16_t unicode_t; +typedef uint8_t byte; + + +int udf_UncompressUnicode(int, byte *, unicode_t *); +int udf_CompressUnicode(int, int, unicode_t *, byte *); +unsigned short udf_cksum(unsigned char *, int); +unsigned short udf_unicode_cksum(unsigned short *, int); +uint16_t udf_ea_cksum(uint8_t *data); +int UDFTransName(unicode_t *, unicode_t *, int); +int UnicodeLength(unicode_t *string); + + +#endif /* _FS_UDF_OSTA_H_ */ diff --git a/sys/fs/udf/udf_readwrite.c b/sys/fs/udf/udf_readwrite.c new file mode 100644 index 000000000..57c4ee2d0 --- /dev/null +++ b/sys/fs/udf/udf_readwrite.c @@ -0,0 +1,723 @@ +/* $NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $ */ + +/* + * Copyright (c) 2007, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +/* --------------------------------------------------------------------- */ + +void +udf_fixup_fid_block(uint8_t *blob, int lb_size, + int rfix_pos, int max_rfix_pos, uint32_t lb_num) +{ + struct fileid_desc *fid; + uint8_t *fid_pos; + int fid_len, found; + + /* needs to be word aligned */ + KASSERT(rfix_pos % 4 == 0); + + /* first resync with the FID stream !!! */ + found = 0; + while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) { + fid_pos = blob + rfix_pos; + fid = (struct fileid_desc *) fid_pos; + if (udf_rw16(fid->tag.id) == TAGID_FID) { + if (udf_check_tag((union dscrptr *) fid) == 0) + found = 1; + } + if (found) + break; + /* try next location; can only be 4 bytes aligned */ + rfix_pos += 4; + } + + /* walk over the fids */ + fid_pos = blob + rfix_pos; + while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) { + fid = (struct fileid_desc *) fid_pos; + if (udf_rw16(fid->tag.id) != TAGID_FID) { + /* end of FID stream; end of directory or currupted */ + break; + } + + /* update sector number and recalculate checkum */ + fid->tag.tag_loc = udf_rw32(lb_num); + udf_validate_tag_sum((union dscrptr *) fid); + + /* if the FID crosses the memory, we're done! */ + if (rfix_pos + UDF_FID_SIZE >= max_rfix_pos) + break; + + fid_len = udf_fidsize(fid); + fid_pos += fid_len; + rfix_pos += fid_len; + } +} + + +void +udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num) +{ + struct desc_tag *tag; + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *eahdr; + int l_ea; + + /* get information from fe/efe */ + tag = (struct desc_tag *) blob; + switch (udf_rw16(tag->id)) { + case TAGID_FENTRY : + fe = (struct file_entry *) blob; + l_ea = udf_rw32(fe->l_ea); + eahdr = (struct extattrhdr_desc *) fe->data; + break; + case TAGID_EXTFENTRY : + efe = (struct extfile_entry *) blob; + l_ea = udf_rw32(efe->l_ea); + eahdr = (struct extattrhdr_desc *) efe->data; + break; + case TAGID_INDIRECTENTRY : + case TAGID_ALLOCEXTENT : + case TAGID_EXTATTR_HDR : + return; + default: + panic("%s: passed bad tag\n", __func__); + } + + /* something recorded here? (why am i called?) */ + if (l_ea == 0) + return; + +#if 0 + /* check extended attribute tag */ + /* TODO XXX what to do when we encounter an error here? */ + error = udf_check_tag(eahdr); + if (error) + return; /* for now */ + if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR) + return; /* for now */ + error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc)); + if (error) + return; /* for now */ +#endif + + DPRINTF(EXTATTR, ("node fixup: found %d bytes of extended attributes\n", + l_ea)); + + /* fixup eahdr tag */ + eahdr->tag.tag_loc = udf_rw32(lb_num); + udf_validate_tag_and_crc_sums((union dscrptr *) eahdr); +} + + +void +udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type) +{ + struct desc_tag *tag, *sbm_tag; + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + uint32_t lb_size, lb_num; + uint32_t intern_pos, max_intern_pos; + int icbflags, addr_type, file_type, intern, has_fids, has_sbm, l_ea; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + /* if its not a node we're done */ + if (udf_c_type != UDF_C_NODE) + return; + + /* NOTE this could also be done in write_internal */ + /* start of a descriptor */ + l_ea = 0; + has_fids = 0; + has_sbm = 0; + intern = 0; + file_type = 0; + max_intern_pos = intern_pos = lb_num = 0; /* shut up gcc! */ + + tag = (struct desc_tag *) blob; + switch (udf_rw16(tag->id)) { + case TAGID_FENTRY : + fe = (struct file_entry *) tag; + l_ea = udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK); + file_type = fe->icbtag.file_type; + intern = (addr_type == UDF_ICB_INTERN_ALLOC); + intern_pos = UDF_FENTRY_SIZE + l_ea; + max_intern_pos = intern_pos + udf_rw64(fe->inf_len); + lb_num = udf_rw32(fe->tag.tag_loc); + break; + case TAGID_EXTFENTRY : + efe = (struct extfile_entry *) tag; + l_ea = udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK); + file_type = efe->icbtag.file_type; + intern = (addr_type == UDF_ICB_INTERN_ALLOC); + intern_pos = UDF_EXTFENTRY_SIZE + l_ea; + max_intern_pos = intern_pos + udf_rw64(efe->inf_len); + lb_num = udf_rw32(efe->tag.tag_loc); + break; + case TAGID_INDIRECTENTRY : + case TAGID_EXTATTR_HDR : + break; + case TAGID_ALLOCEXTENT : + /* force crclen to 8 for UDF version < 2.01 */ + ext = (struct alloc_ext_entry *) tag; + if (udf_rw16(ump->logvol_info->min_udf_readver) <= 0x200) + ext->tag.desc_crc_len = udf_rw16(8); + break; + default: + panic("%s: passed bad tag\n", __func__); + break; + } + + /* determine what to fix if its internally recorded */ + if (intern) { + has_fids = (file_type == UDF_ICB_FILETYPE_DIRECTORY) || + (file_type == UDF_ICB_FILETYPE_STREAMDIR); + has_sbm = (file_type == UDF_ICB_FILETYPE_META_BITMAP); + } + + /* fixup internal extended attributes if present */ + if (l_ea) + udf_fixup_internal_extattr(blob, lb_num); + + /* fixup fids lb numbers */ + if (has_fids) + udf_fixup_fid_block(blob, lb_size, intern_pos, + max_intern_pos, lb_num); + + /* fixup space bitmap descriptor */ + if (has_sbm) { + sbm_tag = (struct desc_tag *) (blob + intern_pos); + sbm_tag->tag_loc = tag->tag_loc; + udf_validate_tag_and_crc_sums((uint8_t *) sbm_tag); + } + + udf_validate_tag_and_crc_sums(blob); +} + +/* --------------------------------------------------------------------- */ + +/* + * Set of generic descriptor readers and writers and their helper functions. + * Descriptors inside `logical space' i.e. inside logically mapped partitions + * can never be longer than one logical sector. + * + * NOTE that these functions *can* be used by the sheduler backends to read + * node descriptors too. + * + * For reading, the size of allocated piece is returned in multiple of sector + * size due to udf_calc_udf_malloc_size(). + */ + + +/* SYNC reading of n blocks from specified sector */ +int +udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors) +{ + struct buf *buf, *nestbuf; + uint32_t buf_offset; + off_t lblkno, rblkno; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int piece; + int error; + + DPRINTF(READ, ("udf_intbreadn() : sectors = %d, sector_size = %d\n", + sectors, sector_size)); + buf = getiobuf(ump->devvp, true); + buf->b_flags = B_READ; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = blob; + buf->b_bcount = sectors * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + error = 0; + buf_offset = 0; + rblkno = start; + lblkno = 0; + while ((sectors > 0) && (error == 0)) { + piece = MIN(MAXPHYS/sector_size, sectors); + DPRINTF(READ, ("read in %d + %d\n", (uint32_t) rblkno, piece)); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblkno * blks; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblkno * blks; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + + lblkno += piece; + rblkno += piece; + buf_offset += piece * sector_size; + sectors -= piece; + } + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* synchronous generic descriptor read */ +int +udf_read_phys_dscr(struct udf_mount *ump, uint32_t sector, + struct malloc_type *mtype, union dscrptr **dstp) +{ + union dscrptr *dst, *new_dst; + uint8_t *pos; + int sectors, dscrlen; + int i, error, sector_size; + + sector_size = ump->discinfo.sector_size; + + *dstp = dst = NULL; + dscrlen = sector_size; + + /* read initial piece */ + dst = malloc(sector_size, mtype, M_WAITOK); + error = udf_read_phys_sectors(ump, UDF_C_DSCR, dst, sector, 1); + DPRINTFIF(DESCRIPTOR, error, ("read error (%d)\n", error)); + + if (!error) { + /* check if its a valid tag */ + error = udf_check_tag(dst); + if (error) { + /* check if its an empty block */ + pos = (uint8_t *) dst; + for (i = 0; i < sector_size; i++, pos++) { + if (*pos) break; + } + if (i == sector_size) { + /* return no error but with no dscrptr */ + /* dispose first block */ + free(dst, mtype); + return 0; + } + } + /* calculate descriptor size */ + dscrlen = udf_tagsize(dst, sector_size); + } + DPRINTFIF(DESCRIPTOR, error, ("bad tag checksum\n")); + + if (!error && (dscrlen > sector_size)) { + DPRINTF(DESCRIPTOR, ("multi block descriptor read\n")); + /* + * Read the rest of descriptor. Since it is only used at mount + * time its overdone to define and use a specific udf_intbreadn + * for this alone. + */ + + new_dst = realloc(dst, dscrlen, mtype, M_WAITOK); + if (new_dst == NULL) { + free(dst, mtype); + return ENOMEM; + } + dst = new_dst; + + sectors = (dscrlen + sector_size -1) / sector_size; + DPRINTF(DESCRIPTOR, ("dscrlen = %d (%d blk)\n", dscrlen, sectors)); + + pos = (uint8_t *) dst + sector_size; + error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos, + sector + 1, sectors-1); + + DPRINTFIF(DESCRIPTOR, error, ("read error on multi (%d)\n", + error)); + } + if (!error) { + error = udf_check_tag_payload(dst, dscrlen); + DPRINTFIF(DESCRIPTOR, error, ("bad payload check sum\n")); + } + if (error && dst) { + free(dst, mtype); + dst = NULL; + } + *dstp = dst; + + return error; +} + + +static void +udf_write_phys_buf(struct udf_mount *ump, int what, struct buf *buf) +{ + struct buf *nestbuf; + uint32_t buf_offset; + off_t lblkno, rblkno; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + uint32_t sectors; + int piece; + int error; + + sectors = buf->b_bcount / sector_size; + DPRINTF(WRITE, ("udf_intbwriten() : sectors = %d, sector_size = %d\n", + sectors, sector_size)); + + /* don't forget to increase pending count for the bwrite itself */ +/* panic("NO WRITING\n"); */ + if (buf->b_vp) { + mutex_enter(buf->b_vp->v_interlock); + buf->b_vp->v_numoutput++; + mutex_exit(buf->b_vp->v_interlock); + } + + error = 0; + buf_offset = 0; + rblkno = buf->b_blkno / blks; + lblkno = 0; + while ((sectors > 0) && (error == 0)) { + piece = MIN(MAXPHYS/sector_size, sectors); + DPRINTF(WRITE, ("write out %d + %d\n", + (uint32_t) rblkno, piece)); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblkno * blks; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblkno * blks; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + + lblkno += piece; + rblkno += piece; + buf_offset += piece * sector_size; + sectors -= piece; + } +} + + +/* SYNC writing of n blocks from specified sector */ +int +udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors) +{ + struct vnode *vp; + struct buf *buf; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int error; + + /* get transfer buffer */ + vp = ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = blob; + buf->b_bcount = sectors * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + /* do the write, wait and return error */ + udf_write_phys_buf(ump, what, buf); + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* synchronous generic descriptor write */ +int +udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node, int what, + union dscrptr *dscr, uint32_t sector, uint32_t logsector) +{ + struct vnode *vp; + struct buf *buf; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int dscrlen; + int error; + + /* set sector number in the descriptor and validate */ + dscr->tag.tag_loc = udf_rw32(logsector); + udf_validate_tag_and_crc_sums(dscr); + + /* calculate descriptor size */ + dscrlen = udf_tagsize(dscr, sector_size); + + /* get transfer buffer */ + vp = udf_node ? udf_node->vnode : ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = (void *) dscr; + buf->b_bcount = dscrlen; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = sector * blks; + buf->b_proc = NULL; + + /* do the write, wait and return error */ + udf_write_phys_buf(ump, what, buf); + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* asynchronous generic descriptor write */ +int +udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector, + void (*dscrwr_callback)(struct buf *)) +{ + struct vnode *vp; + struct buf *buf; + int dscrlen; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + + KASSERT(dscrwr_callback); + DPRINTF(NODE, ("udf_write_phys_dscr_async() called\n")); + + /* set sector number in the descriptor and validate */ + dscr->tag.tag_loc = udf_rw32(logsector); + udf_validate_tag_and_crc_sums(dscr); + + /* calculate descriptor size */ + dscrlen = udf_tagsize(dscr, sector_size); + + /* get transfer buffer */ + vp = udf_node ? udf_node->vnode : ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; // | B_ASYNC; + buf->b_cflags = BC_BUSY; + buf->b_iodone = dscrwr_callback; + buf->b_data = dscr; + buf->b_bcount = dscrlen; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = sector * blks; + buf->b_proc = NULL; + + /* do the write and return no error */ + udf_write_phys_buf(ump, what, buf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* disc strategy dispatchers */ + +int +udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node, struct long_ad *icb, + union dscrptr **dscrptr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = ump; + args.udf_node = udf_node; + args.icb = icb; + args.dscr = NULL; + + error = (strategy->create_logvol_dscr)(&args); + *dscrptr = args.dscr; + + return error; +} + + +void +udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + void *dscr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + args.icb = icb; + args.dscr = dscr; + + (strategy->free_logvol_dscr)(&args); +} + + +int +udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + union dscrptr **dscrptr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = ump; + args.icb = icb; + args.dscr = NULL; + + error = (strategy->read_logvol_dscr)(&args); + *dscrptr = args.dscr; + + return error; +} + + +int +udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr, + struct long_ad *icb, int waitfor) +{ + struct udf_strategy *strategy = udf_node->ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = udf_node->ump; + args.udf_node = udf_node; + args.icb = icb; + args.dscr = dscr; + args.waitfor = waitfor; + + error = (strategy->write_logvol_dscr)(&args); + return error; +} + + +void +udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + args.nestbuf = nestbuf; + + (strategy->queuebuf)(&args); +} + + +void +udf_discstrat_init(struct udf_mount *ump) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + (strategy->discstrat_init)(&args); +} + + +void udf_discstrat_finish(struct udf_mount *ump) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + /* strategy might not have been set, so ignore if not set */ + if (strategy) { + args.ump = ump; + (strategy->discstrat_finish)(&args); + } +} + +/* --------------------------------------------------------------------- */ + diff --git a/sys/fs/udf/udf_rename.c b/sys/fs/udf/udf_rename.c new file mode 100644 index 000000000..1aa6bb246 --- /dev/null +++ b/sys/fs/udf/udf_rename.c @@ -0,0 +1,669 @@ +/* $NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $ */ + +/* + * Copyright (c) 2013 Reinoud Zandijk + * 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. + * + * Comments and trivial code from the reference implementation in tmpfs. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +/* forwards */ +static int udf_sane_rename( struct vnode *, struct componentname *, + struct vnode *, struct componentname *, + kauth_cred_t, bool); +static bool udf_rmdired_p(struct vnode *); +static int udf_gro_lock_directory(struct mount *, struct vnode *); + +static const struct genfs_rename_ops udf_genfs_rename_ops; + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + + +/* + * udf_sane_rename: The hairiest vop, with the saner API. + * + * Arguments: + * + * . fdvp (from directory vnode), + * . fcnp (from component name), + * . tdvp (to directory vnode), + * . tcnp (to component name), + * . cred (credentials structure), and + * . posixly_correct (flag for behaviour if target & source link same file). + * + * fdvp and tdvp may be the same, and must be referenced and unlocked. + */ +static int +udf_sane_rename( struct vnode *fdvp, struct componentname *fcnp, + struct vnode *tdvp, struct componentname *tcnp, + kauth_cred_t cred, bool posixly_correct) +{ + DPRINTF(CALL, ("udf_sane_rename '%s' -> '%s'\n", + fcnp->cn_nameptr, tcnp->cn_nameptr)); + return genfs_sane_rename(&udf_genfs_rename_ops, + fdvp, fcnp, NULL, tdvp, tcnp, NULL, + cred, posixly_correct); +} + + +/* + * udf_rename: the hairiest vop, with the insanest API. Pass to + * genfs_insane_rename immediately. + */ +int +udf_rename(void *v) +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap = v; + DPRINTF(CALL, ("udf_rename called\n")); + return genfs_insane_rename(ap, &udf_sane_rename); +} + + +/* + * udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is + * its parent. + * + * vp and dvp must be locked and referenced. + */ +static bool +udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, + struct vnode *vp, struct vnode *dvp) +{ + struct udf_node *udf_node = VTOI(vp); + int error, isempty; + + KASSERT(mp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != dvp); + KASSERT(vp->v_mount == mp); + KASSERT(dvp->v_mount == mp); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_directory_empty_p called\n")); + /* make sure our `leaf' node's hash is populated */ + dirhash_get(&udf_node->dir_hash); + error = udf_dirhash_fill(udf_node); + if (error) { + dirhash_put(udf_node->dir_hash); + /* VERY unlikely, answer its not empty */ + return 0; + } + + /* check to see if the directory is empty */ + isempty = dirhash_dir_isempty(udf_node->dir_hash); + dirhash_put(udf_node->dir_hash); + + return isempty; +} + + +/* + * udf_gro_rename_check_possible: check whether a rename is possible + * independent of credentials. + */ +static int +udf_gro_rename_check_possible(struct mount *mp, + struct vnode *fdvp, struct vnode *fvp, + struct vnode *tdvp, struct vnode *tvp) +{ + (void)mp; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename_check_possible called\n")); + + /* flags not implemented since they are not defined (yet) in UDF */ + return 0; +} + + +/* + * udf_gro_rename_check_permitted: check whether a rename is permitted given + * our credentials. + */ +static int +udf_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct vnode *fvp, + struct vnode *tdvp, struct vnode *tvp) +{ + struct udf_node *fdir_node = VTOI(fdvp); + struct udf_node *tdir_node = VTOI(tdvp); + struct udf_node *f_node = VTOI(fvp); + struct udf_node *t_node = (tvp? VTOI(tvp): NULL); + mode_t fdmode, tdmode; + uid_t fduid, tduid, fuid, tuid; + gid_t gdummy; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename_check_permitted called\n")); + fdmode = udf_getaccessmode(fdir_node); + tdmode = udf_getaccessmode(tdir_node); + + udf_getownership(fdir_node, &fduid, &gdummy); + udf_getownership(tdir_node, &tduid, &gdummy); + udf_getownership(f_node, &fuid, &gdummy); + + tuid = 0; + if (t_node) + udf_getownership(t_node, &tuid, &gdummy); + + return genfs_ufslike_rename_check_permitted(cred, + fdvp, fdmode, fduid, + fvp, fuid, + tdvp, tdmode, tduid, + tvp, tuid); +} + + +/* + * udf_gro_remove_check_possible: check whether a remove is possible + * independent of credentials. + * + * XXX could check for special attributes? + */ +static int +udf_gro_remove_check_possible(struct mount *mp, + struct vnode *dvp, struct vnode *vp) +{ + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove_check_possible called\n")); + + /* flags not implemented since they are not defined (yet) in UDF */ + return 0; +} + + +/* + * udf_gro_remove_check_permitted: check whether a remove is permitted given + * our credentials. + */ +static int +udf_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, + struct vnode *dvp, struct vnode *vp) +{ + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + mode_t dmode; + uid_t duid, uid; + gid_t gdummy; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove_check_permitted called\n")); + dmode = udf_getaccessmode(dir_node); + + udf_getownership(dir_node, &duid, &gdummy); + udf_getownership(udf_node, &uid, &gdummy); + + return genfs_ufslike_remove_check_permitted(cred, + dvp, dmode, duid, + vp, uid); +} + + +/* + * udf_gro_rename: actually perform the rename operation. + */ +static int +udf_gro_rename(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct componentname *fcnp, + void *fde, struct vnode *fvp, + struct vnode *tdvp, struct componentname *tcnp, + void *tde, struct vnode *tvp) +{ + struct udf_node *fnode, *fdnode, *tnode, *tdnode; + struct vattr fvap; + int error; + + (void)cred; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fcnp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(tcnp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename called\n")); + DPRINTF(NODE, ("udf_gro_rename called : %s -> %s\n", + fcnp->cn_nameptr, tcnp->cn_nameptr)); + + fnode = VTOI(fvp); + fdnode = VTOI(fdvp); + tnode = (tvp == NULL) ? NULL : VTOI(tvp); + tdnode = VTOI(tdvp); + + /* get attribute information */ + error = VOP_GETATTR(fvp, &fvap, NULL); + if (error) + return error; + + /* remove existing entry if present */ + if (tvp) + udf_dir_detach(tdnode->ump, tdnode, tnode, tcnp); + + /* create new directory entry for the node */ + error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp); + if (error) + return error; + + /* unlink old directory entry for the node, if failing, unattach new */ + error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp); + if (error) + goto rollback_attach; + + if ((fdnode != tdnode) && (fvp->v_type == VDIR)) { + /* update fnode's '..' entry */ + error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode); + if (error) + goto rollback; + } + + VN_KNOTE(fvp, NOTE_RENAME); + genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); + return 0; + +rollback: + /* 'try' to recover from this situation */ + udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp); +rollback_attach: + udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp); + + return error; +} + + +/* + * udf_gro_remove: rename an object over another link to itself, effectively + * removing just the original link. + */ +static int +udf_gro_remove(struct mount *mp, kauth_cred_t cred, + struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp) +{ + struct udf_node *dir_node, *udf_node; + + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(cnp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove called\n")); + + dir_node = VTOI(dvp); + udf_node = VTOI(vp); + udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp); + + return 0; +} + + +/* + * udf_gro_lookup: look up and save the lookup results. + */ +static int +udf_gro_lookup(struct mount *mp, struct vnode *dvp, + struct componentname *cnp, void *de_ret, struct vnode **vp_ret) +{ + struct udf_node *dir_node, *res_node; + struct long_ad icb_loc; + const char *name; + int namelen, error; + int found; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(cnp != NULL); + KASSERT(vp_ret != NULL); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + + dir_node = VTOI(dvp); + + DPRINTF(CALL, ("udf_gro_lookup called\n")); + + /* lookup filename in the directory; location icb_loc */ + name = cnp->cn_nameptr; + namelen = cnp->cn_namelen; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + return error; + if (!found) + return ENOENT; + + DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name)); + error = udf_get_node(dir_node->ump, &icb_loc, &res_node); + if (error) + return error; + *vp_ret = res_node->vnode; + VOP_UNLOCK(res_node->vnode); + + return 0; +} + + +/* + * udf_rmdired_p: check whether the directory vp has been rmdired. + * + * vp must be locked and referenced. + */ +static bool +udf_rmdired_p(struct vnode *vp) +{ + DPRINTF(CALL, ("udf_rmdired_p called\n")); + + KASSERT(vp != NULL); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_type == VDIR); + + return (VTOI(vp)->i_flags & IN_DELETED); +} + + +/* + * udf_gro_genealogy: analyze the genealogy of the source and target + * directories. + */ +static int +udf_gro_genealogy(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct vnode *tdvp, + struct vnode **intermediate_node_ret) +{ + struct udf_mount *ump; + struct udf_node *parent_node; + struct vnode *vp, *dvp; + struct long_ad parent_loc; + const char *name; + int namelen; + int error, found; + + (void)cred; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != tdvp); + KASSERT(intermediate_node_ret != NULL); + KASSERT(fdvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + + DPRINTF(CALL, ("udf_gro_genealogy called\n")); + + /* + * We need to provisionally lock tdvp to keep rmdir from deleting it + * -- or any ancestor -- at an inopportune moment. + * + * XXX WHY is this not in genfs's rename? XXX + */ + error = udf_gro_lock_directory(mp, tdvp); + if (error) + return error; + + name = ".."; + namelen = 2; + error = 0; + + ump = VTOI(tdvp)->ump; + + /* if nodes are equal, it is no use looking */ + KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0); + + /* start at destination vnode and walk up the tree */ + vp = tdvp; + vref(vp); + + for (;;) { + KASSERT(vp != NULL); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_mount == mp); + KASSERT(vp->v_type == VDIR); + KASSERT(!udf_rmdired_p(vp)); + + DPRINTF(NODE, ("udf_gro_genealogy : " + "fdvp %p, looking at vp %p\n", + fdvp, vp)); + + /* sanity check */ + if (vp->v_type != VDIR) { + vput(vp); + return ENOTDIR; + } + + /* go down one level */ + error = udf_lookup_name_in_dir(vp, name, namelen, + &parent_loc, &found); + DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, " + "found %d\n", error, found)); + if (!found) + error = ENOENT; + if (error) { + vput(vp); + return error; + } + + /* did we encounter the root node? i.e. loop back */ + if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) { + DPRINTF(NODE, ("ROOT found!\n")); + vput(vp); + *intermediate_node_ret = NULL; + return 0; + } + + /* Did we find that fdvp is an ancestor of tdvp? */ + if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) { + DPRINTF(NODE, ("fdvp is ancestor of tdvp\n")); + *intermediate_node_ret = vp; + VOP_UNLOCK(vp); + return 0; + } + + /* + * Unlock vp so that we can lock the parent, but keep child vp + * referenced until after we have found the parent, so that + * parent_node will not be recycled. + */ + DPRINTF(NODE, ("\tgetting the parent node\n")); + VOP_UNLOCK(vp); + error = udf_get_node(ump, &parent_loc, &parent_node); + vrele(vp); + if (error) + return error; + + dvp = parent_node->vnode; + + /* switch */ + KASSERT(dvp != NULL); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + vp = dvp; + + /* sanity check */ + if (vp->v_type != VDIR) { + /* + * Odd, but can happen if we loose the race and the + * '..' node has been recycled. + */ + vput(vp); + return ENOTDIR; + } + + if (udf_rmdired_p(vp)) { + vput(vp); + return ENOENT; + } + } +} + + +/* + * udf_gro_lock_directory: lock the directory vp, but fail if it has been + * rmdir'd. + */ +static int +udf_gro_lock_directory(struct mount *mp, struct vnode *vp) +{ + + (void)mp; + KASSERT(mp != NULL); + KASSERT(vp != NULL); + KASSERT(vp->v_mount == mp); + + DPRINTF(CALL, ("udf_gro_lock_directory called\n")); + DPRINTF(LOCKING, ("udf_gro_lock_directory called\n")); + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + if (udf_rmdired_p(vp)) { + VOP_UNLOCK(vp); + return ENOENT; + } + + return 0; +} + + +static const struct genfs_rename_ops udf_genfs_rename_ops = { + .gro_directory_empty_p = udf_gro_directory_empty_p, + .gro_rename_check_possible = udf_gro_rename_check_possible, + .gro_rename_check_permitted = udf_gro_rename_check_permitted, + .gro_remove_check_possible = udf_gro_remove_check_possible, + .gro_remove_check_permitted = udf_gro_remove_check_permitted, + .gro_rename = udf_gro_rename, + .gro_remove = udf_gro_remove, + .gro_lookup = udf_gro_lookup, + .gro_genealogy = udf_gro_genealogy, + .gro_lock_directory = udf_gro_lock_directory, +}; diff --git a/sys/fs/udf/udf_strat_bootstrap.c b/sys/fs/udf/udf_strat_bootstrap.c new file mode 100644 index 000000000..74567ad44 --- /dev/null +++ b/sys/fs/udf/udf_strat_bootstrap.c @@ -0,0 +1,145 @@ +/* $NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +static int +udf_create_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_create_logvol_dscr_bootstrap: not possible\n"); + return 0; +} + + +static void +udf_free_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_free_logvol_dscr_bootstrap: no node descriptor reading\n"); +} + + +static int +udf_read_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_read_logvol_dscr_bootstrap: no node descriptor reading\n"); + return 0; +} + + +static int +udf_write_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_write_logvol_dscr_bootstrap: no writing\n"); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_queuebuf_bootstrap(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + KASSERT(buf->b_flags & B_READ); + VOP_STRATEGY(ump->devvp, buf); +} + +static void +udf_discstrat_init_bootstrap(struct udf_strat_args *args) +{ + /* empty */ +} + + +static void +udf_discstrat_finish_bootstrap(struct udf_strat_args *args) +{ + /* empty */ +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_bootstrap = +{ + udf_create_logvol_dscr_bootstrap, + udf_free_logvol_dscr_bootstrap, + udf_read_logvol_dscr_bootstrap, + udf_write_logvol_dscr_bootstrap, + udf_queuebuf_bootstrap, + udf_discstrat_init_bootstrap, + udf_discstrat_finish_bootstrap +}; + + diff --git a/sys/fs/udf/udf_strat_direct.c b/sys/fs/udf/udf_strat_direct.c new file mode 100644 index 000000000..c6cd59121 --- /dev/null +++ b/sys/fs/udf/udf_strat_direct.c @@ -0,0 +1,454 @@ +/* $NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +/* BUFQ's */ +#define UDF_SHED_MAX 3 + +#define UDF_SHED_READING 0 +#define UDF_SHED_WRITING 1 +#define UDF_SHED_SEQWRITING 2 + + +struct strat_private { + struct pool desc_pool; /* node descriptors */ +}; + +/* --------------------------------------------------------------------- */ + +static void +udf_wr_nodedscr_callback(struct buf *buf) +{ + struct udf_node *udf_node; + + KASSERT(buf); + KASSERT(buf->b_data); + + /* called when write action is done */ + DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n")); + + udf_node = VTOI(buf->b_vp); + if (udf_node == NULL) { + putiobuf(buf); + printf("udf_wr_node_callback: NULL node?\n"); + return; + } + + /* XXX right flags to mark dirty again on error? */ + if (buf->b_error) { + /* write error on `defect free' media??? how to solve? */ + /* XXX lookup UDF standard for unallocatable space */ + udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED; + } + + /* decrement outstanding_nodedscr */ + KASSERT(udf_node->outstanding_nodedscr >= 1); + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* unlock the node */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + /* unreference the vnode so it can be recycled */ + holdrele(udf_node->vnode); + + putiobuf(buf); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_getblank_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memset(*dscrptr, 0, lb_size); + + return 0; +} + + +static void +udf_free_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + pool_put(&priv->desc_pool, dscr); +} + + +static int +udf_read_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + union dscrptr *tmpdscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + uint32_t sector, dummy; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + return error; + + /* try to read in fe/efe */ + error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr); + if (error) + return error; + + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memcpy(*dscrptr, tmpdscr, lb_size); + free(tmpdscr, M_UDFTEMP); + + return 0; +} + + +static int +udf_write_nodedscr_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct udf_node *udf_node = args->udf_node; + union dscrptr *dscr = args->dscr; + struct long_ad *icb = args->icb; + int waitfor = args->waitfor; + uint32_t logsector, sector, dummy; + int error, vpart __diagused; + + /* + * we have to decide if we write it out sequential or at its fixed + * position by examining the partition its (to be) written on. + */ + vpart = udf_rw16(udf_node->loc.loc.part_num); + logsector = udf_rw32(icb->loc.lb_num); + KASSERT(ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT); + + sector = 0; + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + goto out; + + /* add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + if (waitfor) { + DPRINTF(WRITE, ("udf_write_nodedscr: sync write\n")); + + error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE, + dscr, sector, logsector); + } else { + DPRINTF(WRITE, ("udf_write_nodedscr: no wait, async write\n")); + + error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE, + dscr, sector, logsector, udf_wr_nodedscr_callback); + /* will be UNLOCKED in call back */ + return error; + } + + holdrele(udf_node->vnode); +out: + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_queue_buf_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + struct buf *nestbuf; + struct desc_tag *tag; + struct long_ad *node_ad_cpy; + uint64_t *lmapping, *pmapping, *lmappos, run_start; + uint32_t sectornr; + uint32_t buf_offset, rbuflen, bpos; + uint16_t vpart_num; + uint8_t *fidblk; + off_t rblk; + int sector_size = ump->discinfo.sector_size; + int len, buf_len, sector, sectors, run_length; + int blks = sector_size / DEV_BSIZE; + int what, class __diagused, queue; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + what = buf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((buf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + if (what == UDF_C_DSCR) + queue = UDF_SHED_WRITING; + if (what == UDF_C_NODE) + queue = UDF_SHED_WRITING; + } + + /* use disc sheduler */ + class = ump->discinfo.mmc_class; + KASSERT((class == MMC_CLASS_UNKN) || (class == MMC_CLASS_DISC) || + (ump->discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) || + (ump->vfs_mountp->mnt_flag & MNT_RDONLY)); + +#ifndef UDF_DEBUG + __USE(blks); +#endif + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + KASSERT(buf->b_udf_c_type == UDF_C_DSCR || + buf->b_udf_c_type == UDF_C_ABSOLUTE || + buf->b_udf_c_type == UDF_C_NODE); + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + /* UDF_SHED_SEQWRITING */ + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * + * lmapping contains lb_num relative to base partition. + */ + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + if (buf->b_udf_c_type == UDF_C_METADATA_SBM) { + if (buf->b_lblkno == 0) { + /* update the tag location inside */ + tag = (struct desc_tag *) buf->b_data; + tag->tag_loc = udf_rw32(*lmapping); + udf_validate_tag_and_crc_sums(buf->b_data); + } + } + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* + * Translate new mappings in lmapping to pmappings and try to + * conglomerate extents to reduce the number of writes. + * + * pmapping to contain lb_nums as used for disc adressing. + */ + pmapping = ump->la_pmapping; + sectors = (buf->b_bcount + sector_size -1) / sector_size; + udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping); + + for (sector = 0; sector < sectors; sector++) { + buf_offset = sector * sector_size; + DPRINTF(WRITE, ("\tprocessing rel sector %d\n", sector)); + + DPRINTF(WRITE, ("\tissue write sector %"PRIu64"\n", + pmapping[sector])); + + run_start = pmapping[sector]; + run_length = 1; + while (sector < sectors-1) { + if (pmapping[sector+1] != pmapping[sector]+1) + break; + run_length++; + sector++; + } + + /* nest an iobuf for the extent */ + rbuflen = run_length * sector_size; + rblk = run_start * (sector_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = sector; + assert(nestbuf->b_vp == buf->b_vp); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = UDF_C_PROCESSED; + + VOP_STRATEGY(ump->devvp, nestbuf); + } +} + + +static void +udf_discstrat_init_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + KASSERT(priv == NULL); + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* + * Initialise pool for descriptors associated with nodes. This is done + * in lb_size units though currently lb_size is dictated to be + * sector_size. + */ + memset(&priv->desc_pool, 0, sizeof(struct pool)); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL, + IPL_NONE); +} + + +static void +udf_discstrat_finish_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + /* destroy our pool */ + pool_destroy(&priv->desc_pool); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_direct = +{ + udf_getblank_nodedscr_direct, + udf_free_nodedscr_direct, + udf_read_nodedscr_direct, + udf_write_nodedscr_direct, + udf_queue_buf_direct, + udf_discstrat_init_direct, + udf_discstrat_finish_direct +}; + diff --git a/sys/fs/udf/udf_strat_rmw.c b/sys/fs/udf/udf_strat_rmw.c new file mode 100644 index 000000000..cf04d8197 --- /dev/null +++ b/sys/fs/udf/udf_strat_rmw.c @@ -0,0 +1,1507 @@ +/* $NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) +#define PRIV(ump) ((struct strat_private *) (ump)->strategy_private) +#define BTOE(buf) ((struct udf_eccline *) ((buf)->b_private)) + +/* --------------------------------------------------------------------- */ + +#define UDF_MAX_PACKET_SIZE 64 /* DONT change this */ + +/* sheduler states */ +#define UDF_SHED_WAITING 1 /* waiting on timeout */ +#define UDF_SHED_READING 2 +#define UDF_SHED_WRITING 3 +#define UDF_SHED_SEQWRITING 4 +#define UDF_SHED_IDLE 5 /* refcnt'd */ +#define UDF_SHED_FREE 6 /* recycleable */ +#define UDF_SHED_MAX 6+1 + +/* flags */ +#define ECC_LOCKED 0x01 /* prevent access */ +#define ECC_WANTED 0x02 /* trying access */ +#define ECC_SEQWRITING 0x04 /* sequential queue */ +#define ECC_FLOATING 0x08 /* not queued yet */ + +#define ECC_WAITTIME 10 + + +TAILQ_HEAD(ecclineq, udf_eccline); +struct udf_eccline { + struct udf_mount *ump; + uint64_t present; /* preserve these */ + uint64_t readin; /* bitmap */ + uint64_t dirty; /* bitmap */ + uint64_t error; /* bitmap */ + uint32_t refcnt; + + struct timespec wait_time; + uint32_t flags; + uint32_t start_sector; /* physical */ + + const char *fname; + int sline; + + struct buf *buf; + void *blob; + + struct buf *bufs[UDF_MAX_PACKET_SIZE]; + uint32_t bufs_bpos[UDF_MAX_PACKET_SIZE]; + int bufs_len[UDF_MAX_PACKET_SIZE]; + + int queued_on; /* on which BUFQ list */ + LIST_ENTRY(udf_eccline) hashchain; /* on sector lookup */ +}; + + +struct strat_private { + lwp_t *queue_lwp; + kcondvar_t discstrat_cv; /* to wait on */ + kmutex_t discstrat_mutex; /* disc strategy */ + kmutex_t seqwrite_mutex; /* protect mappings */ + + int thread_running; /* thread control */ + int run_thread; /* thread control */ + int thread_finished; /* thread control */ + int cur_queue; + + int num_floating; + int num_queued[UDF_SHED_MAX]; + struct bufq_state *queues[UDF_SHED_MAX]; + struct timespec last_queued[UDF_SHED_MAX]; + struct disk_strategy old_strategy_setting; + + struct pool eccline_pool; + struct pool ecclineblob_pool; + LIST_HEAD(, udf_eccline) eccline_hash[UDF_ECCBUF_HASHSIZE]; +}; + +/* --------------------------------------------------------------------- */ + +#define UDF_LOCK_ECCLINE(eccline) udf_lock_eccline(eccline, __FILE__, __LINE__) +#define UDF_UNLOCK_ECCLINE(eccline) udf_unlock_eccline(eccline, __FILE__, __LINE__) + +/* can be called with or without discstrat lock */ +static void +udf_lock_eccline(struct udf_eccline *eccline, const char *fname, int sline) +{ + struct strat_private *priv = PRIV(eccline->ump); + int waslocked, ret; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + waslocked = mutex_owned(&priv->discstrat_mutex); + if (!waslocked) + mutex_enter(&priv->discstrat_mutex); + + /* wait until its unlocked first */ + eccline->refcnt++; + while (eccline->flags & ECC_LOCKED) { + DPRINTF(ECCLINE, ("waiting for lock at %s:%d\n", + fname, sline)); + DPRINTF(ECCLINE, ("was locked at %s:%d\n", + eccline->fname, eccline->sline)); + eccline->flags |= ECC_WANTED; + ret = cv_timedwait(&priv->discstrat_cv, &priv->discstrat_mutex, + hz/8); + if (ret == EWOULDBLOCK) + DPRINTF(LOCKING, ("eccline lock helt, waiting for " + "release")); + } + eccline->flags |= ECC_LOCKED; + eccline->flags &= ~ECC_WANTED; + eccline->refcnt--; + + eccline->fname = fname; + eccline->sline = sline; + + if (!waslocked) + mutex_exit(&priv->discstrat_mutex); +} + + +/* can be called with or without discstrat lock */ +static void +udf_unlock_eccline(struct udf_eccline *eccline, const char *fname, int sline) +{ + struct strat_private *priv = PRIV(eccline->ump); + int waslocked; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + waslocked = mutex_owned(&priv->discstrat_mutex); + if (!waslocked) + mutex_enter(&priv->discstrat_mutex); + + eccline->flags &= ~ECC_LOCKED; + cv_broadcast(&priv->discstrat_cv); + + if (!waslocked) + mutex_exit(&priv->discstrat_mutex); +} + + +/* NOTE discstrat_mutex should be held! */ +static void +udf_dispose_eccline(struct udf_eccline *eccline) +{ + struct strat_private *priv = PRIV(eccline->ump); + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + DPRINTF(ECCLINE, ("dispose eccline with start sector %d, " + "present %0"PRIx64"\n", eccline->start_sector, + eccline->present)); + + KASSERT(eccline->refcnt == 0); + KASSERT(eccline->dirty == 0); + KASSERT(eccline->queued_on == 0); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->flags & ECC_LOCKED); + + LIST_REMOVE(eccline, hashchain); + priv->num_floating--; + + putiobuf(eccline->buf); + pool_put(&priv->ecclineblob_pool, eccline->blob); + pool_put(&priv->eccline_pool, eccline); +} + + +/* NOTE discstrat_mutex should be held! */ +static void +udf_push_eccline(struct udf_eccline *eccline, int newqueue) +{ + struct strat_private *priv = PRIV(eccline->ump); + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + DPRINTF(PARANOIA, ("DEBUG: buf %p pushed on queue %d\n", eccline->buf, newqueue)); + + KASSERT(eccline->queued_on == 0); + KASSERT(eccline->flags & ECC_FLOATING); + + /* set buffer block numbers to make sure its queued correctly */ + eccline->buf->b_lblkno = eccline->start_sector; + eccline->buf->b_blkno = eccline->start_sector; + eccline->buf->b_rawblkno = eccline->start_sector; + + vfs_timestamp(&priv->last_queued[newqueue]); + eccline->flags &= ~ECC_FLOATING; + priv->num_floating--; + eccline->queued_on = newqueue; + priv->num_queued[newqueue]++; + bufq_put(priv->queues[newqueue], eccline->buf); + + UDF_UNLOCK_ECCLINE(eccline); + + /* XXX tickle disc strategy statemachine */ + if (newqueue != UDF_SHED_IDLE) + cv_signal(&priv->discstrat_cv); +} + + +static struct udf_eccline * +udf_peek_eccline(struct strat_private *priv, int queued_on) +{ + struct udf_eccline *eccline; + struct buf *buf; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + for(;;) { + buf = bufq_peek(priv->queues[queued_on]); + /* could have been a race, but we'll revisit later */ + if (buf == NULL) + return NULL; + + eccline = BTOE(buf); + UDF_LOCK_ECCLINE(eccline); + + /* might have changed before we obtained the lock */ + if (eccline->queued_on == queued_on) + break; + + UDF_UNLOCK_ECCLINE(eccline); + } + + KASSERT(eccline->queued_on == queued_on); + KASSERT((eccline->flags & ECC_FLOATING) == 0); + + DPRINTF(PARANOIA, ("DEBUG: buf %p peeked at queue %d\n", + eccline->buf, queued_on)); + + return eccline; +} + + +static struct udf_eccline * +udf_pop_eccline(struct strat_private *priv, int queued_on) +{ + struct udf_eccline *eccline; + struct buf *buf; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + for(;;) { + buf = bufq_get(priv->queues[queued_on]); + if (buf == NULL) { + // KASSERT(priv->num_queued[queued_on] == 0); + return NULL; + } + + eccline = BTOE(buf); + UDF_LOCK_ECCLINE(eccline); + + /* might have changed before we obtained the lock */ + if (eccline->queued_on == queued_on) + break; + + UDF_UNLOCK_ECCLINE(eccline); + } + + KASSERT(eccline->queued_on == queued_on); + KASSERT((eccline->flags & ECC_FLOATING) == 0); + + priv->num_queued[queued_on]--; + eccline->queued_on = 0; + + eccline->flags |= ECC_FLOATING; + priv->num_floating++; + + DPRINTF(PARANOIA, ("DEBUG: buf %p popped from queue %d\n", + eccline->buf, queued_on)); + + return eccline; +} + + +static void +udf_unqueue_eccline(struct strat_private *priv, struct udf_eccline *eccline) +{ + struct buf *ret __diagused; + + UDF_LOCK_ECCLINE(eccline); + if (eccline->queued_on == 0) { + KASSERT(eccline->flags & ECC_FLOATING); + return; + } + + ret = bufq_cancel(priv->queues[eccline->queued_on], eccline->buf); + KASSERT(ret == eccline->buf); + + priv->num_queued[eccline->queued_on]--; + eccline->queued_on = 0; + + eccline->flags |= ECC_FLOATING; + priv->num_floating++; +} + + +static struct udf_eccline * +udf_geteccline(struct udf_mount *ump, uint32_t sector, int flags) +{ + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + uint32_t start_sector, lb_size, blobsize; + uint8_t *eccline_blob; + int line, line_offset; + int num_busy; + + mutex_enter(&priv->discstrat_mutex); + + /* lookup in our line cache hashtable */ + line_offset = sector % ump->packet_size; + start_sector = sector - line_offset; + line = (start_sector/ump->packet_size) & UDF_ECCBUF_HASHMASK; + + KASSERT(priv->thread_running); + +retry: + DPRINTF(ECCLINE, ("get line sector %d, line %d\n", sector, line)); + LIST_FOREACH(eccline, &priv->eccline_hash[line], hashchain) { + if (eccline->start_sector == start_sector) { + DPRINTF(ECCLINE, ("\tfound eccline, start_sector %d\n", + eccline->start_sector)); + udf_unqueue_eccline(priv, eccline); + + mutex_exit(&priv->discstrat_mutex); + return eccline; + } + } + + /* not found in eccline cache */ + DPRINTF(ECCLINE, ("\tnot found in eccline cache\n")); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blobsize = ump->packet_size * lb_size; + + /* dont allow too many pending requests */ + DPRINTF(ECCLINE, ("\tallocating new eccline\n")); + num_busy = (priv->num_queued[UDF_SHED_SEQWRITING] + priv->num_floating); + if ((flags & ECC_SEQWRITING) && (num_busy > UDF_ECCLINE_MAXBUSY)) { + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + goto retry; + } + + eccline_blob = pool_get(&priv->ecclineblob_pool, PR_NOWAIT); + eccline = pool_get(&priv->eccline_pool, PR_NOWAIT); + if ((eccline_blob == NULL) || (eccline == NULL)) { + if (eccline_blob) + pool_put(&priv->ecclineblob_pool, eccline_blob); + if (eccline) + pool_put(&priv->eccline_pool, eccline); + + /* out of memory for now; canibalise freelist */ + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + if (eccline == NULL) { + /* serious trouble; wait and retry */ + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + goto retry; + } + + /* push back line if we're waiting for it or its locked */ + if (eccline->flags & ECC_WANTED) { + /* we won a race, but someone else needed it */ + udf_push_eccline(eccline, UDF_SHED_FREE); + goto retry; + } + + /* unlink this entry */ + LIST_REMOVE(eccline, hashchain); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->queued_on == 0); + + eccline_blob = eccline->blob; + eccline->flags = ECC_FLOATING | ECC_LOCKED; + } else { + eccline->flags = ECC_FLOATING | ECC_LOCKED; + priv->num_floating++; + } + + eccline->queued_on = 0; + eccline->blob = eccline_blob; + eccline->buf = getiobuf(NULL, true); + eccline->buf->b_private = eccline; /* IMPORTANT */ + + /* initialise eccline blob */ + /* XXX memset expensive and strictly not needed XXX */ + memset(eccline->blob, 0, blobsize); + + eccline->ump = ump; + eccline->present = eccline->readin = eccline->dirty = 0; + eccline->error = 0; + eccline->refcnt = 0; + memset(eccline->bufs, 0, UDF_MAX_PACKET_SIZE * sizeof(struct buf *)); + + eccline->start_sector = start_sector; + eccline->buf->b_lblkno = start_sector; + eccline->buf->b_blkno = start_sector; + eccline->buf->b_rawblkno = start_sector; + + LIST_INSERT_HEAD(&priv->eccline_hash[line], eccline, hashchain); + + /* + * TODO possible optimalisation for checking overlap with partitions + * to get a clue on future eccline usage + */ + + KASSERT(eccline->refcnt == 0); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->flags & ECC_LOCKED); + mutex_exit(&priv->discstrat_mutex); + + return eccline; +} + + +static void +udf_puteccline(struct udf_eccline *eccline) +{ + struct strat_private *priv = PRIV(eccline->ump); + struct udf_mount *ump = eccline->ump; + uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1; + int new_queue; + + mutex_enter(&priv->discstrat_mutex); + + DPRINTF(ECCLINE, ("put eccline start sector %d, refcnt %d\n", + eccline->start_sector, eccline->refcnt)); + + KASSERT(eccline->flags & ECC_LOCKED); + KASSERT(eccline->flags & ECC_FLOATING); + + /* clear all read bits that are already read in */ + if (eccline->readin & eccline->present) + eccline->readin &= (~eccline->present) & allbits; + + /* if we have active nodes we dont set it on seqwriting */ + if (eccline->refcnt > 1) + eccline->flags &= ~ECC_SEQWRITING; + + /* select state */ + new_queue = UDF_SHED_FREE; + if (eccline->refcnt > 0) + new_queue = UDF_SHED_IDLE; + if (eccline->flags & ECC_WANTED) + new_queue = UDF_SHED_IDLE; + if (eccline->readin) + new_queue = UDF_SHED_READING; + if (eccline->dirty) { + new_queue = UDF_SHED_WAITING; + vfs_timestamp(&eccline->wait_time); + eccline->wait_time.tv_sec += ECC_WAITTIME; + + if (eccline->present == allbits) { + new_queue = UDF_SHED_WRITING; + if (eccline->flags & ECC_SEQWRITING) + new_queue = UDF_SHED_SEQWRITING; + } + } + udf_push_eccline(eccline, new_queue); + + mutex_exit(&priv->discstrat_mutex); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, lb_size, dummy; + uint8_t *mem; + int error, eccsect; + + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + eccline->readin &= ~bit; /* just in case */ + eccline->present |= bit; + eccline->dirty &= ~bit; /* Err... euhm... clean? */ + + eccline->refcnt++; + + /* clear space */ + mem = ((uint8_t *) eccline->blob) + eccsect * lb_size; + memset(mem, 0, lb_size); + + udf_puteccline(eccline); + + *dscrptr = (union dscrptr *) mem; + return 0; +} + + +static void +udf_free_nodedscr_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, dummy; + int error, eccsect; + + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return; + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + KASSERT(eccline->present & bit); + + eccline->readin &= ~bit; /* just in case */ + /* XXX eccline->dirty? */ + + KASSERT(eccline->refcnt >= 1); + eccline->refcnt--; + + udf_puteccline(eccline); +} + + +static int +udf_read_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, dummy; + uint8_t *pos; + int sector_size = ump->discinfo.sector_size; + int lb_size = udf_rw32(ump->logical_vol->lb_size); + int i, error, dscrlen, eccsect; + + lb_size = lb_size; + KASSERT(sector_size == lb_size); + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + if ((eccline->present & bit) == 0) { + /* mark bit for readin */ + eccline->readin |= bit; + eccline->refcnt++; /* prevent recycling */ + KASSERT(eccline->bufs[eccsect] == NULL); + udf_puteccline(eccline); + + /* wait for completion */ + priv = PRIV(eccline->ump); + mutex_enter(&priv->discstrat_mutex); + while (((eccline->present | eccline->error) & bit) == 0) { + error = cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, + hz/8); + if (error == EWOULDBLOCK) + DPRINTF(LOCKING, ("eccline waiting for read\n")); + } + mutex_exit(&priv->discstrat_mutex); + + /* reget our line */ + eccline = udf_geteccline(ump, sectornr, 0); + KASSERT(eccline->refcnt >= 1); + eccline->refcnt--; /* undo refcnt */ + + if (eccline->error & bit) { + *dscrptr = NULL; + udf_puteccline(eccline); + return EIO; /* XXX error code */ + } + } + + *dscrptr = (union dscrptr *) + (((uint8_t *) eccline->blob) + eccsect * sector_size); + + /* code from read_phys_descr */ + /* check if its a valid tag */ + error = udf_check_tag(*dscrptr); + if (error) { + /* check if its an empty block */ + pos = (uint8_t *) *dscrptr; + for (i = 0; i < sector_size; i++, pos++) { + if (*pos) break; + } + if (i == sector_size) { + /* return no error but with no dscrptr */ + error = 0; + } + *dscrptr = NULL; + udf_puteccline(eccline); + return error; + } + + /* calculate descriptor size */ + dscrlen = udf_tagsize(*dscrptr, sector_size); + error = udf_check_tag_payload(*dscrptr, dscrlen); + if (error) { + *dscrptr = NULL; + udf_puteccline(eccline); + return error; + } + + /* we have a hold since it has a node descriptor */ + eccline->refcnt++; + udf_puteccline(eccline); + + return 0; +} + + +static int +udf_write_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr *dscrptr = args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_node *udf_node = args->udf_node; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, logsectornr, dummy; + // int waitfor = args->waitfor; + int sector_size = ump->discinfo.sector_size; + int lb_size = udf_rw32(ump->logical_vol->lb_size); + int error, eccsect; + + lb_size = lb_size; + KASSERT(sector_size == lb_size); + sectornr = 0; + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + /* paranoia: add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + + /* old callback still pending? */ + if (eccline->bufs[eccsect]) { + DPRINTF(WRITE, ("udf_write_nodedscr_rmw: writing descriptor" + " over buffer?\n")); + nestiobuf_done(eccline->bufs[eccsect], + eccline->bufs_len[eccsect], + 0); + eccline->bufs[eccsect] = NULL; + } + + /* set sector number in the descriptor and validate */ + dscrptr = (union dscrptr *) + (((uint8_t *) eccline->blob) + eccsect * sector_size); + KASSERT(dscrptr == args->dscr); + + logsectornr = udf_rw32(icb->loc.lb_num); + dscrptr->tag.tag_loc = udf_rw32(logsectornr); + udf_validate_tag_and_crc_sums(dscrptr); + + udf_fixup_node_internals(ump, (uint8_t *) dscrptr, UDF_C_NODE); + + /* set our flags */ + KASSERT(eccline->present & bit); + eccline->dirty |= bit; + + KASSERT(udf_tagsize(dscrptr, sector_size) <= sector_size); + + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* XXX still using wakeup! */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + holdrele(udf_node->vnode); + udf_puteccline(eccline); + + /* XXX waitfor not used */ + return 0; +} + + +static void +udf_queuebuf_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + struct desc_tag *tag; + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + struct long_ad *node_ad_cpy; + uint64_t bit, *lmapping, *pmapping, *lmappos, *pmappos, blknr; + uint32_t buf_len, len, sectors, sectornr, our_sectornr; + uint32_t bpos; + uint16_t vpart_num; + uint8_t *fidblk, *src, *dst; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int eccsect, what, queue, error; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + blknr = buf->b_blkno; + our_sectornr = blknr / blks; + + what = buf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((buf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + if (what == UDF_C_DSCR) + queue = UDF_SHED_WRITING; + if (what == UDF_C_NODE) + queue = UDF_SHED_WRITING; + } + + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + + /* mark bits for reading */ + buf_len = buf->b_bcount; + sectornr = our_sectornr; + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + error = eccline->error & bit ? EIO : 0; + if (eccline->present & bit) { + src = (uint8_t *) eccline->blob + + eccsect * sector_size; + dst = (uint8_t *) buf->b_data + bpos; + if (!error) + memcpy(dst, src, len); + nestiobuf_done(buf, len, error); + } else { + eccline->readin |= bit; + KASSERT(eccline->bufs[eccsect] == NULL); + eccline->bufs[eccsect] = buf; + eccline->bufs_bpos[eccsect] = bpos; + eccline->bufs_len[eccsect] = len; + } + bpos += sector_size; + eccsect++; + sectornr++; + buf_len -= len; + } + udf_puteccline(eccline); + return; + } + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + + /* if we have FIDs fixup using buffer's sector number(s) */ + if (buf->b_udf_c_type == UDF_C_FIDS) + panic("UDF_C_FIDS in SHED_WRITING!\n"); + + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* copy parts into the bufs and set for writing */ + buf_len = buf->b_bcount; + sectornr = our_sectornr; + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + KASSERT((eccline->readin & bit) == 0); + eccline->present |= bit; + eccline->dirty |= bit; + if (eccline->bufs[eccsect]) { + /* old callback still pending */ + nestiobuf_done(eccline->bufs[eccsect], + eccline->bufs_len[eccsect], + 0); + eccline->bufs[eccsect] = NULL; + } + + src = (uint8_t *) buf->b_data + bpos; + dst = (uint8_t *) eccline->blob + eccsect * sector_size; + if (len != sector_size) + memset(dst, 0, sector_size); + memcpy(dst, src, len); + + /* note that its finished for this extent */ + eccline->bufs[eccsect] = NULL; + nestiobuf_done(buf, len, 0); + + bpos += sector_size; + eccsect++; + sectornr++; + buf_len -= len; + } + udf_puteccline(eccline); + return; + + } + + /* sequential writing */ + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * Note that it *looks* like the normal writing but its different in + * the details. + * + * lmapping contains lb_num relative to base partition. + * + * XXX should we try to claim/organize the allocated memory to + * block-aligned pieces? + */ + mutex_enter(&priv->seqwrite_mutex); + + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + if (buf->b_udf_c_type == UDF_C_METADATA_SBM) { + if (buf->b_lblkno == 0) { + /* update the tag location inside */ + tag = (struct desc_tag *) buf->b_data; + tag->tag_loc = udf_rw32(*lmapping); + udf_validate_tag_and_crc_sums(buf->b_data); + } + } + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* + * Translate new mappings in lmapping to pmappings. + * pmapping to contain lb_nums as used for disc adressing. + */ + pmapping = ump->la_pmapping; + sectors = (buf->b_bcount + sector_size -1) / sector_size; + udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping); + + /* copy parts into the bufs and set for writing */ + pmappos = pmapping; + buf_len = buf->b_bcount; + sectornr = *pmappos++; + eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + eccsect = sectornr - eccline->start_sector; + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + eccline->flags |= ECC_SEQWRITING; + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + KASSERT((eccline->readin & bit) == 0); + eccline->present |= bit; + eccline->dirty |= bit; + eccline->bufs[eccsect] = NULL; + + src = (uint8_t *) buf->b_data + bpos; + dst = (uint8_t *) + eccline->blob + eccsect * sector_size; + if (len != sector_size) + memset(dst, 0, sector_size); + memcpy(dst, src, len); + + /* note that its finished for this extent */ + nestiobuf_done(buf, len, 0); + + bpos += sector_size; + sectornr = *pmappos++; + buf_len -= len; + } + eccline->flags |= ECC_SEQWRITING; + udf_puteccline(eccline); + mutex_exit(&priv->seqwrite_mutex); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_shedule_read_callback(struct buf *buf) +{ + struct udf_eccline *eccline = BTOE(buf); + struct udf_mount *ump = eccline->ump; + uint64_t bit; + uint8_t *src, *dst; + int sector_size = ump->discinfo.sector_size; + int error, i, len; + + DPRINTF(ECCLINE, ("read callback called on buf %p\n", buf)); + + /* post process read action */ + KASSERT(eccline->flags & ECC_LOCKED); + error = buf->b_error; + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + src = (uint8_t *) buf->b_data + i * sector_size; + dst = (uint8_t *) eccline->blob + i * sector_size; + if (eccline->present & bit) + continue; + eccline->present |= bit; + if (error) + eccline->error |= bit; + if (eccline->bufs[i]) { + dst = (uint8_t *) eccline->bufs[i]->b_data + + eccline->bufs_bpos[i]; + len = eccline->bufs_len[i]; + if (!error) + memcpy(dst, src, len); + nestiobuf_done(eccline->bufs[i], len, error); + eccline->bufs[i] = NULL; + } + + } + KASSERT(buf->b_data == eccline->blob); + KASSERT(eccline->present == ((uint64_t) 1 << ump->packet_size)-1); + + /* + * XXX TODO what to do on read errors? read in all sectors + * synchronously and allocate a sparable entry? + */ + + udf_puteccline(eccline); + DPRINTF(ECCLINE, ("read callback finished\n")); +} + + +static void +udf_shedule_write_callback(struct buf *buf) +{ + struct udf_eccline *eccline = BTOE(buf); + struct udf_mount *ump = eccline->ump; + uint64_t bit; + int error, i; + + DPRINTF(ECCLINE, ("write callback called on buf %p\n", buf)); + + /* post process write action */ + KASSERT(eccline->flags & ECC_LOCKED); + error = buf->b_error; + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + if ((eccline->dirty & bit) == 0) + continue; + if (error) { + eccline->error |= bit; + } else { + eccline->dirty &= ~bit; + } + + KASSERT(eccline->bufs[i] == 0); + } + KASSERT(eccline->dirty == 0); + KASSERT(error == 0); + + /* + * XXX TODO on write errors allocate a sparable entry and reissue + */ + + udf_puteccline(eccline); + DPRINTF(ECCLINE, ("write callback finished\n")); +} + + +static void +udf_issue_eccline(struct udf_eccline *eccline, int queued_on) +{ + struct udf_mount *ump = eccline->ump; + struct strat_private *priv = PRIV(ump); + struct buf *buf, *nestbuf; + uint64_t bit, allbits = ((uint64_t) 1 << ump->packet_size)-1; + uint32_t start; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int i; + + KASSERT(eccline->flags & ECC_LOCKED); + + if (queued_on == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("udf_issue_eccline reading : ")); + /* read all bits that are not yet present */ + eccline->readin = (~eccline->present) & allbits; + KASSERT(eccline->readin); + start = eccline->start_sector; + buf = eccline->buf; + buf->b_flags = B_READ | B_ASYNC; + SET(buf->b_cflags, BC_BUSY); /* mark buffer busy */ + buf->b_oflags = 0; + buf->b_iodone = udf_shedule_read_callback; + buf->b_data = eccline->blob; + buf->b_bcount = ump->packet_size * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = eccline; + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + if (eccline->present != 0) { + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + if (eccline->present & bit) { + nestiobuf_done(buf, sector_size, 0); + continue; + } + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, i * sector_size, + sector_size); + /* adjust blocknumber to read */ + nestbuf->b_blkno = buf->b_blkno + i*blks; + nestbuf->b_rawblkno = buf->b_rawblkno + i*blks; + + DPRINTF(SHEDULE, ("sector %d ", start + i)); + + /* mutex dance since it could lock */ + mutex_exit(&priv->discstrat_mutex); + /* call asynchronous */ + VOP_STRATEGY(ump->devvp, nestbuf); + mutex_enter(&priv->discstrat_mutex); + } + DPRINTF(SHEDULE, ("\n")); + return; + } + } else { + /* write or seqwrite */ + DPRINTF(SHEDULE, ("udf_issue_eccline writing or seqwriting : ")); + DPRINTF(SHEDULE, ("\n\tpresent %"PRIx64", readin %"PRIx64", " + "dirty %"PRIx64"\n\t", eccline->present, eccline->readin, + eccline->dirty)); + KASSERT(eccline->present == allbits); + + start = eccline->start_sector; + buf = eccline->buf; + buf->b_flags = B_WRITE | B_ASYNC; + SET(buf->b_cflags, BC_BUSY); /* mark buffer busy */ + buf->b_oflags = 0; + buf->b_iodone = udf_shedule_write_callback; + buf->b_data = eccline->blob; + buf->b_bcount = ump->packet_size * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = eccline; + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + } + + /* mutex dance since it could lock */ + mutex_exit(&priv->discstrat_mutex); + /* call asynchronous */ + DPRINTF(SHEDULE, ("sector %d for %d\n", + start, ump->packet_size)); + VOP_STRATEGY(ump->devvp, buf); + mutex_enter(&priv->discstrat_mutex); +} + + +static void +udf_discstrat_thread(void *arg) +{ + struct udf_mount *ump = (struct udf_mount *) arg; + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + struct timespec now, *last; + uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1; + int new_queue, wait, work; + + work = 1; + priv->thread_running = 1; + mutex_enter(&priv->discstrat_mutex); + priv->num_floating = 0; + while (priv->run_thread || work || priv->num_floating) { + /* get our time */ + vfs_timestamp(&now); + + /* maintenance: handle eccline state machine */ + for(;;) { + /* only peek at it */ + eccline = udf_peek_eccline(priv, UDF_SHED_WAITING); + if (eccline == NULL) + break; + + /* if not reading, wait until the time has come */ + if ((priv->cur_queue != UDF_SHED_READING) && + (eccline->wait_time.tv_sec - now.tv_sec > 0)) { + UDF_UNLOCK_ECCLINE(eccline); + /* all others are later, so break off */ + break; + } + + /* release */ + UDF_UNLOCK_ECCLINE(eccline); + + /* do get it */ + eccline = udf_pop_eccline(priv, UDF_SHED_WAITING); + + /* requeue according to state */ + new_queue = UDF_SHED_FREE; /* unlikely */ + if (eccline->refcnt > 0) + new_queue = UDF_SHED_IDLE; + if (eccline->flags & ECC_WANTED) + new_queue = UDF_SHED_IDLE; + if (eccline->readin) + new_queue = UDF_SHED_READING; + if (eccline->dirty) { + new_queue = UDF_SHED_READING; + if (eccline->present == allbits) { + new_queue = UDF_SHED_WRITING; + if (eccline->flags & ECC_SEQWRITING) + new_queue = UDF_SHED_SEQWRITING; + } + } + udf_push_eccline(eccline, new_queue); + } + + /* maintenance: free excess ecclines */ + while (priv->num_queued[UDF_SHED_FREE] > UDF_ECCLINE_MAXFREE) { + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + KASSERT(eccline); + KASSERT(eccline->refcnt == 0); + if (eccline->flags & ECC_WANTED) { + /* we won the race, but we dont want to win */ + DPRINTF(ECCLINE, ("Tried removing, pushed back to free list\n")); + udf_push_eccline(eccline, UDF_SHED_IDLE); + } else { + DPRINTF(ECCLINE, ("Removing entry from free list\n")); + udf_dispose_eccline(eccline); + } + } + + /* process the current selected queue */ + /* get our time */ + vfs_timestamp(&now); + last = &priv->last_queued[priv->cur_queue]; + + /* get our line */ + eccline = udf_pop_eccline(priv, priv->cur_queue); + if (eccline) { + wait = 0; + new_queue = priv->cur_queue; + DPRINTF(ECCLINE, ("UDF_ISSUE_ECCLINE\n")); + + udf_issue_eccline(eccline, priv->cur_queue); + } else { + /* don't switch too quickly */ + if (now.tv_sec - last->tv_sec < 2) { + /* wait some time */ + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz); + /* we assume there is work to be done */ + work = 1; + continue; + } + + /* XXX select on queue lengths ? */ + wait = 1; + /* check if we can/should switch */ + new_queue = priv->cur_queue; + if (bufq_peek(priv->queues[UDF_SHED_READING])) + new_queue = UDF_SHED_READING; + if (bufq_peek(priv->queues[UDF_SHED_WRITING])) + new_queue = UDF_SHED_WRITING; + if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING])) + new_queue = UDF_SHED_SEQWRITING; + } + + /* give room */ + mutex_exit(&priv->discstrat_mutex); + + if (new_queue != priv->cur_queue) { + wait = 0; + DPRINTF(SHEDULE, ("switching from %d to %d\n", + priv->cur_queue, new_queue)); + priv->cur_queue = new_queue; + } + mutex_enter(&priv->discstrat_mutex); + + /* wait for more if needed */ + if (wait) + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/4); /* /8 */ + + work = (bufq_peek(priv->queues[UDF_SHED_WAITING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL); + + DPRINTF(PARANOIA, ("work : (%d, %d, %d) -> work %d, float %d\n", + (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL), + (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL), + (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL), + work, priv->num_floating)); + } + + mutex_exit(&priv->discstrat_mutex); + + /* tear down remaining ecclines */ + mutex_enter(&priv->discstrat_mutex); + KASSERT(bufq_peek(priv->queues[UDF_SHED_WAITING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_IDLE]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_READING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL); + + KASSERT(priv->num_queued[UDF_SHED_WAITING] == 0); + KASSERT(priv->num_queued[UDF_SHED_IDLE] == 0); + KASSERT(priv->num_queued[UDF_SHED_READING] == 0); + KASSERT(priv->num_queued[UDF_SHED_WRITING] == 0); + KASSERT(priv->num_queued[UDF_SHED_SEQWRITING] == 0); + + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + while (eccline) { + udf_dispose_eccline(eccline); + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + } + KASSERT(priv->num_queued[UDF_SHED_FREE] == 0); + mutex_exit(&priv->discstrat_mutex); + + priv->thread_running = 0; + priv->thread_finished = 1; + wakeup(&priv->run_thread); + kthread_exit(0); + /* not reached */ +} + +/* --------------------------------------------------------------------- */ + +/* + * Buffer memory pool allocator. + */ + +static void * +ecclinepool_page_alloc(struct pool *pp, int flags) +{ + return (void *)uvm_km_alloc(kernel_map, + MAXBSIZE, MAXBSIZE, + ((flags & PR_WAITOK) ? 0 : UVM_KMF_NOWAIT | UVM_KMF_TRYLOCK) + | UVM_KMF_WIRED /* UVM_KMF_PAGABLE? */); +} + +static void +ecclinepool_page_free(struct pool *pp, void *v) +{ + uvm_km_free(kernel_map, (vaddr_t)v, MAXBSIZE, UVM_KMF_WIRED); +} + +static struct pool_allocator ecclinepool_allocator = { + .pa_alloc = ecclinepool_page_alloc, + .pa_free = ecclinepool_page_free, + .pa_pagesz = MAXBSIZE, +}; + + +static void +udf_discstrat_init_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size, blobsize, hashline; + int i; + + KASSERT(ump); + KASSERT(ump->logical_vol); + KASSERT(priv == NULL); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blobsize = ump->packet_size * lb_size; + KASSERT(lb_size > 0); + KASSERT(ump->packet_size <= 64); + + /* initialise our memory space */ + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* initialise locks */ + cv_init(&priv->discstrat_cv, "udfstrat"); + mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&priv->seqwrite_mutex, MUTEX_DEFAULT, IPL_NONE); + + /* initialise struct eccline pool */ + pool_init(&priv->eccline_pool, sizeof(struct udf_eccline), + 0, 0, 0, "udf_eccline_pool", NULL, IPL_NONE); + + /* initialise eccline blob pool */ + ecclinepool_allocator.pa_pagesz = blobsize; + pool_init(&priv->ecclineblob_pool, blobsize, + 0, 0, 0, "udf_eccline_blob", &ecclinepool_allocator, IPL_NONE); + + /* initialise main queues */ + for (i = 0; i < UDF_SHED_MAX; i++) { + priv->num_queued[i] = 0; + vfs_timestamp(&priv->last_queued[i]); + } + bufq_alloc(&priv->queues[UDF_SHED_WAITING], "fcfs", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "disksort", 0); + + /* initialise administrative queues */ + bufq_alloc(&priv->queues[UDF_SHED_IDLE], "fcfs", 0); + bufq_alloc(&priv->queues[UDF_SHED_FREE], "fcfs", 0); + + for (hashline = 0; hashline < UDF_ECCBUF_HASHSIZE; hashline++) { + LIST_INIT(&priv->eccline_hash[hashline]); + } + + /* create our disk strategy thread */ + priv->cur_queue = UDF_SHED_READING; + priv->thread_finished = 0; + priv->thread_running = 0; + priv->run_thread = 1; + if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/, + udf_discstrat_thread, ump, &priv->queue_lwp, + "%s", "udf_rw")) { + panic("fork udf_rw"); + } + + /* wait for thread to spin up */ + while (!priv->thread_running) { + tsleep(&priv->thread_running, PRIBIO+1, "udfshedstart", hz); + } +} + + +static void +udf_discstrat_finish_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + if (ump == NULL) + return; + + /* stop our sheduling thread */ + KASSERT(priv->run_thread == 1); + priv->run_thread = 0; + wakeup(priv->queue_lwp); + while (!priv->thread_finished) { + tsleep(&priv->run_thread, PRIBIO + 1, "udfshedfin", hz); + } + /* kthread should be finished now */ + + /* cleanup our pools */ + pool_destroy(&priv->eccline_pool); + pool_destroy(&priv->ecclineblob_pool); + + cv_destroy(&priv->discstrat_cv); + mutex_destroy(&priv->discstrat_mutex); + mutex_destroy(&priv->seqwrite_mutex); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_rmw = +{ + udf_create_nodedscr_rmw, + udf_free_nodedscr_rmw, + udf_read_nodedscr_rmw, + udf_write_nodedscr_rmw, + udf_queuebuf_rmw, + udf_discstrat_init_rmw, + udf_discstrat_finish_rmw +}; + diff --git a/sys/fs/udf/udf_strat_sequential.c b/sys/fs/udf/udf_strat_sequential.c new file mode 100644 index 000000000..0600b6a48 --- /dev/null +++ b/sys/fs/udf/udf_strat_sequential.c @@ -0,0 +1,687 @@ +/* $NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +/* BUFQ's */ +#define UDF_SHED_MAX 3 + +#define UDF_SHED_READING 0 +#define UDF_SHED_WRITING 1 +#define UDF_SHED_SEQWRITING 2 + +struct strat_private { + struct pool desc_pool; /* node descriptors */ + + lwp_t *queue_lwp; + kcondvar_t discstrat_cv; /* to wait on */ + kmutex_t discstrat_mutex; /* disc strategy */ + + int run_thread; /* thread control */ + int cur_queue; + + struct disk_strategy old_strategy_setting; + struct bufq_state *queues[UDF_SHED_MAX]; + struct timespec last_queued[UDF_SHED_MAX]; +}; + + +/* --------------------------------------------------------------------- */ + +static void +udf_wr_nodedscr_callback(struct buf *buf) +{ + struct udf_node *udf_node; + + KASSERT(buf); + KASSERT(buf->b_data); + + /* called when write action is done */ + DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n")); + + udf_node = VTOI(buf->b_vp); + if (udf_node == NULL) { + putiobuf(buf); + printf("udf_wr_node_callback: NULL node?\n"); + return; + } + + /* XXX right flags to mark dirty again on error? */ + if (buf->b_error) { + udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED; + /* XXX TODO reshedule on error */ + } + + /* decrement outstanding_nodedscr */ + KASSERT(udf_node->outstanding_nodedscr >= 1); + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* first unlock the node */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + /* unreference the vnode so it can be recycled */ + holdrele(udf_node->vnode); + + putiobuf(buf); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memset(*dscrptr, 0, lb_size); + + return 0; +} + + +static void +udf_free_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + pool_put(&priv->desc_pool, dscr); +} + + +static int +udf_read_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + union dscrptr *tmpdscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + uint32_t sector, dummy; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + return error; + + /* try to read in fe/efe */ + error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr); + if (error) + return error; + + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memcpy(*dscrptr, tmpdscr, lb_size); + free(tmpdscr, M_UDFTEMP); + + return 0; +} + + +static int +udf_write_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct udf_node *udf_node = args->udf_node; + struct long_ad *icb = args->icb; + int waitfor = args->waitfor; + uint32_t logsectornr, sectornr, dummy; + int error, vpart; + + /* + * we have to decide if we write it out sequential or at its fixed + * position by examining the partition its (to be) written on. + */ + vpart = udf_rw16(udf_node->loc.loc.part_num); + logsectornr = udf_rw32(icb->loc.lb_num); + sectornr = 0; + if (ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT) { + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + goto out; + } + + /* add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + if (waitfor) { + DPRINTF(WRITE, ("udf_write_logvol_dscr: sync write\n")); + + error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE, + dscr, sectornr, logsectornr); + } else { + DPRINTF(WRITE, ("udf_write_logvol_dscr: no wait, async write\n")); + + error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE, + dscr, sectornr, logsectornr, udf_wr_nodedscr_callback); + /* will be UNLOCKED in call back */ + return error; + } + + holdrele(udf_node->vnode); +out: + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Main file-system specific sheduler. Due to the nature of optical media + * sheduling can't be performed in the traditional way. Most OS + * implementations i've seen thus read or write a file atomically giving all + * kinds of side effects. + * + * This implementation uses a kernel thread to shedule the queued requests in + * such a way that is semi-optimal for optical media; this means aproximately + * (R*|(Wr*|Ws*))* since switching between reading and writing is expensive in + * time. + */ + +static void +udf_queuebuf_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *nestbuf = args->nestbuf; + struct strat_private *priv = PRIV(ump); + int queue; + int what; + + KASSERT(ump); + KASSERT(nestbuf); + KASSERT(nestbuf->b_iodone == nestiobuf_iodone); + + what = nestbuf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((nestbuf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + } + + /* use our own sheduler lists for more complex sheduling */ + mutex_enter(&priv->discstrat_mutex); + bufq_put(priv->queues[queue], nestbuf); + vfs_timestamp(&priv->last_queued[queue]); + mutex_exit(&priv->discstrat_mutex); + + /* signal our thread that there might be something to do */ + cv_signal(&priv->discstrat_cv); +} + +/* --------------------------------------------------------------------- */ + +/* TODO convert to lb_size */ +static void +udf_VAT_mapping_update(struct udf_mount *ump, struct buf *buf, uint32_t lb_map) +{ + union dscrptr *fdscr = (union dscrptr *) buf->b_data; + struct vnode *vp = buf->b_vp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_num; + uint32_t udf_rw32_lbmap; + int c_type = buf->b_udf_c_type; + int error; + + /* only interested when we're using a VAT */ + KASSERT(ump->vat_node); + KASSERT(ump->vtop_alloc[ump->node_part] == UDF_ALLOC_VAT); + + /* only nodes are recorded in the VAT */ + /* NOTE: and the fileset descriptor (FIXME ?) */ + if (c_type != UDF_C_NODE) + return; + + udf_rw32_lbmap = udf_rw32(lb_map); + + /* if we're the VAT itself, only update our assigned sector number */ + if (udf_node == ump->vat_node) { + fdscr->tag.tag_loc = udf_rw32_lbmap; + udf_validate_tag_sum(fdscr); + DPRINTF(TRANSLATE, ("VAT assigned to sector %u\n", + udf_rw32(udf_rw32_lbmap))); + /* no use mapping the VAT node in the VAT */ + return; + } + + /* record new position in VAT file */ + lb_num = udf_rw32(fdscr->tag.tag_loc); + + /* lb_num = udf_rw32(udf_node->write_loc.loc.lb_num); */ + + DPRINTF(TRANSLATE, ("VAT entry change (log %u -> phys %u)\n", + lb_num, lb_map)); + + /* VAT should be the longer than this write, can't go wrong */ + KASSERT(lb_num <= ump->vat_entries); + + mutex_enter(&ump->allocate_mutex); + error = udf_vat_write(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + mutex_exit(&ump->allocate_mutex); + + if (error) + panic( "udf_VAT_mapping_update: HELP! i couldn't " + "write in the VAT file ?\n"); +} + + +static void +udf_issue_buf(struct udf_mount *ump, int queue, struct buf *buf) +{ + union dscrptr *dscr; + struct long_ad *node_ad_cpy; + struct part_desc *pdesc; + uint64_t *lmapping, *lmappos; + uint32_t sectornr, bpos; + uint32_t ptov; + uint16_t vpart_num; + uint8_t *fidblk; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int len, buf_len; + + /* if reading, just pass to the device's STRATEGY */ + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + KASSERT(buf->b_udf_c_type == UDF_C_ABSOLUTE); + + // udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * + * lmapping contains lb_num relative to base partition. + */ + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* + * NOTE We are using the knowledge here that sequential media will + * always be mapped linearly. Thus no use to explicitly translate the + * lmapping list. + */ + + /* calculate offset from physical base partition */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + ptov = udf_rw32(pdesc->start_loc); + + /* set buffers blkno to the physical block number */ + buf->b_blkno = (*lmapping + ptov) * blks; + + /* fixate floating descriptors */ + if (buf->b_udf_c_type == UDF_C_FLOAT_DSCR) { + /* set our tag location to the absolute position */ + dscr = (union dscrptr *) buf->b_data; + dscr->tag.tag_loc = udf_rw32(*lmapping + ptov); + udf_validate_tag_and_crc_sums(dscr); + } + + /* update mapping in the VAT */ + if (buf->b_udf_c_type == UDF_C_NODE) { + udf_VAT_mapping_update(ump, buf, *lmapping); + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + } + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + + VOP_STRATEGY(ump->devvp, buf); +} + + +static void +udf_doshedule(struct udf_mount *ump) +{ + struct buf *buf; + struct timespec now, *last; + struct strat_private *priv = PRIV(ump); + void (*b_callback)(struct buf *); + int new_queue; + int error; + + buf = bufq_get(priv->queues[priv->cur_queue]); + if (buf) { + /* transfer from the current queue to the device queue */ + mutex_exit(&priv->discstrat_mutex); + + /* transform buffer to synchronous; XXX needed? */ + b_callback = buf->b_iodone; + buf->b_iodone = NULL; + CLR(buf->b_flags, B_ASYNC); + + /* issue and wait on completion */ + udf_issue_buf(ump, priv->cur_queue, buf); + biowait(buf); + + mutex_enter(&priv->discstrat_mutex); + + /* if there is an error, repair this error, otherwise propagate */ + if (buf->b_error && ((buf->b_flags & B_READ) == 0)) { + /* check what we need to do */ + panic("UDF write error, can't handle yet!\n"); + } + + /* propagate result to higher layers */ + if (b_callback) { + buf->b_iodone = b_callback; + (*buf->b_iodone)(buf); + } + + return; + } + + /* Check if we're idling in this state */ + vfs_timestamp(&now); + last = &priv->last_queued[priv->cur_queue]; + if (ump->discinfo.mmc_class == MMC_CLASS_CD) { + /* dont switch too fast for CD media; its expensive in time */ + if (now.tv_sec - last->tv_sec < 3) + return; + } + + /* check if we can/should switch */ + new_queue = priv->cur_queue; + + if (bufq_peek(priv->queues[UDF_SHED_READING])) + new_queue = UDF_SHED_READING; + if (bufq_peek(priv->queues[UDF_SHED_WRITING])) /* only for unmount */ + new_queue = UDF_SHED_WRITING; + if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING])) + new_queue = UDF_SHED_SEQWRITING; + if (priv->cur_queue == UDF_SHED_READING) { + if (new_queue == UDF_SHED_SEQWRITING) { + /* TODO use flag to signal if this is needed */ + mutex_exit(&priv->discstrat_mutex); + + /* update trackinfo for data and metadata */ + error = udf_update_trackinfo(ump, + &ump->data_track); + assert(error == 0); + error = udf_update_trackinfo(ump, + &ump->metadata_track); + assert(error == 0); + mutex_enter(&priv->discstrat_mutex); + } + } + + if (new_queue != priv->cur_queue) { + DPRINTF(SHEDULE, ("switching from %d to %d\n", + priv->cur_queue, new_queue)); + } + + priv->cur_queue = new_queue; +} + + +static void +udf_discstrat_thread(void *arg) +{ + struct udf_mount *ump = (struct udf_mount *) arg; + struct strat_private *priv = PRIV(ump); + int empty; + + empty = 1; + mutex_enter(&priv->discstrat_mutex); + while (priv->run_thread || !empty) { + /* process the current selected queue */ + udf_doshedule(ump); + empty = (bufq_peek(priv->queues[UDF_SHED_READING]) == NULL); + empty &= (bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL); + empty &= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL); + + /* wait for more if needed */ + if (empty) + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + } + mutex_exit(&priv->discstrat_mutex); + + wakeup(&priv->run_thread); + kthread_exit(0); + /* not reached */ +} + +/* --------------------------------------------------------------------- */ + +static void +udf_discstrat_init_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + struct disk_strategy dkstrat; + uint32_t lb_size; + + KASSERT(ump); + KASSERT(ump->logical_vol); + KASSERT(priv == NULL); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + KASSERT(lb_size > 0); + + /* initialise our memory space */ + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* initialise locks */ + cv_init(&priv->discstrat_cv, "udfstrat"); + mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE); + + /* + * Initialise pool for descriptors associated with nodes. This is done + * in lb_size units though currently lb_size is dictated to be + * sector_size. + */ + pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL, + IPL_NONE); + + /* + * remember old device strategy method and explicit set method + * `discsort' since we have our own more complex strategy that is not + * implementable on the CD device and other strategies will get in the + * way. + */ + memset(&priv->old_strategy_setting, 0, + sizeof(struct disk_strategy)); + VOP_IOCTL(ump->devvp, DIOCGSTRATEGY, &priv->old_strategy_setting, + FREAD | FKIOCTL, NOCRED); + memset(&dkstrat, 0, sizeof(struct disk_strategy)); + strcpy(dkstrat.dks_name, "discsort"); + VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &dkstrat, FWRITE | FKIOCTL, + NOCRED); + + /* initialise our internal sheduler */ + priv->cur_queue = UDF_SHED_READING; + bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "fcfs", 0); + vfs_timestamp(&priv->last_queued[UDF_SHED_READING]); + vfs_timestamp(&priv->last_queued[UDF_SHED_WRITING]); + vfs_timestamp(&priv->last_queued[UDF_SHED_SEQWRITING]); + + /* create our disk strategy thread */ + priv->run_thread = 1; + if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/, + udf_discstrat_thread, ump, &priv->queue_lwp, + "%s", "udf_rw")) { + panic("fork udf_rw"); + } +} + + +static void +udf_discstrat_finish_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + int error; + + if (ump == NULL) + return; + + /* stop our sheduling thread */ + KASSERT(priv->run_thread == 1); + priv->run_thread = 0; + wakeup(priv->queue_lwp); + do { + error = tsleep(&priv->run_thread, PRIBIO+1, + "udfshedfin", hz); + } while (error); + /* kthread should be finished now */ + + /* set back old device strategy method */ + VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &priv->old_strategy_setting, + FWRITE, NOCRED); + + /* destroy our pool */ + pool_destroy(&priv->desc_pool); + + mutex_destroy(&priv->discstrat_mutex); + cv_destroy(&priv->discstrat_cv); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_sequential = +{ + udf_create_logvol_dscr_seq, + udf_free_logvol_dscr_seq, + udf_read_logvol_dscr_seq, + udf_write_logvol_dscr_seq, + udf_queuebuf_seq, + udf_discstrat_init_seq, + udf_discstrat_finish_seq +}; + + diff --git a/sys/fs/udf/udf_subr.c b/sys/fs/udf/udf_subr.c new file mode 100644 index 000000000..83254af45 --- /dev/null +++ b/sys/fs/udf/udf_subr.c @@ -0,0 +1,6817 @@ +/* $NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + +#define UDF_SET_SYSTEMFILE(vp) \ + /* XXXAD Is the vnode locked? */ \ + (vp)->v_vflag |= VV_SYSTEM; \ + vref((vp)); \ + vput((vp)); \ + +extern int syncer_maxdelay; /* maximum delay time */ +extern int (**udf_vnodeop_p)(void *); + +/* --------------------------------------------------------------------- */ + +//#ifdef DEBUG +#if 1 + +#if 0 +static void +udf_dumpblob(boid *blob, uint32_t dlen) +{ + int i, j; + + printf("blob = %p\n", blob); + printf("dump of %d bytes\n", dlen); + + for (i = 0; i < dlen; i+ = 16) { + printf("%04x ", i); + for (j = 0; j < 16; j++) { + if (i+j < dlen) { + printf("%02x ", blob[i+j]); + } else { + printf(" "); + } + } + for (j = 0; j < 16; j++) { + if (i+j < dlen) { + if (blob[i+j]>32 && blob[i+j]! = 127) { + printf("%c", blob[i+j]); + } else { + printf("."); + } + } + } + printf("\n"); + } + printf("\n"); + Debugger(); +} +#endif + +static void +udf_dump_discinfo(struct udf_mount *ump) +{ + char bits[128]; + struct mmc_discinfo *di = &ump->discinfo; + + if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0) + return; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); +} + +static void +udf_dump_trackinfo(struct mmc_trackinfo *trackinfo) +{ + char bits[128]; + + if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0) + return; + + printf("Trackinfo for track %d:\n", trackinfo->tracknr); + printf("\tsessionnr %d\n", trackinfo->sessionnr); + printf("\ttrack mode %d\n", trackinfo->track_mode); + printf("\tdata mode %d\n", trackinfo->data_mode); + snprintb(bits, sizeof(bits), MMC_TRACKINFO_FLAGBITS, trackinfo->flags); + printf("\tflags %s\n", bits); + + printf("\ttrack start %d\n", trackinfo->track_start); + printf("\tnext_writable %d\n", trackinfo->next_writable); + printf("\tfree_blocks %d\n", trackinfo->free_blocks); + printf("\tpacket_size %d\n", trackinfo->packet_size); + printf("\ttrack size %d\n", trackinfo->track_size); + printf("\tlast recorded block %d\n", trackinfo->last_recorded); +} + +#else +#define udf_dump_discinfo(a); +#define udf_dump_trackinfo(a); +#endif + + +/* --------------------------------------------------------------------- */ + +/* not called often */ +int +udf_update_discinfo(struct udf_mount *ump) +{ + struct vnode *devvp = ump->devvp; + uint64_t psize; + unsigned secsize; + struct mmc_discinfo *di; + int error; + + DPRINTF(VOLUMES, ("read/update disc info\n")); + di = &ump->discinfo; + memset(di, 0, sizeof(struct mmc_discinfo)); + + /* check if we're on a MMC capable device, i.e. CD/DVD */ + error = VOP_IOCTL(devvp, MMCGETDISCINFO, di, FKIOCTL, NOCRED); + if (error == 0) { + udf_dump_discinfo(ump); + return 0; + } + + /* disc partition support */ + error = getdisksize(devvp, &psize, &secsize); + if (error) + return error; + + /* set up a disc info profile for partitions */ + di->mmc_profile = 0x01; /* disc type */ + di->mmc_class = MMC_CLASS_DISC; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE | + MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE; + di->mmc_cap = di->mmc_cur; + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + /* TODO problem with last_possible_lba on resizable VND; request */ + di->last_possible_lba = psize; + di->sector_size = secsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + udf_dump_discinfo(ump); + return 0; +} + + +int +udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *ti) +{ + struct vnode *devvp = ump->devvp; + struct mmc_discinfo *di = &ump->discinfo; + int error, class; + + DPRINTF(VOLUMES, ("read track info\n")); + + class = di->mmc_class; + if (class != MMC_CLASS_DISC) { + /* tracknr specified in struct ti */ + error = VOP_IOCTL(devvp, MMCGETTRACKINFO, ti, FKIOCTL, NOCRED); + return error; + } + + /* disc partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti (TODO check for resized vnds) */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = 1; + + /* TODO support for resizable vnd */ + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + + +int +udf_setup_writeparams(struct udf_mount *ump) +{ + struct mmc_writeparams mmc_writeparams; + int error; + + if (ump->discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + /* + * only CD burning normally needs setting up, but other disc types + * might need other settings to be made. The MMC framework will set up + * the nessisary recording parameters according to the disc + * characteristics read in. Modifications can be made in the discinfo + * structure passed to change the nature of the disc. + */ + + memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams)); + mmc_writeparams.mmc_class = ump->discinfo.mmc_class; + mmc_writeparams.mmc_cur = ump->discinfo.mmc_cur; + + /* + * UDF dictates first track to determine track mode for the whole + * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1] + * To prevent problems with a `reserved' track in front we start with + * the 2nd track and if that is not valid, go for the 1st. + */ + mmc_writeparams.tracknr = 2; + mmc_writeparams.data_mode = MMC_DATAMODE_DEFAULT; /* XA disc */ + mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT; /* data */ + + error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS, &mmc_writeparams, + FKIOCTL, NOCRED); + if (error) { + mmc_writeparams.tracknr = 1; + error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS, + &mmc_writeparams, FKIOCTL, NOCRED); + } + return error; +} + + +int +udf_synchronise_caches(struct udf_mount *ump) +{ + struct mmc_op mmc_op; + + DPRINTF(CALL, ("udf_synchronise_caches()\n")); + + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + return 0; + + /* discs are done now */ + if (ump->discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + memset(&mmc_op, 0, sizeof(struct mmc_op)); + mmc_op.operation = MMC_OP_SYNCHRONISECACHE; + + /* ignore return code */ + (void) VOP_IOCTL(ump->devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* track/session searching for mounting */ +int +udf_search_tracks(struct udf_mount *ump, struct udf_args *args, + int *first_tracknr, int *last_tracknr) +{ + struct mmc_trackinfo trackinfo; + uint32_t tracknr, start_track, num_tracks; + int error; + + /* if negative, sessionnr is relative to last session */ + if (args->sessionnr < 0) { + args->sessionnr += ump->discinfo.num_sessions; + } + + /* sanity */ + if (args->sessionnr < 0) + args->sessionnr = 0; + if (args->sessionnr > ump->discinfo.num_sessions) + args->sessionnr = ump->discinfo.num_sessions; + + /* search the tracks for this session, zero session nr indicates last */ + if (args->sessionnr == 0) + args->sessionnr = ump->discinfo.num_sessions; + if (ump->discinfo.last_session_state == MMC_STATE_EMPTY) + args->sessionnr--; + + /* sanity again */ + if (args->sessionnr < 0) + args->sessionnr = 0; + + /* search the first and last track of the specified session */ + num_tracks = ump->discinfo.num_tracks; + start_track = ump->discinfo.first_track; + + /* search for first track of this session */ + for (tracknr = start_track; tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + if (trackinfo.sessionnr == args->sessionnr) + break; + } + *first_tracknr = tracknr; + + /* search for last track of this session */ + for (;tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error || (trackinfo.sessionnr != args->sessionnr)) { + tracknr--; + break; + } + } + if (tracknr > num_tracks) + tracknr--; + + *last_tracknr = tracknr; + + if (*last_tracknr < *first_tracknr) { + printf( "udf_search_tracks: sanity check on drive+disc failed, " + "drive returned garbage\n"); + return EINVAL; + } + + assert(*last_tracknr >= *first_tracknr); + return 0; +} + + +/* + * NOTE: this is the only routine in this file that directly peeks into the + * metadata file but since its at a larval state of the mount it can't hurt. + * + * XXX candidate for udf_allocation.c + * XXX clean me up!, change to new node reading code. + */ + +static void +udf_check_track_metadata_overlap(struct udf_mount *ump, + struct mmc_trackinfo *trackinfo) +{ + struct part_desc *part; + struct file_entry *fe; + struct extfile_entry *efe; + struct short_ad *s_ad; + struct long_ad *l_ad; + uint32_t track_start, track_end; + uint32_t phys_part_start, phys_part_end, part_start, part_end; + uint32_t sector_size, len, alloclen, plb_num; + uint8_t *pos; + int addr_type, icblen, icbflags; + + /* get our track extents */ + track_start = trackinfo->track_start; + track_end = track_start + trackinfo->track_size; + + /* get our base partition extent */ + KASSERT(ump->node_part == ump->fids_part); + part = ump->partitions[ump->vtop[ump->node_part]]; + phys_part_start = udf_rw32(part->start_loc); + phys_part_end = phys_part_start + udf_rw32(part->part_len); + + /* no use if its outside the physical partition */ + if ((phys_part_start >= track_end) || (phys_part_end < track_start)) + return; + + /* + * now follow all extents in the fe/efe to see if they refer to this + * track + */ + + sector_size = ump->discinfo.sector_size; + + /* XXX should we claim exclusive access to the metafile ? */ + /* TODO: move to new node read code */ + fe = ump->metadata_node->fe; + efe = ump->metadata_node->efe; + if (fe) { + alloclen = udf_rw32(fe->l_ad); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(efe); + alloclen = udf_rw32(efe->l_ad); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + while (alloclen) { + if (addr_type == UDF_ICB_SHORT_ALLOC) { + icblen = sizeof(struct short_ad); + s_ad = (struct short_ad *) pos; + len = udf_rw32(s_ad->len); + plb_num = udf_rw32(s_ad->lb_num); + } else { + /* should not be present, but why not */ + icblen = sizeof(struct long_ad); + l_ad = (struct long_ad *) pos; + len = udf_rw32(l_ad->len); + plb_num = udf_rw32(l_ad->loc.lb_num); + /* pvpart_num = udf_rw16(l_ad->loc.part_num); */ + } + /* process extent */ + len = UDF_EXT_LEN(len); + + part_start = phys_part_start + plb_num; + part_end = part_start + (len / sector_size); + + if ((part_start >= track_start) && (part_end <= track_end)) { + /* extent is enclosed within this track */ + ump->metadata_track = *trackinfo; + return; + } + + pos += icblen; + alloclen -= icblen; + } +} + + +int +udf_search_writing_tracks(struct udf_mount *ump) +{ + struct vnode *devvp = ump->devvp; + struct mmc_trackinfo trackinfo; + struct mmc_op mmc_op; + struct part_desc *part; + uint32_t tracknr, start_track, num_tracks; + uint32_t track_start, track_end, part_start, part_end; + int node_alloc, error; + + /* + * in the CD/(HD)DVD/BD recordable device model a few tracks within + * the last session might be open but in the UDF device model at most + * three tracks can be open: a reserved track for delayed ISO VRS + * writing, a data track and a metadata track. We search here for the + * data track and the metadata track. Note that the reserved track is + * troublesome but can be detected by its small size of < 512 sectors. + */ + + /* update discinfo since it might have changed */ + error = udf_update_discinfo(ump); + if (error) + return error; + + num_tracks = ump->discinfo.num_tracks; + start_track = ump->discinfo.first_track; + + /* fetch info on first and possibly only track */ + trackinfo.tracknr = start_track; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + /* copy results to our mount point */ + ump->data_track = trackinfo; + ump->metadata_track = trackinfo; + + /* if not sequential, we're done */ + if (num_tracks == 1) + return 0; + + for (tracknr = start_track;tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + /* + * If this track is marked damaged, ask for repair. This is an + * optional command, so ignore its error but report warning. + */ + if (trackinfo.flags & MMC_TRACKINFO_DAMAGED) { + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_REPAIRTRACK; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.tracknr = tracknr; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED); + if (error) + (void)printf("Drive can't explicitly repair " + "damaged track %d, but it might " + "autorepair\n", tracknr); + + /* reget track info */ + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + } + if ((trackinfo.flags & MMC_TRACKINFO_NWA_VALID) == 0) + continue; + + track_start = trackinfo.track_start; + track_end = track_start + trackinfo.track_size; + + /* check for overlap on data partition */ + part = ump->partitions[ump->data_part]; + part_start = udf_rw32(part->start_loc); + part_end = part_start + udf_rw32(part->part_len); + if ((part_start < track_end) && (part_end > track_start)) { + ump->data_track = trackinfo; + /* TODO check if UDF partition data_part is writable */ + } + + /* check for overlap on metadata partition */ + node_alloc = ump->vtop_alloc[ump->node_part]; + if ((node_alloc == UDF_ALLOC_METASEQUENTIAL) || + (node_alloc == UDF_ALLOC_METABITMAP)) { + udf_check_track_metadata_overlap(ump, &trackinfo); + } else { + ump->metadata_track = trackinfo; + } + } + + if ((ump->data_track.flags & MMC_TRACKINFO_NWA_VALID) == 0) + return EROFS; + + if ((ump->metadata_track.flags & MMC_TRACKINFO_NWA_VALID) == 0) + return EROFS; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Check if the blob starts with a good UDF tag. Tags are protected by a + * checksum over the reader except one byte at position 4 that is the checksum + * itself. + */ + +int +udf_check_tag(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *pos, sum, cnt; + + /* check TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) + sum += *pos; + pos++; + } + if (sum != tag->cksum) { + /* bad tag header checksum; this is not a valid tag */ + return EINVAL; + } + + return 0; +} + + +/* + * check tag payload will check descriptor CRC as specified. + * If the descriptor is too long, it will return EIO otherwise EINVAL. + */ + +int +udf_check_tag_payload(void *blob, uint32_t max_length) +{ + struct desc_tag *tag = blob; + uint16_t crc, crc_len; + + crc_len = udf_rw16(tag->desc_crc_len); + + /* check payload CRC if applicable */ + if (crc_len == 0) + return 0; + + if (crc_len > max_length) + return EIO; + + crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, crc_len); + if (crc != udf_rw16(tag->desc_crc)) { + /* bad payload CRC; this is a broken tag */ + return EINVAL; + } + + return 0; +} + + +void +udf_validate_tag_sum(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *pos, sum, cnt; + + /* calculate TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) sum += *pos; + pos++; + } + tag->cksum = sum; /* 8 bit */ +} + + +/* assumes sector number of descriptor to be saved already present */ +void +udf_validate_tag_and_crc_sums(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *btag = (uint8_t *) tag; + uint16_t crc, crc_len; + + crc_len = udf_rw16(tag->desc_crc_len); + + /* check payload CRC if applicable */ + if (crc_len > 0) { + crc = udf_cksum(btag + UDF_DESC_TAG_LENGTH, crc_len); + tag->desc_crc = udf_rw16(crc); + } + + /* calculate TAG header checksum */ + udf_validate_tag_sum(blob); +} + +/* --------------------------------------------------------------------- */ + +/* + * XXX note the different semantics from udfclient: for FIDs it still rounds + * up to sectors. Use udf_fidsize() for a correct length. + */ + +int +udf_tagsize(union dscrptr *dscr, uint32_t lb_size) +{ + uint32_t size, tag_id, num_lb, elmsz; + + tag_id = udf_rw16(dscr->tag.id); + + switch (tag_id) { + case TAGID_LOGVOL : + size = sizeof(struct logvol_desc) - 1; + size += udf_rw32(dscr->lvd.mt_l); + break; + case TAGID_UNALLOC_SPACE : + elmsz = sizeof(struct extent_ad); + size = sizeof(struct unalloc_sp_desc) - elmsz; + size += udf_rw32(dscr->usd.alloc_desc_num) * elmsz; + break; + case TAGID_FID : + size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu); + size = (size + 3) & ~3; + break; + case TAGID_LOGVOL_INTEGRITY : + size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); + size += udf_rw32(dscr->lvid.l_iu); + size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t)); + break; + case TAGID_SPACE_BITMAP : + size = sizeof(struct space_bitmap_desc) - 1; + size += udf_rw32(dscr->sbd.num_bytes); + break; + case TAGID_SPARING_TABLE : + elmsz = sizeof(struct spare_map_entry); + size = sizeof(struct udf_sparing_table) - elmsz; + size += udf_rw16(dscr->spt.rt_l) * elmsz; + break; + case TAGID_FENTRY : + size = sizeof(struct file_entry); + size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; + break; + case TAGID_EXTFENTRY : + size = sizeof(struct extfile_entry); + size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; + break; + case TAGID_FSD : + size = sizeof(struct fileset_desc); + break; + default : + size = sizeof(union dscrptr); + break; + } + + if ((size == 0) || (lb_size == 0)) + return 0; + + if (lb_size == 1) + return size; + + /* round up in sectors */ + num_lb = (size + lb_size -1) / lb_size; + return num_lb * lb_size; +} + + +int +udf_fidsize(struct fileid_desc *fid) +{ + uint32_t size; + + if (udf_rw16(fid->tag.id) != TAGID_FID) + panic("got udf_fidsize on non FID\n"); + + size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); + size = (size + 3) & ~3; + + return size; +} + +/* --------------------------------------------------------------------- */ + +void +udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno) +{ + int ret; + + mutex_enter(&udf_node->node_mutex); + /* wait until free */ + while (udf_node->i_flags & IN_LOCKED) { + ret = cv_timedwait(&udf_node->node_lock, &udf_node->node_mutex, hz/8); + /* TODO check if we should return error; abort */ + if (ret == EWOULDBLOCK) { + DPRINTF(LOCKING, ( "udf_lock_node: udf_node %p would block " + "wanted at %s:%d, previously locked at %s:%d\n", + udf_node, fname, lineno, + udf_node->lock_fname, udf_node->lock_lineno)); + } + } + /* grab */ + udf_node->i_flags |= IN_LOCKED | flag; + /* debug */ + udf_node->lock_fname = fname; + udf_node->lock_lineno = lineno; + + mutex_exit(&udf_node->node_mutex); +} + + +void +udf_unlock_node(struct udf_node *udf_node, int flag) +{ + mutex_enter(&udf_node->node_mutex); + udf_node->i_flags &= ~(IN_LOCKED | flag); + cv_broadcast(&udf_node->node_lock); + mutex_exit(&udf_node->node_mutex); +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_read_anchor(struct udf_mount *ump, uint32_t sector, struct anchor_vdp **dst) +{ + int error; + + error = udf_read_phys_dscr(ump, sector, M_UDFVOLD, + (union dscrptr **) dst); + if (!error) { + /* blank terminator blocks are not allowed here */ + if (*dst == NULL) + return ENOENT; + if (udf_rw16((*dst)->tag.id) != TAGID_ANCHOR) { + error = ENOENT; + free(*dst, M_UDFVOLD); + *dst = NULL; + DPRINTF(VOLUMES, ("Not an anchor\n")); + } + } + + return error; +} + + +int +udf_read_anchors(struct udf_mount *ump) +{ + struct udf_args *args = &ump->mount_args; + struct mmc_trackinfo first_track; + struct mmc_trackinfo second_track; + struct mmc_trackinfo last_track; + struct anchor_vdp **anchorsp; + uint32_t track_start; + uint32_t track_end; + uint32_t positions[4]; + int first_tracknr, last_tracknr; + int error, anch, ok, first_anchor; + + /* search the first and last track of the specified session */ + error = udf_search_tracks(ump, args, &first_tracknr, &last_tracknr); + if (!error) { + first_track.tracknr = first_tracknr; + error = udf_update_trackinfo(ump, &first_track); + } + if (!error) { + last_track.tracknr = last_tracknr; + error = udf_update_trackinfo(ump, &last_track); + } + if ((!error) && (first_tracknr != last_tracknr)) { + second_track.tracknr = first_tracknr+1; + error = udf_update_trackinfo(ump, &second_track); + } + if (error) { + printf("UDF mount: reading disc geometry failed\n"); + return 0; + } + + track_start = first_track.track_start; + + /* `end' is not as straitforward as start. */ + track_end = last_track.track_start + + last_track.track_size - last_track.free_blocks - 1; + + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* end of track is not straitforward here */ + if (last_track.flags & MMC_TRACKINFO_LRA_VALID) + track_end = last_track.last_recorded; + else if (last_track.flags & MMC_TRACKINFO_NWA_VALID) + track_end = last_track.next_writable + - ump->discinfo.link_block_penalty; + } + + /* its no use reading a blank track */ + first_anchor = 0; + if (first_track.flags & MMC_TRACKINFO_BLANK) + first_anchor = 1; + + /* get our packet size */ + ump->packet_size = first_track.packet_size; + if (first_track.flags & MMC_TRACKINFO_BLANK) + ump->packet_size = second_track.packet_size; + + if (ump->packet_size <= 1) { + /* take max, but not bigger than 64 */ + ump->packet_size = MAXPHYS / ump->discinfo.sector_size; + ump->packet_size = MIN(ump->packet_size, 64); + } + KASSERT(ump->packet_size >= 1); + + /* read anchors start+256, start+512, end-256, end */ + positions[0] = track_start+256; + positions[1] = track_end-256; + positions[2] = track_end; + positions[3] = track_start+512; /* [UDF 2.60/6.11.2] */ + /* XXX shouldn't +512 be prefered above +256 for compat with Roxio CD */ + + ok = 0; + anchorsp = ump->anchors; + for (anch = first_anchor; anch < 4; anch++) { + DPRINTF(VOLUMES, ("Read anchor %d at sector %d\n", anch, + positions[anch])); + error = udf_read_anchor(ump, positions[anch], anchorsp); + if (!error) { + anchorsp++; + ok++; + } + } + + /* VATs are only recorded on sequential media, but initialise */ + ump->first_possible_vat_location = track_start + 2; + ump->last_possible_vat_location = track_end + last_track.packet_size; + + return ok; +} + +/* --------------------------------------------------------------------- */ + +int +udf_get_c_type(struct udf_node *udf_node) +{ + int isdir, what; + + isdir = (udf_node->vnode->v_type == VDIR); + what = isdir ? UDF_C_FIDS : UDF_C_USERDATA; + + if (udf_node->ump) + if (udf_node == udf_node->ump->metadatabitmap_node) + what = UDF_C_METADATA_SBM; + + return what; +} + + +int +udf_get_record_vpart(struct udf_mount *ump, int udf_c_type) +{ + int vpart_num; + + vpart_num = ump->data_part; + if (udf_c_type == UDF_C_NODE) + vpart_num = ump->node_part; + if (udf_c_type == UDF_C_FIDS) + vpart_num = ump->fids_part; + + return vpart_num; +} + + +/* + * BUGALERT: some rogue implementations use random physical partition + * numbers to break other implementations so lookup the number. + */ + +static uint16_t +udf_find_raw_phys(struct udf_mount *ump, uint16_t raw_phys_part) +{ + struct part_desc *part; + uint16_t phys_part; + + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + part = ump->partitions[phys_part]; + if (part == NULL) + break; + if (udf_rw16(part->part_num) == raw_phys_part) + break; + } + return phys_part; +} + +/* --------------------------------------------------------------------- */ + +/* we dont try to be smart; we just record the parts */ +#define UDF_UPDATE_DSCR(name, dscr) \ + if (name) \ + free(name, M_UDFVOLD); \ + name = dscr; + +static int +udf_process_vds_descriptor(struct udf_mount *ump, union dscrptr *dscr) +{ + uint16_t phys_part, raw_phys_part; + + DPRINTF(VOLUMES, ("\tprocessing VDS descr %d\n", + udf_rw16(dscr->tag.id))); + switch (udf_rw16(dscr->tag.id)) { + case TAGID_PRI_VOL : /* primary partition */ + UDF_UPDATE_DSCR(ump->primary_vol, &dscr->pvd); + break; + case TAGID_LOGVOL : /* logical volume */ + UDF_UPDATE_DSCR(ump->logical_vol, &dscr->lvd); + break; + case TAGID_UNALLOC_SPACE : /* unallocated space */ + UDF_UPDATE_DSCR(ump->unallocated, &dscr->usd); + break; + case TAGID_IMP_VOL : /* implementation */ + /* XXX do we care about multiple impl. descr ? */ + UDF_UPDATE_DSCR(ump->implementation, &dscr->ivd); + break; + case TAGID_PARTITION : /* physical partition */ + /* not much use if its not allocated */ + if ((udf_rw16(dscr->pd.flags) & UDF_PART_FLAG_ALLOCATED) == 0) { + free(dscr, M_UDFVOLD); + break; + } + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + raw_phys_part = udf_rw16(dscr->pd.part_num); + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + if (phys_part == UDF_PARTITIONS) { + free(dscr, M_UDFVOLD); + return EINVAL; + } + + UDF_UPDATE_DSCR(ump->partitions[phys_part], &dscr->pd); + break; + case TAGID_VOL : /* volume space extender; rare */ + DPRINTF(VOLUMES, ("VDS extender ignored\n")); + free(dscr, M_UDFVOLD); + break; + default : + DPRINTF(VOLUMES, ("Unhandled VDS type %d\n", + udf_rw16(dscr->tag.id))); + free(dscr, M_UDFVOLD); + } + + return 0; +} +#undef UDF_UPDATE_DSCR + +/* --------------------------------------------------------------------- */ + +static int +udf_read_vds_extent(struct udf_mount *ump, uint32_t loc, uint32_t len) +{ + union dscrptr *dscr; + uint32_t sector_size, dscr_size; + int error; + + sector_size = ump->discinfo.sector_size; + + /* loc is sectornr, len is in bytes */ + error = EIO; + while (len) { + error = udf_read_phys_dscr(ump, loc, M_UDFVOLD, &dscr); + if (error) + return error; + + /* blank block is a terminator */ + if (dscr == NULL) + return 0; + + /* TERM descriptor is a terminator */ + if (udf_rw16(dscr->tag.id) == TAGID_TERM) { + free(dscr, M_UDFVOLD); + return 0; + } + + /* process all others */ + dscr_size = udf_tagsize(dscr, sector_size); + error = udf_process_vds_descriptor(ump, dscr); + if (error) { + free(dscr, M_UDFVOLD); + break; + } + assert((dscr_size % sector_size) == 0); + + len -= dscr_size; + loc += dscr_size / sector_size; + } + + return error; +} + + +int +udf_read_vds_space(struct udf_mount *ump) +{ + /* struct udf_args *args = &ump->mount_args; */ + struct anchor_vdp *anchor, *anchor2; + size_t size; + uint32_t main_loc, main_len; + uint32_t reserve_loc, reserve_len; + int error; + + /* + * read in VDS space provided by the anchors; if one descriptor read + * fails, try the mirror sector. + * + * check if 2nd anchor is different from 1st; if so, go for 2nd. This + * avoids the `compatibility features' of DirectCD that may confuse + * stuff completely. + */ + + anchor = ump->anchors[0]; + anchor2 = ump->anchors[1]; + assert(anchor); + + if (anchor2) { + size = sizeof(struct extent_ad); + if (memcmp(&anchor->main_vds_ex, &anchor2->main_vds_ex, size)) + anchor = anchor2; + /* reserve is specified to be a literal copy of main */ + } + + main_loc = udf_rw32(anchor->main_vds_ex.loc); + main_len = udf_rw32(anchor->main_vds_ex.len); + + reserve_loc = udf_rw32(anchor->reserve_vds_ex.loc); + reserve_len = udf_rw32(anchor->reserve_vds_ex.len); + + error = udf_read_vds_extent(ump, main_loc, main_len); + if (error) { + printf("UDF mount: reading in reserve VDS extent\n"); + error = udf_read_vds_extent(ump, reserve_loc, reserve_len); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Read in the logical volume integrity sequence pointed to by our logical + * volume descriptor. Its a sequence that can be extended using fields in the + * integrity descriptor itself. On sequential media only one is found, on + * rewritable media a sequence of descriptors can be found as a form of + * history keeping and on non sequential write-once media the chain is vital + * to allow more and more descriptors to be written. The last descriptor + * written in an extent needs to claim space for a new extent. + */ + +static int +udf_retrieve_lvint(struct udf_mount *ump) +{ + union dscrptr *dscr; + struct logvol_int_desc *lvint; + struct udf_lvintq *trace; + uint32_t lb_size, lbnum, len; + int dscr_type, error, trace_len; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + len = udf_rw32(ump->logical_vol->integrity_seq_loc.len); + lbnum = udf_rw32(ump->logical_vol->integrity_seq_loc.loc); + + /* clean trace */ + memset(ump->lvint_trace, 0, + UDF_LVDINT_SEGMENTS * sizeof(struct udf_lvintq)); + + trace_len = 0; + trace = ump->lvint_trace; + trace->start = lbnum; + trace->end = lbnum + len/lb_size; + trace->pos = 0; + trace->wpos = 0; + + lvint = NULL; + dscr = NULL; + error = 0; + while (len) { + trace->pos = lbnum - trace->start; + trace->wpos = trace->pos + 1; + + /* read in our integrity descriptor */ + error = udf_read_phys_dscr(ump, lbnum, M_UDFVOLD, &dscr); + if (!error) { + if (dscr == NULL) { + trace->wpos = trace->pos; + break; /* empty terminates */ + } + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) { + trace->wpos = trace->pos; + break; /* clean terminator */ + } + if (dscr_type != TAGID_LOGVOL_INTEGRITY) { + /* fatal... corrupt disc */ + error = ENOENT; + break; + } + if (lvint) + free(lvint, M_UDFVOLD); + lvint = &dscr->lvid; + dscr = NULL; + } /* else hope for the best... maybe the next is ok */ + + DPRINTFIF(VOLUMES, lvint, ("logvol integrity read, state %s\n", + udf_rw32(lvint->integrity_type) ? "CLOSED" : "OPEN")); + + /* proceed sequential */ + lbnum += 1; + len -= lb_size; + + /* are we linking to a new piece? */ + if (dscr && lvint->next_extent.len) { + len = udf_rw32(lvint->next_extent.len); + lbnum = udf_rw32(lvint->next_extent.loc); + + if (trace_len >= UDF_LVDINT_SEGMENTS-1) { + /* IEK! segment link full... */ + DPRINTF(VOLUMES, ("lvdint segments full\n")); + error = EINVAL; + } else { + trace++; + trace_len++; + + trace->start = lbnum; + trace->end = lbnum + len/lb_size; + trace->pos = 0; + trace->wpos = 0; + } + } + } + + /* clean up the mess, esp. when there is an error */ + if (dscr) + free(dscr, M_UDFVOLD); + + if (error && lvint) { + free(lvint, M_UDFVOLD); + lvint = NULL; + } + + if (!lvint) + error = ENOENT; + + ump->logvol_integrity = lvint; + return error; +} + + +static int +udf_loose_lvint_history(struct udf_mount *ump) +{ + union dscrptr **bufs, *dscr, *last_dscr; + struct udf_lvintq *trace, *in_trace, *out_trace; + struct logvol_int_desc *lvint; + uint32_t in_ext, in_pos, in_len; + uint32_t out_ext, out_wpos, out_len; + uint32_t lb_num; + uint32_t len, start; + int ext, minext, extlen, cnt, cpy_len, dscr_type; + int losing; + int error; + + DPRINTF(VOLUMES, ("need to lose some lvint history\n")); + + /* search smallest extent */ + trace = &ump->lvint_trace[0]; + minext = trace->end - trace->start; + for (ext = 1; ext < UDF_LVDINT_SEGMENTS; ext++) { + trace = &ump->lvint_trace[ext]; + extlen = trace->end - trace->start; + if (extlen == 0) + break; + minext = MIN(minext, extlen); + } + losing = MIN(minext, UDF_LVINT_LOSSAGE); + /* no sense wiping all */ + if (losing == minext) + losing--; + + DPRINTF(VOLUMES, ("\tlosing %d entries\n", losing)); + + /* get buffer for pieces */ + bufs = malloc(UDF_LVDINT_SEGMENTS * sizeof(void *), M_TEMP, M_WAITOK); + + in_ext = 0; + in_pos = losing; + in_trace = &ump->lvint_trace[in_ext]; + in_len = in_trace->end - in_trace->start; + out_ext = 0; + out_wpos = 0; + out_trace = &ump->lvint_trace[out_ext]; + out_len = out_trace->end - out_trace->start; + + last_dscr = NULL; + for(;;) { + out_trace->pos = out_wpos; + out_trace->wpos = out_trace->pos; + if (in_pos >= in_len) { + in_ext++; + in_pos = 0; + in_trace = &ump->lvint_trace[in_ext]; + in_len = in_trace->end - in_trace->start; + } + if (out_wpos >= out_len) { + out_ext++; + out_wpos = 0; + out_trace = &ump->lvint_trace[out_ext]; + out_len = out_trace->end - out_trace->start; + } + /* copy overlap contents */ + cpy_len = MIN(in_len - in_pos, out_len - out_wpos); + cpy_len = MIN(cpy_len, in_len - in_trace->pos); + if (cpy_len == 0) + break; + + /* copy */ + DPRINTF(VOLUMES, ("\treading %d lvid descriptors\n", cpy_len)); + for (cnt = 0; cnt < cpy_len; cnt++) { + /* read in our integrity descriptor */ + lb_num = in_trace->start + in_pos + cnt; + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, + &dscr); + if (error) { + /* copy last one */ + dscr = last_dscr; + } + bufs[cnt] = dscr; + if (!error) { + if (dscr == NULL) { + out_trace->pos = out_wpos + cnt; + out_trace->wpos = out_trace->pos; + break; /* empty terminates */ + } + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) { + out_trace->pos = out_wpos + cnt; + out_trace->wpos = out_trace->pos; + break; /* clean terminator */ + } + if (dscr_type != TAGID_LOGVOL_INTEGRITY) { + panic( "UDF integrity sequence " + "corrupted while mounted!\n"); + } + last_dscr = dscr; + } + } + + /* patch up if first entry was on error */ + if (bufs[0] == NULL) { + for (cnt = 0; cnt < cpy_len; cnt++) + if (bufs[cnt] != NULL) + break; + last_dscr = bufs[cnt]; + for (; cnt > 0; cnt--) { + bufs[cnt] = last_dscr; + } + } + + /* glue + write out */ + DPRINTF(VOLUMES, ("\twriting %d lvid descriptors\n", cpy_len)); + for (cnt = 0; cnt < cpy_len; cnt++) { + lb_num = out_trace->start + out_wpos + cnt; + lvint = &bufs[cnt]->lvid; + + /* set continuation */ + len = 0; + start = 0; + if (out_wpos + cnt == out_len) { + /* get continuation */ + trace = &ump->lvint_trace[out_ext+1]; + len = trace->end - trace->start; + start = trace->start; + } + lvint->next_extent.len = udf_rw32(len); + lvint->next_extent.loc = udf_rw32(start); + + lb_num = trace->start + trace->wpos; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + bufs[cnt], lb_num, lb_num); + DPRINTFIF(VOLUMES, error, + ("error writing lvint lb_num\n")); + } + + /* free non repeating descriptors */ + last_dscr = NULL; + for (cnt = 0; cnt < cpy_len; cnt++) { + if (bufs[cnt] != last_dscr) + free(bufs[cnt], M_UDFVOLD); + last_dscr = bufs[cnt]; + } + + /* advance */ + in_pos += cpy_len; + out_wpos += cpy_len; + } + + free(bufs, M_TEMP); + + return 0; +} + + +static int +udf_writeout_lvint(struct udf_mount *ump, int lvflag) +{ + struct udf_lvintq *trace; + struct timeval now_v; + struct timespec now_s; + uint32_t sector; + int logvol_integrity; + int space, error; + + DPRINTF(VOLUMES, ("writing out logvol integrity descriptor\n")); + +again: + /* get free space in last chunk */ + trace = ump->lvint_trace; + while (trace->wpos > (trace->end - trace->start)) { + DPRINTF(VOLUMES, ("skip : start = %d, end = %d, pos = %d, " + "wpos = %d\n", trace->start, trace->end, + trace->pos, trace->wpos)); + trace++; + } + + /* check if there is space to append */ + space = (trace->end - trace->start) - trace->wpos; + DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, " + "space = %d\n", trace->start, trace->end, trace->pos, + trace->wpos, space)); + + /* get state */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_CLOSED) { + if ((space < 3) && (lvflag & UDF_APPENDONLY_LVINT)) { + /* TODO extent LVINT space if possible */ + return EROFS; + } + } + + if (space < 1) { + if (lvflag & UDF_APPENDONLY_LVINT) + return EROFS; + /* loose history by re-writing extents */ + error = udf_loose_lvint_history(ump); + if (error) + return error; + goto again; + } + + /* update our integrity descriptor to identify us and timestamp it */ + DPRINTF(VOLUMES, ("updating integrity descriptor\n")); + microtime(&now_v); + TIMEVAL_TO_TIMESPEC(&now_v, &now_s); + udf_timespec_to_timestamp(&now_s, &ump->logvol_integrity->time); + udf_set_regid(&ump->logvol_info->impl_id, IMPL_NAME); + udf_add_impl_regid(ump, &ump->logvol_info->impl_id); + + /* writeout integrity descriptor */ + sector = trace->start + trace->wpos; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) ump->logvol_integrity, + sector, sector); + DPRINTF(VOLUMES, ("writeout lvint : error = %d\n", error)); + if (error) + return error; + + /* advance write position */ + trace->wpos++; space--; + if (space >= 1) { + /* append terminator */ + sector = trace->start + trace->wpos; + error = udf_write_terminator(ump, sector); + + DPRINTF(VOLUMES, ("write terminator : error = %d\n", error)); + } + + space = (trace->end - trace->start) - trace->wpos; + DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, " + "space = %d\n", trace->start, trace->end, trace->pos, + trace->wpos, space)); + DPRINTF(VOLUMES, ("finished writing out logvol integrity descriptor " + "successfull\n")); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_physical_partition_spacetables(struct udf_mount *ump) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct part_desc *partd; + struct part_hdr_desc *parthdr; + struct udf_bitmap *bitmap; + uint32_t phys_part; + uint32_t lb_num, len; + int error, dscr_type; + + /* unallocated space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + lb_num = udf_rw32(partd->start_loc); + lb_num += udf_rw32(parthdr->unalloc_space_bitmap.lb_num); + len = udf_rw32(parthdr->unalloc_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting space bitmap\n")); + ump->part_unalloc_dscr[phys_part] = &dscr->sbd; + + /* fill in ump->part_unalloc_bits */ + bitmap = &ump->part_unalloc_bits[phys_part]; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + free(dscr, M_UDFVOLD); + + printf( "UDF mount: error reading unallocated " + "space bitmap\n"); + return EROFS; + } + } else { + /* blank not allowed */ + printf("UDF mount: blank unallocated space bitmap\n"); + return EROFS; + } + } + + /* unallocated space table (not supported) */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + len = udf_rw32(parthdr->unalloc_space_table.len); + if (len) { + printf("UDF mount: space tables not supported\n"); + return EROFS; + } + } + + /* freed space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + /* freed space map */ + lb_num = udf_rw32(partd->start_loc); + lb_num += udf_rw32(parthdr->freed_space_bitmap.lb_num); + len = udf_rw32(parthdr->freed_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting space bitmap\n")); + ump->part_freed_dscr[phys_part] = &dscr->sbd; + + /* fill in ump->part_freed_bits */ + bitmap = &ump->part_unalloc_bits[phys_part]; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + free(dscr, M_UDFVOLD); + + printf( "UDF mount: error reading freed " + "space bitmap\n"); + return EROFS; + } + } else { + /* blank not allowed */ + printf("UDF mount: blank freed space bitmap\n"); + return EROFS; + } + } + + /* freed space table (not supported) */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + len = udf_rw32(parthdr->freed_space_table.len); + if (len) { + printf("UDF mount: space tables not supported\n"); + return EROFS; + } + } + + return 0; +} + + +/* TODO implement async writeout */ +int +udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct part_desc *partd; + struct part_hdr_desc *parthdr; + uint32_t phys_part; + uint32_t lb_num, len, ptov; + int error_all, error; + + error_all = 0; + /* unallocated space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + ptov = udf_rw32(partd->start_loc); + lb_num = udf_rw32(parthdr->unalloc_space_bitmap.lb_num); + len = udf_rw32(parthdr->unalloc_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Write unalloc. space bitmap %d\n", + lb_num + ptov)); + dscr = (union dscrptr *) ump->part_unalloc_dscr[phys_part]; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) dscr, + ptov + lb_num, lb_num); + if (error) { + DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error)); + error_all = error; + } + } + + /* freed space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + /* freed space map */ + ptov = udf_rw32(partd->start_loc); + lb_num = udf_rw32(parthdr->freed_space_bitmap.lb_num); + len = udf_rw32(parthdr->freed_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Write freed space bitmap %d\n", + lb_num + ptov)); + dscr = (union dscrptr *) ump->part_freed_dscr[phys_part]; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) dscr, + ptov + lb_num, lb_num); + if (error) { + DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error)); + error_all = error; + } + } + + return error_all; +} + + +static int +udf_read_metadata_partition_spacetable(struct udf_mount *ump) +{ + struct udf_node *bitmap_node; + union dscrptr *dscr; + struct udf_bitmap *bitmap; + uint64_t inflen; + int error, dscr_type; + + bitmap_node = ump->metadatabitmap_node; + + /* only read in when metadata bitmap node is read in */ + if (bitmap_node == NULL) + return 0; + + if (bitmap_node->fe) { + inflen = udf_rw64(bitmap_node->fe->inf_len); + } else { + KASSERT(bitmap_node->efe); + inflen = udf_rw64(bitmap_node->efe->inf_len); + } + + DPRINTF(VOLUMES, ("Reading metadata space bitmap for " + "%"PRIu64" bytes\n", inflen)); + + /* allocate space for bitmap */ + dscr = malloc(inflen, M_UDFVOLD, M_CANFAIL | M_WAITOK); + if (!dscr) + return ENOMEM; + + /* set vnode type to regular file or we can't read from it! */ + bitmap_node->vnode->v_type = VREG; + + /* read in complete metadata bitmap file */ + error = vn_rdwr(UIO_READ, bitmap_node->vnode, + dscr, + inflen, 0, + UIO_SYSSPACE, + IO_SYNC | IO_ALTSEMANTICS, FSCRED, + NULL, NULL); + if (error) { + DPRINTF(VOLUMES, ("Error reading metadata space bitmap\n")); + goto errorout; + } + + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting metadata space bitmap\n")); + ump->metadata_unalloc_dscr = &dscr->sbd; + + /* fill in bitmap bits */ + bitmap = &ump->metadata_unalloc_bits; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + DPRINTF(VOLUMES, ("No valid bitmap found!\n")); + goto errorout; + } + + return 0; + +errorout: + free(dscr, M_UDFVOLD); + printf( "UDF mount: error reading unallocated " + "space bitmap for metadata partition\n"); + return EROFS; +} + + +int +udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor) +{ + struct udf_node *bitmap_node; + union dscrptr *dscr; + uint64_t new_inflen; + int dummy, error; + + bitmap_node = ump->metadatabitmap_node; + + /* only write out when metadata bitmap node is known */ + if (bitmap_node == NULL) + return 0; + + if (!bitmap_node->fe) { + KASSERT(bitmap_node->efe); + } + + /* reduce length to zero */ + dscr = (union dscrptr *) ump->metadata_unalloc_dscr; + new_inflen = udf_tagsize(dscr, 1); + + DPRINTF(VOLUMES, ("Resize and write out metadata space bitmap from " + "%"PRIu64" to %"PRIu64" bytes\n", inflen, new_inflen)); + + error = udf_resize_node(bitmap_node, new_inflen, &dummy); + if (error) + printf("Error resizing metadata space bitmap\n"); + + error = vn_rdwr(UIO_WRITE, bitmap_node->vnode, + dscr, + new_inflen, 0, + UIO_SYSSPACE, + IO_ALTSEMANTICS, FSCRED, + NULL, NULL); + + bitmap_node->i_flags |= IN_MODIFIED; + error = vflushbuf(bitmap_node->vnode, FSYNC_WAIT); + if (error == 0) + error = VOP_FSYNC(bitmap_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + + if (error) + printf( "Error writing out metadata partition unalloced " + "space bitmap!\n"); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Checks if ump's vds information is correct and complete + */ + +int +udf_process_vds(struct udf_mount *ump) { + union udf_pmap *mapping; + /* struct udf_args *args = &ump->mount_args; */ + struct logvol_int_desc *lvint; + struct udf_logvol_info *lvinfo; + uint32_t n_pm; + uint8_t *pmap_pos; + char *domain_name, *map_name; + const char *check_name; + char bits[128]; + int pmap_stype, pmap_size; + int pmap_type, log_part, phys_part, raw_phys_part, maps_on; + int n_phys, n_virt, n_spar, n_meta; + int len; + + if (ump == NULL) + return ENOENT; + + /* we need at least an anchor (trivial, but for safety) */ + if (ump->anchors[0] == NULL) + return EINVAL; + + /* we need at least one primary and one logical volume descriptor */ + if ((ump->primary_vol == NULL) || (ump->logical_vol) == NULL) + return EINVAL; + + /* we need at least one partition descriptor */ + if (ump->partitions[0] == NULL) + return EINVAL; + + /* check logical volume sector size verses device sector size */ + if (udf_rw32(ump->logical_vol->lb_size) != ump->discinfo.sector_size) { + printf("UDF mount: format violation, lb_size != sector size\n"); + return EINVAL; + } + + /* check domain name */ + domain_name = ump->logical_vol->domain_id.id; + if (strncmp(domain_name, "*OSTA UDF Compliant", 20)) { + printf("mount_udf: disc not OSTA UDF Compliant, aborting\n"); + return EINVAL; + } + + /* retrieve logical volume integrity sequence */ + (void)udf_retrieve_lvint(ump); + + /* + * We need at least one logvol integrity descriptor recorded. Note + * that its OK to have an open logical volume integrity here. The VAT + * will close/update the integrity. + */ + if (ump->logvol_integrity == NULL) + return EINVAL; + + /* process derived structures */ + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + lvint = ump->logvol_integrity; + lvinfo = (struct udf_logvol_info *) (&lvint->tables[2 * n_pm]); + ump->logvol_info = lvinfo; + + /* TODO check udf versions? */ + + /* + * check logvol mappings: effective virt->log partmap translation + * check and recording of the mapping results. Saves expensive + * strncmp() in tight places. + */ + DPRINTF(VOLUMES, ("checking logvol mappings\n")); + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + pmap_pos = ump->logical_vol->maps; + + if (n_pm > UDF_PMAPS) { + printf("UDF mount: too many mappings\n"); + return EINVAL; + } + + /* count types and set partition numbers */ + ump->data_part = ump->node_part = ump->fids_part = 0; + n_phys = n_virt = n_spar = n_meta = 0; + for (log_part = 0; log_part < n_pm; log_part++) { + mapping = (union udf_pmap *) pmap_pos; + pmap_stype = pmap_pos[0]; + pmap_size = pmap_pos[1]; + switch (pmap_stype) { + case 1: /* physical mapping */ + /* volseq = udf_rw16(mapping->pm1.vol_seq_num); */ + raw_phys_part = udf_rw16(mapping->pm1.part_num); + pmap_type = UDF_VTOP_TYPE_PHYS; + n_phys++; + ump->data_part = log_part; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + case 2: /* virtual/sparable/meta mapping */ + map_name = mapping->pm2.part_id.id; + /* volseq = udf_rw16(mapping->pm2.vol_seq_num); */ + raw_phys_part = udf_rw16(mapping->pm2.part_num); + pmap_type = UDF_VTOP_TYPE_UNKNOWN; + len = UDF_REGID_ID_SIZE; + + check_name = "*UDF Virtual Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_VIRT; + n_virt++; + ump->node_part = log_part; + break; + } + check_name = "*UDF Sparable Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_SPARABLE; + n_spar++; + ump->data_part = log_part; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + } + check_name = "*UDF Metadata Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_META; + n_meta++; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + } + break; + default: + return EINVAL; + } + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + DPRINTF(VOLUMES, ("\t%d -> %d(%d) type %d\n", log_part, + raw_phys_part, phys_part, pmap_type)); + + if (phys_part == UDF_PARTITIONS) + return EINVAL; + if (pmap_type == UDF_VTOP_TYPE_UNKNOWN) + return EINVAL; + + ump->vtop [log_part] = phys_part; + ump->vtop_tp[log_part] = pmap_type; + + pmap_pos += pmap_size; + } + /* not winning the beauty contest */ + ump->vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW; + + /* test some basic UDF assertions/requirements */ + if ((n_virt > 1) || (n_spar > 1) || (n_meta > 1)) + return EINVAL; + + if (n_virt) { + if ((n_phys == 0) || n_spar || n_meta) + return EINVAL; + } + if (n_spar + n_phys == 0) + return EINVAL; + + /* select allocation type for each logical partition */ + for (log_part = 0; log_part < n_pm; log_part++) { + maps_on = ump->vtop[log_part]; + switch (ump->vtop_tp[log_part]) { + case UDF_VTOP_TYPE_PHYS : + assert(maps_on == log_part); + ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP; + break; + case UDF_VTOP_TYPE_VIRT : + ump->vtop_alloc[log_part] = UDF_ALLOC_VAT; + ump->vtop_alloc[maps_on] = UDF_ALLOC_SEQUENTIAL; + break; + case UDF_VTOP_TYPE_SPARABLE : + assert(maps_on == log_part); + ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP; + break; + case UDF_VTOP_TYPE_META : + ump->vtop_alloc[log_part] = UDF_ALLOC_METABITMAP; + if (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) { + /* special case for UDF 2.60 */ + ump->vtop_alloc[log_part] = UDF_ALLOC_METASEQUENTIAL; + ump->vtop_alloc[maps_on] = UDF_ALLOC_SEQUENTIAL; + } + break; + default: + panic("bad alloction type in udf's ump->vtop\n"); + } + } + + /* determine logical volume open/closure actions */ + if (n_virt) { + ump->lvopen = 0; + if (ump->discinfo.last_session_state == MMC_STATE_EMPTY) + ump->lvopen |= UDF_OPEN_SESSION ; + ump->lvclose = UDF_WRITE_VAT; + if (ump->mount_args.udfmflags & UDFMNT_CLOSESESSION) + ump->lvclose |= UDF_CLOSE_SESSION; + } else { + /* `normal' rewritable or non sequential media */ + ump->lvopen = UDF_WRITE_LVINT; + ump->lvclose = UDF_WRITE_LVINT; + if ((ump->discinfo.mmc_cur & MMC_CAP_REWRITABLE) == 0) + ump->lvopen |= UDF_APPENDONLY_LVINT; + if ((ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE)) + ump->lvopen &= ~UDF_APPENDONLY_LVINT; + } + + /* + * Determine sheduler error behaviour. For virtual partitions, update + * the trackinfo; for sparable partitions replace a whole block on the + * sparable table. Allways requeue. + */ + ump->lvreadwrite = 0; + if (n_virt) + ump->lvreadwrite = UDF_UPDATE_TRACKINFO; + if (n_spar) + ump->lvreadwrite = UDF_REMAP_BLOCK; + + /* + * Select our sheduler + */ + ump->strategy = &udf_strat_rmw; + if (n_virt || (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE)) + ump->strategy = &udf_strat_sequential; + if ((ump->discinfo.mmc_class == MMC_CLASS_DISC) || + (ump->discinfo.mmc_class == MMC_CLASS_UNKN)) + ump->strategy = &udf_strat_direct; + if (n_spar) + ump->strategy = &udf_strat_rmw; + +#if 0 + /* read-only access won't benefit from the other shedulers */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + ump->strategy = &udf_strat_direct; +#endif + + /* print results */ + DPRINTF(VOLUMES, ("\tdata partition %d\n", ump->data_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->data_part])); + DPRINTF(VOLUMES, ("\tnode partition %d\n", ump->node_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->node_part])); + DPRINTF(VOLUMES, ("\tfids partition %d\n", ump->fids_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->fids_part])); + + snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvopen); + DPRINTF(VOLUMES, ("\tactions on logvol open %s\n", bits)); + snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvclose); + DPRINTF(VOLUMES, ("\tactions on logvol close %s\n", bits)); + snprintb(bits, sizeof(bits), UDFONERROR_BITS, ump->lvreadwrite); + DPRINTF(VOLUMES, ("\tactions on logvol errors %s\n", bits)); + + DPRINTF(VOLUMES, ("\tselected sheduler `%s`\n", + (ump->strategy == &udf_strat_direct) ? "Direct" : + (ump->strategy == &udf_strat_sequential) ? "Sequential" : + (ump->strategy == &udf_strat_rmw) ? "RMW" : "UNKNOWN!")); + + /* signal its OK for now */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Update logical volume name in all structures that keep a record of it. We + * use memmove since each of them might be specified as a source. + * + * Note that it doesn't update the VAT structure! + */ + +static void +udf_update_logvolname(struct udf_mount *ump, char *logvol_id) +{ + struct logvol_desc *lvd = NULL; + struct fileset_desc *fsd = NULL; + struct udf_lv_info *lvi = NULL; + + DPRINTF(VOLUMES, ("Updating logical volume name\n")); + lvd = ump->logical_vol; + fsd = ump->fileset_desc; + if (ump->implementation) + lvi = &ump->implementation->_impl_use.lv_info; + + /* logvol's id might be specified as origional so use memmove here */ + memmove(lvd->logvol_id, logvol_id, 128); + if (fsd) + memmove(fsd->logvol_id, logvol_id, 128); + if (lvi) + memmove(lvi->logvol_id, logvol_id, 128); +} + +/* --------------------------------------------------------------------- */ + +void +udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid, + uint32_t sector) +{ + assert(ump->logical_vol); + + tag->id = udf_rw16(tagid); + tag->descriptor_ver = ump->logical_vol->tag.descriptor_ver; + tag->cksum = 0; + tag->reserved = 0; + tag->serial_num = ump->logical_vol->tag.serial_num; + tag->tag_loc = udf_rw32(sector); +} + + +uint64_t +udf_advance_uniqueid(struct udf_mount *ump) +{ + uint64_t unique_id; + + mutex_enter(&ump->logvol_mutex); + unique_id = udf_rw64(ump->logvol_integrity->lvint_next_unique_id); + if (unique_id < 0x10) + unique_id = 0x10; + ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id + 1); + mutex_exit(&ump->logvol_mutex); + + return unique_id; +} + + +static void +udf_adjust_filecount(struct udf_node *udf_node, int sign) +{ + struct udf_mount *ump = udf_node->ump; + uint32_t num_dirs, num_files; + int udf_file_type; + + /* get file type */ + if (udf_node->fe) { + udf_file_type = udf_node->fe->icbtag.file_type; + } else { + udf_file_type = udf_node->efe->icbtag.file_type; + } + + /* adjust file count */ + mutex_enter(&ump->allocate_mutex); + if (udf_file_type == UDF_ICB_FILETYPE_DIRECTORY) { + num_dirs = udf_rw32(ump->logvol_info->num_directories); + ump->logvol_info->num_directories = + udf_rw32((num_dirs + sign)); + } else { + num_files = udf_rw32(ump->logvol_info->num_files); + ump->logvol_info->num_files = + udf_rw32((num_files + sign)); + } + mutex_exit(&ump->allocate_mutex); +} + + +void +udf_osta_charset(struct charspec *charspec) +{ + memset(charspec, 0, sizeof(struct charspec)); + charspec->type = 0; + strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); +} + + +/* first call udf_set_regid and then the suffix */ +void +udf_set_regid(struct regid *regid, char const *name) +{ + memset(regid, 0, sizeof(struct regid)); + regid->flags = 0; /* not dirty and not protected */ + strcpy((char *) regid->id, name); +} + + +void +udf_add_domain_regid(struct udf_mount *ump, struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = ump->logvol_info->min_udf_readver; +} + + +void +udf_add_udf_regid(struct udf_mount *ump, struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = ump->logvol_info->min_udf_readver; + + regid->id_suffix[2] = 4; /* unix */ + regid->id_suffix[3] = 8; /* NetBSD */ +} + + +void +udf_add_impl_regid(struct udf_mount *ump, struct regid *regid) +{ + regid->id_suffix[0] = 4; /* unix */ + regid->id_suffix[1] = 8; /* NetBSD */ +} + + +void +udf_add_app_regid(struct udf_mount *ump, struct regid *regid) +{ + regid->id_suffix[0] = APP_VERSION_MAIN; + regid->id_suffix[1] = APP_VERSION_SUB; +} + +static int +udf_create_parentfid(struct udf_mount *ump, struct fileid_desc *fid, + struct long_ad *parent, uint64_t unique_id) +{ + /* the size of an empty FID is 38 but needs to be a multiple of 4 */ + int fidsize = 40; + + udf_inittag(ump, &fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR; + fid->icb = *parent; + fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id); + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +/* + * Extended attribute support. UDF knows of 3 places for extended attributes: + * + * (a) inside the file's (e)fe in the length of the extended attribute area + * before the allocation descriptors/filedata + * + * (b) in a file referenced by (e)fe->ext_attr_icb and + * + * (c) in the e(fe)'s associated stream directory that can hold various + * sub-files. In the stream directory a few fixed named subfiles are reserved + * for NT/Unix ACL's and OS/2 attributes. + * + * NOTE: Extended attributes are read randomly but allways written + * *atomicaly*. For ACL's this interface is propably different but not known + * to me yet. + * + * Order of extended attributes in a space : + * ECMA 167 EAs + * Non block aligned Implementation Use EAs + * Block aligned Implementation Use EAs + * Application Use EAs + */ + +static int +udf_impl_extattr_check(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) { + /* checksum valid? */ + DPRINTF(EXTATTR, ("checking UDF impl. attr checksum\n")); + spos = (uint16_t *) implext->data; + if (udf_rw16(*spos) != udf_ea_cksum((uint8_t *) implext)) + return EINVAL; + } + return 0; +} + +static void +udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) { + /* set checksum */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } +} + + +int +udf_extattr_search_intern(struct udf_node *node, + uint32_t sattr, char const *sattrname, + uint32_t *offsetp, uint32_t *lengthp) +{ + struct extattrhdr_desc *eahdr; + struct extattr_entry *attrhdr; + struct impl_extattr_entry *implext; + uint32_t offset, a_l, sector_size; + int32_t l_ea; + uint8_t *pos; + int error; + + /* get mountpoint */ + sector_size = node->ump->discinfo.sector_size; + + /* get information from fe/efe */ + if (node->fe) { + l_ea = udf_rw32(node->fe->l_ea); + eahdr = (struct extattrhdr_desc *) node->fe->data; + } else { + assert(node->efe); + l_ea = udf_rw32(node->efe->l_ea); + eahdr = (struct extattrhdr_desc *) node->efe->data; + } + + /* something recorded here? */ + if (l_ea == 0) + return ENOENT; + + /* check extended attribute tag; what to do if it fails? */ + error = udf_check_tag(eahdr); + if (error) + return EINVAL; + if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR) + return EINVAL; + error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc)); + if (error) + return EINVAL; + + DPRINTF(EXTATTR, ("Found %d bytes of extended attributes\n", l_ea)); + + /* looking for Ecma-167 attributes? */ + offset = sizeof(struct extattrhdr_desc); + + /* looking for either implemenation use or application use */ + if (sattr == 2048) { /* [4/48.10.8] */ + offset = udf_rw32(eahdr->impl_attr_loc); + if (offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + return ENOENT; + } + if (sattr == 65536) { /* [4/48.10.9] */ + offset = udf_rw32(eahdr->appl_attr_loc); + if (offset == UDF_APPL_ATTR_LOC_NOT_PRESENT) + return ENOENT; + } + + /* paranoia check offset and l_ea */ + if (l_ea + offset >= sector_size - sizeof(struct extattr_entry)) + return EINVAL; + + DPRINTF(EXTATTR, ("Starting at offset %d\n", offset)); + + /* find our extended attribute */ + l_ea -= offset; + pos = (uint8_t *) eahdr + offset; + + while (l_ea >= sizeof(struct extattr_entry)) { + DPRINTF(EXTATTR, ("%d extended attr bytes left\n", l_ea)); + attrhdr = (struct extattr_entry *) pos; + implext = (struct impl_extattr_entry *) pos; + + /* get complete attribute length and check for roque values */ + a_l = udf_rw32(attrhdr->a_l); + DPRINTF(EXTATTR, ("attribute %d:%d, len %d/%d\n", + udf_rw32(attrhdr->type), + attrhdr->subtype, a_l, l_ea)); + if ((a_l == 0) || (a_l > l_ea)) + return EINVAL; + + if (attrhdr->type != sattr) + goto next_attribute; + + /* we might have found it! */ + if (attrhdr->type < 2048) { /* Ecma-167 attribute */ + *offsetp = offset; + *lengthp = a_l; + return 0; /* success */ + } + + /* + * Implementation use and application use extended attributes + * have a name to identify. They share the same structure only + * UDF implementation use extended attributes have a checksum + * we need to check + */ + + DPRINTF(EXTATTR, ("named attribute %s\n", implext->imp_id.id)); + if (strcmp(implext->imp_id.id, sattrname) == 0) { + /* we have found our appl/implementation attribute */ + *offsetp = offset; + *lengthp = a_l; + return 0; /* success */ + } + +next_attribute: + /* next attribute */ + pos += a_l; + l_ea -= a_l; + offset += a_l; + } + /* not found */ + return ENOENT; +} + + +static void +udf_extattr_insert_internal(struct udf_mount *ump, union dscrptr *dscr, + struct extattr_entry *extattr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + struct impl_extattr_entry *implext; + uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len; + uint32_t *l_eap, l_ad; + uint16_t *spos; + uint8_t *bpos, *data; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_eap = &fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_eap = &efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + } else { + panic("Bad tag passed to udf_extattr_insert_internal"); + } + + /* can't append already written to file descriptors yet */ + assert(l_ad == 0); + + /* should have a header! */ + extattrhdr = (struct extattrhdr_desc *) data; + l_ea = udf_rw32(*l_eap); + if (l_ea == 0) { + /* create empty extended attribute header */ + exthdr_len = sizeof(struct extattrhdr_desc); + + udf_inittag(ump, &extattrhdr->tag, TAGID_EXTATTR_HDR, + /* loc */ 0); + extattrhdr->impl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->appl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->tag.desc_crc_len = udf_rw16(8); + + /* record extended attribute header length */ + l_ea = exthdr_len; + *l_eap = udf_rw32(l_ea); + } + + /* extract locations */ + impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc); + appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc); + if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + impl_attr_loc = l_ea; + if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + appl_attr_loc = l_ea; + + /* Ecma 167 EAs */ + if (udf_rw32(extattr->type) < 2048) { + assert(impl_attr_loc == l_ea); + assert(appl_attr_loc == l_ea); + } + + /* implementation use extended attributes */ + if (udf_rw32(extattr->type) == 2048) { + assert(appl_attr_loc == l_ea); + + /* calculate and write extended attribute header checksum */ + implext = (struct impl_extattr_entry *) extattr; + assert(udf_rw32(implext->iu_l) == 4); /* [UDF 3.3.4.5] */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } + + /* application use extended attributes */ + assert(udf_rw32(extattr->type) != 65536); + assert(appl_attr_loc == l_ea); + + /* append the attribute at the end of the current space */ + bpos = data + udf_rw32(*l_eap); + a_l = udf_rw32(extattr->a_l); + + /* update impl. attribute locations */ + if (udf_rw32(extattr->type) < 2048) { + impl_attr_loc = l_ea + a_l; + appl_attr_loc = l_ea + a_l; + } + if (udf_rw32(extattr->type) == 2048) { + appl_attr_loc = l_ea + a_l; + } + + /* copy and advance */ + memcpy(bpos, extattr, a_l); + l_ea += a_l; + *l_eap = udf_rw32(l_ea); + + /* do the `dance` again backwards */ + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) != 2) { + if (impl_attr_loc == l_ea) + impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT; + if (appl_attr_loc == l_ea) + appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT; + } + + /* store offsets */ + extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc); + extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc); +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_update_lvid_from_vat_extattr(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct udf_logvol_info *lvinfo; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry lvext; + const char *extstr = "*UDF VAT LVExtension"; + uint64_t vat_uniqueid; + uint32_t offset, a_l; + uint8_t *ea_start, *lvextpos; + int error; + + /* get mountpoint and lvinfo */ + ump = vat_node->ump; + lvinfo = ump->logvol_info; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_uniqueid = udf_rw64(vat_node->fe->unique_id); + ea_start = vat_node->fe->data; + } else { + vat_uniqueid = udf_rw64(vat_node->efe->unique_id); + ea_start = vat_node->efe->data; + } + + error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l); + if (error) + return error; + + implext = (struct impl_extattr_entry *) (ea_start + offset); + error = udf_impl_extattr_check(implext); + if (error) + return error; + + /* paranoia */ + if (a_l != sizeof(*implext) -1 + udf_rw32(implext->iu_l) + sizeof(lvext)) { + DPRINTF(VOLUMES, ("VAT LVExtension size doesn't compute\n")); + return EINVAL; + } + + /* + * we have found our "VAT LVExtension attribute. BUT due to a + * bug in the specification it might not be word aligned so + * copy first to avoid panics on some machines (!!) + */ + DPRINTF(VOLUMES, ("Found VAT LVExtension attr\n")); + lvextpos = implext->data + udf_rw32(implext->iu_l); + memcpy(&lvext, lvextpos, sizeof(lvext)); + + /* check if it was updated the last time */ + if (udf_rw64(lvext.unique_id_chk) == vat_uniqueid) { + lvinfo->num_files = lvext.num_files; + lvinfo->num_directories = lvext.num_directories; + udf_update_logvolname(ump, lvext.logvol_id); + } else { + DPRINTF(VOLUMES, ("VAT LVExtension out of date\n")); + /* replace VAT LVExt by free space EA */ + memset(implext->imp_id.id, 0, UDF_REGID_ID_SIZE); + strcpy(implext->imp_id.id, "*UDF FreeEASpace"); + udf_calc_impl_extattr_checksum(implext); + } + + return 0; +} + + +static int +udf_update_vat_extattr_from_lvid(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct udf_logvol_info *lvinfo; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry lvext; + const char *extstr = "*UDF VAT LVExtension"; + uint64_t vat_uniqueid; + uint32_t offset, a_l; + uint8_t *ea_start, *lvextpos; + int error; + + /* get mountpoint and lvinfo */ + ump = vat_node->ump; + lvinfo = ump->logvol_info; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_uniqueid = udf_rw64(vat_node->fe->unique_id); + ea_start = vat_node->fe->data; + } else { + vat_uniqueid = udf_rw64(vat_node->efe->unique_id); + ea_start = vat_node->efe->data; + } + + error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l); + if (error) + return error; + /* found, it existed */ + + /* paranoia */ + implext = (struct impl_extattr_entry *) (ea_start + offset); + error = udf_impl_extattr_check(implext); + if (error) { + DPRINTF(VOLUMES, ("VAT LVExtension bad on update\n")); + return error; + } + /* it is correct */ + + /* + * we have found our "VAT LVExtension attribute. BUT due to a + * bug in the specification it might not be word aligned so + * copy first to avoid panics on some machines (!!) + */ + DPRINTF(VOLUMES, ("Updating VAT LVExtension attr\n")); + lvextpos = implext->data + udf_rw32(implext->iu_l); + + lvext.unique_id_chk = vat_uniqueid; + lvext.num_files = lvinfo->num_files; + lvext.num_directories = lvinfo->num_directories; + memmove(lvext.logvol_id, ump->logical_vol->logvol_id, 128); + + memcpy(lvextpos, &lvext, sizeof(lvext)); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset) +{ + struct udf_mount *ump = vat_node->ump; + + if (offset + size > ump->vat_offset + ump->vat_entries * 4) + return EINVAL; + + memcpy(blob, ump->vat_table + offset, size); + return 0; +} + +int +udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset) +{ + struct udf_mount *ump = vat_node->ump; + uint32_t offset_high; + uint8_t *new_vat_table; + + /* extent VAT allocation if needed */ + offset_high = offset + size; + if (offset_high >= ump->vat_table_alloc_len) { + /* realloc */ + new_vat_table = realloc(ump->vat_table, + ump->vat_table_alloc_len + UDF_VAT_CHUNKSIZE, + M_UDFVOLD, M_WAITOK | M_CANFAIL); + if (!new_vat_table) { + printf("udf_vat_write: can't extent VAT, out of mem\n"); + return ENOMEM; + } + ump->vat_table = new_vat_table; + ump->vat_table_alloc_len += UDF_VAT_CHUNKSIZE; + } + ump->vat_table_len = MAX(ump->vat_table_len, offset_high); + + memcpy(ump->vat_table + offset, blob, size); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* TODO support previous VAT location writeout */ +static int +udf_update_vat_descriptor(struct udf_mount *ump) +{ + struct udf_node *vat_node = ump->vat_node; + struct udf_logvol_info *lvinfo = ump->logvol_info; + struct icb_tag *icbtag; + struct udf_oldvat_tail *oldvat_tl; + struct udf_vat *vat; + uint64_t unique_id; + uint32_t lb_size; + uint8_t *raw_vat; + int filetype, error; + + KASSERT(vat_node); + KASSERT(lvinfo); + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* get our new unique_id */ + unique_id = udf_advance_uniqueid(ump); + + /* get information from fe/efe */ + if (vat_node->fe) { + icbtag = &vat_node->fe->icbtag; + vat_node->fe->unique_id = udf_rw64(unique_id); + } else { + icbtag = &vat_node->efe->icbtag; + vat_node->efe->unique_id = udf_rw64(unique_id); + } + + /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */ + filetype = icbtag->file_type; + KASSERT((filetype == 0) || (filetype == UDF_ICB_FILETYPE_VAT)); + + /* allocate piece to process head or tail of VAT file */ + raw_vat = malloc(lb_size, M_TEMP, M_WAITOK); + + if (filetype == 0) { + /* + * Update "*UDF VAT LVExtension" extended attribute from the + * lvint if present. + */ + udf_update_vat_extattr_from_lvid(vat_node); + + /* setup identifying regid */ + oldvat_tl = (struct udf_oldvat_tail *) raw_vat; + memset(oldvat_tl, 0, sizeof(struct udf_oldvat_tail)); + + udf_set_regid(&oldvat_tl->id, "*UDF Virtual Alloc Tbl"); + udf_add_udf_regid(ump, &oldvat_tl->id); + oldvat_tl->prev_vat = udf_rw32(0xffffffff); + + /* write out new tail of virtual allocation table file */ + error = udf_vat_write(vat_node, raw_vat, + sizeof(struct udf_oldvat_tail), ump->vat_entries * 4); + } else { + /* compose the VAT2 header */ + vat = (struct udf_vat *) raw_vat; + memset(vat, 0, sizeof(struct udf_vat)); + + vat->header_len = udf_rw16(152); /* as per spec */ + vat->impl_use_len = udf_rw16(0); + memmove(vat->logvol_id, ump->logical_vol->logvol_id, 128); + vat->prev_vat = udf_rw32(0xffffffff); + vat->num_files = lvinfo->num_files; + vat->num_directories = lvinfo->num_directories; + vat->min_udf_readver = lvinfo->min_udf_readver; + vat->min_udf_writever = lvinfo->min_udf_writever; + vat->max_udf_writever = lvinfo->max_udf_writever; + + error = udf_vat_write(vat_node, raw_vat, + sizeof(struct udf_vat), 0); + } + free(raw_vat, M_TEMP); + + return error; /* success! */ +} + + +int +udf_writeout_vat(struct udf_mount *ump) +{ + struct udf_node *vat_node = ump->vat_node; + int error; + + KASSERT(vat_node); + + DPRINTF(CALL, ("udf_writeout_vat\n")); + +// mutex_enter(&ump->allocate_mutex); + udf_update_vat_descriptor(ump); + + /* write out the VAT contents ; TODO intelligent writing */ + error = vn_rdwr(UIO_WRITE, vat_node->vnode, + ump->vat_table, ump->vat_table_len, 0, + UIO_SYSSPACE, 0, FSCRED, NULL, NULL); + if (error) { + printf("udf_writeout_vat: failed to write out VAT contents\n"); + goto out; + } + +// mutex_exit(&ump->allocate_mutex); + + error = vflushbuf(ump->vat_node->vnode, FSYNC_WAIT); + if (error) + goto out; + error = VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + if (error) + printf("udf_writeout_vat: error writing VAT node!\n"); +out: + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Read in relevant pieces of VAT file and check if its indeed a VAT file + * descriptor. If OK, read in complete VAT file. + */ + +static int +udf_check_for_vat(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct icb_tag *icbtag; + struct timestamp *mtime; + struct udf_vat *vat; + struct udf_oldvat_tail *oldvat_tl; + struct udf_logvol_info *lvinfo; + uint64_t unique_id; + uint32_t vat_length; + uint32_t vat_offset, vat_entries, vat_table_alloc_len; + uint32_t sector_size; + uint32_t *raw_vat; + uint8_t *vat_table; + char *regid_name; + int filetype; + int error; + + /* vat_length is really 64 bits though impossible */ + + DPRINTF(VOLUMES, ("Checking for VAT\n")); + if (!vat_node) + return ENOENT; + + /* get mount info */ + ump = vat_node->ump; + sector_size = udf_rw32(ump->logical_vol->lb_size); + + /* check assertions */ + assert(vat_node->fe || vat_node->efe); + assert(ump->logvol_integrity); + + /* set vnode type to regular file or we can't read from it! */ + vat_node->vnode->v_type = VREG; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_length = udf_rw64(vat_node->fe->inf_len); + icbtag = &vat_node->fe->icbtag; + mtime = &vat_node->fe->mtime; + unique_id = udf_rw64(vat_node->fe->unique_id); + } else { + vat_length = udf_rw64(vat_node->efe->inf_len); + icbtag = &vat_node->efe->icbtag; + mtime = &vat_node->efe->mtime; + unique_id = udf_rw64(vat_node->efe->unique_id); + } + + /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */ + filetype = icbtag->file_type; + if ((filetype != 0) && (filetype != UDF_ICB_FILETYPE_VAT)) + return ENOENT; + + DPRINTF(VOLUMES, ("\tPossible VAT length %d\n", vat_length)); + + vat_table_alloc_len = + ((vat_length + UDF_VAT_CHUNKSIZE-1) / UDF_VAT_CHUNKSIZE) + * UDF_VAT_CHUNKSIZE; + + vat_table = malloc(vat_table_alloc_len, M_UDFVOLD, + M_CANFAIL | M_WAITOK); + if (vat_table == NULL) { + printf("allocation of %d bytes failed for VAT\n", + vat_table_alloc_len); + return ENOMEM; + } + + /* allocate piece to read in head or tail of VAT file */ + raw_vat = malloc(sector_size, M_TEMP, M_WAITOK); + + /* + * check contents of the file if its the old 1.50 VAT table format. + * Its notoriously broken and allthough some implementations support an + * extention as defined in the UDF 1.50 errata document, its doubtfull + * to be useable since a lot of implementations don't maintain it. + */ + lvinfo = ump->logvol_info; + + if (filetype == 0) { + /* definition */ + vat_offset = 0; + vat_entries = (vat_length-36)/4; + + /* read in tail of virtual allocation table file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + (uint8_t *) raw_vat, + sizeof(struct udf_oldvat_tail), + vat_entries * 4, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + goto out; + + /* check 1.50 VAT */ + oldvat_tl = (struct udf_oldvat_tail *) raw_vat; + regid_name = (char *) oldvat_tl->id.id; + error = strncmp(regid_name, "*UDF Virtual Alloc Tbl", 22); + if (error) { + DPRINTF(VOLUMES, ("VAT format 1.50 rejected\n")); + error = ENOENT; + goto out; + } + + /* + * update LVID from "*UDF VAT LVExtension" extended attribute + * if present. + */ + udf_update_lvid_from_vat_extattr(vat_node); + } else { + /* read in head of virtual allocation table file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + (uint8_t *) raw_vat, + sizeof(struct udf_vat), 0, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + goto out; + + /* definition */ + vat = (struct udf_vat *) raw_vat; + vat_offset = vat->header_len; + vat_entries = (vat_length - vat_offset)/4; + + assert(lvinfo); + lvinfo->num_files = vat->num_files; + lvinfo->num_directories = vat->num_directories; + lvinfo->min_udf_readver = vat->min_udf_readver; + lvinfo->min_udf_writever = vat->min_udf_writever; + lvinfo->max_udf_writever = vat->max_udf_writever; + + udf_update_logvolname(ump, vat->logvol_id); + } + + /* read in complete VAT file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + vat_table, + vat_length, 0, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + printf("read in of complete VAT file failed (error %d)\n", + error); + if (error) + goto out; + + DPRINTF(VOLUMES, ("VAT format accepted, marking it closed\n")); + ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id); + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED); + ump->logvol_integrity->time = *mtime; + + ump->vat_table_len = vat_length; + ump->vat_table_alloc_len = vat_table_alloc_len; + ump->vat_table = vat_table; + ump->vat_offset = vat_offset; + ump->vat_entries = vat_entries; + ump->vat_last_free_lb = 0; /* start at beginning */ + +out: + if (error) { + if (vat_table) + free(vat_table, M_UDFVOLD); + } + free(raw_vat, M_TEMP); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_search_vat(struct udf_mount *ump, union udf_pmap *mapping) +{ + struct udf_node *vat_node; + struct long_ad icb_loc; + uint32_t early_vat_loc, vat_loc; + int error; + + /* mapping info not needed */ + mapping = mapping; + + vat_loc = ump->last_possible_vat_location; + early_vat_loc = vat_loc - 256; /* 8 blocks of 32 sectors */ + + DPRINTF(VOLUMES, ("1) last possible %d, early_vat_loc %d \n", + vat_loc, early_vat_loc)); + early_vat_loc = MAX(early_vat_loc, ump->first_possible_vat_location); + + DPRINTF(VOLUMES, ("2) last possible %d, early_vat_loc %d \n", + vat_loc, early_vat_loc)); + + /* start looking from the end of the range */ + do { + DPRINTF(VOLUMES, ("Checking for VAT at sector %d\n", vat_loc)); + icb_loc.loc.part_num = udf_rw16(UDF_VTOP_RAWPART); + icb_loc.loc.lb_num = udf_rw32(vat_loc); + + error = udf_get_node(ump, &icb_loc, &vat_node); + if (!error) { + error = udf_check_for_vat(vat_node); + DPRINTFIF(VOLUMES, !error, + ("VAT accepted at %d\n", vat_loc)); + if (!error) + break; + } + if (vat_node) { + vput(vat_node->vnode); + vat_node = NULL; + } + vat_loc--; /* walk backwards */ + } while (vat_loc >= early_vat_loc); + + /* keep our VAT node around */ + if (vat_node) { + UDF_SET_SYSTEMFILE(vat_node->vnode); + ump->vat_node = vat_node; + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_sparables(struct udf_mount *ump, union udf_pmap *mapping) +{ + union dscrptr *dscr; + struct part_map_spare *pms = &mapping->pms; + uint32_t lb_num; + int spar, error; + + /* + * The partition mapping passed on to us specifies the information we + * need to locate and initialise the sparable partition mapping + * information we need. + */ + + DPRINTF(VOLUMES, ("Read sparable table\n")); + ump->sparable_packet_size = udf_rw16(pms->packet_len); + KASSERT(ump->sparable_packet_size >= ump->packet_size); /* XXX */ + + for (spar = 0; spar < pms->n_st; spar++) { + lb_num = pms->st_loc[spar]; + DPRINTF(VOLUMES, ("Checking for sparing table %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + if (udf_rw16(dscr->tag.id) == TAGID_SPARING_TABLE) { + if (ump->sparing_table) + free(ump->sparing_table, M_UDFVOLD); + ump->sparing_table = &dscr->spt; + dscr = NULL; + DPRINTF(VOLUMES, + ("Sparing table accepted (%d entries)\n", + udf_rw16(ump->sparing_table->rt_l))); + break; /* we're done */ + } + } + if (dscr) + free(dscr, M_UDFVOLD); + } + + if (ump->sparing_table) + return 0; + + return ENOENT; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_metadata_nodes(struct udf_mount *ump, union udf_pmap *mapping) +{ + struct part_map_meta *pmm = &mapping->pmm; + struct long_ad icb_loc; + struct vnode *vp; + uint16_t raw_phys_part, phys_part; + int error; + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + + /* extract our allocation parameters set up on format */ + ump->metadata_alloc_unit_size = udf_rw32(mapping->pmm.alloc_unit_size); + ump->metadata_alignment_unit_size = udf_rw16(mapping->pmm.alignment_unit_size); + ump->metadata_flags = mapping->pmm.flags; + + DPRINTF(VOLUMES, ("Reading in Metadata files\n")); + raw_phys_part = udf_rw16(pmm->part_num); + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + icb_loc.loc.part_num = udf_rw16(phys_part); + + DPRINTF(VOLUMES, ("Metadata file\n")); + icb_loc.loc.lb_num = pmm->meta_file_lbn; + error = udf_get_node(ump, &icb_loc, &ump->metadata_node); + if (ump->metadata_node) { + vp = ump->metadata_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + + icb_loc.loc.lb_num = pmm->meta_mirror_file_lbn; + if (icb_loc.loc.lb_num != -1) { + DPRINTF(VOLUMES, ("Metadata copy file\n")); + error = udf_get_node(ump, &icb_loc, &ump->metadatamirror_node); + if (ump->metadatamirror_node) { + vp = ump->metadatamirror_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + } + + icb_loc.loc.lb_num = pmm->meta_bitmap_file_lbn; + if (icb_loc.loc.lb_num != -1) { + DPRINTF(VOLUMES, ("Metadata bitmap file\n")); + error = udf_get_node(ump, &icb_loc, &ump->metadatabitmap_node); + if (ump->metadatabitmap_node) { + vp = ump->metadatabitmap_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + } + + /* if we're mounting read-only we relax the requirements */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) { + error = EFAULT; + if (ump->metadata_node) + error = 0; + if ((ump->metadata_node == NULL) && (ump->metadatamirror_node)) { + printf( "udf mount: Metadata file not readable, " + "substituting Metadata copy file\n"); + ump->metadata_node = ump->metadatamirror_node; + ump->metadatamirror_node = NULL; + error = 0; + } + } else { + /* mounting read/write */ + /* XXX DISABLED! metadata writing is not working yet XXX */ + if (error) + error = EROFS; + } + DPRINTFIF(VOLUMES, error, ("udf mount: failed to read " + "metadata files\n")); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read_vds_tables(struct udf_mount *ump) +{ + union udf_pmap *mapping; + /* struct udf_args *args = &ump->mount_args; */ + uint32_t n_pm; + uint32_t log_part; + uint8_t *pmap_pos; + int pmap_size; + int error; + + /* Iterate (again) over the part mappings for locations */ + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + pmap_pos = ump->logical_vol->maps; + + for (log_part = 0; log_part < n_pm; log_part++) { + mapping = (union udf_pmap *) pmap_pos; + switch (ump->vtop_tp[log_part]) { + case UDF_VTOP_TYPE_PHYS : + /* nothing */ + break; + case UDF_VTOP_TYPE_VIRT : + /* search and load VAT */ + error = udf_search_vat(ump, mapping); + if (error) + return ENOENT; + break; + case UDF_VTOP_TYPE_SPARABLE : + /* load one of the sparable tables */ + error = udf_read_sparables(ump, mapping); + if (error) + return ENOENT; + break; + case UDF_VTOP_TYPE_META : + /* load the associated file descriptors */ + error = udf_read_metadata_nodes(ump, mapping); + if (error) + return ENOENT; + break; + default: + break; + } + pmap_size = pmap_pos[1]; + pmap_pos += pmap_size; + } + + /* read in and check unallocated and free space info if writing */ + if ((ump->vfs_mountp->mnt_flag & MNT_RDONLY) == 0) { + error = udf_read_physical_partition_spacetables(ump); + if (error) + return error; + + /* also read in metadata partition spacebitmap if defined */ + error = udf_read_metadata_partition_spacetable(ump); + return error; + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read_rootdirs(struct udf_mount *ump) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct udf_node *rootdir_node, *streamdir_node; + struct long_ad fsd_loc, *dir_loc; + uint32_t lb_num, dummy; + uint32_t fsd_len; + int dscr_type; + int error; + + /* TODO implement FSD reading in separate function like integrity? */ + /* get fileset descriptor sequence */ + fsd_loc = ump->logical_vol->lv_fsd_loc; + fsd_len = udf_rw32(fsd_loc.len); + + dscr = NULL; + error = 0; + while (fsd_len || error) { + DPRINTF(VOLUMES, ("fsd_len = %d\n", fsd_len)); + /* translate fsd_loc to lb_num */ + error = udf_translate_vtop(ump, &fsd_loc, &lb_num, &dummy); + if (error) + break; + DPRINTF(VOLUMES, ("Reading FSD at lb %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + /* end markers */ + if (error || (dscr == NULL)) + break; + + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) + break; + if (dscr_type != TAGID_FSD) { + free(dscr, M_UDFVOLD); + return ENOENT; + } + + /* + * TODO check for multiple fileset descriptors; its only + * picking the last now. Also check for FSD + * correctness/interpretability + */ + + /* update */ + if (ump->fileset_desc) { + free(ump->fileset_desc, M_UDFVOLD); + } + ump->fileset_desc = &dscr->fsd; + dscr = NULL; + + /* continue to the next fsd */ + fsd_len -= ump->discinfo.sector_size; + fsd_loc.loc.lb_num = udf_rw32(udf_rw32(fsd_loc.loc.lb_num)+1); + + /* follow up to fsd->next_ex (long_ad) if its not null */ + if (udf_rw32(ump->fileset_desc->next_ex.len)) { + DPRINTF(VOLUMES, ("follow up FSD extent\n")); + fsd_loc = ump->fileset_desc->next_ex; + fsd_len = udf_rw32(ump->fileset_desc->next_ex.len); + } + } + if (dscr) + free(dscr, M_UDFVOLD); + + /* there has to be one */ + if (ump->fileset_desc == NULL) + return ENOENT; + + DPRINTF(VOLUMES, ("FSD read in fine\n")); + DPRINTF(VOLUMES, ("Updating fsd logical volume id\n")); + udf_update_logvolname(ump, ump->logical_vol->logvol_id); + + /* + * Now the FSD is known, read in the rootdirectory and if one exists, + * the system stream dir. Some files in the system streamdir are not + * wanted in this implementation since they are not maintained. If + * writing is enabled we'll delete these files if they exist. + */ + + rootdir_node = streamdir_node = NULL; + dir_loc = NULL; + + /* try to read in the rootdir */ + dir_loc = &ump->fileset_desc->rootdir_icb; + error = udf_get_node(ump, dir_loc, &rootdir_node); + if (error) + return ENOENT; + + /* aparently it read in fine */ + + /* + * Try the system stream directory; not very likely in the ones we + * test, but for completeness. + */ + dir_loc = &ump->fileset_desc->streamdir_icb; + if (udf_rw32(dir_loc->len)) { + printf("udf_read_rootdirs: streamdir defined "); + error = udf_get_node(ump, dir_loc, &streamdir_node); + if (error) { + printf("but error in streamdir reading\n"); + } else { + printf("but ignored\n"); + /* + * TODO process streamdir `baddies' i.e. files we dont + * want if R/W + */ + } + } + + DPRINTF(VOLUMES, ("Rootdir(s) read in fine\n")); + + /* release the vnodes again; they'll be auto-recycled later */ + if (streamdir_node) { + vput(streamdir_node->vnode); + } + if (rootdir_node) { + vput(rootdir_node->vnode); + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* To make absolutely sure we are NOT returning zero, add one :) */ + +long +udf_get_node_id(const struct long_ad *icbptr) +{ + /* ought to be enough since each mountpoint has its own chain */ + return udf_rw32(icbptr->loc.lb_num) + 1; +} + + +int +udf_compare_icb(const struct long_ad *a, const struct long_ad *b) +{ + if (udf_rw16(a->loc.part_num) < udf_rw16(b->loc.part_num)) + return -1; + if (udf_rw16(a->loc.part_num) > udf_rw16(b->loc.part_num)) + return 1; + + if (udf_rw32(a->loc.lb_num) < udf_rw32(b->loc.lb_num)) + return -1; + if (udf_rw32(a->loc.lb_num) > udf_rw32(b->loc.lb_num)) + return 1; + + return 0; +} + + +static int +udf_compare_rbnodes(void *ctx, const void *a, const void *b) +{ + const struct udf_node *a_node = a; + const struct udf_node *b_node = b; + + return udf_compare_icb(&a_node->loc, &b_node->loc); +} + + +static int +udf_compare_rbnode_icb(void *ctx, const void *a, const void *key) +{ + const struct udf_node *a_node = a; + const struct long_ad * const icb = key; + + return udf_compare_icb(&a_node->loc, icb); +} + + +static const rb_tree_ops_t udf_node_rbtree_ops = { + .rbto_compare_nodes = udf_compare_rbnodes, + .rbto_compare_key = udf_compare_rbnode_icb, + .rbto_node_offset = offsetof(struct udf_node, rbnode), + .rbto_context = NULL +}; + + +void +udf_init_nodes_tree(struct udf_mount *ump) +{ + + rb_tree_init(&ump->udf_node_tree, &udf_node_rbtree_ops); +} + + +static struct udf_node * +udf_node_lookup(struct udf_mount *ump, struct long_ad *icbptr) +{ + struct udf_node *udf_node; + struct vnode *vp; + +loop: + mutex_enter(&ump->ihash_lock); + + udf_node = rb_tree_find_node(&ump->udf_node_tree, icbptr); + if (udf_node) { + vp = udf_node->vnode; + assert(vp); + mutex_enter(vp->v_interlock); + mutex_exit(&ump->ihash_lock); + if (vget(vp, LK_EXCLUSIVE)) + goto loop; + return udf_node; + } + mutex_exit(&ump->ihash_lock); + + return NULL; +} + + +static void +udf_register_node(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + + /* add node to the rb tree */ + mutex_enter(&ump->ihash_lock); + rb_tree_insert_node(&ump->udf_node_tree, udf_node); + mutex_exit(&ump->ihash_lock); +} + + +static void +udf_deregister_node(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + + /* remove node from the rb tree */ + mutex_enter(&ump->ihash_lock); + rb_tree_remove_node(&ump->udf_node_tree, udf_node); + mutex_exit(&ump->ihash_lock); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_validate_session_start(struct udf_mount *ump) +{ + struct mmc_trackinfo trackinfo; + struct vrs_desc *vrs; + uint32_t tracknr, sessionnr, sector, sector_size; + uint32_t iso9660_vrs, write_track_start; + uint8_t *buffer, *blank, *pos; + int blks, max_sectors, vrs_len; + int error; + + /* disc appendable? */ + if (ump->discinfo.disc_state == MMC_STATE_FULL) + return EROFS; + + /* already written here? if so, there should be an ISO VDS */ + if (ump->discinfo.last_session_state == MMC_STATE_INCOMPLETE) + return 0; + + /* + * Check if the first track of the session is blank and if so, copy or + * create a dummy ISO descriptor so the disc is valid again. + */ + + tracknr = ump->discinfo.first_track_last_session; + memset(&trackinfo, 0, sizeof(struct mmc_trackinfo)); + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + udf_dump_trackinfo(&trackinfo); + KASSERT(trackinfo.flags & (MMC_TRACKINFO_BLANK | MMC_TRACKINFO_RESERVED)); + KASSERT(trackinfo.sessionnr > 1); + + KASSERT(trackinfo.flags & MMC_TRACKINFO_NWA_VALID); + write_track_start = trackinfo.next_writable; + + /* we have to copy the ISO VRS from a former session */ + DPRINTF(VOLUMES, ("validate_session_start: " + "blank or reserved track, copying VRS\n")); + + /* sessionnr should be the session we're mounting */ + sessionnr = ump->mount_args.sessionnr; + + /* start at the first track */ + tracknr = ump->discinfo.first_track; + while (tracknr <= ump->discinfo.num_tracks) { + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) { + DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n")); + return error; + } + if (trackinfo.sessionnr == sessionnr) + break; + tracknr++; + } + if (trackinfo.sessionnr != sessionnr) { + DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n")); + return ENOENT; + } + + DPRINTF(VOLUMES, ("found possible former ISO VRS at\n")); + udf_dump_trackinfo(&trackinfo); + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum ISO `sector size' 2048 + */ + sector_size = ump->discinfo.sector_size; + iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + trackinfo.track_start; + + buffer = malloc(UDF_ISO_VRS_SIZE, M_TEMP, M_WAITOK); + max_sectors = UDF_ISO_VRS_SIZE / sector_size; + blks = MAX(1, 2048 / sector_size); + + error = 0; + for (sector = 0; sector < max_sectors; sector += blks) { + pos = buffer + sector * sector_size; + error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos, + iso9660_vrs + sector, blks); + if (error) + break; + /* check this ISO descriptor */ + vrs = (struct vrs_desc *) pos; + DPRINTF(VOLUMES, ("got VRS id `%4s`\n", vrs->identifier)); + if (strncmp(vrs->identifier, VRS_CD001, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_CDW02, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_BEA01, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_NSR02, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_NSR03, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_TEA01, 5) == 0) + break; + /* now what? for now, end of sequence */ + break; + } + vrs_len = sector + blks; + if (error) { + DPRINTF(VOLUMES, ("error reading old ISO VRS\n")); + DPRINTF(VOLUMES, ("creating minimal ISO VRS\n")); + + memset(buffer, 0, UDF_ISO_VRS_SIZE); + + vrs = (struct vrs_desc *) (buffer); + vrs->struct_type = 0; + vrs->version = 1; + memcpy(vrs->identifier,VRS_BEA01, 5); + + vrs = (struct vrs_desc *) (buffer + 2048); + vrs->struct_type = 0; + vrs->version = 1; + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) { + memcpy(vrs->identifier,VRS_NSR02, 5); + } else { + memcpy(vrs->identifier,VRS_NSR03, 5); + } + + vrs = (struct vrs_desc *) (buffer + 4096); + vrs->struct_type = 0; + vrs->version = 1; + memcpy(vrs->identifier, VRS_TEA01, 5); + + vrs_len = 3*blks; + } + + DPRINTF(VOLUMES, ("Got VRS of %d sectors long\n", vrs_len)); + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum ISO `sector size' 2048 + */ + sector_size = ump->discinfo.sector_size; + iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + write_track_start; + + /* write out 32 kb */ + blank = malloc(sector_size, M_TEMP, M_WAITOK); + memset(blank, 0, sector_size); + error = 0; + for (sector = write_track_start; sector < iso9660_vrs; sector ++) { + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, + blank, sector, 1); + if (error) + break; + } + if (!error) { + /* write out our ISO VRS */ + KASSERT(sector == iso9660_vrs); + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, buffer, + sector, vrs_len); + sector += vrs_len; + } + if (!error) { + /* fill upto the first anchor at S+256 */ + for (; sector < write_track_start+256; sector++) { + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, + blank, sector, 1); + if (error) + break; + } + } + if (!error) { + /* write out anchor; write at ABSOLUTE place! */ + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_ABSOLUTE, + (union dscrptr *) ump->anchors[0], sector, sector); + if (error) + printf("writeout of anchor failed!\n"); + } + + free(blank, M_TEMP); + free(buffer, M_TEMP); + + if (error) + printf("udf_open_session: error writing iso vrs! : " + "leaving disc in compromised state!\n"); + + /* synchronise device caches */ + (void) udf_synchronise_caches(ump); + + return error; +} + + +int +udf_open_logvol(struct udf_mount *ump) +{ + int logvol_integrity; + int error; + + /* already/still open? */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_OPEN) + return 0; + + /* can we open it ? */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + return EROFS; + + /* setup write parameters */ + DPRINTF(VOLUMES, ("Setting up write parameters\n")); + if ((error = udf_setup_writeparams(ump)) != 0) + return error; + + /* determine data and metadata tracks (most likely same) */ + error = udf_search_writing_tracks(ump); + if (error) { + /* most likely lack of space */ + printf("udf_open_logvol: error searching writing tracks\n"); + return EROFS; + } + + /* writeout/update lvint on disc or only in memory */ + DPRINTF(VOLUMES, ("Opening logical volume\n")); + if (ump->lvopen & UDF_OPEN_SESSION) { + /* TODO optional track reservation opening */ + error = udf_validate_session_start(ump); + if (error) + return error; + + /* determine data and metadata tracks again */ + error = udf_search_writing_tracks(ump); + } + + /* mark it open */ + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_OPEN); + + /* do we need to write it out? */ + if (ump->lvopen & UDF_WRITE_LVINT) { + error = udf_writeout_lvint(ump, ump->lvopen); + /* if we couldn't write it mark it closed again */ + if (error) { + ump->logvol_integrity->integrity_type = + udf_rw32(UDF_INTEGRITY_CLOSED); + return error; + } + } + + return 0; +} + + +int +udf_close_logvol(struct udf_mount *ump, int mntflags) +{ + struct vnode *devvp = ump->devvp; + struct mmc_op mmc_op; + int logvol_integrity; + int error = 0, error1 = 0, error2 = 0; + int tracknr; + int nvats, n, nok; + + /* already/still closed? */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_CLOSED) + return 0; + + /* writeout/update lvint or write out VAT */ + DPRINTF(VOLUMES, ("udf_close_logvol: closing logical volume\n")); +#ifdef DIAGNOSTIC + if (ump->lvclose & UDF_CLOSE_SESSION) + KASSERT(ump->lvclose & UDF_WRITE_VAT); +#endif + + if (ump->lvclose & UDF_WRITE_VAT) { + DPRINTF(VOLUMES, ("lvclose & UDF_WRITE_VAT\n")); + + /* write out the VAT data and all its descriptors */ + DPRINTF(VOLUMES, ("writeout vat_node\n")); + udf_writeout_vat(ump); + (void) vflushbuf(ump->vat_node->vnode, FSYNC_WAIT); + + (void) VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + + if (ump->lvclose & UDF_CLOSE_SESSION) { + DPRINTF(VOLUMES, ("udf_close_logvol: closing session " + "as requested\n")); + } + + /* at least two DVD packets and 3 CD-R packets */ + nvats = 32; + +#if notyet + /* + * TODO calculate the available space and if the disc is + * allmost full, write out till end-256-1 with banks, write + * AVDP and fill up with VATs, then close session and close + * disc. + */ + if (ump->lvclose & UDF_FINALISE_DISC) { + error = udf_write_phys_dscr_sync(ump, NULL, + UDF_C_FLOAT_DSCR, + (union dscrptr *) ump->anchors[0], + 0, 0); + if (error) + printf("writeout of anchor failed!\n"); + + /* pad space with VAT ICBs */ + nvats = 256; + } +#endif + + /* write out a number of VAT nodes */ + nok = 0; + for (n = 0; n < nvats; n++) { + /* will now only write last FE/EFE */ + ump->vat_node->i_flags |= IN_MODIFIED; + error = VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + if (!error) + nok++; + } + if (nok < 14) { + /* arbitrary; but at least one or two CD frames */ + printf("writeout of at least 14 VATs failed\n"); + return error; + } + } + + /* NOTE the disc is in a (minimal) valid state now; no erroring out */ + + /* finish closing of session */ + if (ump->lvclose & UDF_CLOSE_SESSION) { + error = udf_validate_session_start(ump); + if (error) + return error; + + (void) udf_synchronise_caches(ump); + + /* close all associated tracks */ + tracknr = ump->discinfo.first_track_last_session; + error = 0; + while (tracknr <= ump->discinfo.last_track_last_session) { + DPRINTF(VOLUMES, ("\tclosing possible open " + "track %d\n", tracknr)); + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_CLOSETRACK; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.tracknr = tracknr; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: closing of " + "track %d failed\n", tracknr); + tracknr ++; + } + if (!error) { + DPRINTF(VOLUMES, ("closing session\n")); + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_CLOSESESSION; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.sessionnr = ump->discinfo.num_sessions; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: closing of session" + "failed\n"); + } + if (!error) + ump->lvopen |= UDF_OPEN_SESSION; + if (error) { + printf("udf_close_logvol: leaving disc as it is\n"); + ump->lvclose &= ~UDF_FINALISE_DISC; + } + } + + if (ump->lvclose & UDF_FINALISE_DISC) { + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_FINALISEDISC; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.sessionnr = ump->discinfo.num_sessions; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: finalising disc" + "failed\n"); + } + + /* write out partition bitmaps if requested */ + if (ump->lvclose & UDF_WRITE_PART_BITMAPS) { + /* sync writeout metadata spacetable if existing */ + error1 = udf_write_metadata_partition_spacetable(ump, true); + if (error1) + printf( "udf_close_logvol: writeout of metadata space " + "bitmap failed\n"); + + /* sync writeout partition spacetables */ + error2 = udf_write_physical_partition_spacetables(ump, true); + if (error2) + printf( "udf_close_logvol: writeout of space tables " + "failed\n"); + + if (error1 || error2) + return (error1 | error2); + + ump->lvclose &= ~UDF_WRITE_PART_BITMAPS; + } + + /* write out metadata partition nodes if requested */ + if (ump->lvclose & UDF_WRITE_METAPART_NODES) { + /* sync writeout metadata descriptor node */ + error1 = udf_writeout_node(ump->metadata_node, FSYNC_WAIT); + if (error1) + printf( "udf_close_logvol: writeout of metadata partition " + "node failed\n"); + + /* duplicate metadata partition descriptor if needed */ + udf_synchronise_metadatamirror_node(ump); + + /* sync writeout metadatamirror descriptor node */ + error2 = udf_writeout_node(ump->metadatamirror_node, FSYNC_WAIT); + if (error2) + printf( "udf_close_logvol: writeout of metadata partition " + "mirror node failed\n"); + + if (error1 || error2) + return (error1 | error2); + + ump->lvclose &= ~UDF_WRITE_METAPART_NODES; + } + + /* mark it closed */ + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED); + + /* do we need to write out the logical volume integrity? */ + if (ump->lvclose & UDF_WRITE_LVINT) + error = udf_writeout_lvint(ump, ump->lvopen); + if (error) { + /* HELP now what? mark it open again for now */ + ump->logvol_integrity->integrity_type = + udf_rw32(UDF_INTEGRITY_OPEN); + return error; + } + + (void) udf_synchronise_caches(ump); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Genfs interfacing + * + * static const struct genfs_ops udf_genfsops = { + * .gop_size = genfs_size, + * size of transfers + * .gop_alloc = udf_gop_alloc, + * allocate len bytes at offset + * .gop_write = genfs_gop_write, + * putpages interface code + * .gop_markupdate = udf_gop_markupdate, + * set update/modify flags etc. + * } + */ + +/* + * Genfs interface. These four functions are the only ones defined though not + * documented... great.... + */ + +/* + * Called for allocating an extent of the file either by VOP_WRITE() or by + * genfs filling up gaps. + */ +static int +udf_gop_alloc(struct vnode *vp, off_t off, + off_t len, int flags, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + uint64_t lb_start, lb_end; + uint32_t lb_size, num_lb; + int udf_c_type, vpart_num, can_fail; + int error; + + DPRINTF(ALLOC, ("udf_gop_alloc called for offset %"PRIu64" for %"PRIu64" bytes, %s\n", + off, len, flags? "SYNC":"NONE")); + + /* + * request the pages of our vnode and see how many pages will need to + * be allocated and reserve that space + */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + lb_start = off / lb_size; + lb_end = (off + len + lb_size -1) / lb_size; + num_lb = lb_end - lb_start; + + udf_c_type = udf_get_c_type(udf_node); + vpart_num = udf_get_record_vpart(ump, udf_c_type); + + /* all requests can fail */ + can_fail = true; + + /* fid's (directories) can't fail */ + if (udf_c_type == UDF_C_FIDS) + can_fail = false; + + /* system files can't fail */ + if (vp->v_vflag & VV_SYSTEM) + can_fail = false; + + error = udf_reserve_space(ump, udf_node, udf_c_type, + vpart_num, num_lb, can_fail); + + DPRINTF(ALLOC, ("\tlb_start %"PRIu64", lb_end %"PRIu64", num_lb %d\n", + lb_start, lb_end, num_lb)); + + return error; +} + + +/* + * callback from genfs to update our flags + */ +static void +udf_gop_markupdate(struct vnode *vp, int flags) +{ + struct udf_node *udf_node = VTOI(vp); + u_long mask = 0; + + if ((flags & GOP_UPDATE_ACCESSED) != 0) { + mask = IN_ACCESS; + } + if ((flags & GOP_UPDATE_MODIFIED) != 0) { + if (vp->v_type == VREG) { + mask |= IN_CHANGE | IN_UPDATE; + } else { + mask |= IN_MODIFY; + } + } + if (mask) { + udf_node->i_flags |= mask; + } +} + + +static const struct genfs_ops udf_genfsops = { + .gop_size = genfs_size, + .gop_alloc = udf_gop_alloc, + .gop_write = genfs_gop_write_rwmap, + .gop_markupdate = udf_gop_markupdate, +}; + + +/* --------------------------------------------------------------------- */ + +int +udf_write_terminator(struct udf_mount *ump, uint32_t sector) +{ + union dscrptr *dscr; + int error; + + dscr = malloc(ump->discinfo.sector_size, M_TEMP, M_WAITOK|M_ZERO); + udf_inittag(ump, &dscr->tag, TAGID_TERM, sector); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums(dscr); + + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + dscr, sector, sector); + + free(dscr, M_TEMP); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* UDF<->unix converters */ + +/* --------------------------------------------------------------------- */ + +static mode_t +udf_perm_to_unix_mode(uint32_t perm) +{ + mode_t mode; + + mode = ((perm & UDF_FENTRY_PERM_USER_MASK) ); + mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK ) >> 2); + mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4); + + return mode; +} + +/* --------------------------------------------------------------------- */ + +static uint32_t +unix_mode_to_udf_perm(mode_t mode) +{ + uint32_t perm; + + perm = ((mode & S_IRWXO) ); + perm |= ((mode & S_IRWXG) << 2); + perm |= ((mode & S_IRWXU) << 4); + perm |= ((mode & S_IWOTH) << 3); + perm |= ((mode & S_IWGRP) << 5); + perm |= ((mode & S_IWUSR) << 7); + + return perm; +} + +/* --------------------------------------------------------------------- */ + +static uint32_t +udf_icb_to_unix_filetype(uint32_t icbftype) +{ + switch (icbftype) { + case UDF_ICB_FILETYPE_DIRECTORY : + case UDF_ICB_FILETYPE_STREAMDIR : + return S_IFDIR; + case UDF_ICB_FILETYPE_FIFO : + return S_IFIFO; + case UDF_ICB_FILETYPE_CHARDEVICE : + return S_IFCHR; + case UDF_ICB_FILETYPE_BLOCKDEVICE : + return S_IFBLK; + case UDF_ICB_FILETYPE_RANDOMACCESS : + case UDF_ICB_FILETYPE_REALTIME : + return S_IFREG; + case UDF_ICB_FILETYPE_SYMLINK : + return S_IFLNK; + case UDF_ICB_FILETYPE_SOCKET : + return S_IFSOCK; + } + /* no idea what this is */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +void +udf_to_unix_name(char *result, int result_len, char *id, int len, + struct charspec *chsp) +{ + uint16_t *raw_name, *unix_name; + uint16_t *inchp, ch; + uint8_t *outchp; + const char *osta_id = "OSTA Compressed Unicode"; + int ucode_chars, nice_uchars, is_osta_typ0, nout; + + raw_name = malloc(2048 * sizeof(uint16_t), M_UDFTEMP, M_WAITOK); + unix_name = raw_name + 1024; /* split space in half */ + assert(sizeof(char) == sizeof(uint8_t)); + outchp = (uint8_t *) result; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + /* TODO clean up */ + *raw_name = *unix_name = 0; + ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); + ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name)); + nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars); + /* output UTF8 */ + for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) { + ch = *inchp; + nout = wput_utf8(outchp, result_len, ch); + outchp += nout; result_len -= nout; + if (!ch) break; + } + *outchp++ = 0; + } else { + /* assume 8bit char length byte latin-1 */ + assert(*id == 8); + assert(strlen((char *) (id+1)) <= NAME_MAX); + strncpy((char *) result, (char *) (id+1), strlen((char *) (id+1))); + } + free(raw_name, M_UDFTEMP); +} + +/* --------------------------------------------------------------------- */ + +void +unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, + struct charspec *chsp) +{ + uint16_t *raw_name; + uint16_t *outchp; + const char *inchp; + const char *osta_id = "OSTA Compressed Unicode"; + int udf_chars, is_osta_typ0, bits; + size_t cnt; + + /* allocate temporary unicode-16 buffer */ + raw_name = malloc(1024, M_UDFTEMP, M_WAITOK); + + /* convert utf8 to unicode-16 */ + *raw_name = 0; + inchp = name; + outchp = raw_name; + bits = 8; + for (cnt = name_len, udf_chars = 0; cnt;) { + *outchp = wget_utf8(&inchp, &cnt); + if (*outchp > 0xff) + bits=16; + outchp++; + udf_chars++; + } + /* null terminate just in case */ + *outchp++ = 0; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + udf_chars = udf_CompressUnicode(udf_chars, bits, + (unicode_t *) raw_name, + (byte *) result); + } else { + printf("unix to udf name: no CHSP0 ?\n"); + /* XXX assume 8bit char length byte latin-1 */ + *result++ = 8; udf_chars = 1; + strncpy(result, name + 1, name_len); + udf_chars += name_len; + } + *result_len = udf_chars; + free(raw_name, M_UDFTEMP); +} + +/* --------------------------------------------------------------------- */ + +void +udf_timestamp_to_timespec(struct udf_mount *ump, + struct timestamp *timestamp, + struct timespec *timespec) +{ + struct clock_ymdhms ymdhms; + uint32_t usecs, secs, nsecs; + uint16_t tz; + + /* fill in ymdhms structure from timestamp */ + memset(&ymdhms, 0, sizeof(ymdhms)); + ymdhms.dt_year = udf_rw16(timestamp->year); + ymdhms.dt_mon = timestamp->month; + ymdhms.dt_day = timestamp->day; + ymdhms.dt_wday = 0; /* ? */ + ymdhms.dt_hour = timestamp->hour; + ymdhms.dt_min = timestamp->minute; + ymdhms.dt_sec = timestamp->second; + + secs = clock_ymdhms_to_secs(&ymdhms); + usecs = timestamp->usec + + 100*timestamp->hund_usec + 10000*timestamp->centisec; + nsecs = usecs * 1000; + + /* + * Calculate the time zone. The timezone is 12 bit signed 2's + * compliment, so we gotta do some extra magic to handle it right. + */ + tz = udf_rw16(timestamp->type_tz); + tz &= 0x0fff; /* only lower 12 bits are significant */ + if (tz & 0x0800) /* sign extention */ + tz |= 0xf000; + + /* TODO check timezone conversion */ + /* check if we are specified a timezone to convert */ + if (udf_rw16(timestamp->type_tz) & 0x1000) { + if ((int16_t) tz != -2047) + secs -= (int16_t) tz * 60; + } else { + secs -= ump->mount_args.gmtoff; + } + + timespec->tv_sec = secs; + timespec->tv_nsec = nsecs; +} + + +void +udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) +{ + struct clock_ymdhms ymdhms; + uint32_t husec, usec, csec; + + (void) clock_secs_to_ymdhms(timespec->tv_sec, &ymdhms); + + usec = timespec->tv_nsec / 1000; + husec = usec / 100; + usec -= husec * 100; /* only 0-99 in usec */ + csec = husec / 100; /* only 0-99 in csec */ + husec -= csec * 100; /* only 0-99 in husec */ + + /* set method 1 for CUT/GMT */ + timestamp->type_tz = udf_rw16((1<<12) + 0); + timestamp->year = udf_rw16(ymdhms.dt_year); + timestamp->month = ymdhms.dt_mon; + timestamp->day = ymdhms.dt_day; + timestamp->hour = ymdhms.dt_hour; + timestamp->minute = ymdhms.dt_min; + timestamp->second = ymdhms.dt_sec; + timestamp->centisec = csec; + timestamp->hund_usec = husec; + timestamp->usec = usec; +} + +/* --------------------------------------------------------------------- */ + +/* + * Attribute and filetypes converters with get/set pairs + */ + +uint32_t +udf_getaccessmode(struct udf_node *udf_node) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint32_t udf_perm, icbftype; + uint32_t mode, ftype; + uint16_t icbflags; + + UDF_LOCK_NODE(udf_node, 0); + if (fe) { + udf_perm = udf_rw32(fe->perm); + icbftype = fe->icbtag.file_type; + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(udf_node->efe); + udf_perm = udf_rw32(efe->perm); + icbftype = efe->icbtag.file_type; + icbflags = udf_rw16(efe->icbtag.flags); + } + + mode = udf_perm_to_unix_mode(udf_perm); + ftype = udf_icb_to_unix_filetype(icbftype); + + /* set suid, sgid, sticky from flags in fe/efe */ + if (icbflags & UDF_ICB_TAG_FLAGS_SETUID) + mode |= S_ISUID; + if (icbflags & UDF_ICB_TAG_FLAGS_SETGID) + mode |= S_ISGID; + if (icbflags & UDF_ICB_TAG_FLAGS_STICKY) + mode |= S_ISVTX; + + UDF_UNLOCK_NODE(udf_node, 0); + + return mode | ftype; +} + + +void +udf_setaccessmode(struct udf_node *udf_node, mode_t mode) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint32_t udf_perm; + uint16_t icbflags; + + UDF_LOCK_NODE(udf_node, 0); + udf_perm = unix_mode_to_udf_perm(mode & ALLPERMS); + if (fe) { + icbflags = udf_rw16(fe->icbtag.flags); + } else { + icbflags = udf_rw16(efe->icbtag.flags); + } + + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + + if (fe) { + fe->perm = udf_rw32(udf_perm); + fe->icbtag.flags = udf_rw16(icbflags); + } else { + efe->perm = udf_rw32(udf_perm); + efe->icbtag.flags = udf_rw16(icbflags); + } + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +void +udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp) +{ + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uid_t uid; + gid_t gid; + + UDF_LOCK_NODE(udf_node, 0); + if (fe) { + uid = (uid_t)udf_rw32(fe->uid); + gid = (gid_t)udf_rw32(fe->gid); + } else { + assert(udf_node->efe); + uid = (uid_t)udf_rw32(efe->uid); + gid = (gid_t)udf_rw32(efe->gid); + } + + /* do the uid/gid translation game */ + if (uid == (uid_t) -1) + uid = ump->mount_args.anon_uid; + if (gid == (gid_t) -1) + gid = ump->mount_args.anon_gid; + + *uidp = uid; + *gidp = gid; + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +void +udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid) +{ + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uid_t nobody_uid; + gid_t nobody_gid; + + UDF_LOCK_NODE(udf_node, 0); + + /* do the uid/gid translation game */ + nobody_uid = ump->mount_args.nobody_uid; + nobody_gid = ump->mount_args.nobody_gid; + if (uid == nobody_uid) + uid = (uid_t) -1; + if (gid == nobody_gid) + gid = (gid_t) -1; + + if (fe) { + fe->uid = udf_rw32((uint32_t) uid); + fe->gid = udf_rw32((uint32_t) gid); + } else { + efe->uid = udf_rw32((uint32_t) uid); + efe->gid = udf_rw32((uint32_t) gid); + } + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +/* --------------------------------------------------------------------- */ + + +int +udf_dirhash_fill(struct udf_node *dir_node) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct file_entry *fe = dir_node->fe; + struct extfile_entry *efe = dir_node->efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t file_size, pre_diroffset, diroffset; + uint32_t lb_size; + int error; + + /* make sure we have a dirhash to work on */ + dirh = dir_node->dir_hash; + KASSERT(dirh); + KASSERT(dirh->refcnt > 0); + + if (dirh->flags & DIRH_BROKEN) + return EIO; + if (dirh->flags & DIRH_COMPLETE) + return 0; + + /* make sure we have a clean dirhash to add to */ + dirhash_purge_entries(dirh); + + /* get directory filesize */ + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(efe); + file_size = udf_rw64(efe->inf_len); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + /* allocate temporary space for dirent */ + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + error = 0; + diroffset = 0; + while (diroffset < file_size) { + /* transfer a new fid/dirent */ + pre_diroffset = diroffset; + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) { + /* TODO what to do? continue but not add? */ + dirh->flags |= DIRH_BROKEN; + dirhash_purge_entries(dirh); + break; + } + + if ((fid->file_char & UDF_FILE_CHAR_DEL)) { + /* register deleted extent for reuse */ + dirhash_enter_freed(dirh, pre_diroffset, + udf_fidsize(fid)); + } else { + /* append to the dirhash */ + dirhash_enter(dirh, dirent, pre_diroffset, + udf_fidsize(fid), 0); + } + } + dirh->flags |= DIRH_COMPLETE; + + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Directory read and manipulation functions. + * + */ + +int +udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, + struct long_ad *icb_loc, int *found) +{ + struct udf_node *dir_node = VTOI(vp); + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint32_t lb_size; + int hit, error; + + /* set default return */ + *found = 0; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + DPRINTF(DIRHASH, ("dirhash_lookup looking for `%*.*s`\n", + namelen, namelen, name)); + + /* search our dirhash hits */ + memset(icb_loc, 0, sizeof(*icb_loc)); + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &diroffset, fid, dirent); + if (error) + break; + + DPRINTF(DIRHASH, ("dirhash_lookup\tchecking `%*.*s`\n", + dirent->d_namlen, dirent->d_namlen, dirent->d_name)); + + /* see if its our entry */ +#ifdef DIAGNOSTIC + if (dirent->d_namlen != namelen) { + printf("WARNING: dirhash_lookup() returned wrong " + "d_namelen: %d and ought to be %d\n", + dirent->d_namlen, namelen); + printf("\tlooked for `%s' and got `%s'\n", + name, dirent->d_name); + } +#endif + if (strncmp(dirent->d_name, name, namelen) == 0) { + *found = 1; + *icb_loc = fid->icb; + break; + } + } + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_new_fe(struct udf_mount *ump, struct file_entry *fe, int file_type, + struct long_ad *node_icb, struct long_ad *parent_icb, + uint64_t parent_unique_id) +{ + struct timespec now; + struct icb_tag *icb; + struct filetimes_extattr_entry *ft_extattr; + uint64_t unique_id; + uint32_t fidsize, lb_num; + uint8_t *bpos; + int crclen, attrlen; + + lb_num = udf_rw32(node_icb->loc.lb_num); + udf_inittag(ump, &fe->tag, TAGID_FENTRY, lb_num); + icb = &fe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + fe->perm = udf_rw32(0x7fff); /* all is allowed */ + fe->link_cnt = udf_rw16(0); /* explicit setting */ + + fe->ckpoint = udf_rw32(1); /* user supplied file version */ + + vfs_timestamp(&now); + udf_timespec_to_timestamp(&now, &fe->atime); + udf_timespec_to_timestamp(&now, &fe->attrtime); + udf_timespec_to_timestamp(&now, &fe->mtime); + + udf_set_regid(&fe->imp_id, IMPL_NAME); + udf_add_impl_regid(ump, &fe->imp_id); + + unique_id = udf_advance_uniqueid(ump); + fe->unique_id = udf_rw64(unique_id); + fe->l_ea = udf_rw32(0); + + /* create extended attribute to record our creation time */ + attrlen = UDF_FILETIMES_ATTR_SIZE(1); + ft_extattr = malloc(attrlen, M_UDFTEMP, M_WAITOK); + memset(ft_extattr, 0, attrlen); + ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO); + ft_extattr->hdr.subtype = 1; /* [4/48.10.5] */ + ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->d_l = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */ + ft_extattr->existence = UDF_FILETIMES_FILE_CREATION; + udf_timespec_to_timestamp(&now, &ft_extattr->times[0]); + + udf_extattr_insert_internal(ump, (union dscrptr *) fe, + (struct extattr_entry *) ft_extattr); + free(ft_extattr, M_UDFTEMP); + + /* if its a directory, create '..' */ + bpos = (uint8_t *) fe->data + udf_rw32(fe->l_ea); + fidsize = 0; + if (file_type == UDF_ICB_FILETYPE_DIRECTORY) { + fidsize = udf_create_parentfid(ump, + (struct fileid_desc *) bpos, parent_icb, + parent_unique_id); + } + + /* record fidlength information */ + fe->inf_len = udf_rw64(fidsize); + fe->l_ad = udf_rw32(fidsize); + fe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(fe->l_ea) + fidsize; + fe->tag.desc_crc_len = udf_rw16(crclen); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fe); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_new_efe(struct udf_mount *ump, struct extfile_entry *efe, + int file_type, struct long_ad *node_icb, struct long_ad *parent_icb, + uint64_t parent_unique_id) +{ + struct timespec now; + struct icb_tag *icb; + uint64_t unique_id; + uint32_t fidsize, lb_num; + uint8_t *bpos; + int crclen; + + lb_num = udf_rw32(node_icb->loc.lb_num); + udf_inittag(ump, &efe->tag, TAGID_EXTFENTRY, lb_num); + icb = &efe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + efe->perm = udf_rw32(0x7fff); /* all is allowed */ + efe->link_cnt = udf_rw16(0); /* explicit setting */ + + efe->ckpoint = udf_rw32(1); /* user supplied file version */ + + vfs_timestamp(&now); + udf_timespec_to_timestamp(&now, &efe->ctime); + udf_timespec_to_timestamp(&now, &efe->atime); + udf_timespec_to_timestamp(&now, &efe->attrtime); + udf_timespec_to_timestamp(&now, &efe->mtime); + + udf_set_regid(&efe->imp_id, IMPL_NAME); + udf_add_impl_regid(ump, &efe->imp_id); + + unique_id = udf_advance_uniqueid(ump); + efe->unique_id = udf_rw64(unique_id); + efe->l_ea = udf_rw32(0); + + /* if its a directory, create '..' */ + bpos = (uint8_t *) efe->data + udf_rw32(efe->l_ea); + fidsize = 0; + if (file_type == UDF_ICB_FILETYPE_DIRECTORY) { + fidsize = udf_create_parentfid(ump, + (struct fileid_desc *) bpos, parent_icb, + parent_unique_id); + } + + /* record fidlength information */ + efe->obj_size = udf_rw64(fidsize); + efe->inf_len = udf_rw64(fidsize); + efe->l_ad = udf_rw32(fidsize); + efe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(efe->l_ea) + fidsize; + efe->tag.desc_crc_len = udf_rw16(crclen); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) efe); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *udf_node, struct componentname *cnp) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct file_entry *fe = dir_node->fe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint32_t lb_size, fidsize; + int found, error; + char const *name = cnp->cn_nameptr; + int namelen = cnp->cn_namelen; + int hit, refcnt; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get directory filesize */ + if (!fe) { + assert(dir_node->efe); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + /* search our dirhash hits */ + found = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) + break; + + /* see if its our entry */ + KASSERT(dirent->d_namlen == namelen); + if (strncmp(dirent->d_name, name, namelen) == 0) { + found = 1; + break; + } + } + + if (!found) + error = ENOENT; + if (error) + goto error_out; + + /* mark deleted */ + fid->file_char |= UDF_FILE_CHAR_DEL; +#ifdef UDF_COMPLETE_DELETE + memset(&fid->icb, 0, sizeof(fid->icb)); +#endif + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* get size of fid and compensate for the read_fid_stream advance */ + fidsize = udf_fidsize(fid); + diroffset -= fidsize; + + /* write out */ + error = vn_rdwr(UIO_WRITE, dir_node->vnode, + fid, fidsize, diroffset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + if (error) + goto error_out; + + /* get reference count of attached node */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + KASSERT(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } +#ifdef UDF_COMPLETE_DELETE + /* substract reference counter in attached node */ + refcnt -= 1; + if (udf_node->fe) { + udf_node->fe->link_cnt = udf_rw16(refcnt); + } else { + udf_node->efe->link_cnt = udf_rw16(refcnt); + } + + /* prevent writeout when refcnt == 0 */ + if (refcnt == 0) + udf_node->i_flags |= IN_DELETED; + + if (fid->file_char & UDF_FILE_CHAR_DIR) { + int drefcnt; + + /* substract reference counter in directory node */ + /* note subtract 2 (?) for its was also backreferenced */ + if (dir_node->fe) { + drefcnt = udf_rw16(dir_node->fe->link_cnt); + drefcnt -= 1; + dir_node->fe->link_cnt = udf_rw16(drefcnt); + } else { + KASSERT(dir_node->efe); + drefcnt = udf_rw16(dir_node->efe->link_cnt); + drefcnt -= 1; + dir_node->efe->link_cnt = udf_rw16(drefcnt); + } + } + + udf_node->i_flags |= IN_MODIFIED; + dir_node->i_flags |= IN_MODIFIED; +#endif + /* if it is/was a hardlink adjust the file count */ + if (refcnt > 0) + udf_adjust_filecount(udf_node, -1); + + /* remove from the dirhash */ + dirhash_remove(dirh, dirent, diroffset, + udf_fidsize(fid)); + +error_out: + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *new_parent_node) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint64_t new_parent_unique_id; + uint32_t lb_size, fidsize; + int found, error; + char const *name = ".."; + int namelen = 2; + int hit; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get new parent's unique ID */ + fe = new_parent_node->fe; + efe = new_parent_node->efe; + if (fe) { + new_parent_unique_id = udf_rw64(fe->unique_id); + } else { + assert(efe); + new_parent_unique_id = udf_rw64(efe->unique_id); + } + + /* get directory filesize */ + fe = dir_node->fe; + efe = dir_node->efe; + if (!fe) { + assert(efe); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + /* + * NOTE the standard does not dictate the FID entry '..' should be + * first, though in practice it will most likely be. + */ + + /* search our dirhash hits */ + found = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) + break; + + /* see if its our entry */ + KASSERT(dirent->d_namlen == namelen); + if (strncmp(dirent->d_name, name, namelen) == 0) { + found = 1; + break; + } + } + + if (!found) + error = ENOENT; + if (error) + goto error_out; + + /* update our ICB to the new parent, hit of lower 32 bits of uniqueid */ + fid->icb = new_parent_node->write_loc; + fid->icb.longad_uniqueid = udf_rw32(new_parent_unique_id); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* get size of fid and compensate for the read_fid_stream advance */ + fidsize = udf_fidsize(fid); + diroffset -= fidsize; + + /* write out */ + error = vn_rdwr(UIO_WRITE, dir_node->vnode, + fid, fidsize, diroffset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + /* nothing to be done in the dirhash */ + +error_out: + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * We are not allowed to split the fid tag itself over an logical block so + * check the space remaining in the logical block. + * + * We try to select the smallest candidate for recycling or when none is + * found, append a new one at the end of the directory. + */ + +int +udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct fileid_desc *fid; + struct icb_tag *icbtag; + struct charspec osta_charspec; + struct dirent dirent; + uint64_t unique_id, dir_size; + uint64_t fid_pos, end_fid_pos, chosen_fid_pos; + uint32_t chosen_size, chosen_size_diff; + int lb_size, lb_rest, fidsize, this_fidsize, size_diff; + int file_char, refcnt, icbflags, addr_type, hit, error; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get info */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + udf_osta_charset(&osta_charspec); + + if (dir_node->fe) { + dir_size = udf_rw64(dir_node->fe->inf_len); + icbtag = &dir_node->fe->icbtag; + } else { + dir_size = udf_rw64(dir_node->efe->inf_len); + icbtag = &dir_node->efe->icbtag; + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + if (udf_node->fe) { + unique_id = udf_rw64(udf_node->fe->unique_id); + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + unique_id = udf_rw64(udf_node->efe->unique_id); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } + + if (refcnt > 0) { + unique_id = udf_advance_uniqueid(ump); + udf_adjust_filecount(udf_node, 1); + } + + /* determine file characteristics */ + file_char = 0; /* visible non deleted file and not stream metadata */ + if (vap->va_type == VDIR) + file_char = UDF_FILE_CHAR_DIR; + + /* malloc scrap buffer */ + fid = malloc(lb_size, M_TEMP, M_WAITOK|M_ZERO); + + /* calculate _minimum_ fid size */ + unix_to_udf_name((char *) fid->data, &fid->l_fi, + cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec); + fidsize = UDF_FID_SIZE + fid->l_fi; + fidsize = (fidsize + 3) & ~3; /* multiple of 4 */ + + /* find position that will fit the FID */ + chosen_fid_pos = dir_size; + chosen_size = 0; + chosen_size_diff = UINT_MAX; + + /* shut up gcc */ + dirent.d_namlen = 0; + + /* search our dirhash hits */ + error = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup_freed(dirh, fidsize, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit for size */ + this_fidsize = dirh_ep->entry_size; + + /* check this hit */ + fid_pos = dirh_ep->offset; + end_fid_pos = fid_pos + this_fidsize; + size_diff = this_fidsize - fidsize; + lb_rest = lb_size - (end_fid_pos % lb_size); + +#ifndef UDF_COMPLETE_DELETE + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &fid_pos, fid, dirent); + if (error) + goto error_out; + + /* only reuse entries that are wiped */ + /* check if the len + loc are marked zero */ + if (udf_rw32(fid->icb.len) != 0) + continue; + if (udf_rw32(fid->icb.loc.lb_num) != 0) + continue; + if (udf_rw16(fid->icb.loc.part_num) != 0) + continue; +#endif /* UDF_COMPLETE_DELETE */ + + /* select if not splitting the tag and its smaller */ + if ((size_diff >= 0) && + (size_diff < chosen_size_diff) && + (lb_rest >= sizeof(struct desc_tag))) + { + /* UDF 2.3.4.2+3 specifies rules for iu size */ + if ((size_diff == 0) || (size_diff >= 32)) { + chosen_fid_pos = fid_pos; + chosen_size = this_fidsize; + chosen_size_diff = size_diff; + } + } + } + + + /* extend directory if no other candidate found */ + if (chosen_size == 0) { + chosen_fid_pos = dir_size; + chosen_size = fidsize; + chosen_size_diff = 0; + + /* special case UDF 2.00+ 2.3.4.4, no splitting up fid tag */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* pre-grow directory to see if we're to switch */ + udf_grow_node(dir_node, dir_size + chosen_size); + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + } + + /* make sure the next fid desc_tag won't be splitted */ + if (addr_type != UDF_ICB_INTERN_ALLOC) { + end_fid_pos = chosen_fid_pos + chosen_size; + lb_rest = lb_size - (end_fid_pos % lb_size); + + /* pad with implementation use regid if needed */ + if (lb_rest < sizeof(struct desc_tag)) + chosen_size += 32; + } + } + chosen_size_diff = chosen_size - fidsize; + + /* populate the FID */ + memset(fid, 0, lb_size); + udf_inittag(ump, &fid->tag, TAGID_FID, 0); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = file_char; + fid->icb = udf_node->loc; + fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id); + fid->l_iu = udf_rw16(0); + + if (chosen_size > fidsize) { + /* insert implementation-use regid to space it correctly */ + fid->l_iu = udf_rw16(chosen_size_diff); + + /* set implementation use */ + udf_set_regid((struct regid *) fid->data, IMPL_NAME); + udf_add_impl_regid(ump, (struct regid *) fid->data); + } + + /* fill in name */ + unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu), + &fid->l_fi, cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec); + + fid->tag.desc_crc_len = udf_rw16(chosen_size - UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* writeout FID/update parent directory */ + error = vn_rdwr(UIO_WRITE, dvp, + fid, chosen_size, chosen_fid_pos, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + if (error) + goto error_out; + + /* add reference counter in attached node */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + udf_node->fe->link_cnt = udf_rw16(refcnt+1); + } else { + KASSERT(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + udf_node->efe->link_cnt = udf_rw16(refcnt+1); + } + + /* mark not deleted if it was... just in case, but do warn */ + if (udf_node->i_flags & IN_DELETED) { + printf("udf: warning, marking a file undeleted\n"); + udf_node->i_flags &= ~IN_DELETED; + } + + if (file_char & UDF_FILE_CHAR_DIR) { + /* add reference counter in directory node for '..' */ + if (dir_node->fe) { + refcnt = udf_rw16(dir_node->fe->link_cnt); + refcnt++; + dir_node->fe->link_cnt = udf_rw16(refcnt); + } else { + KASSERT(dir_node->efe); + refcnt = udf_rw16(dir_node->efe->link_cnt); + refcnt++; + dir_node->efe->link_cnt = udf_rw16(refcnt); + } + } + + /* append to the dirhash */ + /* NOTE do not use dirent anymore or it won't match later! */ + udf_to_unix_name(dirent.d_name, NAME_MAX, + (char *) fid->data + udf_rw16(fid->l_iu), fid->l_fi, &osta_charspec); + dirent.d_namlen = strlen(dirent.d_name); + dirhash_enter(dirh, &dirent, chosen_fid_pos, + udf_fidsize(fid), 1); + + /* note updates */ + udf_node->i_flags |= IN_CHANGE | IN_MODIFY; /* | IN_CREATE? */ + /* VN_KNOTE(udf_node, ...) */ + udf_update(udf_node->vnode, NULL, NULL, NULL, 0); + +error_out: + free(fid, M_TEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Each node can have an attached streamdir node though not recursively. These + * are otherwise known as named substreams/named extended attributes that have + * no size limitations. + * + * `Normal' extended attributes are indicated with a number and are recorded + * in either the fe/efe descriptor itself for small descriptors or recorded in + * the attached extended attribute file. Since these spaces can get + * fragmented, care ought to be taken. + * + * Since the size of the space reserved for allocation descriptors is limited, + * there is a mechanim provided for extending this space; this is done by a + * special extent to allow schrinking of the allocations without breaking the + * linkage to the allocation extent descriptor. + */ + +int +udf_get_node(struct udf_mount *ump, struct long_ad *node_icb_loc, + struct udf_node **udf_noderes) +{ + union dscrptr *dscr; + struct udf_node *udf_node; + struct vnode *nvp; + struct long_ad icb_loc, last_fe_icb_loc; + uint64_t file_size; + uint32_t lb_size, sector, dummy; + int udf_file_type, dscr_type, strat, strat4096, needs_indirect; + int slot, eof, error; + + DPRINTF(NODE, ("udf_get_node called\n")); + *udf_noderes = udf_node = NULL; + + /* lock to disallow simultanious creation of same udf_node */ + mutex_enter(&ump->get_node_lock); + + DPRINTF(NODE, ("\tlookup in hash table\n")); + /* lookup in hash table */ + assert(ump); + assert(node_icb_loc); + udf_node = udf_node_lookup(ump, node_icb_loc); + if (udf_node) { + DPRINTF(NODE, ("\tgot it from the hash!\n")); + /* vnode is returned locked */ + *udf_noderes = udf_node; + mutex_exit(&ump->get_node_lock); + return 0; + } + + /* garbage check: translate udf_node_icb_loc to sectornr */ + error = udf_translate_vtop(ump, node_icb_loc, §or, &dummy); + if (error) { + DPRINTF(NODE, ("\tcan't translate icb address!\n")); + /* no use, this will fail anyway */ + mutex_exit(&ump->get_node_lock); + return EINVAL; + } + + /* build udf_node (do initialise!) */ + udf_node = pool_get(&udf_node_pool, PR_WAITOK); + memset(udf_node, 0, sizeof(struct udf_node)); + + DPRINTF(NODE, ("\tget new vnode\n")); + /* give it a vnode */ + error = getnewvnode(VT_UDF, ump->vfs_mountp, udf_vnodeop_p, NULL, &nvp); + if (error) { + pool_put(&udf_node_pool, udf_node); + mutex_exit(&ump->get_node_lock); + return error; + } + + /* always return locked vnode */ + if ((error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY))) { + /* recycle vnode and unlock; simultanious will fail too */ + ungetnewvnode(nvp); + mutex_exit(&ump->get_node_lock); + return error; + } + + /* initialise crosslinks, note location of fe/efe for hashing */ + udf_node->ump = ump; + udf_node->vnode = nvp; + nvp->v_data = udf_node; + udf_node->loc = *node_icb_loc; + udf_node->lockf = 0; + mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&udf_node->node_lock, "udf_nlk"); + genfs_node_init(nvp, &udf_genfsops); /* inititise genfs */ + udf_node->outstanding_bufs = 0; + udf_node->outstanding_nodedscr = 0; + udf_node->uncommitted_lbs = 0; + + /* check if we're fetching the root */ + if (ump->fileset_desc) + if (memcmp(&udf_node->loc, &ump->fileset_desc->rootdir_icb, + sizeof(struct long_ad)) == 0) + nvp->v_vflag |= VV_ROOT; + + /* insert into the hash lookup */ + udf_register_node(udf_node); + + /* safe to unlock, the entry is in the hash table, vnode is locked */ + mutex_exit(&ump->get_node_lock); + + icb_loc = *node_icb_loc; + needs_indirect = 0; + strat4096 = 0; + udf_file_type = UDF_ICB_FILETYPE_UNKNOWN; + file_size = 0; + lb_size = udf_rw32(ump->logical_vol->lb_size); + + DPRINTF(NODE, ("\tstart reading descriptors\n")); + do { + /* try to read in fe/efe */ + error = udf_read_logvol_dscr(ump, &icb_loc, &dscr); + + /* blank sector marks end of sequence, check this */ + if ((dscr == NULL) && (!strat4096)) + error = ENOENT; + + /* break if read error or blank sector */ + if (error || (dscr == NULL)) + break; + + /* process descriptor based on the descriptor type */ + dscr_type = udf_rw16(dscr->tag.id); + DPRINTF(NODE, ("\tread descriptor %d\n", dscr_type)); + + /* if dealing with an indirect entry, follow the link */ + if (dscr_type == TAGID_INDIRECTENTRY) { + needs_indirect = 0; + udf_free_logvol_dscr(ump, &icb_loc, dscr); + icb_loc = dscr->inde.indirect_icb; + continue; + } + + /* only file entries and extended file entries allowed here */ + if ((dscr_type != TAGID_FENTRY) && + (dscr_type != TAGID_EXTFENTRY)) { + udf_free_logvol_dscr(ump, &icb_loc, dscr); + error = ENOENT; + break; + } + + KASSERT(udf_tagsize(dscr, lb_size) == lb_size); + + /* choose this one */ + last_fe_icb_loc = icb_loc; + + /* record and process/update (ext)fentry */ + if (dscr_type == TAGID_FENTRY) { + if (udf_node->fe) + udf_free_logvol_dscr(ump, &last_fe_icb_loc, + udf_node->fe); + udf_node->fe = &dscr->fe; + strat = udf_rw16(udf_node->fe->icbtag.strat_type); + udf_file_type = udf_node->fe->icbtag.file_type; + file_size = udf_rw64(udf_node->fe->inf_len); + } else { + if (udf_node->efe) + udf_free_logvol_dscr(ump, &last_fe_icb_loc, + udf_node->efe); + udf_node->efe = &dscr->efe; + strat = udf_rw16(udf_node->efe->icbtag.strat_type); + udf_file_type = udf_node->efe->icbtag.file_type; + file_size = udf_rw64(udf_node->efe->inf_len); + } + + /* check recording strategy (structure) */ + + /* + * Strategy 4096 is a daisy linked chain terminating with an + * unrecorded sector or a TERM descriptor. The next + * descriptor is to be found in the sector that follows the + * current sector. + */ + if (strat == 4096) { + strat4096 = 1; + needs_indirect = 1; + + icb_loc.loc.lb_num = udf_rw32(icb_loc.loc.lb_num) + 1; + } + + /* + * Strategy 4 is the normal strategy and terminates, but if + * we're in strategy 4096, we can't have strategy 4 mixed in + */ + + if (strat == 4) { + if (strat4096) { + error = EINVAL; + break; + } + break; /* done */ + } + } while (!error); + + /* first round of cleanup code */ + if (error) { + DPRINTF(NODE, ("\tnode fe/efe failed!\n")); + /* recycle udf_node */ + udf_dispose_node(udf_node); + + VOP_UNLOCK(nvp); + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return EINVAL; /* error code ok? */ + } + DPRINTF(NODE, ("\tnode fe/efe read in fine\n")); + + /* assert no references to dscr anymore beyong this point */ + assert((udf_node->fe) || (udf_node->efe)); + dscr = NULL; + + /* + * Remember where to record an updated version of the descriptor. If + * there is a sequence of indirect entries, icb_loc will have been + * updated. Its the write disipline to allocate new space and to make + * sure the chain is maintained. + * + * `needs_indirect' flags if the next location is to be filled with + * with an indirect entry. + */ + udf_node->write_loc = icb_loc; + udf_node->needs_indirect = needs_indirect; + + /* + * Go trough all allocations extents of this descriptor and when + * encountering a redirect read in the allocation extension. These are + * daisy-chained. + */ + UDF_LOCK_NODE(udf_node, 0); + udf_node->num_extensions = 0; + + error = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &icb_loc, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(icb_loc.len)), + UDF_EXT_LEN(udf_rw32(icb_loc.len)), + udf_rw32(icb_loc.loc.lb_num), + udf_rw16(icb_loc.loc.part_num))); + if (eof) + break; + slot++; + + if (UDF_EXT_FLAGS(udf_rw32(icb_loc.len)) != UDF_EXT_REDIRECT) + continue; + + DPRINTF(NODE, ("\tgot redirect extent\n")); + if (udf_node->num_extensions >= UDF_MAX_ALLOC_EXTENTS) { + DPRINTF(ALLOC, ("udf_get_node: implementation limit, " + "too many allocation extensions on " + "udf_node\n")); + error = EINVAL; + break; + } + + /* length can only be *one* lb : UDF 2.50/2.3.7.1 */ + if (UDF_EXT_LEN(udf_rw32(icb_loc.len)) != lb_size) { + DPRINTF(ALLOC, ("udf_get_node: bad allocation " + "extension size in udf_node\n")); + error = EINVAL; + break; + } + + DPRINTF(NODE, ("read allocation extent at lb_num %d\n", + UDF_EXT_LEN(udf_rw32(icb_loc.loc.lb_num)))); + /* load in allocation extent */ + error = udf_read_logvol_dscr(ump, &icb_loc, &dscr); + if (error || (dscr == NULL)) + break; + + /* process read-in descriptor */ + dscr_type = udf_rw16(dscr->tag.id); + + if (dscr_type != TAGID_ALLOCEXTENT) { + udf_free_logvol_dscr(ump, &icb_loc, dscr); + error = ENOENT; + break; + } + + DPRINTF(NODE, ("\trecording redirect extent\n")); + udf_node->ext[udf_node->num_extensions] = &dscr->aee; + udf_node->ext_loc[udf_node->num_extensions] = icb_loc; + + udf_node->num_extensions++; + + } /* while */ + UDF_UNLOCK_NODE(udf_node, 0); + + /* second round of cleanup code */ + if (error) { + /* recycle udf_node */ + udf_dispose_node(udf_node); + + VOP_UNLOCK(nvp); + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return EINVAL; /* error code ok? */ + } + + DPRINTF(NODE, ("\tnode read in fine\n")); + + /* + * Translate UDF filetypes into vnode types. + * + * Systemfiles like the meta main and mirror files are not treated as + * normal files, so we type them as having no type. UDF dictates that + * they are not allowed to be visible. + */ + + switch (udf_file_type) { + case UDF_ICB_FILETYPE_DIRECTORY : + case UDF_ICB_FILETYPE_STREAMDIR : + nvp->v_type = VDIR; + break; + case UDF_ICB_FILETYPE_BLOCKDEVICE : + nvp->v_type = VBLK; + break; + case UDF_ICB_FILETYPE_CHARDEVICE : + nvp->v_type = VCHR; + break; + case UDF_ICB_FILETYPE_SOCKET : + nvp->v_type = VSOCK; + break; + case UDF_ICB_FILETYPE_FIFO : + nvp->v_type = VFIFO; + break; + case UDF_ICB_FILETYPE_SYMLINK : + nvp->v_type = VLNK; + break; + case UDF_ICB_FILETYPE_VAT : + case UDF_ICB_FILETYPE_META_MAIN : + case UDF_ICB_FILETYPE_META_MIRROR : + nvp->v_type = VNON; + break; + case UDF_ICB_FILETYPE_RANDOMACCESS : + case UDF_ICB_FILETYPE_REALTIME : + nvp->v_type = VREG; + break; + default: + /* YIKES, something else */ + nvp->v_type = VNON; + } + + /* TODO specfs, fifofs etc etc. vnops setting */ + + /* don't forget to set vnode's v_size */ + uvm_vnp_setsize(nvp, file_size); + + /* TODO ext attr and streamdir udf_nodes */ + + *udf_noderes = udf_node; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_writeout_node(struct udf_node *udf_node, int waitfor) +{ + union dscrptr *dscr; + struct long_ad *loc; + int extnr, error; + + DPRINTF(NODE, ("udf_writeout_node called\n")); + + KASSERT(udf_node->outstanding_bufs == 0); + KASSERT(udf_node->outstanding_nodedscr == 0); + + KASSERT(LIST_EMPTY(&udf_node->vnode->v_dirtyblkhd)); + + if (udf_node->i_flags & IN_DELETED) { + DPRINTF(NODE, ("\tnode deleted; not writing out\n")); + udf_cleanup_reservation(udf_node); + return 0; + } + + /* lock node; unlocked in callback */ + UDF_LOCK_NODE(udf_node, 0); + + /* remove pending reservations, we're written out */ + udf_cleanup_reservation(udf_node); + + /* at least one descriptor writeout */ + udf_node->outstanding_nodedscr = 1; + + /* we're going to write out the descriptor so clear the flags */ + udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED); + + /* if we were rebuild, write out the allocation extents */ + if (udf_node->i_flags & IN_NODE_REBUILD) { + /* mark outstanding node descriptors and issue them */ + udf_node->outstanding_nodedscr += udf_node->num_extensions; + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + loc = &udf_node->ext_loc[extnr]; + dscr = (union dscrptr *) udf_node->ext[extnr]; + error = udf_write_logvol_dscr(udf_node, dscr, loc, 0); + if (error) + return error; + } + /* mark allocation extents written out */ + udf_node->i_flags &= ~(IN_NODE_REBUILD); + } + + if (udf_node->fe) { + KASSERT(udf_node->efe == NULL); + dscr = (union dscrptr *) udf_node->fe; + } else { + KASSERT(udf_node->efe); + KASSERT(udf_node->fe == NULL); + dscr = (union dscrptr *) udf_node->efe; + } + KASSERT(dscr); + + loc = &udf_node->write_loc; + error = udf_write_logvol_dscr(udf_node, dscr, loc, waitfor); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dispose_node(struct udf_node *udf_node) +{ + struct vnode *vp; + int extnr; + + DPRINTF(NODE, ("udf_dispose_node called on node %p\n", udf_node)); + if (!udf_node) { + DPRINTF(NODE, ("UDF: Dispose node on node NULL, ignoring\n")); + return 0; + } + + vp = udf_node->vnode; +#ifdef DIAGNOSTIC + if (vp->v_numoutput) + panic("disposing UDF node with pending I/O's, udf_node = %p, " + "v_numoutput = %d", udf_node, vp->v_numoutput); +#endif + + udf_cleanup_reservation(udf_node); + + /* TODO extended attributes and streamdir */ + + /* remove dirhash if present */ + dirhash_purge(&udf_node->dir_hash); + + /* remove from our hash lookup table */ + udf_deregister_node(udf_node); + + /* destroy our lock */ + mutex_destroy(&udf_node->node_mutex); + cv_destroy(&udf_node->node_lock); + + /* dissociate our udf_node from the vnode */ + genfs_node_destroy(udf_node->vnode); + vp->v_data = NULL; + + /* free associated memory and the node itself */ + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + udf_free_logvol_dscr(udf_node->ump, &udf_node->ext_loc[extnr], + udf_node->ext[extnr]); + udf_node->ext[extnr] = (void *) 0xdeadcccc; + } + + if (udf_node->fe) + udf_free_logvol_dscr(udf_node->ump, &udf_node->loc, + udf_node->fe); + if (udf_node->efe) + udf_free_logvol_dscr(udf_node->ump, &udf_node->loc, + udf_node->efe); + + udf_node->fe = (void *) 0xdeadaaaa; + udf_node->efe = (void *) 0xdeadbbbb; + udf_node->ump = (void *) 0xdeadbeef; + pool_put(&udf_node_pool, udf_node); + + return 0; +} + + + +/* + * create a new node using the specified vnodeops, vap and cnp but with the + * udf_file_type. This allows special files to be created. Use with care. + */ + +static int +udf_create_node_raw(struct vnode *dvp, struct vnode **vpp, int udf_file_type, + int (**vnodeops)(void *), struct vattr *vap, struct componentname *cnp) +{ + union dscrptr *dscr; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node; + struct udf_mount *ump = dir_node->ump; + struct vnode *nvp; + struct long_ad node_icb_loc; + uint64_t parent_unique_id; + uint64_t lmapping; + uint32_t lb_size, lb_num; + uint16_t vpart_num; + uid_t uid; + gid_t gid, parent_gid; + int fid_size, error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *vpp = NULL; + + /* allocate vnode */ + error = getnewvnode(VT_UDF, ump->vfs_mountp, vnodeops, NULL, &nvp); + if (error) + return error; + + /* lock node */ + error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY); + if (error) + goto error_out_unget; + + /* reserve space for one logical block */ + vpart_num = ump->node_part; + error = udf_reserve_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, /* can_fail */ true); + if (error) + goto error_out_unlock; + + /* allocate node */ + error = udf_allocate_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, &lmapping); + if (error) + goto error_out_unreserve; + lb_num = lmapping; + + /* initialise pointer to location */ + memset(&node_icb_loc, 0, sizeof(struct long_ad)); + node_icb_loc.len = udf_rw32(lb_size); + node_icb_loc.loc.lb_num = udf_rw32(lb_num); + node_icb_loc.loc.part_num = udf_rw16(vpart_num); + + /* build udf_node (do initialise!) */ + udf_node = pool_get(&udf_node_pool, PR_WAITOK); + memset(udf_node, 0, sizeof(struct udf_node)); + + /* initialise crosslinks, note location of fe/efe for hashing */ + /* bugalert: synchronise with udf_get_node() */ + udf_node->ump = ump; + udf_node->vnode = nvp; + nvp->v_data = udf_node; + udf_node->loc = node_icb_loc; + udf_node->write_loc = node_icb_loc; + udf_node->lockf = 0; + mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&udf_node->node_lock, "udf_nlk"); + udf_node->outstanding_bufs = 0; + udf_node->outstanding_nodedscr = 0; + udf_node->uncommitted_lbs = 0; + + /* initialise genfs */ + genfs_node_init(nvp, &udf_genfsops); + + /* insert into the hash lookup */ + udf_register_node(udf_node); + + /* get parent's unique ID for refering '..' if its a directory */ + if (dir_node->fe) { + parent_unique_id = udf_rw64(dir_node->fe->unique_id); + parent_gid = (gid_t) udf_rw32(dir_node->fe->gid); + } else { + parent_unique_id = udf_rw64(dir_node->efe->unique_id); + parent_gid = (gid_t) udf_rw32(dir_node->efe->gid); + } + + /* get descriptor */ + udf_create_logvol_dscr(ump, udf_node, &node_icb_loc, &dscr); + + /* choose a fe or an efe for it */ + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) { + udf_node->fe = &dscr->fe; + fid_size = udf_create_new_fe(ump, udf_node->fe, + udf_file_type, &udf_node->loc, + &dir_node->loc, parent_unique_id); + /* TODO add extended attribute for creation time */ + } else { + udf_node->efe = &dscr->efe; + fid_size = udf_create_new_efe(ump, udf_node->efe, + udf_file_type, &udf_node->loc, + &dir_node->loc, parent_unique_id); + } + KASSERT(dscr->tag.tag_loc == udf_node->loc.loc.lb_num); + + /* update vnode's size and type */ + nvp->v_type = vap->va_type; + uvm_vnp_setsize(nvp, fid_size); + + /* set access mode */ + udf_setaccessmode(udf_node, vap->va_mode); + + /* set ownership */ + uid = kauth_cred_geteuid(cnp->cn_cred); + gid = parent_gid; + udf_setownership(udf_node, uid, gid); + + error = udf_dir_attach(ump, dir_node, udf_node, vap, cnp); + if (error) { + /* free disc allocation for node */ + udf_free_allocated_space(ump, lb_num, vpart_num, 1); + + /* recycle udf_node */ + udf_dispose_node(udf_node); + vput(nvp); + + *vpp = NULL; + return error; + } + + /* adjust file count */ + udf_adjust_filecount(udf_node, 1); + + /* return result */ + *vpp = nvp; + + return 0; + +error_out_unreserve: + udf_do_unreserve_space(ump, NULL, vpart_num, 1); + +error_out_unlock: + VOP_UNLOCK(nvp); + +error_out_unget: + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return error; +} + + +int +udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, + struct componentname *cnp) +{ + int (**vnodeops)(void *); + int udf_file_type; + + DPRINTF(NODE, ("udf_create_node called\n")); + + /* what type are we creating ? */ + vnodeops = udf_vnodeop_p; + /* start with a default */ + udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS; + + *vpp = NULL; + + switch (vap->va_type) { + case VREG : + udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS; + break; + case VDIR : + udf_file_type = UDF_ICB_FILETYPE_DIRECTORY; + break; + case VLNK : + udf_file_type = UDF_ICB_FILETYPE_SYMLINK; + break; + case VBLK : + udf_file_type = UDF_ICB_FILETYPE_BLOCKDEVICE; + /* specfs */ + return ENOTSUP; + break; + case VCHR : + udf_file_type = UDF_ICB_FILETYPE_CHARDEVICE; + /* specfs */ + return ENOTSUP; + break; + case VFIFO : + udf_file_type = UDF_ICB_FILETYPE_FIFO; + /* specfs */ + return ENOTSUP; + break; + case VSOCK : + udf_file_type = UDF_ICB_FILETYPE_SOCKET; + /* specfs */ + return ENOTSUP; + break; + case VNON : + case VBAD : + default : + /* nothing; can we even create these? */ + return EINVAL; + } + + return udf_create_node_raw(dvp, vpp, udf_file_type, vnodeops, vap, cnp); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_free_descriptor_space(struct udf_node *udf_node, struct long_ad *loc, void *mem) +{ + struct udf_mount *ump = udf_node->ump; + uint32_t lb_size, lb_num, len, num_lb; + uint16_t vpart_num; + + /* is there really one? */ + if (mem == NULL) + return; + + /* got a descriptor here */ + len = UDF_EXT_LEN(udf_rw32(loc->len)); + lb_num = udf_rw32(loc->loc.lb_num); + vpart_num = udf_rw16(loc->loc.part_num); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + num_lb = (len + lb_size -1) / lb_size; + + udf_free_allocated_space(ump, lb_num, vpart_num, num_lb); +} + +void +udf_delete_node(struct udf_node *udf_node) +{ + void *dscr; + struct long_ad *loc; + int extnr, lvint, dummy; + + /* paranoia check on integrity; should be open!; we could panic */ + lvint = udf_rw32(udf_node->ump->logvol_integrity->integrity_type); + if (lvint == UDF_INTEGRITY_CLOSED) + printf("\tIntegrity was CLOSED!\n"); + + /* whatever the node type, change its size to zero */ + (void) udf_resize_node(udf_node, 0, &dummy); + + /* force it to be `clean'; no use writing it out */ + udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED | IN_ACCESS | + IN_CHANGE | IN_UPDATE | IN_MODIFY); + + /* adjust file count */ + udf_adjust_filecount(udf_node, -1); + + /* + * Free its allocated descriptors; memory will be released when + * vop_reclaim() is called. + */ + loc = &udf_node->loc; + + dscr = udf_node->fe; + udf_free_descriptor_space(udf_node, loc, dscr); + dscr = udf_node->efe; + udf_free_descriptor_space(udf_node, loc, dscr); + + for (extnr = 0; extnr < UDF_MAX_ALLOC_EXTENTS; extnr++) { + dscr = udf_node->ext[extnr]; + loc = &udf_node->ext_loc[extnr]; + udf_free_descriptor_space(udf_node, loc, dscr); + } +} + +/* --------------------------------------------------------------------- */ + +/* set new filesize; node but be LOCKED on entry and is locked on exit */ +int +udf_resize_node(struct udf_node *udf_node, uint64_t new_size, int *extended) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint64_t file_size; + int error; + + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + file_size = udf_rw64(efe->inf_len); + } + + DPRINTF(ATTR, ("\tchanging file length from %"PRIu64" to %"PRIu64"\n", + file_size, new_size)); + + /* if not changing, we're done */ + if (file_size == new_size) + return 0; + + *extended = (new_size > file_size); + if (*extended) { + error = udf_grow_node(udf_node, new_size); + } else { + error = udf_shrink_node(udf_node, new_size); + } + + return error; +} + + +/* --------------------------------------------------------------------- */ + +void +udf_itimes(struct udf_node *udf_node, struct timespec *acc, + struct timespec *mod, struct timespec *birth) +{ + struct timespec now; + struct file_entry *fe; + struct extfile_entry *efe; + struct filetimes_extattr_entry *ft_extattr; + struct timestamp *atime, *mtime, *attrtime, *ctime; + struct timestamp fe_ctime; + struct timespec cur_birth; + uint32_t offset, a_l; + uint8_t *filedata; + int error; + + /* protect against rogue values */ + if (!udf_node) + return; + + fe = udf_node->fe; + efe = udf_node->efe; + + if (!(udf_node->i_flags & (IN_ACCESS|IN_CHANGE|IN_UPDATE|IN_MODIFY))) + return; + + /* get descriptor information */ + if (fe) { + atime = &fe->atime; + mtime = &fe->mtime; + attrtime = &fe->attrtime; + filedata = fe->data; + + /* initial save dummy setting */ + ctime = &fe_ctime; + + /* check our extended attribute if present */ + error = udf_extattr_search_intern(udf_node, + UDF_FILETIMES_ATTR_NO, "", &offset, &a_l); + if (!error) { + ft_extattr = (struct filetimes_extattr_entry *) + (filedata + offset); + if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION) + ctime = &ft_extattr->times[0]; + } + /* TODO create the extended attribute if not found ? */ + } else { + assert(udf_node->efe); + atime = &efe->atime; + mtime = &efe->mtime; + attrtime = &efe->attrtime; + ctime = &efe->ctime; + } + + vfs_timestamp(&now); + + /* set access time */ + if (udf_node->i_flags & IN_ACCESS) { + if (acc == NULL) + acc = &now; + udf_timespec_to_timestamp(acc, atime); + } + + /* set modification time */ + if (udf_node->i_flags & (IN_UPDATE | IN_MODIFY)) { + if (mod == NULL) + mod = &now; + udf_timespec_to_timestamp(mod, mtime); + + /* ensure birthtime is older than set modification! */ + udf_timestamp_to_timespec(udf_node->ump, ctime, &cur_birth); + if ((cur_birth.tv_sec > mod->tv_sec) || + ((cur_birth.tv_sec == mod->tv_sec) && + (cur_birth.tv_nsec > mod->tv_nsec))) { + udf_timespec_to_timestamp(mod, ctime); + } + } + + /* update birthtime if specified */ + /* XXX we assume here that given birthtime is older than mod */ + if (birth && (birth->tv_sec != VNOVAL)) { + udf_timespec_to_timestamp(birth, ctime); + } + + /* set change time */ + if (udf_node->i_flags & (IN_CHANGE | IN_MODIFY)) + udf_timespec_to_timestamp(&now, attrtime); + + /* notify updates to the node itself */ + if (udf_node->i_flags & (IN_ACCESS | IN_MODIFY)) + udf_node->i_flags |= IN_ACCESSED; + if (udf_node->i_flags & (IN_UPDATE | IN_CHANGE)) + udf_node->i_flags |= IN_MODIFIED; + + /* clear modification flags */ + udf_node->i_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY); +} + +/* --------------------------------------------------------------------- */ + +int +udf_update(struct vnode *vp, struct timespec *acc, + struct timespec *mod, struct timespec *birth, int updflags) +{ + union dscrptr *dscrptr; + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + struct regid *impl_id; + int mnt_async = (vp->v_mount->mnt_flag & MNT_ASYNC); + int waitfor, flags; + +#ifdef DEBUG + char bits[128]; + DPRINTF(CALL, ("udf_update(node, %p, %p, %p, %d)\n", acc, mod, birth, + updflags)); + snprintb(bits, sizeof(bits), IN_FLAGBITS, udf_node->i_flags); + DPRINTF(CALL, ("\tnode flags %s\n", bits)); + DPRINTF(CALL, ("\t\tmnt_async = %d\n", mnt_async)); +#endif + + /* set our times */ + udf_itimes(udf_node, acc, mod, birth); + + /* set our implementation id */ + if (udf_node->fe) { + dscrptr = (union dscrptr *) udf_node->fe; + impl_id = &udf_node->fe->imp_id; + } else { + dscrptr = (union dscrptr *) udf_node->efe; + impl_id = &udf_node->efe->imp_id; + } + + /* set our ID */ + udf_set_regid(impl_id, IMPL_NAME); + udf_add_impl_regid(ump, impl_id); + + /* update our crc! on RMW we are not allowed to change a thing */ + udf_validate_tag_and_crc_sums(dscrptr); + + /* if called when mounted readonly, never write back */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return 0; + + /* check if the node is dirty 'enough'*/ + if (updflags & UPDATE_CLOSE) { + flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED); + } else { + flags = udf_node->i_flags & IN_MODIFIED; + } + if (flags == 0) + return 0; + + /* determine if we need to write sync or async */ + waitfor = 0; + if ((flags & IN_MODIFIED) && (mnt_async == 0)) { + /* sync mounted */ + waitfor = updflags & UPDATE_WAIT; + if (updflags & UPDATE_DIROP) + waitfor |= UPDATE_WAIT; + } + if (waitfor) + return VOP_FSYNC(vp, FSCRED, FSYNC_WAIT, 0,0); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + + +/* + * Read one fid and process it into a dirent and advance to the next (*fid) + * has to be allocated a logical block in size, (*dirent) struct dirent length + */ + +int +udf_read_fid_stream(struct vnode *vp, uint64_t *offset, + struct fileid_desc *fid, struct dirent *dirent) +{ + struct udf_node *dir_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + struct file_entry *fe = dir_node->fe; + struct extfile_entry *efe = dir_node->efe; + uint32_t fid_size, lb_size; + uint64_t file_size; + char *fid_name; + int enough, error; + + assert(fid); + assert(dirent); + assert(dir_node); + assert(offset); + assert(*offset != 1); + + DPRINTF(FIDS, ("read_fid_stream called at offset %"PRIu64"\n", *offset)); + /* check if we're past the end of the directory */ + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(dir_node->efe); + file_size = udf_rw64(efe->inf_len); + } + if (*offset >= file_size) + return EINVAL; + + /* get maximum length of FID descriptor */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* initialise return values */ + fid_size = 0; + memset(dirent, 0, sizeof(struct dirent)); + memset(fid, 0, lb_size); + + enough = (file_size - (*offset) >= UDF_FID_SIZE); + if (!enough) { + /* short dir ... */ + return EIO; + } + + error = vn_rdwr(UIO_READ, vp, + fid, MIN(file_size - (*offset), lb_size), *offset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + return error; + + DPRINTF(FIDS, ("\tfid piece read in fine\n")); + /* + * Check if we got a whole descriptor. + * TODO Try to `resync' directory stream when something is very wrong. + */ + + /* check if our FID header is OK */ + error = udf_check_tag(fid); + if (error) { + goto brokendir; + } + DPRINTF(FIDS, ("\ttag check ok\n")); + + if (udf_rw16(fid->tag.id) != TAGID_FID) { + error = EIO; + goto brokendir; + } + DPRINTF(FIDS, ("\ttag checked ok: got TAGID_FID\n")); + + /* check for length */ + fid_size = udf_fidsize(fid); + enough = (file_size - (*offset) >= fid_size); + if (!enough) { + error = EIO; + goto brokendir; + } + DPRINTF(FIDS, ("\tthe complete fid is read in\n")); + + /* check FID contents */ + error = udf_check_tag_payload((union dscrptr *) fid, lb_size); +brokendir: + if (error) { + /* note that is sometimes a bit quick to report */ + printf("UDF: BROKEN DIRECTORY ENTRY\n"); + /* RESYNC? */ + /* TODO: use udf_resync_fid_stream */ + return EIO; + } + DPRINTF(FIDS, ("\tpayload checked ok\n")); + + /* we got a whole and valid descriptor! */ + DPRINTF(FIDS, ("\tinterpret FID\n")); + + /* create resulting dirent structure */ + fid_name = (char *) fid->data + udf_rw16(fid->l_iu); + udf_to_unix_name(dirent->d_name, NAME_MAX, + fid_name, fid->l_fi, &ump->logical_vol->desc_charset); + + /* '..' has no name, so provide one */ + if (fid->file_char & UDF_FILE_CHAR_PAR) + strcpy(dirent->d_name, ".."); + + dirent->d_fileno = udf_get_node_id(&fid->icb); /* inode hash XXX */ + dirent->d_namlen = strlen(dirent->d_name); + dirent->d_reclen = _DIRENT_SIZE(dirent); + + /* + * Note that its not worth trying to go for the filetypes now... its + * too expensive too + */ + dirent->d_type = DT_UNKNOWN; + + /* initial guess for filetype we can make */ + if (fid->file_char & UDF_FILE_CHAR_DIR) + dirent->d_type = DT_DIR; + + /* advance */ + *offset += fid_size; + + return error; +} + + +/* --------------------------------------------------------------------- */ + +static void +udf_sync_pass(struct udf_mount *ump, kauth_cred_t cred, int waitfor, + int pass, int *ndirty) +{ + struct udf_node *udf_node, *n_udf_node; + struct vnode *vp; + int vdirty, error; + int on_type, on_flags, on_vnode; + +derailed: + KASSERT(mutex_owned(&mntvnode_lock)); + + DPRINTF(SYNC, ("sync_pass %d\n", pass)); + udf_node = RB_TREE_MIN(&ump->udf_node_tree); + for (;udf_node; udf_node = n_udf_node) { + DPRINTF(SYNC, (".")); + + udf_node->i_flags &= ~IN_SYNCED; + vp = udf_node->vnode; + + mutex_enter(vp->v_interlock); + n_udf_node = rb_tree_iterate(&ump->udf_node_tree, + udf_node, RB_DIR_RIGHT); + + if (n_udf_node) + n_udf_node->i_flags |= IN_SYNCED; + + /* system nodes are not synced this way */ + if (vp->v_vflag & VV_SYSTEM) { + mutex_exit(vp->v_interlock); + continue; + } + + /* check if its dirty enough to even try */ + on_type = (waitfor == MNT_LAZY || vp->v_type == VNON); + on_flags = ((udf_node->i_flags & + (IN_ACCESSED | IN_UPDATE | IN_MODIFIED)) == 0); + on_vnode = LIST_EMPTY(&vp->v_dirtyblkhd) + && UVM_OBJ_IS_CLEAN(&vp->v_uobj); + if (on_type || (on_flags || on_vnode)) { /* XXX */ + /* not dirty (enough?) */ + mutex_exit(vp->v_interlock); + continue; + } + + mutex_exit(&mntvnode_lock); + error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT); + if (error) { + mutex_enter(&mntvnode_lock); + if (error == ENOENT) + goto derailed; + *ndirty += 1; + continue; + } + + switch (pass) { + case 1: + VOP_FSYNC(vp, cred, 0 | FSYNC_DATAONLY,0,0); + break; + case 2: + vdirty = vp->v_numoutput; + if (vp->v_tag == VT_UDF) + vdirty += udf_node->outstanding_bufs + + udf_node->outstanding_nodedscr; + if (vdirty == 0) + VOP_FSYNC(vp, cred, 0,0,0); + *ndirty += vdirty; + break; + case 3: + vdirty = vp->v_numoutput; + if (vp->v_tag == VT_UDF) + vdirty += udf_node->outstanding_bufs + + udf_node->outstanding_nodedscr; + *ndirty += vdirty; + break; + } + + vput(vp); + mutex_enter(&mntvnode_lock); + } + DPRINTF(SYNC, ("END sync_pass %d\n", pass)); +} + + +void +udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor) +{ + int dummy, ndirty; + + mutex_enter(&mntvnode_lock); +recount: + dummy = 0; + DPRINTF(CALL, ("issue VOP_FSYNC(DATA only) on all nodes\n")); + DPRINTF(SYNC, ("issue VOP_FSYNC(DATA only) on all nodes\n")); + udf_sync_pass(ump, cred, waitfor, 1, &dummy); + + DPRINTF(CALL, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n")); + DPRINTF(SYNC, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n")); + udf_sync_pass(ump, cred, waitfor, 2, &dummy); + + if (waitfor == MNT_WAIT) { + ndirty = ump->devvp->v_numoutput; + DPRINTF(SYNC, ("counting pending blocks: on devvp %d\n", + ndirty)); + udf_sync_pass(ump, cred, waitfor, 3, &ndirty); + DPRINTF(SYNC, ("counted num dirty pending blocks %d\n", + ndirty)); + + if (ndirty) { + /* 1/4 second wait */ + cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock, + hz/4); + goto recount; + } + } + + mutex_exit(&mntvnode_lock); +} + +/* --------------------------------------------------------------------- */ + +/* + * Read and write file extent in/from the buffer. + * + * The splitup of the extent into seperate request-buffers is to minimise + * copying around as much as possible. + * + * block based file reading and writing + */ + +static int +udf_read_internal(struct udf_node *node, uint8_t *blob) +{ + struct udf_mount *ump; + struct file_entry *fe = node->fe; + struct extfile_entry *efe = node->efe; + uint64_t inflen; + uint32_t sector_size; + uint8_t *pos; + int icbflags, addr_type; + + /* get extent and do some paranoia checks */ + ump = node->ump; + sector_size = ump->discinfo.sector_size; + + if (fe) { + inflen = udf_rw64(fe->inf_len); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(node->efe); + inflen = udf_rw64(efe->inf_len); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + assert(addr_type == UDF_ICB_INTERN_ALLOC); + assert(inflen < sector_size); + + /* copy out info */ + memset(blob, 0, sector_size); + memcpy(blob, pos, inflen); + + return 0; +} + + +static int +udf_write_internal(struct udf_node *node, uint8_t *blob) +{ + struct udf_mount *ump; + struct file_entry *fe = node->fe; + struct extfile_entry *efe = node->efe; + uint64_t inflen; + uint32_t sector_size; + uint8_t *pos; + int icbflags, addr_type; + + /* get extent and do some paranoia checks */ + ump = node->ump; + sector_size = ump->discinfo.sector_size; + + if (fe) { + inflen = udf_rw64(fe->inf_len); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(node->efe); + inflen = udf_rw64(efe->inf_len); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + assert(addr_type == UDF_ICB_INTERN_ALLOC); + assert(inflen < sector_size); + + /* copy in blob */ + /* memset(pos, 0, inflen); */ + memcpy(pos, blob, inflen); + + return 0; +} + + +void +udf_read_filebuf(struct udf_node *udf_node, struct buf *buf) +{ + struct buf *nestbuf; + struct udf_mount *ump = udf_node->ump; + uint64_t *mapping; + uint64_t run_start; + uint32_t sector_size; + uint32_t buf_offset, sector, rbuflen, rblk; + uint32_t from, lblkno; + uint32_t sectors; + uint8_t *buf_pos; + int error, run_length, what; + + sector_size = udf_node->ump->discinfo.sector_size; + + from = buf->b_blkno; + sectors = buf->b_bcount / sector_size; + + what = udf_get_c_type(udf_node); + + /* assure we have enough translation slots */ + KASSERT(buf->b_bcount / sector_size <= UDF_MAX_MAPPINGS); + KASSERT(MAXPHYS / sector_size <= UDF_MAX_MAPPINGS); + + if (sectors > UDF_MAX_MAPPINGS) { + printf("udf_read_filebuf: implementation limit on bufsize\n"); + buf->b_error = EIO; + biodone(buf); + return; + } + + mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK); + + error = 0; + DPRINTF(READ, ("\ttranslate %d-%d\n", from, sectors)); + error = udf_translate_file_extent(udf_node, from, sectors, mapping); + if (error) { + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(READ, ("\ttranslate extent went OK\n")); + + /* pre-check if its an internal */ + if (*mapping == UDF_TRANS_INTERN) { + error = udf_read_internal(udf_node, (uint8_t *) buf->b_data); + if (error) + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(READ, ("\tnot intern\n")); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_TRANSLATE) { + printf("Returned translation table:\n"); + for (sector = 0; sector < sectors; sector++) { + printf("%d : %"PRIu64"\n", sector, mapping[sector]); + } + } +#endif + + /* request read-in of data from disc sheduler */ + buf->b_resid = buf->b_bcount; + for (sector = 0; sector < sectors; sector++) { + buf_offset = sector * sector_size; + buf_pos = (uint8_t *) buf->b_data + buf_offset; + DPRINTF(READ, ("\tprocessing rel sector %d\n", sector)); + + /* check if its zero or unmapped to stop reading */ + switch (mapping[sector]) { + case UDF_TRANS_UNMAPPED: + case UDF_TRANS_ZERO: + /* copy zero sector TODO runlength like below */ + memset(buf_pos, 0, sector_size); + DPRINTF(READ, ("\treturning zero sector\n")); + nestiobuf_done(buf, sector_size, 0); + break; + default : + DPRINTF(READ, ("\tread sector " + "%"PRIu64"\n", mapping[sector])); + + lblkno = from + sector; + run_start = mapping[sector]; + run_length = 1; + while (sector < sectors-1) { + if (mapping[sector+1] != mapping[sector]+1) + break; + run_length++; + sector++; + } + + /* + * nest an iobuf and mark it for async reading. Since + * we're using nested buffers, they can't be cached by + * design. + */ + rbuflen = run_length * sector_size; + rblk = run_start * (sector_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + assert(nestbuf->b_vp == udf_node->vnode); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + } + } +out: + /* if we're synchronously reading, wait for the completion */ + if ((buf->b_flags & B_ASYNC) == 0) + biowait(buf); + + DPRINTF(READ, ("\tend of read_filebuf\n")); + free(mapping, M_TEMP); + return; +} + + +void +udf_write_filebuf(struct udf_node *udf_node, struct buf *buf) +{ + struct buf *nestbuf; + struct udf_mount *ump = udf_node->ump; + uint64_t *mapping; + uint64_t run_start; + uint32_t lb_size; + uint32_t buf_offset, lb_num, rbuflen, rblk; + uint32_t from, lblkno; + uint32_t num_lb; + int error, run_length, what, s; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + from = buf->b_blkno; + num_lb = buf->b_bcount / lb_size; + + what = udf_get_c_type(udf_node); + + /* assure we have enough translation slots */ + KASSERT(buf->b_bcount / lb_size <= UDF_MAX_MAPPINGS); + KASSERT(MAXPHYS / lb_size <= UDF_MAX_MAPPINGS); + + if (num_lb > UDF_MAX_MAPPINGS) { + printf("udf_write_filebuf: implementation limit on bufsize\n"); + buf->b_error = EIO; + biodone(buf); + return; + } + + mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK); + + error = 0; + DPRINTF(WRITE, ("\ttranslate %d-%d\n", from, num_lb)); + error = udf_translate_file_extent(udf_node, from, num_lb, mapping); + if (error) { + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(WRITE, ("\ttranslate extent went OK\n")); + + /* if its internally mapped, we can write it in the descriptor itself */ + if (*mapping == UDF_TRANS_INTERN) { + /* TODO paranoia check if we ARE going to have enough space */ + error = udf_write_internal(udf_node, (uint8_t *) buf->b_data); + if (error) + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(WRITE, ("\tnot intern\n")); + + /* request write out of data to disc sheduler */ + buf->b_resid = buf->b_bcount; + for (lb_num = 0; lb_num < num_lb; lb_num++) { + buf_offset = lb_num * lb_size; + DPRINTF(WRITE, ("\tprocessing rel lb_num %d\n", lb_num)); + + /* + * Mappings are not that important here. Just before we write + * the lb_num we late-allocate them when needed and update the + * mapping in the udf_node. + */ + + /* XXX why not ignore the mapping altogether ? */ + DPRINTF(WRITE, ("\twrite lb_num " + "%"PRIu64, mapping[lb_num])); + + lblkno = from + lb_num; + run_start = mapping[lb_num]; + run_length = 1; + while (lb_num < num_lb-1) { + if (mapping[lb_num+1] != mapping[lb_num]+1) + if (mapping[lb_num+1] != mapping[lb_num]) + break; + run_length++; + lb_num++; + } + DPRINTF(WRITE, ("+ %d\n", run_length)); + + /* nest an iobuf on the master buffer for the extent */ + rbuflen = run_length * lb_size; + rblk = run_start * (lb_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + KASSERT(nestbuf->b_vp == udf_node->vnode); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = what; + + /* increment our outstanding bufs counter */ + s = splbio(); + udf_node->outstanding_bufs++; + splx(s); + + udf_discstrat_queuebuf(ump, nestbuf); + } +out: + /* if we're synchronously writing, wait for the completion */ + if ((buf->b_flags & B_ASYNC) == 0) + biowait(buf); + + DPRINTF(WRITE, ("\tend of write_filebuf\n")); + free(mapping, M_TEMP); + return; +} + +/* --------------------------------------------------------------------- */ diff --git a/sys/fs/udf/udf_subr.h b/sys/fs/udf/udf_subr.h new file mode 100644 index 000000000..7146c1353 --- /dev/null +++ b/sys/fs/udf/udf_subr.h @@ -0,0 +1,229 @@ +/* $NetBSD: udf_subr.h,v 1.19 2013/07/07 19:49:44 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#ifndef _FS_UDF_UDF_SUBR_H_ +#define _FS_UDF_UDF_SUBR_H_ + +/* handies */ +#define VFSTOUDF(mp) ((struct udf_mount *)mp->mnt_data) + + +/* device information updating */ +int udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *trackinfo); +int udf_update_discinfo(struct udf_mount *ump); +int udf_search_tracks(struct udf_mount *ump, struct udf_args *args, + int *first_tracknr, int *last_tracknr); +int udf_search_writing_tracks(struct udf_mount *ump); +int udf_setup_writeparams(struct udf_mount *ump); +int udf_synchronise_caches(struct udf_mount *ump); + +/* tags operations */ +int udf_fidsize(struct fileid_desc *fid); +int udf_check_tag(void *blob); +int udf_check_tag_payload(void *blob, uint32_t max_length); +void udf_validate_tag_sum(void *blob); +void udf_validate_tag_and_crc_sums(void *blob); +int udf_tagsize(union dscrptr *dscr, uint32_t udf_sector_size); + +/* read/write descriptors */ +int udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors); +int udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors); +int udf_read_phys_dscr( + struct udf_mount *ump, + uint32_t sector, + struct malloc_type *mtype, /* where to allocate */ + union dscrptr **dstp); /* out */ + +int udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector); +int udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector, + void (*dscrwr_callback)(struct buf *)); + +/* read/write node descriptors */ +int udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node, + struct long_ad *icb, union dscrptr **dscrptr); +void udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb_loc, + void *dscr); +int udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + union dscrptr **dscrptr); +int udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr, + struct long_ad *icb, int waitfor); + + +/* volume descriptors readers and checkers */ +int udf_read_anchors(struct udf_mount *ump); +int udf_read_vds_space(struct udf_mount *ump); +int udf_process_vds(struct udf_mount *ump); +int udf_read_vds_tables(struct udf_mount *ump); +int udf_read_rootdirs(struct udf_mount *ump); + +/* open/close and sync volumes */ +int udf_open_logvol(struct udf_mount *ump); +int udf_close_logvol(struct udf_mount *ump, int mntflags); +int udf_writeout_vat(struct udf_mount *ump); +int udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor); +int udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor); +void udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor); +void udf_synchronise_metadatamirror_node(struct udf_mount *ump); + +/* translation services */ +int udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc, + uint32_t *lb_numres, uint32_t *extres); +void udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors, + uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping); +int udf_translate_file_extent(struct udf_node *node, + uint32_t from, uint32_t num_lb, uint64_t *map); +void udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb, int *eof); +int udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb); + +int udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset); +int udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset); + +/* disc allocation */ +int udf_get_c_type(struct udf_node *udf_node); +int udf_get_record_vpart(struct udf_mount *ump, int udf_c_type); +void udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb); +void udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb); +int udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail); +void udf_cleanup_reservation(struct udf_node *udf_node); +int udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping); +void udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num, uint16_t vpart_num, uint32_t num_lb); +void udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf, uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_num); +int udf_grow_node(struct udf_node *node, uint64_t new_size); +int udf_shrink_node(struct udf_node *node, uint64_t new_size); +void udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks); + +/* node readers and writers */ +uint64_t udf_advance_uniqueid(struct udf_mount *ump); + +#define UDF_LOCK_NODE(udf_node, flag) udf_lock_node(udf_node, (flag), __FILE__, __LINE__) +#define UDF_UNLOCK_NODE(udf_node, flag) udf_unlock_node(udf_node, (flag)) +void udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno); +void udf_unlock_node(struct udf_node *udf_node, int flag); + +int udf_get_node(struct udf_mount *ump, struct long_ad *icbloc, struct udf_node **noderes); +int udf_writeout_node(struct udf_node *udf_node, int waitfor); +int udf_dispose_node(struct udf_node *node); + +/* node ops */ +int udf_resize_node(struct udf_node *node, uint64_t new_size, int *extended); +int udf_extattr_search_intern(struct udf_node *node, uint32_t sattr, char const *sattrname, uint32_t *offsetp, uint32_t *lengthp); + +/* node data buffer read/write */ +void udf_read_filebuf(struct udf_node *node, struct buf *buf); +void udf_write_filebuf(struct udf_node *node, struct buf *buf); +void udf_fixup_fid_block(uint8_t *blob, int lb_size, int rfix_pos, int max_rfix_pos, uint32_t lb_num); +void udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num); +void udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type); + +/* device strategy */ +void udf_discstrat_init(struct udf_mount *ump); +void udf_discstrat_finish(struct udf_mount *ump); +void udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf); + +/* structure writers */ +int udf_write_terminator(struct udf_mount *ump, uint32_t sector); + +/* structure creators */ +void udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid, uint32_t sector); +void udf_set_regid(struct regid *regid, char const *name); +void udf_add_domain_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_udf_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_impl_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_app_regid(struct udf_mount *ump, struct regid *regid); + +/* directory operations and helpers */ +void udf_osta_charset(struct charspec *charspec); +int udf_read_fid_stream(struct vnode *vp, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent); +int udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, struct long_ad *icb_loc, int *found); +int udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, struct componentname *cnp); +void udf_delete_node(struct udf_node *udf_node); + +int udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred); + +int udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct componentname *cnp); +int udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp); +int udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *new_parent_node); +int udf_dirhash_fill(struct udf_node *dir_node); + +/* update and times */ +void udf_add_to_dirtylist(struct udf_node *udf_node); +void udf_remove_from_dirtylist(struct udf_node *udf_node); +void udf_itimes(struct udf_node *udf_node, struct timespec *acc, + struct timespec *mod, struct timespec *birth); +int udf_update(struct vnode *node, struct timespec *acc, + struct timespec *mod, struct timespec *birth, int updflags); + +/* helpers and converters */ +void udf_init_nodes_tree(struct udf_mount *ump); +long udf_get_node_id(const struct long_ad *icbptr); /* for `inode' numbering */ +int udf_compare_icb(const struct long_ad *a, const struct long_ad *b); +uint32_t udf_getaccessmode(struct udf_node *node); +void udf_setaccessmode(struct udf_node *udf_node, mode_t mode); +void udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp); +void udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid); + +void udf_to_unix_name(char *result, int result_len, char *id, int len, struct charspec *chsp); +void unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, struct charspec *chsp); + +void udf_timestamp_to_timespec(struct udf_mount *ump, struct timestamp *timestamp, struct timespec *timespec); +void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp); + +/* vnode operations */ +int udf_inactive(void *v); +int udf_reclaim(void *v); +int udf_readdir(void *v); +int udf_getattr(void *v); +int udf_setattr(void *v); +int udf_pathconf(void *v); +int udf_open(void *v); +int udf_close(void *v); +int udf_access(void *v); +int udf_read(void *v); +int udf_write(void *v); +int udf_trivial_bmap(void *v); +int udf_vfsstrategy(void *v); +int udf_lookup(void *v); +int udf_create(void *v); +int udf_mknod(void *v); +int udf_link(void *); +int udf_symlink(void *v); +int udf_readlink(void *v); +int udf_rename(void *v); +int udf_remove(void *v); +int udf_mkdir(void *v); +int udf_rmdir(void *v); +int udf_fsync(void *v); +int udf_advlock(void *v); + +#endif /* !_FS_UDF_UDF_SUBR_H_ */ diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c new file mode 100644 index 000000000..7fb4467af --- /dev/null +++ b/sys/fs/udf/udf_vfsops.c @@ -0,0 +1,949 @@ +/* $NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + +MODULE(MODULE_CLASS_VFS, udf, NULL); + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +/* verbose levels of the udf filingsystem */ +int udf_verbose = UDF_DEBUGGING; + +/* malloc regions */ +MALLOC_JUSTDEFINE(M_UDFMNT, "UDF mount", "UDF mount structures"); +MALLOC_JUSTDEFINE(M_UDFVOLD, "UDF volspace", "UDF volume space descriptors"); +MALLOC_JUSTDEFINE(M_UDFTEMP, "UDF temp", "UDF scrap space"); +struct pool udf_node_pool; + +/* supported functions predefined */ +VFS_PROTOS(udf); + +static struct sysctllog *udf_sysctl_log; + +/* internal functions */ +static int udf_mountfs(struct vnode *, struct mount *, struct lwp *, struct udf_args *); + + +/* --------------------------------------------------------------------- */ + +/* predefine vnode-op list descriptor */ +extern const struct vnodeopv_desc udf_vnodeop_opv_desc; + +const struct vnodeopv_desc * const udf_vnodeopv_descs[] = { + &udf_vnodeop_opv_desc, + NULL, +}; + + +/* vfsops descriptor linked in as anchor point for the filingsystem */ +struct vfsops udf_vfsops = { + MOUNT_UDF, /* vfs_name */ + sizeof (struct udf_args), + udf_mount, + udf_start, + udf_unmount, + udf_root, + (void *)eopnotsupp, /* vfs_quotactl */ + udf_statvfs, + udf_sync, + udf_vget, + udf_fhtovp, + udf_vptofh, + udf_init, + udf_reinit, + udf_done, + udf_mountroot, + udf_snapshot, + vfs_stdextattrctl, + (void *)eopnotsupp, /* vfs_suspendctl */ + genfs_renamelock_enter, + genfs_renamelock_exit, + (void *)eopnotsupp, + udf_vnodeopv_descs, + 0, /* int vfs_refcount */ + { NULL, NULL, }, /* LIST_ENTRY(vfsops) */ +}; + +/* --------------------------------------------------------------------- */ + +/* file system starts here */ +void +udf_init(void) +{ + size_t size; + + /* setup memory types */ + malloc_type_attach(M_UDFMNT); + malloc_type_attach(M_UDFVOLD); + malloc_type_attach(M_UDFTEMP); + + /* init node pools */ + size = sizeof(struct udf_node); + pool_init(&udf_node_pool, size, 0, 0, 0, + "udf_node_pool", NULL, IPL_NONE); +} + + +void +udf_reinit(void) +{ + /* nothing to do */ +} + + +void +udf_done(void) +{ + /* remove pools */ + pool_destroy(&udf_node_pool); + + malloc_type_detach(M_UDFMNT); + malloc_type_detach(M_UDFVOLD); + malloc_type_detach(M_UDFTEMP); +} + +/* + * If running a DEBUG kernel, provide an easy way to set the debug flags when + * running into a problem. + */ +#define UDF_VERBOSE_SYSCTLOPT 1 + +static int +udf_modcmd(modcmd_t cmd, void *arg) +{ + const struct sysctlnode *node; + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = vfs_attach(&udf_vfsops); + if (error != 0) + break; + /* + * XXX the "24" below could be dynamic, thereby eliminating one + * more instance of the "number to vfs" mapping problem, but + * "24" is the order as taken from sys/mount.h + */ + sysctl_createv(&udf_sysctl_log, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "vfs", NULL, + NULL, 0, NULL, 0, + CTL_VFS, CTL_EOL); + sysctl_createv(&udf_sysctl_log, 0, NULL, &node, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "udf", + SYSCTL_DESCR("OSTA Universal File System"), + NULL, 0, NULL, 0, + CTL_VFS, 24, CTL_EOL); +#ifdef DEBUG + sysctl_createv(&udf_sysctl_log, 0, NULL, &node, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "verbose", + SYSCTL_DESCR("Bitmask for filesystem debugging"), + NULL, 0, &udf_verbose, 0, + CTL_VFS, 24, UDF_VERBOSE_SYSCTLOPT, CTL_EOL); +#endif + break; + case MODULE_CMD_FINI: + error = vfs_detach(&udf_vfsops); + if (error != 0) + break; + sysctl_teardown(&udf_sysctl_log); + break; + default: + error = ENOTTY; + break; + } + + return (error); +} + +/* --------------------------------------------------------------------- */ + +int +udf_mountroot(void) +{ + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +#define MPFREE(a, lst) \ + if ((a)) free((a), lst); +static void +free_udf_mountinfo(struct mount *mp) +{ + struct udf_mount *ump; + int i; + + if (!mp) + return; + + ump = VFSTOUDF(mp); + if (ump) { + /* clear our data */ + for (i = 0; i < UDF_ANCHORS; i++) + MPFREE(ump->anchors[i], M_UDFVOLD); + MPFREE(ump->primary_vol, M_UDFVOLD); + MPFREE(ump->logical_vol, M_UDFVOLD); + MPFREE(ump->unallocated, M_UDFVOLD); + MPFREE(ump->implementation, M_UDFVOLD); + MPFREE(ump->logvol_integrity, M_UDFVOLD); + for (i = 0; i < UDF_PARTITIONS; i++) { + MPFREE(ump->partitions[i], M_UDFVOLD); + MPFREE(ump->part_unalloc_dscr[i], M_UDFVOLD); + MPFREE(ump->part_freed_dscr[i], M_UDFVOLD); + } + MPFREE(ump->metadata_unalloc_dscr, M_UDFVOLD); + + MPFREE(ump->fileset_desc, M_UDFVOLD); + MPFREE(ump->sparing_table, M_UDFVOLD); + + MPFREE(ump->la_node_ad_cpy, M_UDFMNT); + MPFREE(ump->la_pmapping, M_TEMP); + MPFREE(ump->la_lmapping, M_TEMP); + + mutex_destroy(&ump->ihash_lock); + mutex_destroy(&ump->get_node_lock); + mutex_destroy(&ump->logvol_mutex); + mutex_destroy(&ump->allocate_mutex); + cv_destroy(&ump->dirtynodes_cv); + + MPFREE(ump->vat_table, M_UDFVOLD); + + free(ump, M_UDFMNT); + } +} +#undef MPFREE + +/* --------------------------------------------------------------------- */ + +/* if the system nodes exist, release them */ +static void +udf_release_system_nodes(struct mount *mp) +{ + struct udf_mount *ump = VFSTOUDF(mp); + int error; + + /* if we haven't even got an ump, dont bother */ + if (!ump) + return; + + /* VAT partition support */ + if (ump->vat_node) + vrele(ump->vat_node->vnode); + + /* Metadata partition support */ + if (ump->metadata_node) + vrele(ump->metadata_node->vnode); + if (ump->metadatamirror_node) + vrele(ump->metadatamirror_node->vnode); + if (ump->metadatabitmap_node) + vrele(ump->metadatabitmap_node->vnode); + + /* This flush should NOT write anything nor allow any node to remain */ + if ((error = vflush(ump->vfs_mountp, NULLVP, 0)) != 0) + panic("Failure to flush UDF system vnodes\n"); +} + + +int +udf_mount(struct mount *mp, const char *path, + void *data, size_t *data_len) +{ + struct lwp *l = curlwp; + struct udf_args *args = data; + struct udf_mount *ump; + struct vnode *devvp; + int openflags, accessmode, error; + + DPRINTF(CALL, ("udf_mount called\n")); + + if (*data_len < sizeof *args) + return EINVAL; + + if (mp->mnt_flag & MNT_GETARGS) { + /* request for the mount arguments */ + ump = VFSTOUDF(mp); + if (ump == NULL) + return EINVAL; + *args = ump->mount_args; + *data_len = sizeof *args; + return 0; + } + + /* handle request for updating mount parameters */ + /* TODO can't update my mountpoint yet */ + if (mp->mnt_flag & MNT_UPDATE) { + return EOPNOTSUPP; + } + + /* OK, so we are asked to mount the device */ + + /* check/translate struct version */ + /* TODO sanity checking other mount arguments */ + if (args->version != 1) { + printf("mount_udf: unrecognized argument structure version\n"); + return EINVAL; + } + + /* lookup name to get its vnode */ + error = namei_simple_user(args->fspec, + NSM_FOLLOW_NOEMULROOT, &devvp); + if (error) + return error; + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_VOLUMES) + vprint("UDF mount, trying to mount \n", devvp); +#endif + + /* check if its a block device specified */ + if (devvp->v_type != VBLK) { + vrele(devvp); + return ENOTBLK; + } + if (bdevsw_lookup(devvp->v_rdev) == NULL) { + vrele(devvp); + return ENXIO; + } + + /* + * If mount by non-root, then verify that user has necessary + * permissions on the device. + */ + accessmode = VREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, + KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode)); + VOP_UNLOCK(devvp); + if (error) { + vrele(devvp); + return error; + } + + /* + * Open device and try to mount it! + */ + if (mp->mnt_flag & MNT_RDONLY) { + openflags = FREAD; + } else { + openflags = FREAD | FWRITE; + } + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_OPEN(devvp, openflags, FSCRED); + VOP_UNLOCK(devvp); + if (error == 0) { + /* opened ok, try mounting */ + error = udf_mountfs(devvp, mp, l, args); + if (error) { + udf_release_system_nodes(mp); + /* cleanup */ + udf_discstrat_finish(VFSTOUDF(mp)); + free_udf_mountinfo(mp); + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + (void) VOP_CLOSE(devvp, openflags, NOCRED); + VOP_UNLOCK(devvp); + } + } + if (error) { + /* devvp is still locked */ + vrele(devvp); + return error; + } + + /* register our mountpoint being on this device */ + spec_node_setmountedfs(devvp, mp); + + /* successfully mounted */ + DPRINTF(VOLUMES, ("udf_mount() successfull\n")); + + error = set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, + mp->mnt_op->vfs_name, mp, l); + if (error) + return error; + + /* If we're not opened read-only, open its logical volume */ + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if ((error = udf_open_logvol(VFSTOUDF(mp))) != 0) { + printf( "mount_udf: can't open logical volume for " + "writing, downgrading access to read-only\n"); + mp->mnt_flag |= MNT_RDONLY; + /* FIXME we can't return error now on open failure */ + return 0; + } + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef DEBUG +static void +udf_unmount_sanity_check(struct mount *mp) +{ + struct vnode *vp; + + printf("On unmount, i found the following nodes:\n"); + TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) { + vprint("", vp); + if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) { + printf(" is locked\n"); + } + if (vp->v_usecount > 1) + printf(" more than one usecount %d\n", vp->v_usecount); + } +} +#endif + + +int +udf_unmount(struct mount *mp, int mntflags) +{ + struct udf_mount *ump; + int error, flags, closeflags; + + DPRINTF(CALL, ("udf_umount called\n")); + + ump = VFSTOUDF(mp); + if (!ump) + panic("UDF unmount: empty ump\n"); + + flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0; + /* TODO remove these paranoid functions */ +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* + * By specifying SKIPSYSTEM we can skip vnodes marked with VV_SYSTEM. + * This hardly documented feature allows us to exempt certain files + * from being flushed. + */ + if ((error = vflush(mp, NULLVP, flags | SKIPSYSTEM)) != 0) + return error; + + /* update nodes and wait for completion of writeout of system nodes */ + udf_sync(mp, FSYNC_WAIT, NOCRED); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* flush again, to check if we are still busy for something else */ + if ((error = vflush(ump->vfs_mountp, NULLVP, flags | SKIPSYSTEM)) != 0) + return error; + + DPRINTF(VOLUMES, ("flush OK on unmount\n")); + + /* close logical volume and close session if requested */ + if ((error = udf_close_logvol(ump, mntflags)) != 0) + return error; + +#ifdef DEBUG + DPRINTF(VOLUMES, ("FINAL sanity check\n")); + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* NOTE release system nodes should NOT write anything */ + udf_release_system_nodes(mp); + + /* finalise disc strategy */ + udf_discstrat_finish(ump); + + /* synchronise device caches */ + (void) udf_synchronise_caches(ump); + + /* close device */ + DPRINTF(VOLUMES, ("closing device\n")); + if (mp->mnt_flag & MNT_RDONLY) { + closeflags = FREAD; + } else { + closeflags = FREAD | FWRITE; + } + + /* devvp is still locked by us */ + vn_lock(ump->devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_CLOSE(ump->devvp, closeflags, NOCRED); + if (error) + printf("Error during closure of device! error %d, " + "device might stay locked\n", error); + DPRINTF(VOLUMES, ("device close ok\n")); + + /* clear our mount reference and release device node */ + spec_node_setmountedfs(ump->devvp, NULL); + vput(ump->devvp); + + /* free our ump */ + free_udf_mountinfo(mp); + + /* free ump struct references */ + mp->mnt_data = NULL; + mp->mnt_flag &= ~MNT_LOCAL; + + DPRINTF(VOLUMES, ("Fin unmount\n")); + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Helper function of udf_mount() that actually mounts the disc. + */ + +static int +udf_mountfs(struct vnode *devvp, struct mount *mp, + struct lwp *l, struct udf_args *args) +{ + struct udf_mount *ump; + uint32_t sector_size, lb_size, bshift; + uint32_t logvol_integrity; + int num_anchors, error; + + /* flush out any old buffers remaining from a previous use. */ + if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0))) + return error; + + /* setup basic mount information */ + mp->mnt_data = NULL; + mp->mnt_stat.f_fsidx.__fsid_val[0] = (uint32_t) devvp->v_rdev; + mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_UDF); + mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; + mp->mnt_stat.f_namemax = UDF_MAXNAMLEN; + mp->mnt_flag |= MNT_LOCAL; +// mp->mnt_iflag |= IMNT_MPSAFE; + + /* allocate udf part of mount structure; malloc always succeeds */ + ump = malloc(sizeof(struct udf_mount), M_UDFMNT, M_WAITOK | M_ZERO); + + /* init locks */ + mutex_init(&ump->logvol_mutex, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->ihash_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->get_node_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->allocate_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&ump->dirtynodes_cv, "udfsync2"); + + /* init rbtree for nodes, ordered by their icb address (long_ad) */ + udf_init_nodes_tree(ump); + + /* set up linkage */ + mp->mnt_data = ump; + ump->vfs_mountp = mp; + + /* set up arguments and device */ + ump->mount_args = *args; + ump->devvp = devvp; + if ((error = udf_update_discinfo(ump))) { + printf("UDF mount: error inspecting fs node\n"); + return error; + } + + /* inspect sector size */ + sector_size = ump->discinfo.sector_size; + bshift = 1; + while ((1 << bshift) < sector_size) + bshift++; + if ((1 << bshift) != sector_size) { + printf("UDF mount: " + "hit NetBSD implementation fence on sector size\n"); + return EIO; + } + + /* temporary check to overcome sectorsize >= 8192 bytes panic */ + if (sector_size >= 8192) { + printf("UDF mount: " + "hit implementation limit, sectorsize to big\n"); + return EIO; + } + + /* + * Inspect if we're asked to mount read-write on a non recordable or + * closed sequential disc. + */ + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if ((ump->discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { + printf("UDF mount: disc is not recordable\n"); + return EROFS; + } + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + if (ump->discinfo.disc_state == MMC_STATE_FULL) { + printf("UDF mount: disc is not appendable\n"); + return EROFS; + } + + /* + * TODO if the last session is closed check if there + * is enough space to open/close new session + */ + } + /* double check if we're not mounting a pervious session RW */ + if (args->sessionnr != 0) { + printf("UDF mount: updating a previous session " + "not yet allowed\n"); + return EROFS; + } + } + + /* initialise bootstrap disc strategy */ + ump->strategy = &udf_strat_bootstrap; + udf_discstrat_init(ump); + + /* read all anchors to get volume descriptor sequence */ + num_anchors = udf_read_anchors(ump); + if (num_anchors == 0) + return EINVAL; + + DPRINTF(VOLUMES, ("Read %d anchors on this disc, session %d\n", + num_anchors, args->sessionnr)); + + /* read in volume descriptor sequence */ + if ((error = udf_read_vds_space(ump))) { + printf("UDF mount: error reading volume space\n"); + return error; + } + + /* close down bootstrap disc strategy */ + udf_discstrat_finish(ump); + + /* check consistency and completeness */ + if ((error = udf_process_vds(ump))) { + printf( "UDF mount: disc not properly formatted" + "(bad VDS)\n"); + return error; + } + + /* switch to new disc strategy */ + KASSERT(ump->strategy != &udf_strat_bootstrap); + udf_discstrat_init(ump); + + /* initialise late allocation administration space */ + ump->la_lmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS, + M_TEMP, M_WAITOK); + ump->la_pmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS, + M_TEMP, M_WAITOK); + + /* setup node cleanup extents copy space */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + ump->la_node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS, + M_UDFMNT, M_WAITOK); + memset(ump->la_node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS); + + /* setup rest of mount information */ + mp->mnt_data = ump; + + /* bshift is allways equal to disc sector size */ + mp->mnt_dev_bshift = bshift; + mp->mnt_fs_bshift = bshift; + + /* note that the mp info needs to be initialised for reading! */ + /* read vds support tables like VAT, sparable etc. */ + if ((error = udf_read_vds_tables(ump))) { + printf( "UDF mount: error in format or damaged disc " + "(VDS tables failing)\n"); + return error; + } + + /* check if volume integrity is closed otherwise its dirty */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity != UDF_INTEGRITY_CLOSED) { + printf("UDF mount: file system is not clean; "); + printf("please fsck(8)\n"); + return EPERM; + } + + /* read root directory */ + if ((error = udf_read_rootdirs(ump))) { + printf( "UDF mount: " + "disc not properly formatted or damaged disc " + "(rootdirs failing)\n"); + return error; + } + + /* success! */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_start(struct mount *mp, int flags) +{ + /* do we have to do something here? */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *vp; + struct long_ad *dir_loc; + struct udf_mount *ump = VFSTOUDF(mp); + struct udf_node *root_dir; + int error; + + DPRINTF(CALL, ("udf_root called\n")); + + dir_loc = &ump->fileset_desc->rootdir_icb; + error = udf_get_node(ump, dir_loc, &root_dir); + + if (!root_dir) + error = ENOENT; + if (error) + return error; + + vp = root_dir->vnode; + KASSERT(vp->v_vflag & VV_ROOT); + + *vpp = vp; + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_statvfs(struct mount *mp, struct statvfs *sbp) +{ + struct udf_mount *ump = VFSTOUDF(mp); + struct logvol_int_desc *lvid; + struct udf_logvol_info *impl; + uint64_t freeblks, sizeblks; + int num_part; + + DPRINTF(CALL, ("udf_statvfs called\n")); + sbp->f_flag = mp->mnt_flag; + sbp->f_bsize = ump->discinfo.sector_size; + sbp->f_frsize = ump->discinfo.sector_size; + sbp->f_iosize = ump->discinfo.sector_size; + + mutex_enter(&ump->allocate_mutex); + + udf_calc_freespace(ump, &sizeblks, &freeblks); + + sbp->f_blocks = sizeblks; + sbp->f_bfree = freeblks; + sbp->f_files = 0; + + lvid = ump->logvol_integrity; + num_part = udf_rw32(lvid->num_part); + impl = (struct udf_logvol_info *) (lvid->tables + 2*num_part); + if (impl) { + sbp->f_files = udf_rw32(impl->num_files); + sbp->f_files += udf_rw32(impl->num_directories); + } + + /* XXX read only for now XXX */ + sbp->f_bavail = 0; + sbp->f_bresvd = 0; + + /* tricky, next only aplies to ffs i think, so set to zero */ + sbp->f_ffree = 0; + sbp->f_favail = 0; + sbp->f_fresvd = 0; + + mutex_exit(&ump->allocate_mutex); + + copy_statvfs_info(sbp, mp); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * TODO what about writing out free space maps, lvid etc? only on `waitfor' + * i.e. explicit syncing by the user? + */ + +static int +udf_sync_writeout_system_files(struct udf_mount *ump, int clearflags) +{ + int error; + + /* XXX lock for VAT en bitmaps? */ + /* metadata nodes are written synchronous */ + DPRINTF(CALL, ("udf_sync: syncing metadata\n")); + if (ump->lvclose & UDF_WRITE_VAT) + udf_writeout_vat(ump); + + error = 0; + if (ump->lvclose & UDF_WRITE_PART_BITMAPS) { + /* writeout metadata spacetable if existing */ + error = udf_write_metadata_partition_spacetable(ump, MNT_WAIT); + if (error) + printf( "udf_writeout_system_files : " + " writeout of metadata space bitmap failed\n"); + + /* writeout partition spacetables */ + error = udf_write_physical_partition_spacetables(ump, MNT_WAIT); + if (error) + printf( "udf_writeout_system_files : " + "writeout of space tables failed\n"); + if (!error && clearflags) + ump->lvclose &= ~UDF_WRITE_PART_BITMAPS; + } + + return error; +} + + +int +udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred) +{ + struct udf_mount *ump = VFSTOUDF(mp); + + DPRINTF(CALL, ("udf_sync called\n")); + /* if called when mounted readonly, just ignore */ + if (mp->mnt_flag & MNT_RDONLY) + return 0; + + if (ump->syncing && !waitfor) { + printf("UDF: skipping autosync\n"); + return 0; + } + + /* get sync lock */ + ump->syncing = 1; + + /* pre-sync */ + udf_do_sync(ump, cred, waitfor); + + if (waitfor == MNT_WAIT) + udf_sync_writeout_system_files(ump, true); + + DPRINTF(CALL, ("end of udf_sync()\n")); + ump->syncing = 0; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Get vnode for the file system type specific file id ino for the fs. Its + * used for reference to files by unique ID and for NFSv3. + * (optional) TODO lookup why some sources state NFSv3 + */ +int +udf_vget(struct mount *mp, ino_t ino, + struct vnode **vpp) +{ + DPRINTF(NOTIMPL, ("udf_vget called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Lookup vnode for file handle specified + */ +int +udf_fhtovp(struct mount *mp, struct fid *fhp, + struct vnode **vpp) +{ + DPRINTF(NOTIMPL, ("udf_fhtovp called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Create an unique file handle. Its structure is opaque and won't be used by + * other subsystems. It should uniquely identify the file in the filingsystem + * and enough information to know if a file has been removed and/or resources + * have been recycled. + */ +int +udf_vptofh(struct vnode *vp, struct fid *fid, + size_t *fh_size) +{ + DPRINTF(NOTIMPL, ("udf_vptofh called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Create a filingsystem snapshot at the specified timestamp. Could be + * implemented by explicitly creating a new session or with spare room in the + * integrity descriptor space + */ +int +udf_snapshot(struct mount *mp, struct vnode *vp, + struct timespec *tm) +{ + DPRINTF(NOTIMPL, ("udf_snapshot called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c new file mode 100644 index 000000000..697d89162 --- /dev/null +++ b/sys/fs/udf/udf_vnops.c @@ -0,0 +1,2183 @@ +/* $NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * 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. + * + * Generic parts are derived from software contributed to The NetBSD Foundation + * by Julio M. Merino Vidal, developed as part of Google's Summer of Code + * 2005 program. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $"); +#endif /* not lint */ + + +#include +#include +#include +#include /* defines plimit structure in proc struct */ +#include +#include /* define FWRITE ... */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + + +/* externs */ +extern int prtactive; + +/* implementations of vnode functions; table follows at end */ +/* --------------------------------------------------------------------- */ + +int +udf_inactive(void *v) +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + bool *a_recycle; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int refcnt; + + DPRINTF(NODE, ("udf_inactive called for udf_node %p\n", VTOI(vp))); + + if (udf_node == NULL) { + DPRINTF(NODE, ("udf_inactive: inactive NULL UDF node\n")); + VOP_UNLOCK(vp); + return 0; + } + + /* + * Optionally flush metadata to disc. If the file has not been + * referenced anymore in a directory we ought to free up the resources + * on disc if applicable. + */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + assert(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } + + if ((refcnt == 0) && (vp->v_vflag & VV_SYSTEM)) { + DPRINTF(VOLUMES, ("UDF_INACTIVE deleting VV_SYSTEM\n")); + /* system nodes are not writen out on inactive, so flush */ + udf_node->i_flags = 0; + } + + *ap->a_recycle = false; + if ((refcnt == 0) && ((vp->v_vflag & VV_SYSTEM) == 0)) { + /* remove this file's allocation */ + DPRINTF(NODE, ("udf_inactive deleting unlinked file\n")); + *ap->a_recycle = true; + udf_delete_node(udf_node); + VOP_UNLOCK(vp); + return 0; + } + + /* write out its node */ + if (udf_node->i_flags & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) + udf_update(vp, NULL, NULL, NULL, 0); + VOP_UNLOCK(vp); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred, struct lwp *lwp); + +int +udf_reclaim(void *v) +{ + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + + DPRINTF(NODE, ("udf_reclaim called for node %p\n", udf_node)); + if (prtactive && vp->v_usecount > 1) + vprint("udf_reclaim(): pushing active", vp); + + if (udf_node == NULL) { + DPRINTF(NODE, ("udf_reclaim(): null udfnode\n")); + return 0; + } + + /* update note for closure */ + udf_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); + + /* async check to see if all node descriptors are written out */ + while ((volatile int) udf_node->outstanding_nodedscr > 0) { + vprint("udf_reclaim(): waiting for writeout\n", vp); + tsleep(&udf_node->outstanding_nodedscr, PRIBIO, "recl wait", hz/8); + } + + /* dispose all node knowledge */ + udf_dispose_node(udf_node); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read(void *v) +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int ioflag = ap->a_ioflag; + int advice = IO_ADV_DECODE(ap->a_ioflag); + struct uvm_object *uobj; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size; + vsize_t len; + int error; + + /* + * XXX reading from extended attributes not yet implemented. FreeBSD + * has it in mind to forward the IO_EXT read call to the + * VOP_READEXTATTR(). + */ + + DPRINTF(READ, ("udf_read called\n")); + + /* can this happen? some filingsystems have this check */ + if (uio->uio_offset < 0) + return EINVAL; + if (uio->uio_resid == 0) + return 0; + + /* protect against rogue programs reading raw directories and links */ + if ((ioflag & IO_ALTSEMANTICS) == 0) { + if (vp->v_type == VDIR) + return EISDIR; + /* all but regular files just give EINVAL */ + if (vp->v_type != VREG) + return EINVAL; + } + + assert(udf_node); + assert(udf_node->fe || udf_node->efe); + + /* get file/directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + /* read contents using buffercache */ + uobj = &vp->v_uobj; + error = 0; + while (uio->uio_resid > 0) { + /* reached end? */ + if (file_size <= uio->uio_offset) + break; + + /* maximise length to file extremity */ + len = MIN(file_size - uio->uio_offset, uio->uio_resid); + if (len == 0) + break; + + /* ubc, here we come, prepare to trap */ + error = ubc_uiomove(uobj, uio, len, advice, + UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); + if (error) + break; + } + + /* note access time unless not requested */ + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { + udf_node->i_flags |= IN_ACCESS; + if ((ioflag & IO_SYNC) == IO_SYNC) + error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int ioflag = ap->a_ioflag; + kauth_cred_t cred = ap->a_cred; + int advice = IO_ADV_DECODE(ap->a_ioflag); + struct uvm_object *uobj; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size, old_size, old_offset; + vsize_t len; + int aflag = ioflag & IO_SYNC ? B_SYNC : 0; + int error; + int resid, extended; + + /* + * XXX writing to extended attributes not yet implemented. FreeBSD has + * it in mind to forward the IO_EXT read call to the + * VOP_READEXTATTR(). + */ + + DPRINTF(WRITE, ("udf_write called\n")); + + /* can this happen? some filingsystems have this check */ + if (uio->uio_offset < 0) + return EINVAL; + if (uio->uio_resid == 0) + return 0; + + /* protect against rogue programs writing raw directories or links */ + if ((ioflag & IO_ALTSEMANTICS) == 0) { + if (vp->v_type == VDIR) + return EISDIR; + /* all but regular files just give EINVAL for now */ + if (vp->v_type != VREG) + return EINVAL; + } + + assert(udf_node); + assert(udf_node->fe || udf_node->efe); + + /* get file/directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + old_size = file_size; + + /* if explicitly asked to append, uio_offset can be wrong? */ + if (ioflag & IO_APPEND) + uio->uio_offset = file_size; + + extended = (uio->uio_offset + uio->uio_resid > file_size); + if (extended) { + DPRINTF(WRITE, ("extending file from %"PRIu64" to %"PRIu64"\n", + file_size, uio->uio_offset + uio->uio_resid)); + error = udf_grow_node(udf_node, uio->uio_offset + uio->uio_resid); + if (error) + return error; + file_size = uio->uio_offset + uio->uio_resid; + } + + /* write contents using buffercache */ + uobj = &vp->v_uobj; + resid = uio->uio_resid; + error = 0; + + uvm_vnp_setwritesize(vp, file_size); + old_offset = uio->uio_offset; + while (uio->uio_resid > 0) { + /* maximise length to file extremity */ + len = MIN(file_size - uio->uio_offset, uio->uio_resid); + if (len == 0) + break; + + genfs_node_wrlock(vp); + error = GOP_ALLOC(vp, uio->uio_offset, len, aflag, cred); + genfs_node_unlock(vp); + if (error) + break; + + /* ubc, here we come, prepare to trap */ + error = ubc_uiomove(uobj, uio, len, advice, + UBC_WRITE | UBC_UNMAP_FLAG(vp)); + if (error) + break; + + /* + * flush what we just wrote if necessary. + * XXXUBC simplistic async flushing. + * + * Directories are excluded since its file data that we want + * to purge. + */ + if ((vp->v_type != VDIR) && + (old_offset >> 16 != uio->uio_offset >> 16)) { + mutex_enter(vp->v_interlock); + error = VOP_PUTPAGES(vp, (old_offset >> 16) << 16, + (uio->uio_offset >> 16) << 16, + PGO_CLEANIT | PGO_LAZY); + old_offset = uio->uio_offset; + } + } + uvm_vnp_setsize(vp, file_size); + + /* mark node changed and request update */ + udf_node->i_flags |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + /* + * XXX TODO FFS has code here to reset setuid & setgid when we're not + * the superuser as a precaution against tampering. + */ + + /* if we wrote a thing, note write action on vnode */ + if (resid > uio->uio_resid) + VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); + + if (error) { + /* bring back file size to its former size */ + /* take notice of its errors? */ + (void) udf_chsize(vp, (u_quad_t) old_size, cred); + + /* roll back uio */ + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } else { + /* if we write and we're synchronous, update node */ + if ((resid > uio->uio_resid) && ((ioflag & IO_SYNC) == IO_SYNC)) + error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT); + } + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * `Special' bmap functionality that translates all incomming requests to + * translate to vop_strategy() calls with the same blocknumbers effectively + * not translating at all. + */ + +int +udf_trivial_bmap(void *v) +{ + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; /* our node */ + struct vnode **vpp = ap->a_vpp; /* return node */ + daddr_t *bnp = ap->a_bnp; /* translated */ + daddr_t bn = ap->a_bn; /* origional */ + int *runp = ap->a_runp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_size; + + /* get logical block size */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + /* could return `-1' to indicate holes/zeros */ + /* translate 1:1 */ + *bnp = bn; + + /* set the vnode to read the data from with strategy on itself */ + if (vpp) + *vpp = vp; + + /* set runlength of maximum block size */ + if (runp) + *runp = MAXPHYS / lb_size; /* or with -1 ? */ + + /* return success */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_vfsstrategy(void *v) +{ + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct buf *bp = ap->a_bp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_size, sectors; + + DPRINTF(STRATEGY, ("udf_strategy called\n")); + + /* check if we ought to be here */ + if (vp->v_type == VBLK || vp->v_type == VCHR) + panic("udf_strategy: spec"); + + /* only filebuffers ought to be read/write by this, no descriptors */ + assert(bp->b_blkno >= 0); + + /* get sector size */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + /* calculate length to fetch/store in sectors */ + sectors = bp->b_bcount / lb_size; + assert(bp->b_bcount > 0); + + /* NEVER assume later that this buffer is already translated */ + /* bp->b_lblkno = bp->b_blkno; */ + + /* check assertions: we OUGHT to always get multiples of this */ + assert(sectors * lb_size == bp->b_bcount); + + /* issue buffer */ + if (bp->b_flags & B_READ) { + DPRINTF(STRATEGY, ("\tread vp %p buf %p (blk no %"PRIu64")" + ", for %d sectors\n", + vp, bp, bp->b_blkno, sectors)); + + /* read buffer from the udf_node, translate vtop on the way*/ + udf_read_filebuf(udf_node, bp); + } else { + DPRINTF(STRATEGY, ("\twrite vp %p buf %p (blk no %"PRIu64")" + ", for %d sectors\n", + vp, bp, bp->b_blkno, sectors)); + + /* write buffer to the udf_node, translate vtop on the way*/ + udf_write_filebuf(udf_node, bp); + } + + return bp->b_error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_readdir(void *v) +{ + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + int *a_eofflag; + off_t **a_cookies; + int *a_ncookies; + } */ *ap = v; + struct uio *uio = ap->a_uio; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t file_size, diroffset, transoffset; + uint32_t lb_size; + int error; + + DPRINTF(READDIR, ("udf_readdir called\n")); + + /* This operation only makes sense on directory nodes. */ + if (vp->v_type != VDIR) + return ENOTDIR; + + /* get directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK | M_ZERO); + + /* + * Add `.' pseudo entry if at offset zero since its not in the fid + * stream + */ + if (uio->uio_offset == 0) { + DPRINTF(READDIR, ("\t'.' inserted\n")); + strcpy(dirent->d_name, "."); + dirent->d_fileno = udf_get_node_id(&udf_node->loc); + dirent->d_type = DT_DIR; + dirent->d_namlen = strlen(dirent->d_name); + dirent->d_reclen = _DIRENT_SIZE(dirent); + uiomove(dirent, _DIRENT_SIZE(dirent), uio); + + /* mark with magic value that we have done the dummy */ + uio->uio_offset = UDF_DIRCOOKIE_DOT; + } + + /* we are called just as long as we keep on pushing data in */ + error = 0; + if (uio->uio_offset < file_size) { + /* allocate temporary space for fid */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + if (uio->uio_offset == UDF_DIRCOOKIE_DOT) + uio->uio_offset = 0; + + diroffset = uio->uio_offset; + transoffset = diroffset; + while (diroffset < file_size) { + DPRINTF(READDIR, ("\tread in fid stream\n")); + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &diroffset, fid, dirent); + DPRINTFIF(READDIR, error, ("read error in read fid " + "stream : %d\n", error)); + if (error) + break; + + /* + * If there isn't enough space in the uio to return a + * whole dirent, break off read + */ + if (uio->uio_resid < _DIRENT_SIZE(dirent)) + break; + + /* remember the last entry we transfered */ + transoffset = diroffset; + + /* skip deleted entries */ + if (fid->file_char & UDF_FILE_CHAR_DEL) + continue; + + /* skip not visible files */ + if (fid->file_char & UDF_FILE_CHAR_VIS) + continue; + + /* copy dirent to the caller */ + DPRINTF(READDIR, ("\tread dirent `%s', type %d\n", + dirent->d_name, dirent->d_type)); + uiomove(dirent, _DIRENT_SIZE(dirent), uio); + } + + /* pass on last transfered offset */ + uio->uio_offset = transoffset; + free(fid, M_UDFTEMP); + } + + if (ap->a_eofflag) + *ap->a_eofflag = (uio->uio_offset >= file_size); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_READDIR) { + printf("returning offset %d\n", (uint32_t) uio->uio_offset); + if (ap->a_eofflag) + printf("returning EOF ? %d\n", *ap->a_eofflag); + if (error) + printf("readdir returning error %d\n", error); + } +#endif + + free(dirent, M_UDFTEMP); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_lookup(void *v) +{ + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node, *res_node; + struct udf_mount *ump; + struct long_ad icb_loc; + mode_t mode; + uid_t d_uid; + gid_t d_gid; + const char *name; + int namelen, nameiop, islastcn, mounted_ro; + int error, found; + + dir_node = VTOI(dvp); + ump = dir_node->ump; + *vpp = NULL; + + DPRINTF(LOOKUP, ("udf_lookup called, lookup `%s`\n", + cnp->cn_nameptr)); + + /* simplify/clarification flags */ + nameiop = cnp->cn_nameiop; + islastcn = cnp->cn_flags & ISLASTCN; + mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY; + + /* check exec/dirread permissions first */ + error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred); + if (error) + return error; + + DPRINTF(LOOKUP, ("\taccess ok\n")); + + /* + * If requesting a modify on the last path element on a read-only + * filingsystem, reject lookup; XXX why is this repeated in every FS ? + */ + if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME)) + return EROFS; + + DPRINTF(LOOKUP, ("\tlooking up cnp->cn_nameptr '%s'\n", + cnp->cn_nameptr)); + /* look in the namecache */ + if (cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, + cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) { + return *vpp == NULLVP ? ENOENT : 0; + } + + DPRINTF(LOOKUP, ("\tNOT found in cache\n")); + + /* + * Obviously, the file is not (anymore) in the namecache, we have to + * search for it. There are three basic cases: '.', '..' and others. + * + * Following the guidelines of VOP_LOOKUP manpage and tmpfs. + */ + error = 0; + if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) { + DPRINTF(LOOKUP, ("\tlookup '.'\n")); + /* special case 1 '.' */ + if (islastcn && cnp->cn_nameiop == RENAME) { + error = EISDIR; + goto out; + } + vref(dvp); + *vpp = dvp; + /* done */ + goto done; + } else if (cnp->cn_flags & ISDOTDOT) { + /* special case 2 '..' */ + DPRINTF(LOOKUP, ("\tlookup '..'\n")); + + if (islastcn && cnp->cn_nameiop == RENAME) { + error = EINVAL; + goto out; + } + + /* get our node */ + name = ".."; + namelen = 2; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + goto out; + if (!found) + error = ENOENT; + + /* first unlock parent */ + VOP_UNLOCK(dvp); + + if (error == 0) { + DPRINTF(LOOKUP, ("\tfound '..'\n")); + /* try to create/reuse the node */ + error = udf_get_node(ump, &icb_loc, &res_node); + + if (!error) { + DPRINTF(LOOKUP, + ("\tnode retrieved/created OK\n")); + *vpp = res_node->vnode; + } + } + + /* try to relock parent */ + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + goto out; + } + + /* all other files */ + DPRINTF(LOOKUP, ("\tlookup file/dir in directory\n")); + + /* lookup filename in the directory; location icb_loc */ + name = cnp->cn_nameptr; + namelen = cnp->cn_namelen; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + goto out; + if (!found) { + DPRINTF(LOOKUP, ("\tNOT found\n")); + /* + * The entry was not found in the directory. This is + * valid if we are creating or renaming an entry and + * are working on the last component of the path name. + */ + if (islastcn && (cnp->cn_nameiop == CREATE || + cnp->cn_nameiop == RENAME)) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); + if (error) { + goto out; + } + error = EJUSTRETURN; + } else { + error = ENOENT; + } + /* done */ + goto done; + } + + /* + * XXX NOTE tmpfs has a test here that tests that intermediate + * components i.e. not the last one ought to be either a directory or + * a link. It seems to function well without this code. + */ + + /* try to create/reuse the node */ + error = udf_get_node(ump, &icb_loc, &res_node); + if (error) + goto out; + + /* check permissions */ + if (islastcn && (cnp->cn_nameiop == DELETE || + cnp->cn_nameiop == RENAME) ) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); + if (error) { + vput(res_node->vnode); + goto out; + } + + /* + * Check if the directory has its sticky bit set. If so, ask + * for clearance since only the owner of a file or directory + * can remove/rename from taht directory. + */ + mode = udf_getaccessmode(dir_node); + if ((mode & S_ISTXT) != 0) { + udf_getownership(dir_node, &d_uid, &d_gid); + error = kauth_authorize_vnode(cnp->cn_cred, + KAUTH_VNODE_DELETE, res_node->vnode, + dir_node->vnode, genfs_can_sticky(cnp->cn_cred, + d_uid, d_uid)); + if (error) { + error = EPERM; + vput(res_node->vnode); + goto out; + } + } + } + + *vpp = res_node->vnode; + +done: + /* + * Store result in the cache if requested. If we are creating a file, + * the file might not be found and thus putting it into the namecache + * might be seen as negative caching. + */ + if (nameiop != CREATE) + cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, + cnp->cn_flags); + +out: + DPRINTFIF(LOOKUP, error, ("udf_lookup returing error %d\n", error)); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_getattr(void *v) +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct lwp *a_l; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + struct filetimes_extattr_entry *ft_extattr; + struct device_extattr_entry *devattr; + struct vattr *vap = ap->a_vap; + struct timestamp *atime, *mtime, *attrtime, *creatime; + uint64_t filesize, blkssize; + uint32_t nlink; + uint32_t offset, a_l; + uint8_t *filedata; + uid_t uid; + gid_t gid; + int error; + + DPRINTF(CALL, ("udf_getattr called\n")); + + /* update times before we returning values */ + udf_itimes(udf_node, NULL, NULL, NULL); + + /* get descriptor information */ + if (fe) { + nlink = udf_rw16(fe->link_cnt); + uid = (uid_t)udf_rw32(fe->uid); + gid = (gid_t)udf_rw32(fe->gid); + filesize = udf_rw64(fe->inf_len); + blkssize = udf_rw64(fe->logblks_rec); + atime = &fe->atime; + mtime = &fe->mtime; + attrtime = &fe->attrtime; + filedata = fe->data; + + /* initial guess */ + creatime = mtime; + + /* check our extended attribute if present */ + error = udf_extattr_search_intern(udf_node, + UDF_FILETIMES_ATTR_NO, "", &offset, &a_l); + if (!error) { + ft_extattr = (struct filetimes_extattr_entry *) + (filedata + offset); + if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION) + creatime = &ft_extattr->times[0]; + } + } else { + assert(udf_node->efe); + nlink = udf_rw16(efe->link_cnt); + uid = (uid_t)udf_rw32(efe->uid); + gid = (gid_t)udf_rw32(efe->gid); + filesize = udf_rw64(efe->inf_len); /* XXX or obj_size? */ + blkssize = udf_rw64(efe->logblks_rec); + atime = &efe->atime; + mtime = &efe->mtime; + attrtime = &efe->attrtime; + creatime = &efe->ctime; + filedata = efe->data; + } + + /* do the uid/gid translation game */ + if (uid == (uid_t) -1) + uid = ump->mount_args.anon_uid; + if (gid == (gid_t) -1) + gid = ump->mount_args.anon_gid; + + /* fill in struct vattr with values from the node */ + vattr_null(vap); + vap->va_type = vp->v_type; + vap->va_mode = udf_getaccessmode(udf_node); + vap->va_nlink = nlink; + vap->va_uid = uid; + vap->va_gid = gid; + vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; + vap->va_fileid = udf_get_node_id(&udf_node->loc); /* inode hash XXX */ + vap->va_size = filesize; + vap->va_blocksize = udf_node->ump->discinfo.sector_size; /* wise? */ + + /* + * BUG-ALERT: UDF doesn't count '.' as an entry, so we'll have to add + * 1 to the link count if its a directory we're requested attributes + * of. + */ + if (vap->va_type == VDIR) + vap->va_nlink++; + + /* access times */ + udf_timestamp_to_timespec(ump, atime, &vap->va_atime); + udf_timestamp_to_timespec(ump, mtime, &vap->va_mtime); + udf_timestamp_to_timespec(ump, attrtime, &vap->va_ctime); + udf_timestamp_to_timespec(ump, creatime, &vap->va_birthtime); + + vap->va_gen = 1; /* no multiple generations yes (!?) */ + vap->va_flags = 0; /* no flags */ + vap->va_bytes = blkssize * udf_node->ump->discinfo.sector_size; + vap->va_filerev = 1; /* TODO file revision numbers? */ + vap->va_vaflags = 0; + /* TODO get vaflags from the extended attributes? */ + + if ((vap->va_type == VBLK) || (vap->va_type == VCHR)) { + error = udf_extattr_search_intern(udf_node, + UDF_DEVICESPEC_ATTR_NO, "", + &offset, &a_l); + /* if error, deny access */ + if (error || (filedata == NULL)) { + vap->va_mode = 0; /* or v_type = VNON? */ + } else { + devattr = (struct device_extattr_entry *) + filedata + offset; + vap->va_rdev = makedev( + udf_rw32(devattr->major), + udf_rw32(devattr->minor) + ); + /* TODO we could check the implementator */ + } + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_chown(struct vnode *vp, uid_t new_uid, gid_t new_gid, + kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve old values */ + udf_getownership(udf_node, &uid, &gid); + + /* only one could be specified */ + if (new_uid == VNOVAL) + new_uid = uid; + if (new_gid == VNOVAL) + new_gid = gid; + + /* check if we can fit it in an 32 bits */ + if ((uid_t) ((uint32_t) new_uid) != new_uid) + return EINVAL; + if ((gid_t) ((uint32_t) new_gid) != new_gid) + return EINVAL; + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, + vp, NULL, genfs_can_chown(cred, uid, gid, new_uid, new_gid)); + if (error) + return (error); + + /* change the ownership */ + udf_setownership(udf_node, new_uid, new_gid); + + /* mark node changed */ + udf_node->i_flags |= IN_CHANGE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + return 0; +} + + +static int +udf_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve uid/gid values */ + udf_getownership(udf_node, &uid, &gid); + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, + NULL, genfs_can_chmod(vp->v_type, cred, uid, gid, mode)); + if (error) + return (error); + + /* change mode */ + udf_setaccessmode(udf_node, mode); + + /* mark node changed */ + udf_node->i_flags |= IN_CHANGE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + return 0; +} + + +/* exported */ +int +udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + int error, extended; + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* Decide whether this is a valid operation based on the file type. */ + switch (vp->v_type) { + case VDIR: + return EISDIR; + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + break; + case VBLK: + /* FALLTHROUGH */ + case VCHR: + /* FALLTHROUGH */ + case VFIFO: + /* Allow modifications of special files even if in the file + * system is mounted read-only (we are not modifying the + * files themselves, but the objects they represent). */ + return 0; + default: + /* Anything else is unsupported. */ + return EOPNOTSUPP; + } + +#if notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + /* resize file to the requested size */ + error = udf_resize_node(udf_node, newsize, &extended); + + if (error == 0) { + /* mark change */ + udf_node->i_flags |= IN_CHANGE | IN_MODIFY; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + VN_KNOTE(vp, NOTE_ATTRIB | (extended ? NOTE_EXTEND : 0)); + udf_update(vp, NULL, NULL, NULL, 0); + } + + return error; +} + + +static int +udf_chflags(struct vnode *vp, mode_t mode, kauth_cred_t cred) +{ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* + * XXX we can't do this yet, as its not described in the standard yet + */ + + return EOPNOTSUPP; +} + + +static int +udf_chtimes(struct vnode *vp, + struct timespec *atime, struct timespec *mtime, + struct timespec *birthtime, int setattrflags, + kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve uid/gid values */ + udf_getownership(udf_node, &uid, &gid); + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, + NULL, genfs_can_chtimes(vp, setattrflags, uid, cred)); + if (error) + return (error); + + /* update node flags depending on what times are passed */ + if (atime->tv_sec != VNOVAL) + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) + udf_node->i_flags |= IN_ACCESS; + if ((mtime->tv_sec != VNOVAL) || (birthtime->tv_sec != VNOVAL)) { + udf_node->i_flags |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + } + + return udf_update(vp, atime, mtime, birthtime, 0); +} + + +int +udf_setattr(void *v) +{ + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct lwp *a_l; + } */ *ap = v; + struct vnode *vp = ap->a_vp; +/* struct udf_node *udf_node = VTOI(vp); */ +/* struct udf_mount *ump = udf_node->ump; */ + kauth_cred_t cred = ap->a_cred; + struct vattr *vap = ap->a_vap; + int error; + + DPRINTF(CALL, ("udf_setattr called\n")); + + /* Abort if any unsettable attribute is given. */ + error = 0; + if (vap->va_type != VNON || + vap->va_nlink != VNOVAL || + vap->va_fsid != VNOVAL || + vap->va_fileid != VNOVAL || + vap->va_blocksize != VNOVAL || +#ifdef notyet + /* checks are debated */ + vap->va_ctime.tv_sec != VNOVAL || + vap->va_ctime.tv_nsec != VNOVAL || + vap->va_birthtime.tv_sec != VNOVAL || + vap->va_birthtime.tv_nsec != VNOVAL || +#endif + vap->va_gen != VNOVAL || + vap->va_rdev != VNOVAL || + vap->va_bytes != VNOVAL) + error = EINVAL; + + DPRINTF(ATTR, ("setattr changing:\n")); + if (error == 0 && (vap->va_flags != VNOVAL)) { + DPRINTF(ATTR, ("\tchflags\n")); + error = udf_chflags(vp, vap->va_flags, cred); + } + + if (error == 0 && (vap->va_size != VNOVAL)) { + DPRINTF(ATTR, ("\tchsize\n")); + error = udf_chsize(vp, vap->va_size, cred); + } + + if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) { + DPRINTF(ATTR, ("\tchown\n")); + error = udf_chown(vp, vap->va_uid, vap->va_gid, cred); + } + + if (error == 0 && (vap->va_mode != VNOVAL)) { + DPRINTF(ATTR, ("\tchmod\n")); + error = udf_chmod(vp, vap->va_mode, cred); + } + + if (error == 0 && + ((vap->va_atime.tv_sec != VNOVAL && + vap->va_atime.tv_nsec != VNOVAL) || + (vap->va_mtime.tv_sec != VNOVAL && + vap->va_mtime.tv_nsec != VNOVAL)) + ) { + DPRINTF(ATTR, ("\tchtimes\n")); + error = udf_chtimes(vp, &vap->va_atime, &vap->va_mtime, + &vap->va_birthtime, vap->va_vaflags, cred); + } + VN_KNOTE(vp, NOTE_ATTRIB); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Return POSIX pathconf information for UDF file systems. + */ +int +udf_pathconf(void *v) +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap = v; + uint32_t bits; + + DPRINTF(CALL, ("udf_pathconf called\n")); + + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = (1<<16)-1; /* 16 bits */ + return 0; + case _PC_NAME_MAX: + *ap->a_retval = UDF_MAXNAMLEN; + return 0; + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + return 0; + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + return 0; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return 0; + case _PC_NO_TRUNC: + *ap->a_retval = 1; + return 0; + case _PC_SYNC_IO: + *ap->a_retval = 0; /* synchronised is off for performance */ + return 0; + case _PC_FILESIZEBITS: + /* 64 bit file offsets -> 2+floor(2log(2^64-1)) = 2 + 63 = 65 */ + bits = 64; /* XXX ought to deliver 65 */ +#if 0 + if (udf_node) + bits = 64 * vp->v_mount->mnt_dev_bshift; +#endif + *ap->a_retval = bits; + return 0; + } + + return EINVAL; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_open(void *v) +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + int flags; + + DPRINTF(CALL, ("udf_open called\n")); + + /* + * Files marked append-only must be opened for appending. + * TODO: get chflags(2) flags from extened attribute. + */ + flags = 0; + if ((flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_close(void *v) +{ + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int async = vp->v_mount->mnt_flag & MNT_ASYNC; + int error; + + DPRINTF(CALL, ("udf_close called\n")); + udf_node = udf_node; /* shut up gcc */ + + if (!async && (vp->v_type != VDIR)) { + mutex_enter(vp->v_interlock); + error = VOP_PUTPAGES(vp, 0, 0, PGO_CLEANIT); + if (error) + return error; + } + + mutex_enter(vp->v_interlock); + if (vp->v_usecount > 1) + udf_itimes(udf_node, NULL, NULL, NULL); + mutex_exit(vp->v_interlock); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode) +{ + int flags; + + /* check if we are allowed to write */ + switch (vap->va_type) { + case VDIR: + case VLNK: + case VREG: + /* + * normal nodes: check if we're on a read-only mounted + * filingsystem and bomb out if we're trying to write. + */ + if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) + return EROFS; + break; + case VBLK: + case VCHR: + case VSOCK: + case VFIFO: + /* + * special nodes: even on read-only mounted filingsystems + * these are allowed to be written to if permissions allow. + */ + break; + default: + /* no idea what this is */ + return EINVAL; + } + + /* noone may write immutable files */ + /* TODO: get chflags(2) flags from extened attribute. */ + flags = 0; + if ((mode & VWRITE) && (flags & IMMUTABLE)) + return EPERM; + + return 0; +} + +static int +udf_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode, + kauth_cred_t cred) +{ + /* ask the generic genfs_can_access to advice on security */ + return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, + vp->v_type, vap->va_mode), vp, NULL, genfs_can_access(vp->v_type, + vap->va_mode, vap->va_uid, vap->va_gid, mode, cred)); +} + +int +udf_access(void *v) +{ + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + mode_t mode = ap->a_mode; + kauth_cred_t cred = ap->a_cred; + /* struct udf_node *udf_node = VTOI(vp); */ + struct vattr vap; + int error; + + DPRINTF(CALL, ("udf_access called\n")); + + error = VOP_GETATTR(vp, &vap, NULL); + if (error) + return error; + + error = udf_check_possible(vp, &vap, mode); + if (error) + return error; + + error = udf_check_permitted(vp, &vap, mode, cred); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_create(void *v) +{ + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_create called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_mknod(void *v) +{ + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_mknod called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_mkdir(void *v) +{ + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_mkdir called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_do_link(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) +{ + struct udf_node *udf_node, *dir_node; + struct vattr vap; + int error; + + DPRINTF(CALL, ("udf_link called\n")); + KASSERT(dvp != vp); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == vp->v_mount); + + /* get attributes */ + dir_node = VTOI(dvp); + udf_node = VTOI(vp); + + error = VOP_GETATTR(vp, &vap, FSCRED); + if (error) { + VOP_UNLOCK(vp); + return error; + } + + /* check link count overflow */ + if (vap.va_nlink >= (1<<16)-1) { /* uint16_t */ + VOP_UNLOCK(vp); + return EMLINK; + } + + error = udf_dir_attach(dir_node->ump, dir_node, udf_node, &vap, cnp); + if (error) + VOP_UNLOCK(vp); + return error; +} + +int +udf_link(void *v) +{ + struct vop_link_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + struct componentname *cnp = ap->a_cnp; + int error; + + error = udf_do_link(dvp, vp, cnp); + if (error) + VOP_ABORTOP(dvp, cnp); + + VN_KNOTE(vp, NOTE_LINK); + VN_KNOTE(dvp, NOTE_WRITE); + vput(dvp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_do_symlink(struct udf_node *udf_node, char *target) +{ + struct pathcomp pathcomp; + uint8_t *pathbuf, *pathpos, *compnamepos; + char *mntonname; + int pathlen, len, compnamelen, mntonnamelen; + int error; + + /* process `target' to an UDF structure */ + pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK); + pathpos = pathbuf; + pathlen = 0; + + if (*target == '/') { + /* symlink starts from the root */ + len = UDF_PATH_COMP_SIZE; + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_ROOT; + + /* check if its mount-point relative! */ + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + if (strlen(target) >= mntonnamelen) { + if (strncmp(target, mntonname, mntonnamelen) == 0) { + pathcomp.type = UDF_PATH_COMP_MOUNTROOT; + target += mntonnamelen; + } + } else { + target++; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + error = 0; + while (*target) { + /* ignore multiple '/' */ + while (*target == '/') { + target++; + } + if (!*target) + break; + + /* extract component name */ + compnamelen = 0; + compnamepos = target; + while ((*target) && (*target != '/')) { + target++; + compnamelen++; + } + + /* just trunc if too long ?? (security issue) */ + if (compnamelen >= 127) { + error = ENAMETOOLONG; + break; + } + + /* convert unix name to UDF name */ + len = sizeof(struct pathcomp); + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_NAME; + len = UDF_PATH_COMP_SIZE; + + if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0)) + pathcomp.type = UDF_PATH_COMP_PARENTDIR; + if ((compnamelen == 1) && (*compnamepos == '.')) + pathcomp.type = UDF_PATH_COMP_CURDIR; + + if (pathcomp.type == UDF_PATH_COMP_NAME) { + unix_to_udf_name( + (char *) &pathcomp.ident, &pathcomp.l_ci, + compnamepos, compnamelen, + &udf_node->ump->logical_vol->desc_charset); + len = UDF_PATH_COMP_SIZE + pathcomp.l_ci; + } + + if (pathlen + len >= UDF_SYMLINKBUFLEN) { + error = ENAMETOOLONG; + break; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + if (error) { + /* aparently too big */ + free(pathbuf, M_UDFTEMP); + return error; + } + + error = udf_grow_node(udf_node, pathlen); + if (error) { + /* failed to pregrow node */ + free(pathbuf, M_UDFTEMP); + return error; + } + + /* write out structure on the new file */ + error = vn_rdwr(UIO_WRITE, udf_node->vnode, + pathbuf, pathlen, 0, + UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS, + FSCRED, NULL, NULL); + + /* return status of symlink contents writeout */ + free(pathbuf, M_UDFTEMP); + return error; +} + + +int +udf_symlink(void *v) +{ + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node; + struct udf_node *udf_node; + int error; + + DPRINTF(CALL, ("udf_symlink called\n")); + DPRINTF(CALL, ("\tlinking to `%s`\n", ap->a_target)); + error = udf_create_node(dvp, vpp, vap, cnp); + KASSERT(((error == 0) && (*vpp != NULL)) || ((error && (*vpp == NULL)))); + if (!error) { + dir_node = VTOI(dvp); + udf_node = VTOI(*vpp); + KASSERT(udf_node); + error = udf_do_symlink(udf_node, ap->a_target); + if (error) { + /* remove node */ + udf_shrink_node(udf_node, 0); + udf_dir_detach(udf_node->ump, dir_node, udf_node, cnp); + } + } + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_readlink(void *v) +{ + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + kauth_cred_t cred = ap->a_cred; + struct udf_node *udf_node; + struct pathcomp pathcomp; + struct vattr vattr; + uint8_t *pathbuf, *targetbuf, *tmpname; + uint8_t *pathpos, *targetpos; + char *mntonname; + int pathlen, targetlen, namelen, mntonnamelen, len, l_ci; + int first, error; + + DPRINTF(CALL, ("udf_readlink called\n")); + + udf_node = VTOI(vp); + error = VOP_GETATTR(vp, &vattr, cred); + if (error) + return error; + + /* claim temporary buffers for translation */ + pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK); + targetbuf = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK); + tmpname = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK); + memset(pathbuf, 0, UDF_SYMLINKBUFLEN); + memset(targetbuf, 0, PATH_MAX); + + /* read contents of file in our temporary buffer */ + error = vn_rdwr(UIO_READ, udf_node->vnode, + pathbuf, vattr.va_size, 0, + UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS, + FSCRED, NULL, NULL); + if (error) { + /* failed to read in symlink contents */ + free(pathbuf, M_UDFTEMP); + free(targetbuf, M_UDFTEMP); + free(tmpname, M_UDFTEMP); + return error; + } + + /* convert to a unix path */ + pathpos = pathbuf; + pathlen = 0; + targetpos = targetbuf; + targetlen = PATH_MAX; + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + + error = 0; + first = 1; + while (vattr.va_size - pathlen >= UDF_PATH_COMP_SIZE) { + len = UDF_PATH_COMP_SIZE; + memcpy(&pathcomp, pathpos, len); + l_ci = pathcomp.l_ci; + switch (pathcomp.type) { + case UDF_PATH_COMP_ROOT : + /* XXX should check for l_ci; bugcompatible now */ + if ((targetlen < 1) || !first) { + error = EINVAL; + break; + } + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_MOUNTROOT : + /* XXX what should it be if l_ci > 0 ? [4/48.16.1.2] */ + if (l_ci || (targetlen < mntonnamelen+1) || !first) { + error = EINVAL; + break; + } + memcpy(targetpos, mntonname, mntonnamelen); + targetpos += mntonnamelen; targetlen -= mntonnamelen; + if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) { + /* more follows, so must be directory */ + *targetpos++ = '/'; targetlen--; + } + break; + case UDF_PATH_COMP_PARENTDIR : + /* XXX should check for l_ci; bugcompatible now */ + if (targetlen < 3) { + error = EINVAL; + break; + } + *targetpos++ = '.'; targetlen--; + *targetpos++ = '.'; targetlen--; + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_CURDIR : + /* XXX should check for l_ci; bugcompatible now */ + if (targetlen < 2) { + error = EINVAL; + break; + } + *targetpos++ = '.'; targetlen--; + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_NAME : + if (l_ci == 0) { + error = EINVAL; + break; + } + memset(tmpname, 0, PATH_MAX); + memcpy(&pathcomp, pathpos, len + l_ci); + udf_to_unix_name(tmpname, MAXPATHLEN, + pathcomp.ident, l_ci, + &udf_node->ump->logical_vol->desc_charset); + namelen = strlen(tmpname); + if (targetlen < namelen + 1) { + error = EINVAL; + break; + } + memcpy(targetpos, tmpname, namelen); + targetpos += namelen; targetlen -= namelen; + if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) { + /* more follows, so must be directory */ + *targetpos++ = '/'; targetlen--; + } + break; + default : + error = EINVAL; + break; + } + first = 0; + if (error) + break; + pathpos += UDF_PATH_COMP_SIZE + l_ci; + pathlen += UDF_PATH_COMP_SIZE + l_ci; + + } + /* all processed? */ + if (vattr.va_size - pathlen > 0) + error = EINVAL; + + /* uiomove() to destination */ + if (!error) + uiomove(targetbuf, PATH_MAX - targetlen, uio); + + free(pathbuf, M_UDFTEMP); + free(targetbuf, M_UDFTEMP); + free(tmpname, M_UDFTEMP); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * udf_rename() moved to udf_rename.c + */ + +/* --------------------------------------------------------------------- */ + +int +udf_remove(void *v) +{ + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + int error; + + DPRINTF(CALL, ("udf_remove called\n")); + if (vp->v_type != VDIR) { + error = udf_dir_detach(ump, dir_node, udf_node, cnp); + DPRINTFIF(NODE, error, ("\tgot error removing file\n")); + } else { + DPRINTF(NODE, ("\tis a directory: perm. denied\n")); + error = EPERM; + } + + if (error == 0) { + VN_KNOTE(vp, NOTE_DELETE); + VN_KNOTE(dvp, NOTE_WRITE); + } + + if (dvp == vp) + vrele(vp); + else + vput(vp); + vput(dvp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_rmdir(void *v) +{ + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + int error, isempty; + + DPRINTF(NOTIMPL, ("udf_rmdir '%s' called\n", cnp->cn_nameptr)); + + /* don't allow '.' to be deleted */ + if (dir_node == udf_node) { + vrele(dvp); + vput(vp); + return EINVAL; + } + + /* make sure our `leaf' node's hash is populated */ + dirhash_get(&udf_node->dir_hash); + error = udf_dirhash_fill(udf_node); + if (error) { + dirhash_put(udf_node->dir_hash); + return error; + } + + /* check to see if the directory is empty */ + isempty = dirhash_dir_isempty(udf_node->dir_hash); + dirhash_put(udf_node->dir_hash); + + if (!isempty) { + vput(dvp); + vput(vp); + return ENOTEMPTY; + } + + /* detach the node from the directory, udf_node is an empty dir here */ + error = udf_dir_detach(ump, dir_node, udf_node, cnp); + if (error == 0) { + cache_purge(vp); +// cache_purge(dvp); /* XXX from msdosfs, why? */ + /* + * Bug alert: we need to remove '..' from the detaching + * udf_node so further lookups of this are not possible. This + * prevents a process in a deleted directory from going to its + * deleted parent. Since `udf_node' is garanteed to be empty + * here, trunc it so no fids are there. + */ + dirhash_purge(&udf_node->dir_hash); + udf_shrink_node(udf_node, 0); + VN_KNOTE(vp, NOTE_DELETE); + } + DPRINTFIF(NODE, error, ("\tgot error removing dir\n")); + + /* unput the nodes and exit */ + vput(dvp); + vput(vp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_fsync(void *v) +{ + struct vop_fsync_args /* { + struct vnode *a_vp; + kauth_cred_t a_cred; + int a_flags; + off_t offlo; + off_t offhi; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int error, flags, wait; + + DPRINTF(SYNC, ("udf_fsync called on %p : %s, %s\n", + udf_node, + (ap->a_flags & FSYNC_WAIT) ? "wait":"no wait", + (ap->a_flags & FSYNC_DATAONLY) ? "data_only":"complete")); + + /* flush data and wait for it when requested */ + wait = (ap->a_flags & FSYNC_WAIT) ? UPDATE_WAIT : 0; + error = vflushbuf(vp, ap->a_flags); + if (error) + return error; + + if (udf_node == NULL) { + printf("udf_fsync() called on NULL udf_node!\n"); + return 0; + } + if (vp->v_tag != VT_UDF) { + printf("udf_fsync() called on node not tagged as UDF node!\n"); + return 0; + } + + /* set our times */ + udf_itimes(udf_node, NULL, NULL, NULL); + + /* if called when mounted readonly, never write back */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return 0; + + /* if only data is requested, return */ + if (ap->a_flags & FSYNC_DATAONLY) + return 0; + + /* check if the node is dirty 'enough'*/ + flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED); + if (flags == 0) + return 0; + + /* if we don't have to wait, check for IO pending */ + if (!wait) { + if (vp->v_numoutput > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on v_numoutput\n", udf_node)); + return 0; + } + if (udf_node->outstanding_bufs > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_bufs\n", udf_node)); + return 0; + } + if (udf_node->outstanding_nodedscr > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_nodedscr\n", udf_node)); + return 0; + } + } + + /* wait until vp->v_numoutput reaches zero i.e. is finished */ + if (wait) { + DPRINTF(SYNC, ("udf_fsync %p, waiting\n", udf_node)); + mutex_enter(vp->v_interlock); + while (vp->v_numoutput) { + DPRINTF(SYNC, ("udf_fsync %p, v_numoutput %d\n", udf_node, vp->v_numoutput)); + cv_timedwait(&vp->v_cv, vp->v_interlock, hz/8); + } + mutex_exit(vp->v_interlock); + DPRINTF(SYNC, ("udf_fsync %p, fin wait\n", udf_node)); + } + + /* write out node and wait for it if requested */ + DPRINTF(SYNC, ("udf_fsync %p, writeout node\n", udf_node)); + error = udf_writeout_node(udf_node, wait); + if (error) + return error; + + /* TODO/XXX if ap->a_flags & FSYNC_CACHE, we ought to do a disc sync */ + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_advlock(void *v) +{ + struct vop_advlock_args /* { + struct vnode *a_vp; + void *a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size; + + DPRINTF(LOCKING, ("udf_advlock called\n")); + + /* get directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + return lf_advlock(ap, &udf_node->lockf, file_size); +} + +/* --------------------------------------------------------------------- */ + +/* Global vfs vnode data structures for udfs */ +int (**udf_vnodeop_p)(void *); + +const struct vnodeopv_entry_desc udf_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, udf_lookup }, /* lookup */ + { &vop_create_desc, udf_create }, /* create */ + { &vop_mknod_desc, udf_mknod }, /* mknod */ /* TODO */ + { &vop_open_desc, udf_open }, /* open */ + { &vop_close_desc, udf_close }, /* close */ + { &vop_access_desc, udf_access }, /* access */ + { &vop_getattr_desc, udf_getattr }, /* getattr */ + { &vop_setattr_desc, udf_setattr }, /* setattr */ /* TODO chflags */ + { &vop_read_desc, udf_read }, /* read */ + { &vop_write_desc, udf_write }, /* write */ /* WRITE */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ /* TODO? */ + { &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */ /* TODO? */ + { &vop_poll_desc, genfs_poll }, /* poll */ /* TODO/OK? */ + { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ /* ? */ + { &vop_revoke_desc, genfs_revoke }, /* revoke */ /* TODO? */ + { &vop_mmap_desc, genfs_mmap }, /* mmap */ /* OK? */ + { &vop_fsync_desc, udf_fsync }, /* fsync */ + { &vop_seek_desc, genfs_seek }, /* seek */ + { &vop_remove_desc, udf_remove }, /* remove */ + { &vop_link_desc, udf_link }, /* link */ /* TODO */ + { &vop_rename_desc, udf_rename }, /* rename */ /* TODO */ + { &vop_mkdir_desc, udf_mkdir }, /* mkdir */ + { &vop_rmdir_desc, udf_rmdir }, /* rmdir */ + { &vop_symlink_desc, udf_symlink }, /* symlink */ /* TODO */ + { &vop_readdir_desc, udf_readdir }, /* readdir */ + { &vop_readlink_desc, udf_readlink }, /* readlink */ /* TEST ME */ + { &vop_abortop_desc, genfs_abortop }, /* abortop */ /* TODO/OK? */ + { &vop_inactive_desc, udf_inactive }, /* inactive */ + { &vop_reclaim_desc, udf_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, udf_trivial_bmap }, /* bmap */ /* 1:1 bmap */ + { &vop_strategy_desc, udf_vfsstrategy },/* strategy */ +/* { &vop_print_desc, udf_print }, */ /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, udf_pathconf }, /* pathconf */ + { &vop_advlock_desc, udf_advlock }, /* advlock */ /* TEST ME */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ /* ->strategy */ + { &vop_getpages_desc, genfs_getpages }, /* getpages */ + { &vop_putpages_desc, genfs_putpages }, /* putpages */ + { NULL, NULL } +}; + + +const struct vnodeopv_desc udf_vnodeop_opv_desc = { + &udf_vnodeop_p, udf_vnodeop_entries +}; diff --git a/sys/fs/v7fs/Makefile b/sys/fs/v7fs/Makefile new file mode 100644 index 000000000..e677fa395 --- /dev/null +++ b/sys/fs/v7fs/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2011/06/27 11:52:24 uch Exp $ + +INCSDIR= /usr/include/fs/v7fs + +INCS= v7fs.h v7fs_args.h + +.include diff --git a/sys/fs/v7fs/files.v7fs b/sys/fs/v7fs/files.v7fs new file mode 100644 index 000000000..509253f84 --- /dev/null +++ b/sys/fs/v7fs/files.v7fs @@ -0,0 +1,22 @@ +# $NetBSD: files.v7fs,v 1.1 2011/06/27 11:52:24 uch Exp $ + +deffs V7FS +defflag opt_v7fs.h V7FS_EI + +# Core. OS independent. These files are used by userland program.(fsck,newfs) +file fs/v7fs/v7fs_endian.c v7fs +file fs/v7fs/v7fs_superblock.c v7fs +file fs/v7fs/v7fs_inode.c v7fs +file fs/v7fs/v7fs_dirent.c v7fs +file fs/v7fs/v7fs_datablock.c v7fs +file fs/v7fs/v7fs_file.c v7fs +file fs/v7fs/v7fs_io.c v7fs +# util. +file fs/v7fs/v7fs_file_util.c v7fs +file fs/v7fs/v7fs_inode_util.c v7fs +file fs/v7fs/v7fs_superblock_util.c v7fs +# OS glue +file fs/v7fs/v7fs_io_kern.c v7fs +file fs/v7fs/v7fs_extern.c v7fs +file fs/v7fs/v7fs_vnops.c v7fs +file fs/v7fs/v7fs_vfsops.c v7fs diff --git a/sys/fs/v7fs/v7fs.h b/sys/fs/v7fs/v7fs.h new file mode 100644 index 000000000..c35bacecc --- /dev/null +++ b/sys/fs/v7fs/v7fs.h @@ -0,0 +1,180 @@ +/* $NetBSD: v7fs.h,v 1.2 2011/07/16 12:35:40 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_H_ +/* 7th Edition of Unix(PDP-11) Filesystem definition. */ +#define _V7FS_H_ +#include +#ifndef _KERNEL +#include +#endif +/* + * V7 File System + * + * +------------------ + * |Boot block (512byte) sector [0] + * | + * +------------------ + * |Super block (512byte) sector [1] + * | + * +------------------ + * |v7fs_inode(64byte sector [2] + * . + * . + * | + * +------------------ + * |data block sector [datablock_start_sector] + * | + * . + * . + * | + * +------------------ + * <- [sector volume_size] + * + * | + * +------------------ volume size. + * + * Max volume size is 8GB (24bit daddr_t) + * Max file size is ~1GB + * + */ + +/* V7 type. */ +typedef uint16_t v7fs_ino_t; +typedef uint32_t v7fs_daddr_t; +typedef int32_t v7fs_time_t; +typedef uint32_t v7fs_off_t; +typedef uint16_t v7fs_dev_t; +typedef uint16_t v7fs_mode_t; +#define V7FS_DADDR_MAX 0x00ffffff +#define V7FS_INODE_MAX 0xffff + +#define V7FS_BSIZE 512 +#define V7FS_BSHIFT 9 +#define V7FS_ROUND_BSIZE(x) \ + ((((x) + (V7FS_BSIZE - 1)) & ~(V7FS_BSIZE - 1))) +#define V7FS_TRUNC_BSIZE(x) ((x) & ~(V7FS_BSIZE - 1)) + +#define V7FS_RESIDUE_BSIZE(x) \ + ((x) - ((((x) - 1) >> V7FS_BSHIFT) << V7FS_BSHIFT)) + +/* Disk location. */ +#define V7FS_BOOTBLOCK_SECTOR 0 +#define V7FS_SUPERBLOCK_SECTOR 1 +#define V7FS_ILIST_SECTOR 2 + +/* Superblock */ +/* cache. */ +#define V7FS_MAX_FREEBLOCK 50 +#define V7FS_MAX_FREEINODE 100 +struct v7fs_superblock { + /* [3 ... (datablock_start_sector-1)]are ilist */ + uint16_t datablock_start_sector; + v7fs_daddr_t volume_size; + int16_t nfreeblock; /* # of freeblock in superblock cache. */ + v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK]; /* cache. */ + int16_t nfreeinode; /* # of free inode in superblock cache. */ + v7fs_ino_t freeinode[V7FS_MAX_FREEINODE]; /* cache. */ + int8_t lock_freeblock; + int8_t lock_freeinode; + int8_t modified; + int8_t readonly; + v7fs_time_t update_time; + v7fs_daddr_t total_freeblock; + v7fs_ino_t total_freeinode; +} __packed; + +/* Datablock */ +#define V7FS_NADDR 13 +#define V7FS_NADDR_DIRECT 10 +#define V7FS_NADDR_INDEX1 10 +#define V7FS_NADDR_INDEX2 11 +#define V7FS_NADDR_INDEX3 12 +/* daddr index. */ +#define V7FS_DADDR_PER_BLOCK (V7FS_BSIZE / sizeof(v7fs_daddr_t)) +struct v7fs_freeblock { + int16_t nfreeblock; + v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK]; +} __packed; + + +/* Dirent */ +#define V7FS_NAME_MAX 14 +#define V7FS_PATH_MAX PATH_MAX /* No V7 limit. */ +#define V7FS_LINK_MAX LINK_MAX /* No V7 limit. */ +struct v7fs_dirent { + v7fs_ino_t inode_number; + char name[V7FS_NAME_MAX]; +} __packed; /*16byte */ + +/* Inode */ +#define V7FS_BALBLK_INODE 1 /* monument */ +#define V7FS_ROOT_INODE 2 +#define V7FS_MAX_INODE(s) \ + (((s)->datablock_start_sector - V7FS_ILIST_SECTOR) * \ + V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage)) +#define V7FS_INODE_PER_BLOCK \ + (V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage)) +#define V7FS_ILISTBLK_MAX (V7FS_INODE_MAX / V7FS_INODE_PER_BLOCK) + +struct v7fs_inode_diskimage { + int16_t mode; + int16_t nlink; /* [DIR] # of child directories. [REG] link count. */ + int16_t uid; + int16_t gid; + v7fs_off_t filesize; /* byte */ +#define V7FS_DINODE_ADDR_LEN 40 + /* 39 used; 13 addresses of 3 byte each. */ + uint8_t addr[V7FS_DINODE_ADDR_LEN]; + /*for device node: addr[0] is major << 8 | minor. */ + v7fs_time_t atime; + v7fs_time_t mtime; + v7fs_time_t ctime; +} __packed; /*64byte */ + +/* File type */ +#define V7FS_IFMT 0170000 /* File type mask */ +#define V7FS_IFCHR 0020000 /* charcter device */ +#define V7FS_IFDIR 0040000 /* directory */ +#define V7FS_IFBLK 0060000 /* block device */ +#define V7FS_IFREG 0100000 /* file. */ +/* Obsoleted file type. */ +#define V7FS_IFMPC 0030000 /* multiplexed char special */ +#define V7FS_IFMPB 0070000 /* multiplexed block special */ +/* Don't apear original V7 filesystem. Found at 2.10BSD. */ +#define V7FSBSD_IFLNK 0120000 /* symbolic link */ +#define V7FSBSD_IFSOCK 0140000 /* socket */ +/* Don't apear original V7 filesystem. NetBSD. */ +#define V7FSBSD_IFFIFO 0010000 /* Named pipe. */ + +#define V7FSBSD_MAXSYMLINKLEN V7FS_BSIZE + +#endif /*!_V7FS_H_ */ diff --git a/sys/fs/v7fs/v7fs_args.h b/sys/fs/v7fs/v7fs_args.h new file mode 100644 index 000000000..017518cba --- /dev/null +++ b/sys/fs/v7fs/v7fs_args.h @@ -0,0 +1,40 @@ +/* $NetBSD: v7fs_args.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _FS_V7FS_V7FS_ARGS_H_ +#define _FS_V7FS_V7FS_ARGS_H_ + +struct v7fs_args { + char *fspec; /* blocks special holding the fs to mount */ + int endian; /* target filesystem endian */ +}; + +#endif /* _FS_V7FS_V7FS_ARGS_H_ */ diff --git a/sys/fs/v7fs/v7fs_datablock.c b/sys/fs/v7fs/v7fs_datablock.c new file mode 100644 index 000000000..cd8452159 --- /dev/null +++ b/sys/fs/v7fs/v7fs_datablock.c @@ -0,0 +1,729 @@ +/* $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_DATABLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t); +static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_remove_self(struct v7fs_self *, v7fs_daddr_t); + +#ifdef V7FS_DATABLOCK_DEBUG +void daddr_map_dump(const struct v7fs_daddr_map *); +#else +#define daddr_map_dump(x) ((void)0) +#endif + +bool +datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk) +{ + const struct v7fs_superblock *sb = &fs->superblock; + bool ok = (blk >= sb->datablock_start_sector) && + (blk < sb->volume_size); + +#ifdef V7FS_DATABLOCK_DEBUG + if (!ok) { + DPRINTF("Bad data block #%d\n", blk); + } +#endif + + return ok; +} + +int +v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number) +{ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_daddr_t blk; + int error = 0; + + *block_number = 0; + SUPERB_LOCK(fs); + do { + if (!sb->total_freeblock) { + DPRINTF("free block exhausted!!!\n"); + SUPERB_UNLOCK(fs); + return ENOSPC; + } + + /* Get free block from superblock cache. */ + blk = sb->freeblock[--sb->nfreeblock]; + sb->total_freeblock--; + sb->modified = 1; + + /* If nfreeblock is zero, it block is next freeblock link. */ + if (sb->nfreeblock == 0) { + if ((error = v7fs_freeblock_update(fs, blk))) { + DPRINTF("no freeblock!!!\n"); + SUPERB_UNLOCK(fs); + return error; + } + /* This freeblock link is no longer required. */ + /* use as data block. */ + } + } while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */ + SUPERB_UNLOCK(fs); + + DPRINTF("Get freeblock %d\n", blk); + /* Zero clear datablock. */ + void *buf; + if (!(buf = scratch_read(fs, blk))) + return EIO; + memset(buf, 0, V7FS_BSIZE); + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + scratch_free(fs, buf); + + if (error == 0) + *block_number = blk; + + return error; +} + +static int +v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk) +{ + struct v7fs_superblock *sb = &fs->superblock; + void *buf; + int error = 0; + + if (!datablock_number_sanity(fs, blk)) + return EIO; + + /* Add to in-core freelist. */ + SUPERB_LOCK(fs); + if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) { + sb->freeblock[sb->nfreeblock++] = blk; + sb->total_freeblock++; + sb->modified = 1; + DPRINTF("n_freeblock=%d\n", sb->total_freeblock); + SUPERB_UNLOCK(fs); + return 0; + } + + /* No space to push. */ + /* Make this block to freeblock list.and current cache moved to this. */ + if (!(buf = scratch_read(fs, blk))) { + SUPERB_UNLOCK(fs); + return EIO; /* Fatal */ + } + + struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf; + fb->nfreeblock = V7FS_MAX_FREEBLOCK; + int i; + for (i = 0; i < V7FS_MAX_FREEBLOCK; i++) + fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]); + + if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) { + error = EIO; /* Fatal */ + } else { + /* Link. on next allocate, this block is used as datablock, */ + /* and swap outed freeblock list is restored. */ + sb->freeblock[0] = blk; + sb->nfreeblock = 1; + sb->total_freeblock++; + sb->modified = 1; + DPRINTF("n_freeblock=%d\n", sb->total_freeblock); + } + SUPERB_UNLOCK(fs); + scratch_free(fs, buf); + + return error; +} + +int +v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map) +{ +#define NIDX V7FS_DADDR_PER_BLOCK +#define DIRECT_SZ (V7FS_NADDR_DIRECT * V7FS_BSIZE) +#define IDX1_SZ (NIDX * V7FS_BSIZE) +#define IDX2_SZ (NIDX * NIDX * V7FS_BSIZE) +#define ROUND(x, a) ((((x) + ((a) - 1)) & ~((a) - 1))) + if (!sz) { + map->level = 0; + map->index[0] = 0; + return 0; + } + + sz = V7FS_ROUND_BSIZE(sz); + + /* Direct */ + if (sz <= DIRECT_SZ) { + map->level = 0; + map->index[0] = (sz >> V7FS_BSHIFT) - 1; + return 0; + } + /* Index 1 */ + sz -= DIRECT_SZ; + + if (sz <= IDX1_SZ) { + map->level = 1; + map->index[0] = (sz >> V7FS_BSHIFT) - 1; + return 0; + } + sz -= IDX1_SZ; + + /* Index 2 */ + if (sz <= IDX2_SZ) { + map->level = 2; + map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1; + map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >> + V7FS_BSHIFT) - 1; + return 0; + } + sz -= IDX2_SZ; + + /* Index 3 */ + map->level = 3; + map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1; + sz -= map->index[0] * IDX2_SZ; + map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1; + sz -= map->index[1] * IDX1_SZ; + map->index[2] = (sz >> V7FS_BSHIFT) - 1; + + return map->index[2] >= NIDX ? ENOSPC : 0; +} + +int +v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + size_t i; + v7fs_daddr_t blk, blk2; + size_t filesize; + bool last; + int ret; + + if (!(filesize = v7fs_inode_filesize(p))) + return V7FS_ITERATOR_END; +#ifdef V7FS_DATABLOCK_DEBUG + size_t sz = filesize; +#endif + + /* Direct */ + for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) { + blk = p->addr[i]; + if (!datablock_number_sanity(fs, blk)) { + DPRINTF("inode#%d direct=%zu filesize=%zu\n", + p->inode_number, i, sz); + return EIO; + } + + last = filesize <= V7FS_BSIZE; + if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE))) + return ret; + if (last) + return V7FS_ITERATOR_END; + } + + /* Index 1 */ + blk = p->addr[V7FS_NADDR_INDEX1]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop1(fs, blk, &filesize, func, ctx))) + return ret; + + /* Index 2 */ + blk = p->addr[V7FS_NADDR_INDEX2]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop2(fs, blk, &filesize, func, ctx))) + return ret; + + /* Index 3 */ + blk = p->addr[V7FS_NADDR_INDEX3]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) { + blk2 = v7fs_link(fs, blk, i); + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx))) + return ret; + } + + return EFBIG; +} + +static int +v7fs_loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + v7fs_daddr_t blk; + int ret; + size_t j; + + for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) { + blk = v7fs_link(fs, listblk, j); + if (!datablock_number_sanity(fs, blk)) + return EIO; + if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx))) + return ret; + } + + return 0; +} + +static int +v7fs_loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + v7fs_daddr_t blk; + bool last; + int ret; + size_t k; + + for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) { + blk = v7fs_link(fs, listblk, k); + if (!datablock_number_sanity(fs, blk)) + return EIO; + last = *filesize <= V7FS_BSIZE; + if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE))) + return ret; + if (last) + return V7FS_ITERATOR_END; + } + + return 0; +} + +v7fs_daddr_t +v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode, + v7fs_off_t ofs) +{ + struct v7fs_daddr_map map; + v7fs_daddr_t blk = 0; + v7fs_daddr_t *addr = inode->addr; + + /* Inquire last data block location. */ + if (v7fs_datablock_addr(ofs, &map) != 0) + return 0; + + switch (map.level) + { + case 0: /*Direct */ + blk = inode->addr[map.index[0]]; + break; + case 1: /*Index1 */ + blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]); + break; + case 2: /*Index2 */ + blk = v7fs_link(fs, v7fs_link(fs, + addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]); + break; + case 3: /*Index3 */ + blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs, + addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]), + map.index[2]); + break; + } + + return blk; +} + +int +v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz) +{ + size_t old_filesize = inode->filesize; + size_t new_filesize = old_filesize + sz; + struct v7fs_daddr_map oldmap, newmap; + v7fs_daddr_t blk, idxblk; + int error; + v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT; + v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT; + + if (old_nblk == new_nblk) { + inode->filesize += sz; + v7fs_inode_writeback(fs, inode); + return 0; /* no need to expand. */ + } + struct v7fs_inode backup = *inode; + v7fs_daddr_t required_blk = new_nblk - old_nblk; + + DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize, + required_blk); + + v7fs_datablock_addr(old_filesize, &oldmap); + v7fs_daddr_t i; + for (i = 0; i < required_blk; i++) { + v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap); + daddr_map_dump(&oldmap); + daddr_map_dump(&newmap); + + if (oldmap.level != newmap.level) { + /* Allocate index area */ + if ((error = v7fs_datablock_allocate(fs, &idxblk))) + return error; + + switch (newmap.level) { + case 1: + DPRINTF("0->1\n"); + inode->addr[V7FS_NADDR_INDEX1] = idxblk; + blk = v7fs_add_leaf(fs, idxblk, 0); + break; + case 2: + DPRINTF("1->2\n"); + inode->addr[V7FS_NADDR_INDEX2] = idxblk; + blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs, + idxblk, 0), 0); + break; + case 3: + DPRINTF("2->3\n"); + inode->addr[V7FS_NADDR_INDEX3] = idxblk; + blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs, + v7fs_add_leaf(fs, idxblk, 0), 0), 0); + break; + } + } else { + switch (newmap.level) { + case 0: + if ((error = v7fs_datablock_allocate(fs, &blk))) + return error; + inode->addr[newmap.index[0]] = blk; + DPRINTF("direct index %d = blk%d\n", + newmap.index[0], blk); + break; + case 1: + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + blk = v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + break; + case 2: + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + if (oldmap.index[0] != newmap.index[0]) { + v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + } + blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk, + newmap.index[0]), newmap.index[1]); + break; + case 3: + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + + if (oldmap.index[0] != newmap.index[0]) { + v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + } + + if (oldmap.index[1] != newmap.index[1]) { + v7fs_add_leaf(fs, v7fs_link(fs, idxblk, + newmap.index[0]), newmap.index[1]); + } + blk = v7fs_add_leaf(fs, v7fs_link(fs, + v7fs_link(fs, idxblk, newmap.index[0]), + newmap.index[1]), newmap.index[2]); + break; + } + } + if (!blk) { + *inode = backup; /* structure copy; */ + return ENOSPC; + } + oldmap = newmap; + } + inode->filesize += sz; + v7fs_inode_writeback(fs, inode); + + return 0; +} + +static v7fs_daddr_t +v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n) +{ + v7fs_daddr_t *list; + v7fs_daddr_t blk; + void *buf; + + if (!datablock_number_sanity(fs, listblk)) + return 0; + if (!(buf = scratch_read(fs, listblk))) + return 0; + list = (v7fs_daddr_t *)buf; + blk = V7FS_VAL32(fs, list[n]); + scratch_free(fs, buf); + + if (!datablock_number_sanity(fs, blk)) + return 0; + + return blk; +} + +static v7fs_daddr_t +v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx) +{ + v7fs_daddr_t newblk; + v7fs_daddr_t *daddr_list; + int error = 0; + void *buf; + + if (!up) + return 0; + if (!datablock_number_sanity(fs, up)) + return 0; + + if ((error = v7fs_datablock_allocate(fs, &newblk))) + return 0; + if (!(buf = scratch_read(fs, up))) + return 0; + daddr_list = (v7fs_daddr_t *)buf; + daddr_list[idx] = V7FS_VAL32(fs, newblk); + if (!fs->io.write(fs->io.cookie, buf, up)) + newblk = 0; + scratch_free(fs, buf); + + return newblk; +} + +int +v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode, + size_t sz) +{ + size_t old_filesize = inode->filesize; + size_t new_filesize = old_filesize - sz; + struct v7fs_daddr_map oldmap, newmap; + v7fs_daddr_t blk, idxblk; + int error = 0; + v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT; + v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT; + + if (old_nblk == new_nblk) { + inode->filesize -= sz; + v7fs_inode_writeback(fs, inode); + return 0; /* no need to contract; */ + } + v7fs_daddr_t erase_blk = old_nblk - new_nblk; + + DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize, + erase_blk); + + v7fs_datablock_addr(old_filesize, &oldmap); + v7fs_daddr_t i; + for (i = 0; i < erase_blk; i++) { + v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap); + + if (oldmap.level != newmap.level) { + switch (newmap.level) { + case 0: /*1->0 */ + DPRINTF("1->0\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + inode->addr[V7FS_NADDR_INDEX1] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, idxblk)); + break; + case 1: /*2->1 */ + DPRINTF("2->1\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + inode->addr[V7FS_NADDR_INDEX2] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, v7fs_remove_self(fs, + idxblk))); + break; + case 2:/*3->2 */ + DPRINTF("3->2\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + inode->addr[V7FS_NADDR_INDEX3] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, v7fs_remove_self(fs, + v7fs_remove_self(fs, idxblk)))); + break; + } + } else { + switch (newmap.level) { + case 0: + DPRINTF("[0] %d\n", oldmap.index[0]); + blk = inode->addr[oldmap.index[0]]; + error = v7fs_datablock_deallocate(fs, blk); + break; + case 1: + DPRINTF("[1] %d\n", oldmap.index[0]); + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + v7fs_remove_leaf(fs, idxblk, oldmap.index[0]); + + break; + case 2: + DPRINTF("[2] %d %d\n", oldmap.index[0], + oldmap.index[1]); + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + v7fs_remove_leaf(fs, v7fs_link(fs, idxblk, + oldmap.index[0]), oldmap.index[1]); + if (oldmap.index[0] != newmap.index[0]) { + v7fs_remove_leaf(fs, idxblk, + oldmap.index[0]); + } + break; + case 3: + DPRINTF("[2] %d %d %d\n", oldmap.index[0], + oldmap.index[1], oldmap.index[2]); + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + v7fs_remove_leaf(fs, v7fs_link(fs, + v7fs_link(fs, idxblk, oldmap.index[0]), + oldmap.index[1]), oldmap.index[2]); + + if (oldmap.index[1] != newmap.index[1]) { + v7fs_remove_leaf(fs, v7fs_link(fs, + idxblk, oldmap.index[0]), + oldmap.index[1]); + } + if (oldmap.index[0] != newmap.index[0]) { + v7fs_remove_leaf(fs, idxblk, + oldmap.index[0]); + } + break; + } + } + oldmap = newmap; + } + inode->filesize -= sz; + v7fs_inode_writeback(fs, inode); + + return error; +} + +static v7fs_daddr_t +v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n) +{ + v7fs_daddr_t *daddr_list; + v7fs_daddr_t blk; + void *buf; + + if (!(buf = scratch_read(fs, idxblk))) + return 0; + daddr_list = (v7fs_daddr_t *)buf; + blk = V7FS_VAL32(fs, daddr_list[n]); + daddr_list[n] = 0; + fs->io.write(fs->io.cookie, buf, idxblk); + scratch_free(fs, buf); + + return blk; /* unlinked block. */ +} + +static v7fs_daddr_t +v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up) +{ + v7fs_daddr_t down; + + if (!datablock_number_sanity(fs, up)) + return 0; + + /* At 1st, remove from datablock list. */ + down = v7fs_unlink(fs, up, 0); + + /* link self to freelist. */ + v7fs_datablock_deallocate(fs, up); + + return down; +} + +static v7fs_daddr_t +v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n) +{ + v7fs_daddr_t down; + + if (!datablock_number_sanity(fs, up)) + return 0; + + /* At 1st, remove from datablock list. */ + down = v7fs_unlink(fs, up, n); + + /* link leaf to freelist. */ + v7fs_datablock_deallocate(fs, down); + + return down; +} + +int +v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz, + struct v7fs_inode *inode) +{ + ssize_t diff = newsz - v7fs_inode_filesize(inode); + int error = 0; + + if (diff > 0) + error = v7fs_datablock_expand(fs, inode, diff); + else if (diff < 0) + error = v7fs_datablock_contract(fs, inode, -diff); + + return error; +} + +#ifdef V7FS_DATABLOCK_DEBUG +void +daddr_map_dump(const struct v7fs_daddr_map *map) +{ + + DPRINTF("level %d ", map->level); + int m, n = !map->level ? 1 : map->level; + for (m = 0; m < n; m++) + printf("[%d]", map->index[m]); + printf("\n"); +} +#endif diff --git a/sys/fs/v7fs/v7fs_datablock.h b/sys/fs/v7fs/v7fs_datablock.h new file mode 100644 index 000000000..42a45adef --- /dev/null +++ b/sys/fs/v7fs/v7fs_datablock.h @@ -0,0 +1,52 @@ +/* $NetBSD: v7fs_datablock.h,v 1.2 2011/07/16 12:35:32 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_DATABLOCK_H_ +#define _V7FS_DATABLOCK_H_ + +__BEGIN_DECLS +bool datablock_number_sanity(const struct v7fs_self *, v7fs_daddr_t); +int v7fs_datablock_allocate(struct v7fs_self *, v7fs_daddr_t *); +int v7fs_datablock_foreach(struct v7fs_self *, struct v7fs_inode *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +v7fs_daddr_t v7fs_datablock_last(struct v7fs_self *, struct v7fs_inode *, + v7fs_off_t); +int v7fs_datablock_expand(struct v7fs_self *, struct v7fs_inode *, size_t); +int v7fs_datablock_contract(struct v7fs_self *, struct v7fs_inode *, size_t); +int v7fs_datablock_size_change(struct v7fs_self *, size_t, struct v7fs_inode *); + +struct v7fs_daddr_map { + int level; /* direct, index1, index2, index3 */ + v7fs_daddr_t index[3]; +}; +int v7fs_datablock_addr(size_t, struct v7fs_daddr_map *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_dirent.c b/sys/fs/v7fs/v7fs_dirent.c new file mode 100644 index 000000000..99431e7b6 --- /dev/null +++ b/sys/fs/v7fs/v7fs_dirent.c @@ -0,0 +1,89 @@ +/* $NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" + +#ifdef V7FS_DIRENT_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +bool +v7fs_dirent_endian_convert(struct v7fs_self *fs, struct v7fs_dirent *dir, int n) +{ + struct v7fs_superblock *sb = &fs->superblock; + int i; + v7fs_ino_t ino; + bool ok = true; + + for (i = 0; i < n; i++, dir++) { + ino = V7FS_VAL16(fs, dir->inode_number); + if (v7fs_inode_number_sanity(sb, ino)) { + DPRINTF("Invalid inode# %d %s\n", ino, dir->name); + ok = false; + } + dir->inode_number = ino; + } + + return ok; +} + +void +v7fs_dirent_filename(char *dst/* size must be V7FS_NAME_MAX + 1 */, + const char *src) +{ + + strncpy(dst, src, V7FS_NAME_MAX); + dst[V7FS_NAME_MAX] = '\0'; +} diff --git a/sys/fs/v7fs/v7fs_dirent.h b/sys/fs/v7fs/v7fs_dirent.h new file mode 100644 index 000000000..46ca28d4e --- /dev/null +++ b/sys/fs/v7fs/v7fs_dirent.h @@ -0,0 +1,38 @@ +/* $NetBSD: v7fs_dirent.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_DIRENT_H_ +#define _V7FS_DIRENT_H_ +__BEGIN_DECLS +bool v7fs_dirent_endian_convert(struct v7fs_self *, struct v7fs_dirent *, int); +void v7fs_dirent_filename(char *, const char *); +__END_DECLS +#endif /*!_V7FS_DIRENT_H_ */ diff --git a/sys/fs/v7fs/v7fs_endian.c b/sys/fs/v7fs/v7fs_endian.c new file mode 100644 index 000000000..d3cd23020 --- /dev/null +++ b/sys/fs/v7fs/v7fs_endian.c @@ -0,0 +1,231 @@ +/* $NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include "v7fs.h" +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifndef BYTE_ORDER +#error +#endif + +/* PDP to Little */ +#define bswap32pdp_le(x) \ + ((uint32_t) \ + ((((x) & 0xffff0000) >> 16) | \ + (((x) & 0x0000ffff) << 16))) +/* PDP to Big */ +#define bswap32pdp_be(x) \ + ((uint32_t) \ + ((((x) & 0xff00ff00) >> 8) | \ + (((x) & 0x00ff00ff) << 8))) +#ifdef V7FS_EI +static uint32_t val32_normal_order(uint32_t); +static uint32_t val32_reverse_order(uint32_t); +#if BYTE_ORDER == LITTLE_ENDIAN +static uint32_t val32_pdp_to_little(uint32_t); +#else +static uint32_t val32_pdp_to_big(uint32_t); +#endif +static uint16_t val16_normal_order(uint16_t); +static uint16_t val16_reverse_order(uint16_t); +static v7fs_daddr_t val24_reverse_order_read(uint8_t *); +static void val24_reverse_order_write(v7fs_daddr_t, uint8_t *); +static v7fs_daddr_t val24_pdp_read(uint8_t *); +static void val24_pdp_write(v7fs_daddr_t, uint8_t *); + +static uint32_t +val32_normal_order(uint32_t v) +{ + + return v; +} + +static uint32_t +val32_reverse_order(uint32_t v) +{ + + return bswap32(v); +} +#if BYTE_ORDER == LITTLE_ENDIAN +static uint32_t +val32_pdp_to_little(uint32_t v) +{ + + return bswap32pdp_le(v); +} +#else +static uint32_t +val32_pdp_to_big(uint32_t v) +{ + + return bswap32pdp_be(v); +} +#endif +static uint16_t +val16_normal_order(uint16_t v) +{ + + return v; +} + +static uint16_t +val16_reverse_order(uint16_t v) +{ + + return bswap16(v); +} + +static v7fs_daddr_t +val24_reverse_order_read(uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return (a[0] << 16) | (a[1] << 8) | a[2]; +#else + return (a[2] << 16) | (a[1] << 8) | a[0]; +#endif +} + +static void +val24_reverse_order_write(v7fs_daddr_t addr, uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + a[0] = (addr >> 16) & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = addr & 0xff; +#else + a[0] = addr & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = (addr >> 16) & 0xff; +#endif +} + +static v7fs_daddr_t +val24_pdp_read(uint8_t *a) +{ + + return (a[0] << 16) | a[1] | (a[2] << 8); +} + +static void +val24_pdp_write(v7fs_daddr_t addr, uint8_t *a) +{ + + a[0] = (addr >> 16) & 0xff; + a[1] = addr & 0xff; + a[2] = (addr >> 8) & 0xff; +} + +void +v7fs_endian_init(struct v7fs_self *fs) +{ + struct endian_conversion_ops *ops = &fs->val; + + switch (fs->endian) + { +#if BYTE_ORDER == LITTLE_ENDIAN + case LITTLE_ENDIAN: + ops->conv32 = val32_normal_order; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_normal_order_read; + ops->conv24write = val24_normal_order_write; + break; + case BIG_ENDIAN: + ops->conv32 = val32_reverse_order; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_reverse_order_read; + ops->conv24write = val24_reverse_order_write; + break; + case PDP_ENDIAN: + ops->conv32 = val32_pdp_to_little; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_pdp_read; + ops->conv24write = val24_pdp_write; + break; +#else /* BIG_ENDIAN */ + case LITTLE_ENDIAN: + ops->conv32 = val32_reverse_order; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_reverse_order_read; + ops->conv24write = val24_reverse_order_write; + break; + case BIG_ENDIAN: + ops->conv32 = val32_normal_order; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_normal_order_read; + ops->conv24write = val24_normal_order_write; + break; + case PDP_ENDIAN: + ops->conv32 = val32_pdp_to_big; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_pdp_read; + ops->conv24write = val24_pdp_write; + break; +#endif + } +} +#endif /* V7FS_EI */ +v7fs_daddr_t +val24_normal_order_read(uint8_t *a) +{ + /*(v7fs_daddr_t)cast is required for int 16bit system. */ +#if BYTE_ORDER == LITTLE_ENDIAN + return ((v7fs_daddr_t)a[2] << 16) | ((v7fs_daddr_t)a[1] << 8) | + (v7fs_daddr_t)a[0]; +#else + return ((v7fs_daddr_t)a[0] << 16) | ((v7fs_daddr_t)a[1] << 8) | + (v7fs_daddr_t)a[2]; +#endif +} + +void +val24_normal_order_write(v7fs_daddr_t addr, uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + a[0] = addr & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = (addr >> 16) & 0xff; +#else + a[0] = (addr >> 16) & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = addr & 0xff; +#endif +} diff --git a/sys/fs/v7fs/v7fs_endian.h b/sys/fs/v7fs/v7fs_endian.h new file mode 100644 index 000000000..1d6411cae --- /dev/null +++ b/sys/fs/v7fs/v7fs_endian.h @@ -0,0 +1,52 @@ +/* $NetBSD: v7fs_endian.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_ENDIAN_H_ +#define _V7FS_ENDIAN_H_ + +struct v7fs_self; +__BEGIN_DECLS; +v7fs_daddr_t val24_normal_order_read(uint8_t *); +void val24_normal_order_write(v7fs_daddr_t, uint8_t *); +__END_DECLS +#ifdef V7FS_EI +#define V7FS_VAL32(x, v) ((*(x)->val.conv32)(v)) +#define V7FS_VAL16(x, v) ((*(x)->val.conv16)(v)) +#define V7FS_VAL24_READ(x, a) ((*(x)->val.conv24read)(a)) +#define V7FS_VAL24_WRITE(x, v, a) ((*(x)->val.conv24write)(v, a)) +void v7fs_endian_init(struct v7fs_self *); +#else +#define V7FS_VAL32(x, v) (v) +#define V7FS_VAL16(x, v) (v) +#define V7FS_VAL24_READ(x, a) (val24_normal_order_read(a)) +#define V7FS_VAL24_WRITE(x, v, a) (val24_normal_order_write(v, a)) +#endif /*V7FS_EI */ +#endif /*!_V7FS_ENDIAN_H_ */ diff --git a/sys/fs/v7fs/v7fs_extern.c b/sys/fs/v7fs/v7fs_extern.c new file mode 100644 index 000000000..7d4c73706 --- /dev/null +++ b/sys/fs/v7fs/v7fs_extern.c @@ -0,0 +1,262 @@ +/* $NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $"); + +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +MODULE(MODULE_CLASS_VFS, v7fs, NULL); + +/* External interfaces */ + +int (**v7fs_vnodeop_p)(void *); /* filled by getnewvnode (vnode.h) */ + +const struct vnodeopv_entry_desc v7fs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, v7fs_lookup }, /* lookup */ + { &vop_create_desc, v7fs_create }, /* create */ + { &vop_mknod_desc, v7fs_mknod }, /* mknod */ + { &vop_open_desc, v7fs_open }, /* open */ + { &vop_close_desc, v7fs_close }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, v7fs_read }, /* read */ + { &vop_write_desc, v7fs_write }, /* write */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */ + { &vop_poll_desc, genfs_poll }, /* poll */ + { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, genfs_revoke }, /* revoke */ + { &vop_mmap_desc, genfs_mmap }, /* mmap */ + { &vop_fsync_desc, v7fs_fsync }, /* fsync */ + { &vop_seek_desc, genfs_seek }, /* seek */ + { &vop_remove_desc, v7fs_remove }, /* remove */ + { &vop_link_desc, v7fs_link }, /* link */ + { &vop_rename_desc, v7fs_rename }, /* rename */ + { &vop_mkdir_desc, v7fs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, v7fs_rmdir }, /* rmdir */ + { &vop_symlink_desc, v7fs_symlink }, /* symlink */ + { &vop_readdir_desc, v7fs_readdir }, /* readdir */ + { &vop_readlink_desc, v7fs_readlink }, /* readlink */ + { &vop_abortop_desc, genfs_abortop }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, v7fs_bmap }, /* bmap */ + { &vop_strategy_desc, v7fs_strategy }, /* strategy */ + { &vop_print_desc, v7fs_print }, /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, v7fs_pathconf }, /* pathconf */ + { &vop_advlock_desc, v7fs_advlock }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_getpages_desc, genfs_getpages }, /* getpages */ + { &vop_putpages_desc, genfs_putpages }, /* putpages */ + { NULL, NULL } +}; + + +int (**v7fs_specop_p)(void *); +const struct vnodeopv_entry_desc v7fs_specop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, spec_lookup }, /* lookup */ + { &vop_create_desc, spec_create }, /* create xxx*/ + { &vop_mknod_desc, spec_mknod }, /* mknod xxx*/ + { &vop_open_desc, spec_open }, /* open */ + { &vop_close_desc, spec_close }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, spec_read }, /* read */ + { &vop_write_desc, spec_write }, /* write */ + { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_poll_desc, spec_poll }, /* poll */ + { &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, spec_revoke }, /* revoke */ + { &vop_mmap_desc, spec_mmap }, /* mmap */ + { &vop_fsync_desc, spec_fsync }, /* fsync */ + { &vop_seek_desc, spec_seek }, /* seek */ + { &vop_remove_desc, spec_remove }, /* remove */ + { &vop_link_desc, spec_link }, /* link */ + { &vop_rename_desc, spec_rename }, /* rename */ + { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ + { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ + { &vop_symlink_desc, spec_symlink }, /* symlink */ + { &vop_readdir_desc, spec_readdir }, /* readdir */ + { &vop_readlink_desc, spec_readlink }, /* readlink */ + { &vop_abortop_desc, spec_abortop }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, spec_bmap }, /* bmap */ + { &vop_strategy_desc, spec_strategy }, /* strategy */ + { &vop_print_desc, spec_print }, /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ + { &vop_advlock_desc, spec_advlock }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_getpages_desc, spec_getpages }, /* getpages */ + { &vop_putpages_desc, spec_putpages }, /* putpages */ + { NULL, NULL } +}; + +int (**v7fs_fifoop_p)(void *); +const struct vnodeopv_entry_desc v7fs_fifoop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, vn_fifo_bypass }, /* lookup */ + { &vop_create_desc, vn_fifo_bypass }, /* create */ + { &vop_mknod_desc, vn_fifo_bypass }, /* mknod */ + { &vop_open_desc, vn_fifo_bypass }, /* open */ + { &vop_close_desc, vn_fifo_bypass }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, vn_fifo_bypass }, /* read */ + { &vop_write_desc, vn_fifo_bypass }, /* write */ + { &vop_ioctl_desc, vn_fifo_bypass }, /* ioctl */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_poll_desc, vn_fifo_bypass }, /* poll */ + { &vop_kqfilter_desc, vn_fifo_bypass }, /* kqfilter */ + { &vop_revoke_desc, vn_fifo_bypass }, /* revoke */ + { &vop_mmap_desc, vn_fifo_bypass }, /* mmap */ + { &vop_fsync_desc, vn_fifo_bypass }, /* fsync */ + { &vop_seek_desc, vn_fifo_bypass }, /* seek */ + { &vop_remove_desc, vn_fifo_bypass }, /* remove */ + { &vop_link_desc, vn_fifo_bypass }, /* link */ + { &vop_rename_desc, vn_fifo_bypass }, /* rename */ + { &vop_mkdir_desc, vn_fifo_bypass }, /* mkdir */ + { &vop_rmdir_desc, vn_fifo_bypass }, /* rmdir */ + { &vop_symlink_desc, vn_fifo_bypass }, /* symlink */ + { &vop_readdir_desc, vn_fifo_bypass }, /* readdir */ + { &vop_readlink_desc, vn_fifo_bypass }, /* readlink */ + { &vop_abortop_desc, vn_fifo_bypass }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, vn_fifo_bypass }, /* lock */ + { &vop_unlock_desc, vn_fifo_bypass }, /* unlock */ + { &vop_bmap_desc, vn_fifo_bypass }, /* bmap */ + { &vop_strategy_desc, vn_fifo_bypass }, /* strategy */ + { &vop_print_desc, vn_fifo_bypass }, /* print */ + { &vop_islocked_desc, vn_fifo_bypass }, /* islocked */ + { &vop_pathconf_desc, vn_fifo_bypass }, /* pathconf */ + { &vop_advlock_desc, vn_fifo_bypass }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_putpages_desc, vn_fifo_bypass }, /* putpages */ + { NULL, NULL } +}; + +const struct vnodeopv_desc v7fs_fifoop_opv_desc = { + &v7fs_fifoop_p, + v7fs_fifoop_entries +}; + +const struct vnodeopv_desc v7fs_vnodeop_opv_desc = { + &v7fs_vnodeop_p, + v7fs_vnodeop_entries +}; + +const struct vnodeopv_desc v7fs_specop_opv_desc = { + &v7fs_specop_p, + v7fs_specop_entries +}; + +const struct vnodeopv_desc *v7fs_vnodeopv_descs[] = { + &v7fs_vnodeop_opv_desc, + &v7fs_specop_opv_desc, + &v7fs_fifoop_opv_desc, + NULL, +}; + +const struct genfs_ops v7fs_genfsops = { + .gop_size = genfs_size, + .gop_alloc = v7fs_gop_alloc, + .gop_write = genfs_gop_write, +}; + +struct vfsops v7fs_vfsops = { + MOUNT_V7FS, + sizeof(struct v7fs_args), + v7fs_mount, + v7fs_start, + v7fs_unmount, + v7fs_root, + (void *)eopnotsupp, /* vfs_quotactl */ + v7fs_statvfs, + v7fs_sync, + v7fs_vget, + v7fs_fhtovp, + v7fs_vptofh, + v7fs_init, + v7fs_reinit, + v7fs_done, + v7fs_mountroot, + (int (*)(struct mount *, struct vnode *, struct timespec *)) + eopnotsupp, /* snapshot */ + vfs_stdextattrctl, + (void *)eopnotsupp, /* vfs_suspendctl */ + genfs_renamelock_enter, + genfs_renamelock_exit, + (void *)eopnotsupp, + v7fs_vnodeopv_descs, + 0, + { NULL, NULL } +}; + +static int +v7fs_modcmd(modcmd_t cmd, void *arg) +{ + + switch (cmd) { + case MODULE_CMD_INIT: + return vfs_attach(&v7fs_vfsops); + case MODULE_CMD_FINI: + return vfs_detach(&v7fs_vfsops); + default: + return ENOTTY; + } +} diff --git a/sys/fs/v7fs/v7fs_extern.h b/sys/fs/v7fs/v7fs_extern.h new file mode 100644 index 000000000..80c4f9cc2 --- /dev/null +++ b/sys/fs/v7fs/v7fs_extern.h @@ -0,0 +1,116 @@ +/* $NetBSD: v7fs_extern.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _FS_V7FS_EXTERN_H_ +#define _FS_V7FS_EXTERN_H_ + +#include + +#include +#include +#include + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" + +struct v7fs_mount { + struct mount *mountp; + struct vnode *devvp; /* block device mounted vnode */ + struct v7fs_self *core; /* filesystem dependent implementation*/ + LIST_HEAD(, v7fs_node) v7fs_node_head; +}; + +struct v7fs_node { + struct genfs_node gnode; + struct v7fs_inode inode; /* filesystem dependent implementation */ + struct vnode *vnode; /* back-link */ + struct v7fs_mount *v7fsmount; /* our filesystem */ + struct lockf *lockf; /* advlock */ + + int update_ctime; + int update_atime; + int update_mtime; + + LIST_ENTRY(v7fs_node) link; +}; + +#define VFSTOV7FS(mp) ((struct v7fs_mount *)((mp)->mnt_data)) + +__BEGIN_DECLS +/* v-node ops. */ +int v7fs_lookup(void *); +int v7fs_create(void *); +int v7fs_open(void *); +int v7fs_close(void *); +int v7fs_access(void *); +int v7fs_getattr(void *); +int v7fs_setattr(void *); +int v7fs_read(void *); +int v7fs_write(void *); +int v7fs_fsync(void *); +int v7fs_remove(void *); +int v7fs_rename(void *); +int v7fs_readdir(void *); +int v7fs_inactive(void *); +int v7fs_reclaim(void *); +int v7fs_bmap(void *); +int v7fs_strategy(void *); +int v7fs_print(void *); +int v7fs_advlock(void *); +int v7fs_pathconf(void *); + +int v7fs_link(void *); +int v7fs_symlink(void *); +int v7fs_readlink(void *); + +int v7fs_mkdir(void *); +int v7fs_rmdir(void *); + +int v7fs_mknod(void *); + +/* vfs ops. */ +VFS_PROTOS(v7fs); + +int v7fs_mountroot(void); +extern int (**v7fs_vnodeop_p)(void *); +extern int (**v7fs_specop_p)(void *); +extern int (**v7fs_fifoop_p)(void *); + +/* genfs ops */ +int v7fs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t); +extern const struct genfs_ops v7fs_genfsops; + +/* internal service */ +int v7fs_update(struct vnode *, const struct timespec *, + const struct timespec *, int); +__END_DECLS +#endif /* _FS_V7FS_EXTERN_H_ */ diff --git a/sys/fs/v7fs/v7fs_file.c b/sys/fs/v7fs/v7fs_file.c new file mode 100644 index 000000000..f0d32b07a --- /dev/null +++ b/sys/fs/v7fs/v7fs_file.c @@ -0,0 +1,422 @@ +/* $NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#ifdef _KERNEL +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" +#include "v7fs_file.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_FILE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int lookup_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); +static int remove_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); + +int +v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name, v7fs_ino_t *ino) +{ + char filename[V7FS_NAME_MAX + 1]; + char *q; + int error; + size_t len; + + if ((q = strchr(name, '/'))) { + /* Zap following path. */ + len = MIN(V7FS_NAME_MAX, q - name); + memcpy(filename, name, len); + filename[len] = '\0'; /* '/' -> '\0' */ + } else { + v7fs_dirent_filename(filename, name); + } + DPRINTF("%s(%s) dir=%d\n", filename, name, parent_dir->inode_number); + + struct v7fs_lookup_arg lookup_arg = { .name = filename, + .inode_number = 0 }; + if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) { + DPRINTF("not found.\n"); + return ENOENT; + } + + *ino = lookup_arg.inode_number; + DPRINTF("done. ino=%d\n", *ino); + + return 0; +} + +static int +lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + const char *name = p->name; + void *buf; + size_t i, n; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + v7fs_dirent_endian_convert(fs, dir, n); + + for (i = 0; i < n; i++, dir++) { + if (dir->inode_number < 1) { + DPRINTF("*** bad inode #%d ***\n", dir->inode_number); + continue; + } + + if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0) + { + p->inode_number = dir->inode_number; + ret = V7FS_ITERATOR_BREAK; /* found */ + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +int +v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *srcname, struct v7fs_fileattr *attr, v7fs_ino_t *ino) +{ + struct v7fs_inode inode; + char filename[V7FS_NAME_MAX + 1]; + struct v7fs_dirent *dir; + int error; + + /* Truncate filename. */ + v7fs_dirent_filename(filename, srcname); + DPRINTF("%s(%s)\n", filename, srcname); + + /* Check filename. */ + if (v7fs_file_lookup_by_name(fs, parent_dir, filename, ino) == 0) { + DPRINTF("%s exists\n", filename); + return EEXIST; + } + + /* Get new inode. */ + if ((error = v7fs_inode_allocate(fs, ino))) + return error; + + /* Set initial attribute. */ + memset(&inode, 0, sizeof(inode)); + inode.inode_number = *ino; + inode.mode = attr->mode; + inode.uid = attr->uid; + inode.gid = attr->gid; + if (attr->ctime) + inode.ctime = attr->ctime; + if (attr->mtime) + inode.mtime = attr->mtime; + if (attr->atime) + inode.atime = attr->atime; + + switch (inode.mode & V7FS_IFMT) { + default: + DPRINTF("Can't allocate %o type.\n", inode.mode); + v7fs_inode_deallocate(fs, *ino); + return EINVAL; + case V7FS_IFCHR: + /* FALLTHROUGH */ + case V7FS_IFBLK: + inode.nlink = 1; + inode.device = attr->device; + inode.addr[0] = inode.device; + break; + case V7FSBSD_IFFIFO: + /* FALLTHROUGH */ + case V7FSBSD_IFSOCK: + /* FALLTHROUGH */ + case V7FSBSD_IFLNK: + /* FALLTHROUGH */ + case V7FS_IFREG: + inode.nlink = 1; + break; + case V7FS_IFDIR: + inode.nlink = 2; /* . + .. */ + if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2 + ))) { + v7fs_inode_deallocate(fs, *ino); + return error; + } + v7fs_daddr_t blk = inode.addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + v7fs_inode_deallocate(fs, *ino); + return EIO; + } + dir = (struct v7fs_dirent *)buf; + strcpy(dir[0].name, "."); + dir[0].inode_number = V7FS_VAL16(fs, *ino); + strcpy(dir[1].name, ".."); + dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number); + if (!fs->io.write(fs->io.cookie, buf, blk)) { + scratch_free(fs, buf); + return EIO; + } + scratch_free(fs, buf); + break; + } + + v7fs_inode_writeback(fs, &inode); + + /* Link this inode to parent directory. */ + if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, filename))) + { + DPRINTF("can't add dirent.\n"); + return error; + } + + return 0; +} + +int +v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name) +{ + v7fs_ino_t ino; + struct v7fs_inode inode; + int error; + + DPRINTF("%s\n", name); + if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, &ino))) { + DPRINTF("no such a file: %s\n", name); + return error; + } + DPRINTF("%s->#%d\n", name, ino); + if ((error = v7fs_inode_load(fs, &inode, ino))) + return error; + + if (v7fs_inode_isdir(&inode)) { + char filename[V7FS_NAME_MAX + 1]; + v7fs_dirent_filename(filename, name); + /* Check parent */ + if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) { + DPRINTF("can not remove '..'\n"); + return EINVAL; /* t_vnops rename_dotdot */ + } + /* Check empty */ + if (v7fs_inode_filesize(&inode) != + sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) { + DPRINTF("directory not empty.\n"); + return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/ + } + inode.nlink = 0; /* remove this. */ + } else { + /* Decrement reference count. */ + --inode.nlink; /* regular file. */ + DPRINTF("%s nlink=%d\n", name, inode.nlink); + } + + + if ((error = v7fs_directory_remove_entry(fs, parent_dir, name))) + return error; + DPRINTF("remove dirent\n"); + + if (inode.nlink == 0) { + v7fs_datablock_contract(fs, &inode, inode.filesize); + DPRINTF("remove datablock\n"); + v7fs_inode_deallocate(fs, ino); + DPRINTF("remove inode\n"); + } else { + v7fs_inode_writeback(fs, &inode); + } + + return 0; +} + +int +v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + v7fs_ino_t ino, const char *srcname) +{ + struct v7fs_inode inode; + struct v7fs_dirent *dir; + int error = 0; + v7fs_daddr_t blk; + void *buf; + char filename[V7FS_NAME_MAX + 1]; + + /* Truncate filename. */ + v7fs_dirent_filename(filename, srcname); + DPRINTF("%s(%s) %d\n", filename, srcname, ino); + + /* Target inode */ + if ((error = v7fs_inode_load(fs, &inode, ino))) + return error; + + /* Expand datablock. */ + if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir)))) + return error; + + /* Read last entry. */ + if (!(blk = v7fs_datablock_last(fs, parent_dir, + v7fs_inode_filesize(parent_dir)))) + return EIO; + + /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */ + if (!(buf = scratch_read(fs, blk))) + return EIO; + + size_t sz = v7fs_inode_filesize(parent_dir); + sz = V7FS_RESIDUE_BSIZE(sz); /* last block payload. */ + int n = sz / sizeof(*dir) - 1; + /* Add dirent. */ + dir = (struct v7fs_dirent *)buf; + dir[n].inode_number = V7FS_VAL16(fs, ino); + memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX); + /* Write back datablock */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + scratch_free(fs, buf); + + if (v7fs_inode_isdir(&inode)) { + parent_dir->nlink++; + v7fs_inode_writeback(fs, parent_dir); + } + + DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); + + return error; +} + +int +v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name) +{ + struct v7fs_inode inode; + int error; + struct v7fs_dirent lastdirent; + v7fs_daddr_t lastblk; + size_t sz, lastsz; + v7fs_off_t pos; + void *buf; + + /* Setup replaced entry. */ + sz = parent_dir->filesize; + lastblk = v7fs_datablock_last(fs, parent_dir, + v7fs_inode_filesize(parent_dir)); + lastsz = V7FS_RESIDUE_BSIZE(sz); + pos = lastsz - sizeof(lastdirent); + + if (!(buf = scratch_read(fs, lastblk))) + return EIO; + lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos)); + scratch_free(fs, buf); + DPRINTF("last dirent=%d %s pos=%d\n", + V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos); + + struct v7fs_lookup_arg lookup_arg = + { .name = name, .replace = &lastdirent/*disk endian */ }; + /* Search entry that removed. replace it to last dirent. */ + if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) + return ENOENT; + + /* Contract dirent entries. */ + v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent)); + DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); + + /* Target inode */ + if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number))) + return error; + + if (v7fs_inode_isdir(&inode)) { + parent_dir->nlink--; + v7fs_inode_writeback(fs, parent_dir); + } + + return 0; +} + +static int +remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i; + int ret = 0; + + DPRINTF("match start blk=%x\n", blk); + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + + for (i = 0; i < sz / sizeof(*dir); i++, dir++) { + DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number)); + if (strncmp(p->name, + (const char *)dir->name, V7FS_NAME_MAX) == 0) { + p->inode_number = V7FS_VAL16(fs, dir->inode_number); + /* Replace to last dirent. */ + *dir = *(p->replace); /* disk endian */ + /* Write back. */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + ret = EIO; + else + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} diff --git a/sys/fs/v7fs/v7fs_file.h b/sys/fs/v7fs/v7fs_file.h new file mode 100644 index 000000000..26c39fb5c --- /dev/null +++ b/sys/fs/v7fs/v7fs_file.h @@ -0,0 +1,65 @@ +/* $NetBSD: v7fs_file.h,v 1.2 2011/07/16 12:35:40 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_FILE_H_ +#define _V7FS_FILE_H_ + +struct v7fs_lookup_arg { + const char *name; + char *buf; + v7fs_ino_t inode_number; + struct v7fs_dirent *replace; +}; + +__BEGIN_DECLS +/* core */ +int v7fs_file_lookup_by_name(struct v7fs_self *, struct v7fs_inode *, + const char*, v7fs_ino_t *); +int v7fs_file_allocate(struct v7fs_self *, struct v7fs_inode *, const char *, + struct v7fs_fileattr *, v7fs_ino_t *); +int v7fs_file_deallocate(struct v7fs_self *, struct v7fs_inode *, const char *); +int v7fs_directory_add_entry(struct v7fs_self *,struct v7fs_inode *, v7fs_ino_t, + const char *); +int v7fs_directory_remove_entry(struct v7fs_self *,struct v7fs_inode *, + const char *); + +/* util */ +int v7fs_file_rename(struct v7fs_self *, struct v7fs_inode *, const char *, + struct v7fs_inode *, const char *); +int v7fs_directory_replace_entry(struct v7fs_self *, struct v7fs_inode *, + const char *, v7fs_ino_t); +int v7fs_file_link(struct v7fs_self *, struct v7fs_inode *, struct v7fs_inode *, + const char *); +bool v7fs_file_lookup_by_number(struct v7fs_self *, struct v7fs_inode *, + v7fs_ino_t, char *); +int v7fs_file_symlink(struct v7fs_self *, struct v7fs_inode *, const char *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_file_util.c b/sys/fs/v7fs/v7fs_file_util.c new file mode 100644 index 000000000..a6cfb32e8 --- /dev/null +++ b/sys/fs/v7fs/v7fs_file_util.c @@ -0,0 +1,344 @@ +/* $NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $"); +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" +#include "v7fs_file.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_FILE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int replace_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); +static int lookup_by_number_subr(struct v7fs_self *, void *, v7fs_daddr_t, + size_t); +static int can_dirmove(struct v7fs_self *, v7fs_ino_t, v7fs_ino_t); +static int lookup_parent_from_dir_subr(struct v7fs_self *, void *, + v7fs_daddr_t, size_t); + +int +v7fs_file_link(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + struct v7fs_inode *p, const char *name) +{ + int error = 0; + + DPRINTF("%d %d %s\n", parent_dir->inode_number, p->inode_number, name); + if ((error = v7fs_directory_add_entry(fs, parent_dir, p->inode_number, + name))) { + DPRINTF("can't add entry"); + return error; + } + p->nlink++; + v7fs_inode_writeback(fs, p); + + return 0; +} + +int +v7fs_file_symlink(struct v7fs_self *fs, struct v7fs_inode *p, + const char *target) +{ + int error; + size_t len = strlen(target) + 1; + + if (len > V7FSBSD_MAXSYMLINKLEN) {/* limited target 512byte pathname */ + DPRINTF("too long pathname."); + return ENAMETOOLONG; + } + + if ((error = v7fs_datablock_expand(fs, p, len))) { + return error; + } + + v7fs_daddr_t blk = p->addr[0]; /* 1block only. */ + void *buf; + if (!(buf = scratch_read(fs, blk))) { + return EIO; + } + + strncpy(buf, target, V7FS_BSIZE); + if (!fs->io.write(fs->io.cookie, buf, blk)) { + scratch_free(fs, buf); + return EIO; + } + scratch_free(fs, buf); + v7fs_inode_writeback(fs, p); + + return 0; +} + +int +v7fs_file_rename(struct v7fs_self *fs, struct v7fs_inode *parent_from, + const char *from, struct v7fs_inode *parent_to, const char *to) +{ + v7fs_ino_t from_ino, to_ino; + struct v7fs_inode inode; + int error; + bool dir_move; + + /* Check source file */ + if ((error = v7fs_file_lookup_by_name(fs, parent_from, from, + &from_ino))) { + DPRINTF("%s don't exists\n", from); + return error; + } + v7fs_inode_load(fs, &inode, from_ino); + dir_move = v7fs_inode_isdir(&inode); + + /* Check target file */ + error = v7fs_file_lookup_by_name(fs, parent_to, to, &to_ino); + if (error == 0) { /* found */ + DPRINTF("%s already exists\n", to); + if ((error = v7fs_file_deallocate(fs, parent_to, to))) { + DPRINTF("%s can't remove %d\n", to, error); + return error; + } + } else if (error != ENOENT) { + DPRINTF("error=%d\n", error); + return error; + } + /* Check directory hierarchy. t_vnops rename_dir(5) */ + if (dir_move && (error = can_dirmove(fs, from_ino, + parent_to->inode_number))) { + DPRINTF("dst '%s' is child dir of '%s'. error=%d\n", to, from, + error); + return error; + } + + if ((error = v7fs_directory_add_entry(fs, parent_to, from_ino, to))) { + DPRINTF("can't add entry"); + return error; + } + + if ((error = v7fs_directory_remove_entry(fs, parent_from, from))) { + DPRINTF("can't remove entry"); + return error; + } + + if (dir_move && (parent_from != parent_to)) { + /* If directory move, update ".." */ + if ((error = v7fs_directory_replace_entry(fs, &inode, "..", + parent_to->inode_number))) { + DPRINTF("can't replace parent dir"); + return error; + } + v7fs_inode_writeback(fs, &inode); + } + + return 0; +} + + +int +v7fs_directory_replace_entry(struct v7fs_self *fs, struct v7fs_inode *self_dir, + const char *name, v7fs_ino_t ino) +{ + int error; + + /* Search entry that replaced. replace it to new inode number. */ + struct v7fs_lookup_arg lookup_arg = { .name = name, + .inode_number = ino }; + if ((error = v7fs_datablock_foreach(fs, self_dir, replace_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) + return ENOENT; + + return 0; +} + +static int +replace_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i, n; + int ret = 0; + + DPRINTF("match start blk=%x\n", blk); + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + + for (i = 0; i < n; i++, dir++) { /*disk endian */ + if (strncmp(p->name, (const char *)dir->name, V7FS_NAME_MAX) + == 0) { + /* Replace inode# */ + dir->inode_number = V7FS_VAL16(fs, p->inode_number); + /* Write back. */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + ret = EIO; + else + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +bool +v7fs_file_lookup_by_number(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + v7fs_ino_t ino, char *buf) +{ + int ret; + + ret = v7fs_datablock_foreach(fs, parent_dir, lookup_by_number_subr, + &(struct v7fs_lookup_arg){ .inode_number = ino, .buf = buf }); + + return ret == V7FS_ITERATOR_BREAK; +} + +static int +lookup_by_number_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, + size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i, n; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + v7fs_dirent_endian_convert(fs, dir, n); + + for (i = 0; i < n; i++, dir++) { + if (dir->inode_number == p->inode_number) { + if (p->buf) + v7fs_dirent_filename(p->buf, dir->name); + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +struct lookup_parent_arg { + v7fs_ino_t parent_ino; +}; + +static int +can_dirmove(struct v7fs_self *fs, v7fs_ino_t from_ino, v7fs_ino_t to_ino) +{ + struct v7fs_inode inode; + v7fs_ino_t parent; + int error; + + /* Start dir. */ + if ((error = v7fs_inode_load(fs, &inode, to_ino))) + return error; + + if (!v7fs_inode_isdir(&inode)) + return ENOTDIR; + + /* Lookup the parent. */ + do { + struct lookup_parent_arg arg; + /* Search parent dir */ + arg.parent_ino = 0; + v7fs_datablock_foreach(fs, &inode, lookup_parent_from_dir_subr, + &arg); + if ((parent = arg.parent_ino) == 0) { + DPRINTF("***parent missing\n"); + return ENOENT; + } + /* Load parent dir */ + if ((error = v7fs_inode_load(fs, &inode, parent))) + return error; + if (parent == from_ino) { + DPRINTF("#%d is child dir of #%d\n", to_ino, from_ino); + return EINVAL; + } + } while (parent != V7FS_ROOT_INODE); + + return 0; +} + +static int +lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, + size_t sz) +{ + struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx; + char name[V7FS_NAME_MAX + 1]; + void *buf; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return 0; + struct v7fs_dirent *dir = (struct v7fs_dirent *)buf; + size_t i, n = sz / sizeof(*dir); + if (!v7fs_dirent_endian_convert(fs, dir, n)) { + scratch_free(fs, buf); + return V7FS_ITERATOR_ERROR; + } + + for (i = 0; i < n; i++, dir++) { + v7fs_dirent_filename(name, dir->name); + if (strncmp(dir->name, "..", V7FS_NAME_MAX) != 0) + continue; + + arg->parent_ino = dir->inode_number; + ret = V7FS_ITERATOR_BREAK; + break; + } + + scratch_free(fs, buf); + return ret; +} diff --git a/sys/fs/v7fs/v7fs_impl.h b/sys/fs/v7fs/v7fs_impl.h new file mode 100644 index 000000000..a6cf6a39d --- /dev/null +++ b/sys/fs/v7fs/v7fs_impl.h @@ -0,0 +1,154 @@ +/* $NetBSD: v7fs_impl.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +/* V7FS implementation. */ +#ifndef _V7FS_IMPL_H_ +#define _V7FS_IMPL_H_ + +#ifndef _KERNEL +#include +#include +#define KDASSERT(x) assert(x) +#endif + +struct block_io_ops { + void *cookie; + bool (*drive)(void *, uint8_t); + bool (*read)(void *, uint8_t *, daddr_t); + bool (*read_n)(void *, uint8_t *, daddr_t, int); + bool (*write)(void *, uint8_t *, daddr_t); + bool (*write_n)(void *, uint8_t *, daddr_t, int); +}; + +#ifdef V7FS_EI +struct endian_conversion_ops { + uint32_t (*conv32)(uint32_t); + uint16_t (*conv16)(uint16_t); + /* For daddr packing */ + v7fs_daddr_t (*conv24read)(uint8_t *); + void (*conv24write)(v7fs_daddr_t, uint8_t *); +}; +#endif +#ifdef _KERNEL +#define V7FS_LOCK +#endif +#ifdef V7FS_LOCK +struct lock_ops { + void *cookie; + void (*lock)(void*); + void (*unlock)(void *); +}; +#define SUPERB_LOCK(x) ((x)->sb_lock.lock((x)->sb_lock.cookie)) +#define SUPERB_UNLOCK(x) ((x)->sb_lock.unlock((x)->sb_lock.cookie)) +#define ILIST_LOCK(x) ((x)->ilist_lock.lock((x)->ilist_lock.cookie)) +#define ILIST_UNLOCK(x) ((x)->ilist_lock.unlock((x)->ilist_lock.cookie)) +#define MEM_LOCK(x) ((x)->mem_lock.lock((x)->mem_lock.cookie)) +#define MEM_UNLOCK(x) ((x)->mem_lock.unlock((x)->mem_lock.cookie)) +#else /*V7FS_LOCK */ +#define SUPERB_LOCK(x) ((void)0) +#define SUPERB_UNLOCK(x) ((void)0) +#define ILIST_LOCK(x) ((void)0) +#define ILIST_UNLOCK(x) ((void)0) +#define MEM_LOCK(x) ((void)0) +#define MEM_UNLOCK(x) ((void)0) +#endif /*V7FS_LOCK */ + +struct v7fs_stat { + int32_t total_blocks; + int32_t free_blocks; + int32_t total_inode; + int32_t free_inode; + int32_t total_files; +}; + +struct v7fs_fileattr { + int16_t uid; + int16_t gid; + v7fs_mode_t mode; + v7fs_dev_t device; + v7fs_time_t ctime; + v7fs_time_t mtime; + v7fs_time_t atime; +}; + +struct v7fs_self { +#define V7FS_SELF_NSCRATCH 3 + uint8_t scratch[V7FS_SELF_NSCRATCH][V7FS_BSIZE]; + int scratch_free; /* free block bitmap. */ + int scratch_remain; /* for statistic */ + struct block_io_ops io; +#ifdef V7FS_EI + struct endian_conversion_ops val; +#endif +#ifdef V7FS_LOCK + /* in-core superblock access. (freeblock/freeinode) split? -uch */ + struct lock_ops sb_lock; + struct lock_ops ilist_lock; /* disk ilist access. */ + struct lock_ops mem_lock; /* work memory allocation lock. */ +#endif + struct v7fs_superblock superblock; + struct v7fs_stat stat; + int endian; +}; + +struct v7fs_mount_device { + union { + void *vnode; /* NetBSD kernel */ + int fd; /* NetBSD newfs,fsck */ + const char *filename;/* misc test */ + } device; + daddr_t sectors; /*total size in sector. */ + int endian; +}; + +#define V7FS_ITERATOR_BREAK (-1) +#define V7FS_ITERATOR_END (-2) +#define V7FS_ITERATOR_ERROR (-3) +__BEGIN_DECLS +int v7fs_io_init(struct v7fs_self **, const struct v7fs_mount_device *, size_t); +void v7fs_io_fini(struct v7fs_self *); +void *scratch_read(struct v7fs_self *, daddr_t); +void scratch_free(struct v7fs_self *, void *); +int scratch_remain(const struct v7fs_self *); +__END_DECLS + +#if 0 +#define V7FS_IO_DEBUG +#define V7FS_SUPERBLOCK_DEBUG +#define V7FS_DATABLOCK_DEBUG +#define V7FS_INODE_DEBUG +#define V7FS_DIRENT_DEBUG +#define V7FS_FILE_DEBUG +#define V7FS_VFSOPS_DEBUG +#define V7FS_VNOPS_DEBUG +#endif + +#endif /*!_V7FS_IMPL_H_ */ diff --git a/sys/fs/v7fs/v7fs_inode.c b/sys/fs/v7fs/v7fs_inode.c new file mode 100644 index 000000000..c3e683f8a --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode.c @@ -0,0 +1,301 @@ +/* $NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_INODE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static void v7fs_inode_setup_disk_image(const struct v7fs_self *, + struct v7fs_inode *, struct v7fs_inode_diskimage *); +static int v7fs_inode_inquire_disk_location(const struct v7fs_self *, + v7fs_ino_t, v7fs_daddr_t *, v7fs_daddr_t *); +#ifdef V7FS_INODE_DEBUG +static int v7fs_inode_block_sanity(const struct v7fs_superblock *, + v7fs_daddr_t); + +static int +v7fs_inode_block_sanity(const struct v7fs_superblock *sb, v7fs_daddr_t blk) +{ + + if ((blk < V7FS_ILIST_SECTOR) || (blk >= sb->datablock_start_sector)) { + DPRINTF("invalid inode block#%d (%d-%d)\n", blk, + V7FS_ILIST_SECTOR, sb->datablock_start_sector); + return ENOSPC; + } + + return 0; +} +#endif /* V7FS_INODE_DEBUG */ + +int +v7fs_inode_number_sanity(const struct v7fs_superblock *sb, v7fs_ino_t ino) +{ + + if (ino < V7FS_ROOT_INODE || ((size_t)ino >= V7FS_MAX_INODE(sb))) { + DPRINTF("invalid inode#%d (%d-%zu)\n", ino, + V7FS_ROOT_INODE, V7FS_MAX_INODE(sb)); + return ENOSPC; + } + + return 0; +} + +int +v7fs_inode_allocate(struct v7fs_self *fs, v7fs_ino_t *ino) +{ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_ino_t inode_number; + int error = ENOSPC; + *ino = 0; + + SUPERB_LOCK(fs); + if (sb->total_freeinode == 0) { + DPRINTF("inode exhausted!(1)\n"); + goto errexit; + } + + /* If there is no free inode cache, update it. */ + if (sb->nfreeinode <= 0 && (error = v7fs_freeinode_update(fs))) { + DPRINTF("inode exhausted!(2)\n"); + goto errexit; + } + /* Get inode from superblock cache. */ + KDASSERT(sb->nfreeinode <= V7FS_MAX_FREEINODE); + inode_number = sb->freeinode[--sb->nfreeinode]; + sb->total_freeinode--; + sb->modified = 1; + + if ((error = v7fs_inode_number_sanity(sb, inode_number))) { + DPRINTF("new inode#%d %d %d\n", inode_number, sb->nfreeinode, + sb->total_freeinode); + DPRINTF("free inode list corupt\n"); + goto errexit; + } + *ino = inode_number; + +errexit: + SUPERB_UNLOCK(fs); + + return error; +} + +void +v7fs_inode_deallocate(struct v7fs_self *fs, v7fs_ino_t ino) +{ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_inode inode; + + memset(&inode, 0, sizeof(inode)); + inode.inode_number = ino; + v7fs_inode_writeback(fs, &inode); + + SUPERB_LOCK(fs); + if (sb->nfreeinode < V7FS_MAX_FREEINODE) { + /* link to freeinode list. */ + sb->freeinode[sb->nfreeinode++] = ino; + } + /* If superblock inode cache is full, this inode charged by + v7fs_freeinode_update() later. */ + sb->total_freeinode++; + sb->modified = true; + SUPERB_UNLOCK(fs); +} + +void +v7fs_inode_setup_memory_image(const struct v7fs_self *fs __unused, + struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk) +{ +#define conv16(m) (mem->m = V7FS_VAL16(fs, (disk->m))) +#define conv32(m) (mem->m = V7FS_VAL32(fs, (disk->m))) + uint32_t addr; + int i; + + memset(mem, 0, sizeof(*mem)); + conv16(mode); + conv16(nlink); + conv16(uid); + conv16(gid); + conv32(filesize); + conv32(atime); + conv32(mtime); + conv32(ctime); + + for (i = 0; i < V7FS_NADDR; i++) { + int j = i * 3; /* 3 byte each. (v7fs_daddr is 24bit) */ + /* expand to 4byte with endian conversion. */ + addr = V7FS_VAL24_READ(fs, &disk->addr[j]); + mem->addr[i] = addr; + } + mem->device = 0; + if (v7fs_inode_iscdev(mem) || v7fs_inode_isbdev(mem)) { + mem->device = mem->addr[0]; + } + +#undef conv16 +#undef conv32 +} + +static void +v7fs_inode_setup_disk_image(const struct v7fs_self *fs __unused, + struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk) +{ +#define conv16(m) (disk->m = V7FS_VAL16(fs, (mem->m))) +#define conv32(m) (disk->m = V7FS_VAL32(fs, (mem->m))) + + conv16(mode); + conv16(nlink); + conv16(uid); + conv16(gid); + conv32(filesize); + conv32(atime); + conv32(mtime); + conv32(ctime); + + int i; + for (i = 0; i < V7FS_NADDR; i++) { + int j = i * 3; /* 3 byte each. */ + V7FS_VAL24_WRITE(fs, mem->addr[i], disk->addr + j); + } +#undef conv16 +#undef conv32 +} + +/* Load inode from disk. */ +int +v7fs_inode_load(struct v7fs_self *fs, struct v7fs_inode *p, v7fs_ino_t n) +{ + v7fs_daddr_t blk, ofs; + struct v7fs_inode_diskimage *di; + void *buf; + + if (v7fs_inode_inquire_disk_location(fs, n, &blk, &ofs) != 0) + return ENOENT; + + ILIST_LOCK(fs); + if (!(buf = scratch_read(fs, blk))) { + ILIST_UNLOCK(fs); + return EIO; + } + ILIST_UNLOCK(fs); + di = (struct v7fs_inode_diskimage *)buf; + + /* Decode disk address, convert endian. */ + v7fs_inode_setup_memory_image(fs, p, di + ofs); + p->inode_number = n; + + scratch_free(fs, buf); + + return 0; +} + +/* Write back inode to disk. */ +int +v7fs_inode_writeback(struct v7fs_self *fs, struct v7fs_inode *mem) +{ + struct v7fs_inode_diskimage disk; + v7fs_ino_t ino = mem->inode_number; + v7fs_daddr_t blk; + v7fs_daddr_t ofs; + void *buf; + int error = 0; + + if (v7fs_inode_inquire_disk_location(fs, ino, &blk, &ofs) != 0) + return ENOENT; + + v7fs_inode_setup_disk_image(fs, mem, &disk); + + ILIST_LOCK(fs); + if (!(buf = scratch_read(fs, blk))) { + ILIST_UNLOCK(fs); + return EIO; + } + struct v7fs_inode_diskimage *di = (struct v7fs_inode_diskimage *)buf; + di[ofs] = disk; /* structure copy; */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + ILIST_UNLOCK(fs); + + scratch_free(fs, buf); + + return error; +} + +static int +v7fs_inode_inquire_disk_location(const struct v7fs_self *fs + __unused, v7fs_ino_t n, v7fs_daddr_t *block, + v7fs_daddr_t *offset) +{ + v7fs_daddr_t ofs, blk; +#ifdef V7FS_INODE_DEBUG + v7fs_inode_number_sanity(&fs->superblock, n); +#endif + ofs = (n - 1/*inode start from 1*/) * + sizeof(struct v7fs_inode_diskimage); + blk = ofs >> V7FS_BSHIFT; + + *block = blk + V7FS_ILIST_SECTOR; + *offset = (ofs - blk * V7FS_BSIZE) / + sizeof(struct v7fs_inode_diskimage); +#ifdef V7FS_INODE_DEBUG + return v7fs_inode_block_sanity(&fs->superblock, *block); +#else + return 0; +#endif +} + diff --git a/sys/fs/v7fs/v7fs_inode.h b/sys/fs/v7fs/v7fs_inode.h new file mode 100644 index 000000000..7dc42db5a --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode.h @@ -0,0 +1,91 @@ +/* $NetBSD: v7fs_inode.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_INODE_H_ +#define _V7FS_INODE_H_ + +/* Software implementation of inode. (memory image) */ +struct v7fs_inode { + v7fs_ino_t inode_number; /* inode location */ + /* attr */ + uint16_t mode; + int16_t nlink; + int16_t uid; + int16_t gid; + v7fs_time_t atime; + v7fs_time_t mtime; + v7fs_time_t ctime; + /* open mode */ + bool append_mode; + + v7fs_dev_t device; /* for special file.(cdev, bdev) */ + /* payload. */ + v7fs_off_t filesize; /* size of file (byte) */ + v7fs_daddr_t addr[V7FS_NADDR]; /* data block address list */ +}; + +#define v7fs_inode_filesize(i) ((i)->filesize) +#define v7fs_inode_allocated(i) ((i)->mode) +#define v7fs_inode_nlink(i) ((i)->nlink) +/* V7 original */ +#define v7fs_inode_isdir(i) (((i)->mode & V7FS_IFMT) == V7FS_IFDIR) +#define v7fs_inode_isfile(i) (((i)->mode & V7FS_IFMT) == V7FS_IFREG) +#define v7fs_inode_iscdev(i) (((i)->mode & V7FS_IFMT) == V7FS_IFCHR) +#define v7fs_inode_isbdev(i) (((i)->mode & V7FS_IFMT) == V7FS_IFBLK) +/* 2BSD extension (implementation is different) */ +#define v7fs_inode_islnk(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFLNK) +#define v7fs_inode_issock(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFSOCK) +/* NetBSD Extension */ +#define v7fs_inode_isfifo(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFFIFO) + +__BEGIN_DECLS +/* Free inode access ops. */ +int v7fs_inode_allocate(struct v7fs_self *, v7fs_ino_t *); +void v7fs_inode_deallocate(struct v7fs_self *, v7fs_ino_t); + +/* Disk I/O ops. */ +int v7fs_inode_load(struct v7fs_self *, struct v7fs_inode *, v7fs_ino_t); +int v7fs_inode_writeback(struct v7fs_self *, struct v7fs_inode *); +void v7fs_inode_setup_memory_image(const struct v7fs_self *, + struct v7fs_inode *, struct v7fs_inode_diskimage *); + +/* Check. */ +int v7fs_inode_number_sanity(const struct v7fs_superblock *, v7fs_ino_t); + +/* Util. */ +void v7fs_inode_chmod(struct v7fs_inode *, v7fs_mode_t); +void v7fs_inode_dump(const struct v7fs_inode *); + +/* Loop over all inode in ilist. */ +int v7fs_ilist_foreach(struct v7fs_self *, int (*)(struct v7fs_self *, void *, + struct v7fs_inode *, v7fs_ino_t), void *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_inode_util.c b/sys/fs/v7fs/v7fs_inode_util.c new file mode 100644 index 000000000..94dfd7a3b --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode_util.c @@ -0,0 +1,128 @@ +/* $NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" + +#ifdef V7FS_INODE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +void +v7fs_inode_chmod(struct v7fs_inode *inode, v7fs_mode_t mode) +{ +#define V7FS_MODE_MASK 0xfff + DPRINTF("setattr %08o -> %08o\n", inode->mode, mode); + + inode->mode &= ~V7FS_MODE_MASK; + inode->mode |= (mode & V7FS_MODE_MASK); + DPRINTF("setattr %08o -> %08o\n", inode->mode, mode); +} + +void +v7fs_inode_dump(const struct v7fs_inode *p) +{ + printf("nlink %d mode %06o %d/%d %d bytes\n", + p->nlink, p->mode, + p->uid, p->gid, p->filesize); + + printf("atime %d mtime %d ctime %d\n", + p->atime, p->mtime, p->ctime); +#ifndef _KERNEL + time_t at = p->atime; + time_t mt = p->mtime; + time_t ct = p->ctime; + printf(" atime %s mtime %s ctime %s", ctime(&at), ctime(&mt), + ctime(&ct)); +#endif + if (v7fs_inode_iscdev(p) || v7fs_inode_isbdev(p)) { + printf("device:%d/%d\n", (p->device >> 8), p->device & 0xff); + } + printf("\n"); +} + + +int +v7fs_ilist_foreach +(struct v7fs_self *fs, + int (*func)(struct v7fs_self *, void *, struct v7fs_inode *, v7fs_ino_t), + void *ctx) +{ + struct v7fs_superblock *sb = &fs->superblock; + size_t i, j, k; + int ret; + + /* Loop over ilist. */ + for (k = 1, i = V7FS_ILIST_SECTOR; i < sb->datablock_start_sector; + i++) { + struct v7fs_inode_diskimage *di; + struct v7fs_inode inode; + void *buf; + + if (!(buf = scratch_read(fs, i))) { + DPRINTF("block %zu I/O error.\n", i); + k += V7FS_INODE_PER_BLOCK; + continue; + } + di = (struct v7fs_inode_diskimage *)buf; + for (j = 0; j < V7FS_INODE_PER_BLOCK; j++, k++) { + v7fs_inode_setup_memory_image(fs, &inode, di + j); + inode.inode_number = k; + if ((ret = func(fs, ctx, &inode, k))) { + scratch_free(fs, buf); + return ret; + } + } + scratch_free(fs, buf); + } + return 0; +} diff --git a/sys/fs/v7fs/v7fs_io.c b/sys/fs/v7fs/v7fs_io.c new file mode 100644 index 000000000..3b5cca41a --- /dev/null +++ b/sys/fs/v7fs/v7fs_io.c @@ -0,0 +1,141 @@ +/* $NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" + +#if defined _KERNEL +#define STATIC_BUFFER +#endif + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +void * +scratch_read(struct v7fs_self *fs, daddr_t blk) +{ +#ifdef STATIC_BUFFER + int i; + MEM_LOCK(fs); + for (i = 0; i < V7FS_SELF_NSCRATCH; i++) { + if (fs->scratch_free & (1 << i)) { + fs->scratch_free &= ~(1 << i); + break; + } + } + if (i == V7FS_SELF_NSCRATCH) { + DPRINTF("No scratch area. increase V7FS_SELF_NSCRATCH\n"); + assert(0); + MEM_UNLOCK(fs); + return NULL; + } + + if (!fs->io.read(fs->io.cookie, fs->scratch[i], blk)) { + DPRINTF("*** I/O error block %ld\n", (long)blk); + fs->scratch_free |= (1 << i); + MEM_UNLOCK(fs); + return NULL; + } + MEM_UNLOCK(fs); + /* Statistic */ + int n; + if ((n = scratch_remain(fs)) < fs->scratch_remain) + fs->scratch_remain = n; + + return fs->scratch[i]; +#else + uint8_t *buf = malloc(V7FS_BSIZE); + if (!fs->io.read(fs->io.cookie, buf, blk)) { + DPRINTF("*** I/O error block %ld\n",(long)blk); + free(buf); + return NULL; + } + return buf; +#endif +} + +int +scratch_remain(const struct v7fs_self *fs) +{ +#ifdef STATIC_BUFFER + int nfree; + int i; + MEM_LOCK(fs); + for (i = 0, nfree = 0; i < V7FS_SELF_NSCRATCH; i++) { + if (fs->scratch_free & (1 << i)) { + nfree++; + } + } + MEM_UNLOCK(fs); + return nfree; +#else + return -1; +#endif +} + +void +scratch_free(struct v7fs_self *fs __unused, void *p) +{ +#ifdef STATIC_BUFFER + int i; + MEM_LOCK(fs); + for (i = 0; i < V7FS_SELF_NSCRATCH; i++) + if (fs->scratch[i] == p) { + fs->scratch_free |= (1 << i); + break; + } + MEM_UNLOCK(fs); + assert(i != V7FS_SELF_NSCRATCH); +#else + free(p); +#endif +} diff --git a/sys/fs/v7fs/v7fs_io_kern.c b/sys/fs/v7fs/v7fs_io_kern.c new file mode 100644 index 000000000..0151dc616 --- /dev/null +++ b/sys/fs/v7fs/v7fs_io_kern.c @@ -0,0 +1,241 @@ +/* $NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif +#include + +__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +struct local_io { + struct vnode *vp; + kauth_cred_t cred; +}; + +static bool v7fs_os_read_n(void *, uint8_t *, daddr_t, int); +static bool v7fs_os_read(void *, uint8_t *, daddr_t); +static bool v7fs_os_write_n(void *, uint8_t *, daddr_t, int); +static bool v7fs_os_write(void *, uint8_t *, daddr_t); +static void v7fs_os_lock(void *); +static void v7fs_os_unlock(void *); +static bool lock_init(struct lock_ops *); + +int +v7fs_io_init(struct v7fs_self **fs, + const struct v7fs_mount_device *mount_device, size_t block_size) +{ + struct vnode *vp = mount_device->device.vnode; + struct v7fs_self *p; + struct local_io *local; + int error = 0; + + if ((p = kmem_zalloc(sizeof(*p), KM_SLEEP)) == NULL) + return ENOMEM; + + p->scratch_free = -1; + p->scratch_remain = V7FS_SELF_NSCRATCH; + + /* Endian */ + p->endian = mount_device->endian; +#ifdef V7FS_EI + v7fs_endian_init(p); +#endif + /* IO */ + if ((local = kmem_zalloc(sizeof(*local), KM_SLEEP)) == NULL) { + error = ENOMEM; + goto errexit; + } + p->io.read = v7fs_os_read; + p->io.read_n = v7fs_os_read_n; + p->io.write = v7fs_os_write; + p->io.write_n = v7fs_os_write_n; + p->scratch_free = -1; /* free all scratch buffer */ + + p->io.cookie = local; + local->vp = vp; + local->cred = NOCRED; /* upper layer check cred. */ + + /*LOCK */ + error = ENOMEM; + if (!lock_init(&p->sb_lock)) + goto errexit; + if (!lock_init(&p->ilist_lock)) + goto errexit; + if (!lock_init(&p->mem_lock)) + goto errexit; + error = 0; + + *fs = p; + return 0; + +errexit: + v7fs_io_fini(p); + return error; +} + +static bool +lock_init(struct lock_ops *ops) +{ + if ((ops->cookie = kmem_zalloc(sizeof(kmutex_t), KM_SLEEP)) == NULL) { + return false; + } + mutex_init(ops->cookie, MUTEX_DEFAULT, IPL_NONE); + ops->lock = v7fs_os_lock; + ops->unlock = v7fs_os_unlock; + return true; +} + +void +v7fs_io_fini(struct v7fs_self *fs) +{ + if (fs->io.cookie) { + kmem_free(fs->io.cookie, sizeof(struct local_io)); + } + if (fs->sb_lock.cookie) { + mutex_destroy(fs->sb_lock.cookie); + kmem_free(fs->sb_lock.cookie, sizeof(kmutex_t)); + } + if (fs->ilist_lock.cookie) { + mutex_destroy(fs->ilist_lock.cookie); + kmem_free(fs->ilist_lock.cookie, sizeof(kmutex_t)); + } + if (fs->mem_lock.cookie) { + mutex_destroy(fs->mem_lock.cookie); + kmem_free(fs->mem_lock.cookie, sizeof(kmutex_t)); + } + kmem_free(fs, sizeof(*fs)); +} + +static bool +v7fs_os_read_n(void *self, uint8_t *buf, daddr_t block, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (!v7fs_os_read(self, buf, block)) + return false; + buf += DEV_BSIZE; + block++; + } + + return true; +} + +static bool +v7fs_os_read(void *self, uint8_t *buf, daddr_t block) +{ + struct local_io *bio = (struct local_io *)self; + struct buf *bp = NULL; + + if (bread(bio->vp, block, DEV_BSIZE, bio->cred, 0, &bp) != 0) + goto error_exit; + memcpy(buf, bp->b_data, DEV_BSIZE); + brelse(bp, 0); + + return true; +error_exit: + DPRINTF("block %ld read failed.\n", (long)block); + + if (bp != NULL) + brelse(bp, 0); + return false; +} + +static bool +v7fs_os_write_n(void *self, uint8_t *buf, daddr_t block, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (!v7fs_os_write(self, buf, block)) + return false; + buf += DEV_BSIZE; + block++; + } + + return true; +} + +static bool +v7fs_os_write(void *self, uint8_t *buf, daddr_t block) +{ + struct local_io *bio = (struct local_io *)self; + struct buf *bp; + + if ((bp = getblk(bio->vp, block, DEV_BSIZE, 0, 0)) == 0) { + DPRINTF("getblk failed. block=%ld\n", (long)block); + return false; + } + + memcpy(bp->b_data, buf, DEV_BSIZE); + + if (bwrite(bp) != 0) { + DPRINTF("bwrite failed. block=%ld\n", (long)block); + return false; + } + + return true; +} + +static void +v7fs_os_lock(void *self) +{ + + mutex_enter((kmutex_t *)self); +} + +static void +v7fs_os_unlock(void *self) +{ + + mutex_exit((kmutex_t *)self); +} diff --git a/sys/fs/v7fs/v7fs_io_user.c b/sys/fs/v7fs/v7fs_io_user.c new file mode 100644 index 000000000..907a78b50 --- /dev/null +++ b/sys/fs/v7fs/v7fs_io_user.c @@ -0,0 +1,172 @@ +/* $NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +__RCSID("$NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "v7fs.h" +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +struct local_io { + int fd; + size_t size; + size_t blksz; + uint8_t *addr; +} local; + +static bool read_sector(void *, uint8_t *, daddr_t); +static bool write_sector(void *, uint8_t *, daddr_t); +static bool read_mmap(void *, uint8_t *, daddr_t); +static bool write_mmap(void *, uint8_t *, daddr_t); + +int +v7fs_io_init(struct v7fs_self **fs, const struct v7fs_mount_device *mount, + size_t block_size) +{ + struct v7fs_self *p; + + if (!(p = (struct v7fs_self *)malloc(sizeof(*p)))) + return ENOMEM; + memset(p, 0, sizeof(*p)); + + /* Endian */ + p->endian = mount->endian; +#ifdef V7FS_EI + v7fs_endian_init(p); +#endif + local.blksz = block_size; + local.fd = mount->device.fd; + local.size = mount->sectors * block_size; + local.addr = mmap(NULL, local.size, PROT_READ | PROT_WRITE | PROT_NONE, + MAP_FILE | MAP_SHARED/*writeback*/, local.fd, 0); + if (local.addr == MAP_FAILED) { + local.addr = 0; + p->io.read = read_sector; + p->io.write = write_sector; + } else { + DPRINTF("mmaped addr=%p\n", local.addr); + p->io.read = read_mmap; + p->io.write = write_mmap; + } + + p->io.cookie = &local; + *fs = p; + + return 0; +} + +void +v7fs_io_fini(struct v7fs_self *fs) +{ + struct local_io *lio = (struct local_io *)fs->io.cookie; + + if (lio->addr) { + if (munmap(lio->addr, lio->size) != 0) + warn(0); + } + fsync(lio->fd); + + free(fs); +} + +static bool +read_sector(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + int fd = lio->fd; + + if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) || + (read(fd, buf, blksz) < (ssize_t)blksz)) { + warn("sector=%ld\n", (long)sector); + return false; + } + + return true; +} + +static bool +write_sector(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + int fd = lio->fd; + + if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) || + (write(fd, buf, blksz) < (ssize_t)blksz)) { + warn("sector=%ld\n", (long)sector); + return false; + } + + return true; +} + +static bool +read_mmap(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + + memcpy(buf, lio->addr + sector * blksz, blksz); + + return true; +} + +static bool +write_mmap(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + + memcpy(lio->addr + sector * blksz, buf, blksz); + + return true; +} diff --git a/sys/fs/v7fs/v7fs_superblock.c b/sys/fs/v7fs/v7fs_superblock.c new file mode 100644 index 000000000..413e0faf7 --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock.c @@ -0,0 +1,263 @@ +/* $NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include /* errno */ +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_SUPERBLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#define DPRINTF_(fmt, args...) printf(fmt, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#define DPRINTF_(fmt, args...) ((void)0) +#endif + +static void v7fs_superblock_endian_convert(struct v7fs_self *, + struct v7fs_superblock *, struct v7fs_superblock *); +static int v7fs_superblock_sanity(struct v7fs_self *); + +/* Load superblock from disk. */ +int +v7fs_superblock_load(struct v7fs_self *fs) +{ + struct v7fs_superblock *disksb; + void *buf; + int error; + + if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR))) + return EIO; + disksb = (struct v7fs_superblock *)buf; + v7fs_superblock_endian_convert(fs, &fs->superblock, disksb); + scratch_free(fs, buf); + + if ((error = v7fs_superblock_sanity(fs))) + return error; + + return 0; +} + +/* Writeback superblock to disk. */ +int +v7fs_superblock_writeback(struct v7fs_self *fs) +{ + struct v7fs_superblock *memsb = &fs->superblock; + struct v7fs_superblock *disksb; + void *buf; + int error = 0; + + if (!memsb->modified) + return 0; + + if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR))) + return EIO; + disksb = (struct v7fs_superblock *)buf; + v7fs_superblock_endian_convert(fs, disksb, memsb); + if (!fs->io.write(fs->io.cookie, buf, V7FS_SUPERBLOCK_SECTOR)) + error = EIO; + scratch_free(fs, buf); + + memsb->modified = 0; + DPRINTF("done. %d\n", error); + + return error; +} + +/* Check endian mismatch. */ +static int +v7fs_superblock_sanity(struct v7fs_self *fs) +{ + const struct v7fs_superblock *sb = &fs->superblock; + void *buf = 0; + + if ((sb->volume_size < 128) || /* smaller than 64KB. */ + (sb->datablock_start_sector > sb->volume_size) || + (sb->nfreeinode > V7FS_MAX_FREEINODE) || + (sb->nfreeblock > V7FS_MAX_FREEBLOCK) || + (sb->update_time < 0) || + (sb->total_freeblock > sb->volume_size) || + ((sb->nfreeinode == 0) && (sb->nfreeblock == 0) && + (sb->total_freeblock == 0) && (sb->total_freeinode == 0)) || + (!(buf = scratch_read(fs, sb->volume_size - 1)))) { + DPRINTF("invalid super block.\n"); + return EINVAL; + } + if (buf) + scratch_free(fs, buf); + + return 0; +} + +/* Fill free block to superblock cache. */ +int +v7fs_freeblock_update(struct v7fs_self *fs, v7fs_daddr_t blk) +{ + /* Assume superblock is locked by caller. */ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_freeblock *fb; + void *buf; + int error; + + /* Read next freeblock table from disk. */ + if (!datablock_number_sanity(fs, blk) || !(buf = scratch_read(fs, blk))) + return EIO; + + /* Update in-core superblock freelist. */ + fb = (struct v7fs_freeblock *)buf; + if ((error = v7fs_freeblock_endian_convert(fs, fb))) { + scratch_free(fs, buf); + return error; + } + DPRINTF("freeblock table#%d, nfree=%d\n", blk, fb->nfreeblock); + + memcpy(sb->freeblock, fb->freeblock, sizeof(blk) * fb->nfreeblock); + sb->nfreeblock = fb->nfreeblock; + sb->modified = true; + scratch_free(fs, buf); + + return 0; +} + +int +v7fs_freeblock_endian_convert(struct v7fs_self *fs __unused, + struct v7fs_freeblock *fb __unused) +{ +#ifdef V7FS_EI + int i; + int16_t nfree; + + nfree = V7FS_VAL16(fs, fb->nfreeblock); + if (nfree <= 0 || nfree > V7FS_MAX_FREEBLOCK) { + DPRINTF("invalid freeblock list. %d (max=%d)\n", nfree, + V7FS_MAX_FREEBLOCK); + return ENOSPC; + } + fb->nfreeblock = nfree; + + for (i = 0; i < nfree; i++) { + fb->freeblock[i] = V7FS_VAL32(fs, fb->freeblock[i]); + } +#endif /* V7FS_EI */ + + return 0; +} + +/* Fill free inode to superblock cache. */ +int +v7fs_freeinode_update(struct v7fs_self *fs) +{ + /* Assume superblock is locked by caller. */ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_ino_t *freeinode = sb->freeinode; + size_t i, j, k; + v7fs_ino_t ino; + + /* Loop over all inode list. */ + for (i = V7FS_ILIST_SECTOR, ino = 1/* inode start from 1*/, k = 0; + i < sb->datablock_start_sector; i++) { + struct v7fs_inode_diskimage *di; + void *buf; + if (!(buf = scratch_read(fs, i))) { + DPRINTF("block %zu I/O error.\n", i); + ino += V7FS_INODE_PER_BLOCK; + continue; + } + di = (struct v7fs_inode_diskimage *)buf; + + for (j = 0; + (j < V7FS_INODE_PER_BLOCK) && (k < V7FS_MAX_FREEINODE); + j++, di++, ino++) { + if (v7fs_inode_allocated(di)) + continue; + DPRINTF("free inode%d\n", ino); + freeinode[k++] = ino; + } + scratch_free(fs, buf); + } + sb->nfreeinode = k; + + return 0; +} + +static void +v7fs_superblock_endian_convert(struct v7fs_self *fs __unused, + struct v7fs_superblock *to, struct v7fs_superblock *from) +{ +#ifdef V7FS_EI +#define conv16(m) (to->m = V7FS_VAL16(fs, from->m)) +#define conv32(m) (to->m = V7FS_VAL32(fs, from->m)) + int i; + + conv16(datablock_start_sector); + conv32(volume_size); + conv16(nfreeblock); + v7fs_daddr_t *dfrom = from->freeblock; + v7fs_daddr_t *dto = to->freeblock; + for (i = 0; i < V7FS_MAX_FREEBLOCK; i++, dfrom++, dto++) + *dto = V7FS_VAL32(fs, *dfrom); + + conv16(nfreeinode); + v7fs_ino_t *ifrom = from->freeinode; + v7fs_ino_t *ito = to->freeinode; + for (i = 0; i < V7FS_MAX_FREEINODE; i++, ifrom++, ito++) + *ito = V7FS_VAL16(fs, *ifrom); + + conv32(update_time); + conv32(total_freeblock); + conv16(total_freeinode); +#undef conv16 +#undef conv32 +#else /* V7FS_EI */ + memcpy(to, from , sizeof(*to)); +#endif /* V7FS_EI */ +} diff --git a/sys/fs/v7fs/v7fs_superblock.h b/sys/fs/v7fs/v7fs_superblock.h new file mode 100644 index 000000000..b58716cee --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock.h @@ -0,0 +1,48 @@ +/* $NetBSD: v7fs_superblock.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _V7FS_SUPERBLOCK_H_ +#define _V7FS_SUPERBLOCK_H_ +struct v7fs_self; + +__BEGIN_DECLS +/* core */ +int v7fs_superblock_load(struct v7fs_self *); +int v7fs_superblock_writeback(struct v7fs_self *); +int v7fs_freeblock_update(struct v7fs_self *, v7fs_daddr_t); +int v7fs_freeblock_endian_convert(struct v7fs_self *, struct v7fs_freeblock *); +int v7fs_freeinode_update(struct v7fs_self *); + +/* util. */ +void v7fs_superblock_status(struct v7fs_self *); +void v7fs_superblock_dump(const struct v7fs_self *); +__END_DECLS +#endif /*!_V7FS_SUPERBLOCK_H_ */ diff --git a/sys/fs/v7fs/v7fs_superblock_util.c b/sys/fs/v7fs/v7fs_superblock_util.c new file mode 100644 index 000000000..27417ee35 --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock_util.c @@ -0,0 +1,101 @@ +/* $NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include /* errno */ +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" + +#ifdef V7FS_SUPERBLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#define DPRINTF_(fmt, args...) printf(fmt, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#define DPRINTF_(fmt, args...) ((void)0) +#endif + +void +v7fs_superblock_status(struct v7fs_self *fs) +{ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_stat *stat = &fs->stat; + + stat->total_blocks = sb->volume_size - sb->datablock_start_sector; + stat->total_inode = V7FS_MAX_INODE(sb); + stat->free_inode = sb->total_freeinode; + stat->free_blocks = sb->total_freeblock; + stat->total_files = stat->total_inode - sb->total_freeinode - 1; + + DPRINTF("block %d/%d, inode %d/%d\n", stat->free_blocks, + stat->total_blocks, stat->free_inode, stat->total_inode); +} + +void +v7fs_superblock_dump(const struct v7fs_self *fs) +{ + const struct v7fs_superblock *sb = &fs->superblock; + +#define print(x) printf("%s: %d\n", #x, sb->x) + print(datablock_start_sector); + print(volume_size); + print(nfreeblock); + print(nfreeinode); + print(update_time); + print(lock_freeblock); + print(lock_freeinode); + print(modified); + print(readonly); +#if !defined _KERNEL + time_t t = sb->update_time; + printf("%s", ctime(&t)); +#endif + print(total_freeblock); + print(total_freeinode); +#undef print +} diff --git a/sys/fs/v7fs/v7fs_vfsops.c b/sys/fs/v7fs/v7fs_vfsops.c new file mode 100644 index 000000000..e8e95ee86 --- /dev/null +++ b/sys/fs/v7fs/v7fs_vfsops.c @@ -0,0 +1,617 @@ +/* $NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* v-node */ +#include +#include +/* devsw */ +#include + +#include "v7fs_extern.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_VFSOPS_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(arg...) ((void)0) +#endif + +struct pool v7fs_node_pool; + +static int v7fs_mountfs(struct vnode *, struct mount *, int); +static int v7fs_openfs(struct vnode *, struct mount *, struct lwp *); +static void v7fs_closefs(struct vnode *, struct mount *); +static int is_v7fs_partition(struct vnode *); +static enum vtype v7fs_mode_to_vtype(v7fs_mode_t mode); +int v7fs_vnode_reload(struct mount *, struct vnode *); + +int +v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) +{ + struct lwp *l = curlwp; + struct v7fs_args *args = data; + struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; + struct vnode *devvp = NULL; + int error = 0; + bool update = mp->mnt_flag & MNT_UPDATE; + + DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : ""); + + if (*data_len < sizeof(*args)) + return EINVAL; + + if (mp->mnt_flag & MNT_GETARGS) { + if (!v7fsmount) + return EIO; + args->fspec = NULL; + args->endian = v7fsmount->core->endian; + *data_len = sizeof(*args); + return 0; + } + + DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian); + if (args->fspec == NULL) { + /* nothing to do. */ + return EINVAL; + } + + if (args->fspec != NULL) { + /* Look up the name and verify that it's sane. */ + error = namei_simple_user(args->fspec, + NSM_FOLLOW_NOEMULROOT, &devvp); + if (error != 0) + return (error); + DPRINTF("mount device=%lx\n", (long)devvp->v_rdev); + + if (!update) { + /* + * Be sure this is a valid block device + */ + if (devvp->v_type != VBLK) + error = ENOTBLK; + else if (bdevsw_lookup(devvp->v_rdev) == NULL) + error = ENXIO; + } else { + KDASSERT(v7fsmount); + /* + * Be sure we're still naming the same device + * used for our initial mount + */ + if (devvp != v7fsmount->devvp) { + DPRINTF("devvp %p != %p rootvp=%p\n", devvp, + v7fsmount->devvp, rootvp); + if (rootvp == v7fsmount->devvp) { + vrele(devvp); + devvp = rootvp; + vref(devvp); + } else { + error = EINVAL; + } + } + } + } + + /* + * If mount by non-root, then verify that user has necessary + * permissions on the device. + * + * Permission to update a mount is checked higher, so here we presume + * updating the mount is okay (for example, as far as securelevel goes) + * which leaves us with the normal check. + */ + if (error == 0) { + int accessmode = VREAD; + if (update ? + (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : + (mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, + KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, + KAUTH_ARG(accessmode)); + } + + if (error) { + vrele(devvp); + return error; + } + + if (!update) { + if ((error = v7fs_openfs(devvp, mp, l))) { + vrele(devvp); + return error; + } + + if ((error = v7fs_mountfs(devvp, mp, args->endian))) { + v7fs_closefs(devvp, mp); + VOP_UNLOCK(devvp); + vrele(devvp); + return error; + } + VOP_UNLOCK(devvp); + } else if (mp->mnt_flag & MNT_RDONLY) { + /* XXX: r/w -> read only */ + } + + return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, + mp->mnt_op->vfs_name, mp, l); +} + +static int +is_v7fs_partition(struct vnode *devvp) +{ + struct dkwedge_info dkw; + int error; + + if ((error = getdiskinfo(devvp, &dkw)) != 0) { + DPRINTF("getdiskinfo=%d\n", error); + return error; + } + DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size); + + return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL; +} + +static int +v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l) +{ + kauth_cred_t cred = l->l_cred; + int oflags; + int error; + + /* Flush buffer */ + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0))) + goto unlock_exit; + + /* Open block device */ + oflags = FREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + oflags |= FWRITE; + + if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) { + DPRINTF("VOP_OPEN=%d\n", error); + goto unlock_exit; + } + + return 0; /* lock held */ + +unlock_exit: + VOP_UNLOCK(devvp); + + return error; +} + +static void +v7fs_closefs(struct vnode *devvp, struct mount *mp) +{ + int oflags = FREAD; + + if ((mp->mnt_flag & MNT_RDONLY) == 0) + oflags |= FWRITE; + + VOP_CLOSE(devvp, oflags, NOCRED); +} + +static int +v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian) +{ + struct v7fs_mount *v7fsmount; + int error; + struct v7fs_mount_device mount; + + DPRINTF("%d\n",endian); + + v7fsmount = kmem_zalloc(sizeof(*v7fsmount), KM_SLEEP); + if (v7fsmount == NULL) { + return ENOMEM; + } + v7fsmount->devvp = devvp; + v7fsmount->mountp = mp; + + mount.device.vnode = devvp; + mount.endian = endian; + + if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) { + goto err_exit; + } + struct v7fs_self *fs = v7fsmount->core; + + if ((error = v7fs_superblock_load(fs))) { + v7fs_io_fini(fs); + goto err_exit; + } + + LIST_INIT(&v7fsmount->v7fs_node_head); + + mp->mnt_data = v7fsmount; + mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev; + mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS); + mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; + mp->mnt_stat.f_namemax = V7FS_NAME_MAX; + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_dev_bshift = V7FS_BSHIFT; + mp->mnt_fs_bshift = V7FS_BSHIFT; + + return 0; + +err_exit: + kmem_free(v7fsmount, sizeof(*v7fsmount)); + return error; +} + +int +v7fs_start(struct mount *mp, int flags) +{ + + DPRINTF("\n"); + /* Nothing to do. */ + return 0; +} + +int +v7fs_unmount(struct mount *mp, int mntflags) +{ + struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; + int error; + + DPRINTF("%p\n", v7fsmount); + + if ((error = vflush(mp, NULLVP, + mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0) + return error; + + vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED); + vput(v7fsmount->devvp); + + v7fs_io_fini(v7fsmount->core); + + kmem_free(v7fsmount, sizeof(*v7fsmount)); + mp->mnt_data = NULL; + mp->mnt_flag &= ~MNT_LOCAL; + + return 0; +} + +int +v7fs_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *vp; + int error; + + DPRINTF("\n"); + if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) { + DPRINTF("error=%d\n", error); + return error; + } + *vpp = vp; + DPRINTF("done.\n"); + + return 0; +} + +int +v7fs_statvfs(struct mount *mp, struct statvfs *f) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + + DPRINTF("scratch remain=%d\n", fs->scratch_remain); + + v7fs_superblock_status(fs); + + f->f_bsize = V7FS_BSIZE; + f->f_frsize = V7FS_BSIZE; + f->f_iosize = V7FS_BSIZE; + f->f_blocks = fs->stat.total_blocks; + f->f_bfree = fs->stat.free_blocks; + f->f_bavail = fs->stat.free_blocks; + f->f_bresvd = 0; + f->f_files = fs->stat.total_files; + f->f_ffree = fs->stat.free_inode; + f->f_favail = f->f_ffree; + f->f_fresvd = 0; + copy_statvfs_info(f, mp); + + return 0; +} + +int +v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_node *v7fs_node; + struct v7fs_inode *inode; + struct vnode *v; + int err, error; + int retry_cnt; + + DPRINTF("\n"); + + v7fs_superblock_writeback(fs); + for (retry_cnt = 0; retry_cnt < 2; retry_cnt++) { + error = 0; + + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + inode = &v7fs_node->inode; + if (!v7fs_inode_allocated(inode)) { + continue; + } + v = v7fs_node->vnode; + mutex_enter(v->v_interlock); + mutex_exit(&mntvnode_lock); + err = vget(v, LK_EXCLUSIVE | LK_NOWAIT); + if (err == 0) { + err = VOP_FSYNC(v, cred, FSYNC_WAIT, 0, 0); + vput(v); + } + if (err != 0) + error = err; + mutex_enter(&mntvnode_lock); + } + mutex_exit(&mntvnode_lock); + + if (error == 0) + break; + } + + return error; +} + +static enum vtype +v7fs_mode_to_vtype (v7fs_mode_t mode) +{ + enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK }; + + if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO) + return VFIFO; + + return table[((mode >> 13) & 7) - 1]; +} + +int +v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct vnode *vp; + struct v7fs_node *v7fs_node; + struct v7fs_inode inode; + int error; + + /* Lookup requested i-node */ + if ((error = v7fs_inode_load(fs, &inode, ino))) { + DPRINTF("v7fs_inode_load failed.\n"); + return error; + } + +retry: + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + if (v7fs_node->inode.inode_number == ino) { + vp = v7fs_node->vnode; + mutex_enter(vp->v_interlock); + mutex_exit(&mntvnode_lock); + if (vget(vp, LK_EXCLUSIVE) == 0) { + *vpp = vp; + return 0; + } else { + DPRINTF("retry!\n"); + goto retry; + } + } + } + mutex_exit(&mntvnode_lock); + + /* Allocate v-node. */ + if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) { + DPRINTF("getnewvnode error.\n"); + return error; + } + /* Lock vnode here */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + /* Allocate i-node */ + vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK); + memset(vp->v_data, 0, sizeof(*v7fs_node)); + v7fs_node = vp->v_data; + mutex_enter(&mntvnode_lock); + LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link); + mutex_exit(&mntvnode_lock); + v7fs_node->vnode = vp; + v7fs_node->v7fsmount = v7fsmount; + v7fs_node->inode = inode;/*structure copy */ + v7fs_node->lockf = NULL; /* advlock */ + + genfs_node_init(vp, &v7fs_genfsops); + uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode)); + + if (ino == V7FS_ROOT_INODE) { + vp->v_type = VDIR; + vp->v_vflag |= VV_ROOT; + } else { + vp->v_type = v7fs_mode_to_vtype(inode.mode); + + if (vp->v_type == VBLK || vp->v_type == VCHR) { + dev_t rdev = inode.device; + vp->v_op = v7fs_specop_p; + spec_node_init(vp, rdev); + } else if (vp->v_type == VFIFO) { + vp->v_op = v7fs_fifoop_p; + } + } + + *vpp = vp; + + return 0; +} + + +int +v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp) +{ + + DPRINTF("\n"); + /* notyet */ + return EOPNOTSUPP; +} + +int +v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size) +{ + + DPRINTF("\n"); + /* notyet */ + return EOPNOTSUPP; +} + +void +v7fs_init(void) +{ + + DPRINTF("\n"); + pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0, + "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE); +} + +void +v7fs_reinit(void) +{ + + /* Nothing to do. */ + DPRINTF("\n"); +} + +void +v7fs_done(void) +{ + + DPRINTF("\n"); + pool_destroy(&v7fs_node_pool); +} + +int +v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, + kauth_cred_t cred) +{ + + DPRINTF("\n"); + return 0; +} + +int +v7fs_mountroot(void) +{ + struct mount *mp; + int error; + + DPRINTF(""); + /* On mountroot, devvp (rootdev) is opened by vfs_mountroot */ + if ((error = is_v7fs_partition (rootvp))) + return error; + + if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) { + DPRINTF("mountalloc error=%d\n", error); + vrele(rootvp); + return error; + } + + if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) { + DPRINTF("mountfs error=%d\n", error); + vfs_unbusy(mp, false, NULL); + vfs_destroy(mp); + return error; + } + + mountlist_append(mp); + + vfs_unbusy(mp, false, NULL); + + return 0; +} + +/* Reload disk inode information */ +int +v7fs_vnode_reload(struct mount *mp, struct vnode *vp) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_node *v7fs_node; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + int target_ino = inode->inode_number; + int error = 0; + + DPRINTF("#%d\n", target_ino); + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + inode = &v7fs_node->inode; + if (!v7fs_inode_allocated(inode)) { + continue; + } + if (inode->inode_number == target_ino) { + error = v7fs_inode_load(fs, &v7fs_node->inode, + target_ino); + DPRINTF("sync #%d error=%d\n", target_ino, error); + break; + } + } + mutex_exit(&mntvnode_lock); + + return error; +} diff --git a/sys/fs/v7fs/v7fs_vnops.c b/sys/fs/v7fs/v7fs_vnops.c new file mode 100644 index 000000000..3491f810e --- /dev/null +++ b/sys/fs/v7fs/v7fs_vnops.c @@ -0,0 +1,1328 @@ +/* $NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /*APPEND */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef V7FS_VNOPS_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(arg...) ((void)0) +#endif + +int v7fs_vnode_reload(struct mount *, struct vnode *); + +static v7fs_mode_t vtype_to_v7fs_mode(enum vtype); +static uint8_t v7fs_mode_to_d_type(v7fs_mode_t); + +static v7fs_mode_t +vtype_to_v7fs_mode(enum vtype type) +{ + /* Convert Vnode types to V7FS types (sys/vnode.h)*/ + v7fs_mode_t table[] = { 0, V7FS_IFREG, V7FS_IFDIR, V7FS_IFBLK, + V7FS_IFCHR, V7FSBSD_IFLNK, V7FSBSD_IFSOCK, + V7FSBSD_IFFIFO }; + return table[type]; +} + +static uint8_t +v7fs_mode_to_d_type(v7fs_mode_t mode) +{ + /* Convert V7FS types to dirent d_type (sys/dirent.h)*/ + + return (mode & V7FS_IFMT) >> 12; +} + +int +v7fs_lookup(void *v) +{ + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *dvp = a->a_dvp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_inode *parent = &parent_node->inode; + struct v7fs_self *fs = parent_node->v7fsmount->core;/* my filesystem */ + struct vnode *vpp; + struct componentname *cnp = a->a_cnp; + int nameiop = cnp->cn_nameiop; + const char *name = cnp->cn_nameptr; + int namelen = cnp->cn_namelen; + int flags = cnp->cn_flags; + bool isdotdot = flags & ISDOTDOT; + bool islastcn = flags & ISLASTCN; + v7fs_ino_t ino; + int error; +#ifdef V7FS_VNOPS_DEBUG + const char *opname[] = { "LOOKUP", "CREATE", "DELETE", "RENAME" }; +#endif + DPRINTF("'%s' op=%s flags=%d parent=%d %o %dbyte\n", name, + opname[nameiop], cnp->cn_flags, parent->inode_number, parent->mode, + parent->filesize); + + *a->a_vpp = 0; + + /* Check directory permission for search */ + if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred))) { + DPRINTF("***perm.\n"); + return error; + } + + /* Deny last component write operation on a read-only mount */ + if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) && + (nameiop == DELETE || nameiop == RENAME)) { + DPRINTF("***ROFS.\n"); + return EROFS; + } + + /* "." */ + if (namelen == 1 && name[0] == '.') { + if ((nameiop == RENAME) && islastcn) { + return EISDIR; /* t_vnops rename_dir(3) */ + } + vref(dvp); /* v_usecount++ */ + *a->a_vpp = dvp; + DPRINTF("done.(.)\n"); + return 0; + } + + /* ".." and reguler file. */ + if ((error = v7fs_file_lookup_by_name(fs, parent, name, &ino))) { + /* Not found. Tell this entry be able to allocate. */ + if (((nameiop == CREATE) || (nameiop == RENAME)) && islastcn) { + /* Check directory permission to allocate. */ + if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) { + DPRINTF("access denied. (%s)\n", name); + return error; + } + DPRINTF("EJUSTRETURN op=%d (%s)\n", nameiop, name); + return EJUSTRETURN; + } + DPRINTF("lastcn=%d\n", flags & ISLASTCN); + return error; + } + + if ((nameiop == DELETE) && islastcn) { + if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) { + DPRINTF("access denied. (%s)\n", name); + return error; + } + } + + /* Entry found. Allocate v-node */ + // Check permissions? + vpp = 0; + if (isdotdot) { + VOP_UNLOCK(dvp); /* preserve reference count. (not vput) */ + } + DPRINTF("enter vget\n"); + if ((error = v7fs_vget(dvp->v_mount, ino, &vpp))) { + DPRINTF("***can't get vnode.\n"); + return error; + } + DPRINTF("exit vget\n"); + if (isdotdot) { + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + } + *a->a_vpp = vpp; + DPRINTF("done.(%s)\n", name); + + return 0; +} + +int +v7fs_create(void *v) +{ + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct mount *mp = v7fsmount->mountp; + struct v7fs_fileattr attr; + struct vattr *va = a->a_vap; + kauth_cred_t cr = a->a_cnp->cn_cred; + v7fs_ino_t ino; + int error = 0; + + DPRINTF("%s parent#%d\n", a->a_cnp->cn_nameptr, + parent_node->inode.inode_number); + KDASSERT((va->va_type == VREG) || (va->va_type == VSOCK)); + + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode (va->va_type); + attr.device = 0; + + /* Allocate disk entry. and register its entry to parent directory. */ + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr, &attr, &ino))) { + DPRINTF("v7fs_file_allocate failed.\n"); + goto unlock_exit; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); + + /* Get myself vnode. */ + *a->a_vpp = 0; + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("v7fs_vget failed.\n"); + goto unlock_exit; + } + + /* Scheduling update time. real update by v7fs_update */ + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + DPRINTF("allocated %s->#%d\n", a->a_cnp->cn_nameptr, ino); + +unlock_exit: + /* unlock parent directory */ + vput(a->a_dvp); /* locked at v7fs_lookup(); */ + + return error; +} + +int +v7fs_mknod(void *v) +{ + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct componentname *cnp = a->a_cnp; + kauth_cred_t cr = cnp->cn_cred; + struct vnode *dvp = a->a_dvp; + struct vattr *va = a->a_vap; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct mount *mp = v7fsmount->mountp; + struct v7fs_fileattr attr; + + v7fs_ino_t ino; + int error = 0; + + DPRINTF("%s %06o %lx %d\n", cnp->cn_nameptr, va->va_mode, + (long)va->va_rdev, va->va_type); + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + attr.device = va->va_rdev; + + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + cnp->cn_nameptr, &attr, &ino))) + goto unlock_exit; + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + goto unlock_exit; + } + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + +unlock_exit: + vput(dvp); + + return error; +} + +int +v7fs_open(void *v) +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + } */ *a = v; + + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + + DPRINTF("inode %d\n", inode->inode_number); + /* Append mode file pointer is managed by kernel. */ + if (inode->append_mode && + ((a->a_mode & (FWRITE | O_APPEND)) == FWRITE)) { + DPRINTF("file is already opened by append mode.\n"); + return EPERM; + } + + return 0; +} + +int +v7fs_close(void *v) +{ + struct vop_close_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + int a_fflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; +#ifdef V7FS_VNOPS_DEBUG + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; +#endif + DPRINTF("#%d (i)%dbyte (v)%zubyte\n", inode->inode_number, + v7fs_inode_filesize(inode), vp->v_size); + + /* Update timestamp */ + v7fs_update(vp, 0, 0, UPDATE_WAIT); + + return 0; +} + +static int +v7fs_check_possible(struct vnode *vp, struct v7fs_node *v7node, + mode_t mode) +{ + + if (!(mode & VWRITE)) + return 0; + + switch (vp->v_type) { + default: + /* special file is always writable. */ + return 0; + case VDIR: + case VLNK: + case VREG: + break; + } + + return vp->v_mount->mnt_flag & MNT_RDONLY ? EROFS : 0; +} + +static int +v7fs_check_permitted(struct vnode *vp, struct v7fs_node *v7node, + mode_t mode, kauth_cred_t cred) +{ + + struct v7fs_inode *inode = &v7node->inode; + + return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, + vp->v_type, inode->mode), vp, NULL, genfs_can_access(vp->v_type, + inode->mode, inode->uid, inode->gid, mode, cred)); +} + +int +v7fs_access(void *v) +{ + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct v7fs_node *v7node = vp->v_data; + int error; + + error = v7fs_check_possible(vp, v7node, ap->a_mode); + if (error) + return error; + + error = v7fs_check_permitted(vp, v7node, ap->a_mode, ap->a_cred); + + return error; +} + +int +v7fs_getattr(void *v) +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + struct vattr *vap = ap->a_vap; + + DPRINTF("\n"); + vap->va_type = vp->v_type; + vap->va_mode = inode->mode; + vap->va_nlink = inode->nlink; + vap->va_uid = inode->uid; + vap->va_gid = inode->gid; + vap->va_fsid = v7fsmount->devvp->v_rdev; + vap->va_fileid = inode->inode_number; + vap->va_size = vp->v_size; + vap->va_atime.tv_sec = inode->atime; + vap->va_mtime.tv_sec = inode->mtime; + vap->va_ctime.tv_sec = inode->ctime; + vap->va_birthtime.tv_sec = 0; + vap->va_gen = 1; + vap->va_flags = inode->append_mode ? SF_APPEND : 0; + vap->va_rdev = inode->device; + vap->va_bytes = vap->va_size; /* No sparse support. */ + vap->va_filerev = 0; + vap->va_vaflags = 0; + /* PAGE_SIZE is larger than sizeof(struct dirent). OK. + getcwd_scandir()@vfs_getcwd.c */ + vap->va_blocksize = PAGE_SIZE; + + return 0; +} + +int +v7fs_setattr(void *v) +{ + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct proc *p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct v7fs_inode *inode = &v7node->inode; + kauth_cred_t cred = ap->a_cred; + struct timespec *acc, *mod; + int error = 0; + acc = mod = NULL; + + DPRINTF("\n"); + + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + switch (vp->v_type) { + default: + /* special file is always writable. */ + break; + case VDIR: + case VLNK: + case VREG: + DPRINTF("read-only mount\n"); + return EROFS; + } + } + + if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || + (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || + (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || + ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { + DPRINTF("invalid request\n"); + return EINVAL; + } + /* File pointer mode. */ + if (vap->va_flags != VNOVAL) { + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, + vp, NULL, genfs_can_chflags(cred, vp->v_type, inode->uid, + false)); + if (error) + return error; + inode->append_mode = vap->va_flags & SF_APPEND; + } + + /* File size change. */ + if ((vap->va_size != VNOVAL) && (vp->v_type == VREG)) { + error = v7fs_datablock_size_change(fs, vap->va_size, inode); + if (error == 0) + uvm_vnp_setsize(vp, vap->va_size); + } + uid_t uid = inode->uid; + gid_t gid = inode->gid; + + if (vap->va_uid != (uid_t)VNOVAL) { + uid = vap->va_uid; + error = kauth_authorize_vnode(cred, + KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL, + genfs_can_chown(cred, inode->uid, inode->gid, uid, + gid)); + if (error) + return error; + inode->uid = uid; + } + if (vap->va_gid != (uid_t)VNOVAL) { + gid = vap->va_gid; + error = kauth_authorize_vnode(cred, + KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL, + genfs_can_chown(cred, inode->uid, inode->gid, uid, + gid)); + if (error) + return error; + inode->gid = gid; + } + if (vap->va_mode != (mode_t)VNOVAL) { + mode_t mode = vap->va_mode; + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, + vp, NULL, genfs_can_chmod(vp->v_type, cred, inode->uid, inode->gid, + mode)); + if (error) { + return error; + } + v7fs_inode_chmod(inode, mode); + } + if ((vap->va_atime.tv_sec != VNOVAL) || + (vap->va_mtime.tv_sec != VNOVAL) || + (vap->va_ctime.tv_sec != VNOVAL)) { + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, + NULL, genfs_can_chtimes(vp, vap->va_vaflags, inode->uid, + cred)); + if (error) + return error; + + if (vap->va_atime.tv_sec != VNOVAL) { + acc = &vap->va_atime; + } + if (vap->va_mtime.tv_sec != VNOVAL) { + mod = &vap->va_mtime; + v7node->update_mtime = true; + } + if (vap->va_ctime.tv_sec != VNOVAL) { + v7node->update_ctime = true; + } + } + + v7node->update_atime = true; + v7fs_update(vp, acc, mod, 0); + + return error; +} + +int +v7fs_read(void *v) +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct uio *uio = a->a_uio; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + vsize_t sz, filesz = v7fs_inode_filesize(inode); + const int advice = IO_ADV_DECODE(a->a_ioflag); + int error = 0; + + DPRINTF("type=%d inode=%d\n", vp->v_type, v7node->inode.inode_number); + + while (uio->uio_resid > 0) { + if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0) + break; + + error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, UBC_READ | + UBC_PARTIALOK | UBC_UNMAP_FLAG(v)); + if (error) { + break; + } + DPRINTF("read %zubyte\n", sz); + } + v7node->update_atime = true; + + return error; +} + +int +v7fs_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct uio *uio = a->a_uio; + int advice = IO_ADV_DECODE(a->a_ioflag); + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + vsize_t sz; + int error = 0; + + if (uio->uio_resid == 0) + return 0; + + sz = v7fs_inode_filesize(inode); + DPRINTF("(i)%ld (v)%zu ofs=%zu + res=%zu = %zu\n", sz, vp->v_size, + uio->uio_offset, uio->uio_resid, uio->uio_offset + uio->uio_resid); + + /* Append mode file offset is managed by kernel. */ + if (a->a_ioflag & IO_APPEND) + uio->uio_offset = sz; + + /* If write region is over filesize, expand. */ + size_t newsize= uio->uio_offset + uio->uio_resid; + ssize_t expand = newsize - sz; + if (expand > 0) { + if ((error = v7fs_datablock_expand(fs, inode, expand))) + return error; + uvm_vnp_setsize(vp, newsize); + } + + while (uio->uio_resid > 0) { + sz = uio->uio_resid; + if ((error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, + UBC_WRITE | UBC_UNMAP_FLAG(v)))) + break; + DPRINTF("write %zubyte\n", sz); + } + v7node->update_mtime = true; + + return error; +} + +int +v7fs_fsync(void *v) +{ + struct vop_fsync_args /* { + struct vnode *a_vp; + kauth_cred_t a_cred; + int a_flags; + off_t offlo; + off_t offhi; + } */ *a = v; + struct vnode *vp = a->a_vp; + int error, wait; + + DPRINTF("%p\n", a->a_vp); + if (a->a_flags & FSYNC_CACHE) { + return EOPNOTSUPP; + } + + wait = (a->a_flags & FSYNC_WAIT); + error = vflushbuf(vp, a->a_flags); + + if (error == 0 && (a->a_flags & FSYNC_DATAONLY) == 0) + error = v7fs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0); + + return error; +} + +int +v7fs_remove(void *v) +{ + struct vop_remove_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_dvp; + struct vnode * a_vp; + struct componentname * a_cnp; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct vnode *vp = a->a_vp; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + struct vnode *dvp = a->a_dvp; + struct v7fs_self *fs = v7fsmount->core; + bool remove; + int error = 0; + + DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); + + if (vp->v_type == VDIR) { + error = EPERM; + goto out; + } + + remove = v7fs_inode_nlink(inode) == 1; + if (remove) + uvm_vnp_setsize(vp, 0); + + if ((error = v7fs_file_deallocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr))) { + DPRINTF("v7fs_file_delete failed.\n"); + goto out; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + /* This inode is no longer used. -> v7fs_inactive */ + if (remove) + memset(inode, 0, sizeof(*inode)); + +out: + if (dvp == vp) + vrele(vp); /* v_usecount-- of unlocked vp */ + else + vput(vp); /* unlock vp and then v_usecount-- */ + vput(dvp); + + return error; +} + +int +v7fs_link(void *v) +{ + struct vop_link_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *dvp = a->a_dvp; + struct vnode *vp = a->a_vp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_node *node = vp->v_data; + struct v7fs_inode *parent = &parent_node->inode; + struct v7fs_inode *p = &node->inode; + struct v7fs_self *fs = node->v7fsmount->core; + struct componentname *cnp = a->a_cnp; + int error = 0; + + DPRINTF("%p\n", vp); + /* Lock soruce file */ + if ((error = vn_lock(vp, LK_EXCLUSIVE))) { + DPRINTF("lock failed. %p\n", vp); + VOP_ABORTOP(dvp, cnp); + goto unlock; + } + error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr); + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + VOP_UNLOCK(vp); +unlock: + vput(dvp); + + return error; +} + +int +v7fs_rename(void *v) +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; from parent-directory + struct vnode *a_fvp; from file + struct componentname *a_fcnp; + struct vnode *a_tdvp; to parent-directory + struct vnode *a_tvp; to file + struct componentname *a_tcnp; + } */ *a = v; + struct vnode *fvp = a->a_fvp; + struct vnode *tvp = a->a_tvp; + struct vnode *fdvp = a->a_fdvp; + struct vnode *tdvp = a->a_tdvp; + struct v7fs_node *parent_from = fdvp->v_data; + struct v7fs_node *parent_to = tdvp->v_data; + struct v7fs_node *v7node = fvp->v_data; + struct v7fs_self *fs = v7node->v7fsmount->core; + const char *from_name = a->a_fcnp->cn_nameptr; + const char *to_name = a->a_tcnp->cn_nameptr; + int error; + + DPRINTF("%s->%s %p %p\n", from_name, to_name, fvp, tvp); + + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + DPRINTF("cross-device link\n"); + goto out; + } + // XXXsource file lock? + error = v7fs_file_rename(fs, &parent_from->inode, from_name, + &parent_to->inode, to_name); + /* 'to file' inode may be changed. (hard-linked and it is cached.) + t_vnops rename_reg_nodir */ + if (tvp) { + v7fs_vnode_reload(parent_from->v7fsmount->mountp, tvp); + } + /* Sync dirent size change. */ + uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode)); + uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode)); +out: + if (tvp) + vput(tvp); /* locked on entry */ + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + vrele(fdvp); + vrele(fvp); + + return error; +} + +int +v7fs_mkdir(void *v) +{ + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct componentname *cnp = a->a_cnp; + kauth_cred_t cr = cnp->cn_cred; + struct vnode *dvp = a->a_dvp; + struct vattr *va = a->a_vap; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_fileattr attr; + struct mount *mp = v7fsmount->mountp; + v7fs_ino_t ino; + int error = 0; + + DPRINTF("\n"); + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + cnp->cn_nameptr, &attr, &ino))) + goto unlock_exit; + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + } + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + +unlock_exit: + vput(dvp); + + return error; +} + +int +v7fs_rmdir(void *v) +{ + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct vnode *dvp = a->a_dvp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + struct v7fs_self *fs = v7fsmount->core; + int error = 0; + + DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); + + KDASSERT(vp->v_type == VDIR); + + if ((error = v7fs_file_deallocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr))) { + DPRINTF("v7fs_directory_deallocate failed.\n"); + goto out; + } + uvm_vnp_setsize(vp, 0); + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + /* This inode is no longer used. -> v7fs_inactive */ + memset(inode, 0, sizeof(*inode)); +out: + vput(vp); + vput(dvp); + + return error; +} + +struct v7fs_readdir_arg { + struct dirent *dp; + struct uio *uio; + int start; + int end; + int cnt; +}; +static int readdir_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); + +int +readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx; + struct v7fs_dirent *dir; + struct dirent *dp = p->dp; + struct v7fs_inode inode; + char filename[V7FS_NAME_MAX + 1]; + int i, n; + int error = 0; + void *buf; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + dir = (struct v7fs_dirent *)buf; + + n = sz / sizeof(*dir); + + for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) { + if (p->cnt < p->start) + continue; + + if ((error = v7fs_inode_load(fs, &inode, dir->inode_number))) + break; + + v7fs_dirent_filename(filename, dir->name); + + DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename, + v7fs_inode_isdir(&inode) ? "DIR" : "FILE"); + memset(dp, 0, sizeof(*dp)); + dp->d_fileno = dir->inode_number; + dp->d_type = v7fs_mode_to_d_type(inode.mode); + dp->d_namlen = strlen(filename); + strcpy(dp->d_name, filename); + dp->d_reclen = sizeof(*dp); + if ((error = uiomove(dp, dp->d_reclen, p->uio))) { + DPRINTF("uiomove failed.\n"); + break; + } + } + scratch_free(fs, buf); + + if (p->cnt == p->end) + return V7FS_ITERATOR_BREAK; + + return error; +} + +int +v7fs_readdir(void *v) +{ + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + int *a_eofflag; + off_t **a_cookies; + int *a_ncookies; + } */ *a = v; + struct uio *uio = a->a_uio; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct dirent *dp; + int error; + + DPRINTF("offset=%zu residue=%zu\n", uio->uio_offset, uio->uio_resid); + + KDASSERT(vp->v_type == VDIR); + KDASSERT(uio->uio_offset >= 0); + KDASSERT(v7fs_inode_isdir(inode)); + + struct v7fs_readdir_arg arg; + arg.start = uio->uio_offset / sizeof(*dp); + arg.end = arg.start + uio->uio_resid / sizeof(*dp); + if (arg.start == arg.end) {/* user buffer has not enuf space. */ + DPRINTF("uio buffer too small\n"); + return ENOMEM; + } + dp = kmem_zalloc(sizeof(*dp), KM_SLEEP); + arg.cnt = 0; + arg.dp = dp; + arg.uio = uio; + + *a->a_eofflag = false; + error = v7fs_datablock_foreach(fs, inode, readdir_subr, &arg); + if (error == V7FS_ITERATOR_END) { + *a->a_eofflag = true; + } + if (error < 0) + error = 0; + + kmem_free(dp, sizeof(*dp)); + + return error; +} + +int +v7fs_inactive(void *v) +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + bool *a_recycle; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + + DPRINTF("%p #%d\n", vp, inode->inode_number); + if (v7fs_inode_allocated(inode)) { + v7fs_update(vp, 0, 0, UPDATE_WAIT); + *a->a_recycle = false; + } else { + *a->a_recycle = true; + } + + VOP_UNLOCK(vp); + + return 0; +} + +int +v7fs_reclaim(void *v) +{ + /*This vnode is no longer referenced by kernel. */ + extern struct pool v7fs_node_pool; + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + + DPRINTF("%p #%d\n", vp, v7node->inode.inode_number); + mutex_enter(&mntvnode_lock); + LIST_REMOVE(v7node, link); + mutex_exit(&mntvnode_lock); + genfs_node_destroy(vp); + pool_put(&v7fs_node_pool, v7node); + vp->v_data = NULL; + + return 0; +} + +int +v7fs_bmap(void *v) +{ + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct v7fs_inode *inode = &v7node->inode; + int error = 0; + + DPRINTF("inode=%d offset=%zu %p\n", inode->inode_number, a->a_bn, vp); + DPRINTF("filesize: %d\n", inode->filesize); + if (!a->a_bnp) + return 0; + + v7fs_daddr_t blk; + if (!(blk = v7fs_datablock_last(fs, inode, + (a->a_bn + 1) << V7FS_BSHIFT))) { + /* +1 converts block # to file offset. */ + return ENOSPC; + } + + *a->a_bnp = blk; + + if (a->a_vpp) + *a->a_vpp = v7fsmount->devvp; + if (a->a_runp) + *a->a_runp = 0; /*XXX TODO */ + + DPRINTF("%d %zu->%zu status=%d\n", inode->inode_number, a->a_bn, + *a->a_bnp, error); + + return error; +} + +int +v7fs_strategy(void *v) +{ + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *a = v; + struct buf *b = a->a_bp; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + int error; + + DPRINTF("%p\n", vp); + KDASSERT(vp->v_type == VREG); + if (b->b_blkno == b->b_lblkno) { + error = VOP_BMAP(vp, b->b_lblkno, NULL, &b->b_blkno, NULL); + if (error) { + b->b_error = error; + biodone(b); + return error; + } + if ((long)b->b_blkno == -1) + clrbuf(b); + } + if ((long)b->b_blkno == -1) { + biodone(b); + return 0; + } + + return VOP_STRATEGY(v7fsmount->devvp, b); +} + +int +v7fs_print(void *v) +{ + struct vop_print_args /* { + struct vnode *a_vp; + } */ *a = v; + struct v7fs_node *v7node = a->a_vp->v_data; + + v7fs_inode_dump(&v7node->inode); + + return 0; +} + +int +v7fs_advlock(void *v) +{ + struct vop_advlock_args /* { + struct vnode *a_vp; + void *a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *a = v; + struct v7fs_node *v7node = a->a_vp->v_data; + + DPRINTF("op=%d\n", a->a_op); + + return lf_advlock(a, &v7node->lockf, + v7fs_inode_filesize(&v7node->inode)); +} + +int +v7fs_pathconf(void *v) +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *a = v; + int err = 0; + + DPRINTF("%p\n", a->a_vp); + + switch (a->a_name) { + case _PC_LINK_MAX: + *a->a_retval = V7FS_LINK_MAX; + break; + case _PC_NAME_MAX: + *a->a_retval = V7FS_NAME_MAX; + break; + case _PC_PATH_MAX: + *a->a_retval = V7FS_PATH_MAX; + break; + case _PC_CHOWN_RESTRICTED: + *a->a_retval = 1; + break; + case _PC_NO_TRUNC: + *a->a_retval = 0; + break; + case _PC_SYNC_IO: + *a->a_retval = 1; + break; + case _PC_FILESIZEBITS: + *a->a_retval = 30; /* ~1G */ + break; + case _PC_SYMLINK_MAX: + *a->a_retval = V7FSBSD_MAXSYMLINKLEN; + break; + case _PC_2_SYMLINKS: + *a->a_retval = 1; + break; + default: + err = EINVAL; + break; + } + + return err; +} + +int +v7fs_update(struct vnode *vp, const struct timespec *acc, + const struct timespec *mod, int flags) +{ + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + bool update = false; + + DPRINTF("%p %zu %d\n", vp, vp->v_size, v7fs_inode_filesize(inode)); + KDASSERT(vp->v_size == v7fs_inode_filesize(inode)); + + if (v7node->update_atime) { + inode->atime = acc ? acc->tv_sec : time_second; + v7node->update_atime = false; + update = true; + } + if (v7node->update_ctime) { + inode->ctime = time_second; + v7node->update_ctime = false; + update = true; + } + if (v7node->update_mtime) { + inode->mtime = mod ? mod->tv_sec : time_second; + v7node->update_mtime = false; + update = true; + } + + if (update) + v7fs_inode_writeback(fs, inode); + + return 0; +} + +int +v7fs_symlink(void *v) +{ + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct vattr *va = a->a_vap; + kauth_cred_t cr = a->a_cnp->cn_cred; + struct componentname *cnp = a->a_cnp; + struct v7fs_fileattr attr; + v7fs_ino_t ino; + const char *from = a->a_target; + const char *to = cnp->cn_nameptr; + size_t len = strlen(from) + 1; + int error = 0; + + if (len > V7FS_BSIZE) { /* limited to 512byte pathname */ + DPRINTF("too long pathname."); + return ENAMETOOLONG; + } + + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + + if ((error = v7fs_file_allocate + (fs, &parent_node->inode, to, &attr, &ino))) { + goto unlock_exit; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); + + /* Get myself vnode. */ + if ((error = v7fs_vget(v7fsmount->mountp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + } + + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + struct v7fs_inode *p = &newnode->inode; + v7fs_file_symlink(fs, p, from); + uvm_vnp_setsize(*a->a_vpp, v7fs_inode_filesize(p)); + + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; +unlock_exit: + /* unlock parent directory */ + vput(a->a_dvp); + + return error; +} + +int +v7fs_readlink(void *v) +{ + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + } */ *a = v; + struct uio *uio = a->a_uio; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + int error = 0; + + KDASSERT(vp->v_type == VLNK); + KDASSERT(uio->uio_offset >= 0); + KDASSERT(v7fs_inode_islnk(inode)); + + v7fs_daddr_t blk = inode->addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + error = EIO; + goto error_exit; + } + + if ((error = uiomove(buf, strlen(buf), uio))) { + DPRINTF("uiomove failed.\n"); + } + scratch_free(fs, buf); + +error_exit: + return error; +} diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index bd000bb5c..a0a8d69a4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -15,7 +15,7 @@ SUBDIR= \ \ \ link \ - \ + makefs \ mtree \ \ \ diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile new file mode 100644 index 000000000..7522fc979 --- /dev/null +++ b/usr.sbin/makefs/Makefile @@ -0,0 +1,37 @@ +# $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $ +# + +WARNS?= 5 + +.include + +PROG= makefs +SRCS= cd9660.c chfs.c ffs.c v7fs.c msdos.c udf.c\ + getid.c \ + makefs.c misc.c \ + pack_dev.c \ + spec.c \ + walk.c +MAN= makefs.8 + +MKNODSRC= ${NETBSDSRCDIR}/sbin/mknod +MTREESRC= ${NETBSDSRCDIR}/usr.sbin/mtree + +CPPFLAGS+= -I${.CURDIR} -I${MKNODSRC} -I${MTREESRC} -DMAKEFS +#CPPFLAGS+= -DMSDOSFS_DEBUG +.PATH: ${MKNODSRC} ${MTREESRC} + +.include "${.CURDIR}/cd9660/Makefile.inc" +.include "${.CURDIR}/chfs/Makefile.inc" +.include "${.CURDIR}/ffs/Makefile.inc" +.include "${.CURDIR}/v7fs/Makefile.inc" +.include "${.CURDIR}/msdos/Makefile.inc" +.include "${.CURDIR}/udf/Makefile.inc" + +.if !defined(HOSTPROG) +DPADD+= ${LIBUTIL} +LDADD+= -lutil +.endif + +.include +# DO NOT DELETE diff --git a/usr.sbin/makefs/README b/usr.sbin/makefs/README new file mode 100644 index 000000000..7779a06e3 --- /dev/null +++ b/usr.sbin/makefs/README @@ -0,0 +1,129 @@ +$NetBSD: README,v 1.5 2011/07/18 08:58:38 uch 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: + ffs BSD fast file system + cd9660 ISO 9660 file system + v7fs 7th edition(V7) file system + +Support for the following file systems maybe be added in the future + ext2fs Linux EXT2 file system + fat 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) + - mtree(8) specification file containing permissions and ownership + to use in image, overridding the settings in the directory tree + - 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. + - (optionally) Change the permissions in the tree according to + the mtree(8) specfile + - 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 it's 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/TODO b/usr.sbin/makefs/TODO new file mode 100644 index 000000000..dfd306a03 --- /dev/null +++ b/usr.sbin/makefs/TODO @@ -0,0 +1,41 @@ +$NetBSD: TODO,v 1.7 2007/12/10 23:54:35 dyoung Exp $ + +todo +---- + +- read files from multiple directories with or without root + specification, e.g., makefs -t cd9660 output.iso dir1 root2=dir2 + dir3 root4=dir4 + +- display block numbers for a given file (e.g, /boot) + +- finish makefs.8 + +- testing + +- even more testing + +- add support for converting a tar file (instead of a directory tree); + suggested by kpneal@pobox.com + + +outstanding bugs +---------------- + +- size estimation is still out (need to take into account indirect blocks!) + +- parameter checking when density is rather high or low. + +- filling up a file system (running out of inodes or whatever) + doesn't do the right thing. + + +discuss +------- + +- consider replacing ffs_balloc() et al with own code that doesn't + need hacked-up buf.c code + +- whacking on newfs/mkfs.c to allow .PATH-ing directly into makefs(8). + this would involve passing all of mkfs()'s parameters in a single + struct rather than a lot of global vars, etc. diff --git a/usr.sbin/makefs/cd9660.c b/usr.sbin/makefs/cd9660.c new file mode 100644 index 000000000..c449e0ca4 --- /dev/null +++ b/usr.sbin/makefs/cd9660.c @@ -0,0 +1,2148 @@ +/* $NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb 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. + * + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#include +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include + +#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 *, int, + 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, int length, + char testmode, char * dest) +{ + int len, 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("cd9660_parse_opts: got `%s'\n", 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\n"); + 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("cd9660_makefs: ISO level is %i\n", + diskStructure->isoLevel); + if (diskStructure->isoLevel < 2 && + diskStructure->allow_multidot) + errx(1, "allow-multidot requires iso level of 2\n"); + + 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("cd9660_makefs: image %s directory %s root %p\n", + 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(1, "cd9660_makefs: converted directory is empty. " + "Tree conversion failed\n"); + } else if (error != 0) { + errx(1, "cd9660_makefs: tree conversion failed\n"); + } else { + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: tree converted\n"); + } + + /* 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("cd9660_makefs: done converting tree\n"); + + /* 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(1, "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("cd9660_makefs: Path table conversion complete. " + "Each table is %i bytes, or %" PRIu64 " sectors.\n", + 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("cd9660_makefs: Sectors 0-15 reserved\n"); + printf("cd9660_makefs: Primary path tables starts in sector %" + PRId64 "\n", diskStructure->primaryLittleEndianTableSector); + printf("cd9660_makefs: File data starts in sector %" + PRId64 "\n", diskStructure->dataFirstSector); + printf("cd9660_makefs: Total sectors: %" + PRId64 "\n", 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("cd9660_makefs: done\n"); +} + +/* Generic function pointer - implement later */ +typedef int (*cd9660node_func)(cd9660node *); + +static void +cd9660_finalize_PVD(iso9660_disk *diskStructure) +{ + time_t tim; + + /* 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 */ + time(&tim); + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.creation_date, + tim); + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.modification_date, + tim); + + /* + cd9660_set_date(diskStructure->primaryDescriptor.expiration_date, now); + */ + memset(diskStructure->primaryDescriptor.expiration_date, '0' ,16); + diskStructure->primaryDescriptor.expiration_date[16] = 0; + + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.effective_date, + tim); +} + +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 tim; + 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 */ + time(&tim); + + cd9660_time_915(newnode->isoDirRecord->date, tim); + + 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("cd9660_translate_node: NULL node passed, " + "returning\n"); + 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, 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 + __USE(dot); + __USE(semi); + __USE(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("cd9660_recurse_on_collision: count is %i \n", 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; + } + + /* 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\n", __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(1, "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 000000000..ef9f44f34 --- /dev/null +++ b/usr.sbin/makefs/cd9660.h @@ -0,0 +1,357 @@ +/* $NetBSD: cd9660.h,v 1.20 2013/01/29 15:52:25 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 + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 *, int); + +/* 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/Makefile.inc b/usr.sbin/makefs/cd9660/Makefile.inc new file mode 100644 index 000000000..bffba6601 --- /dev/null +++ b/usr.sbin/makefs/cd9660/Makefile.inc @@ -0,0 +1,15 @@ +# $NetBSD: Makefile.inc,v 1.3 2012/08/10 12:10:29 joerg Exp $ +# + +.PATH: ${.CURDIR}/cd9660 ${NETBSDSRCDIR}/sys/fs/cd9660 + +CPPFLAGS+=-I${NETBSDSRCDIR}/sys/fs/cd9660 + +SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c +SRCS+= cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c + +.if !defined(HOSTPROGNAME) +.for f in cd9660_debug cd9660_write +COPTS.${f}.c+= -Wno-pointer-sign +.endfor +.endif diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.c b/usr.sbin/makefs/cd9660/cd9660_archimedes.c new file mode 100644 index 000000000..45bf2ace1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.c @@ -0,0 +1,130 @@ +/* $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). + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include + +#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 000000000..61338d269 --- /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 000000000..ae1e954c8 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_conversion.c @@ -0,0 +1,203 @@ +/* $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" + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $"); +#endif /* !__lint */ + + +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 000000000..33e2b3e01 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_debug.c @@ -0,0 +1,498 @@ +/* $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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $"); +#endif /* !__lint */ + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#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) +{ +#if !HAVE_NBTOOL_CONFIG_H + 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); +#else + printf("Sorry, debugging is not supported in host-tools mode.\n"); +#endif +} + +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("\n" ,num); + printf("%i\n", pttemp->length[0]); + printf("%i\n", + pttemp->extended_attribute_length[0]); + printf("%i\n", + debug_get_encoded_number(pttemp->parent_number,mode)); + debug_dump_to_xml_padded_hex_output("name", + pttemp->name, pttemp->length[0]); + printf("\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("\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("\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("\n",element); + + printf("<%s:hex>",element); + for (i = 0; i < len; i++) { + t = (unsigned char)buf[i]; + printf(" %x",t); + } + printf("\n",element); +} + +int +debug_get_encoded_number(unsigned char* buf, int mode) +{ +#if !HAVE_NBTOOL_CONFIG_H + 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); + } +#endif + return 0; +} + +void +debug_dump_integer(const char *element, char* buf, int mode) +{ + printf("<%s>%i\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("\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("\n"); +} + + +void +debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector) +{ + printf("\n", sector); + printf(""); + 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("\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("\n"); +} + diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c new file mode 100644 index 000000000..dc93425a0 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.c @@ -0,0 +1,698 @@ +/* $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 +#include + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $"); +#endif /* !__lint */ + +#ifdef DEBUG +#define ELTORITO_DPRINTF(__x) printf __x +#else +#define ELTORITO_DPRINTF(__x) +#endif + +#include + +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 000000000..266148395 --- /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 000000000..fa822746d --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_strings.c @@ -0,0 +1,127 @@ +/* $NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#include +#endif + +#include +#include +#include + +#include "makefs.h" +#include "cd9660.h" + +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 christos Exp $"); +#endif /* !__lint */ + + +void +cd9660_uppercase_characters(char *str, int len) +{ + int 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 000000000..edb4ba4d1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_write.c @@ -0,0 +1,509 @@ +/* $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 +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $"); +#endif /* !__lint */ + +#include + +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 000000000..9189be049 --- /dev/null +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c @@ -0,0 +1,838 @@ +/* $NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud 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 +#include +#include + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $"); +#endif /* !__lint */ + +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_dev) > 32) + cd9660_bothendian_dword((uint64_t)fnode->inode->st.st_dev >> 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_dev & 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 000000000..1f54ae840 --- /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 +#include "cd9660.h" +#include + +#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/chfs.c b/usr.sbin/makefs/chfs.c new file mode 100644 index 000000000..de14246c0 --- /dev/null +++ b/usr.sbin/makefs/chfs.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "chfs_makefs.h" + +#include "chfs/chfs_mkfs.h" + +static void chfs_validate(const char *, fsnode *, fsinfo_t *); +static int chfs_create_image(const char *, fsinfo_t *); +static int chfs_populate_dir(const char *, fsnode *, fsnode *, fsinfo_t *); + + +void +chfs_prep_opts(fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts = ecalloc(1, sizeof(*chfs_opts)); + + const option_t chfs_options[] = { + { 'p', "pagesize", &chfs_opts->pagesize, OPT_INT32, + 1, INT_MAX, "page size" }, + { 'e', "eraseblock", &chfs_opts->eraseblock, OPT_INT32, + 1, INT_MAX, "eraseblock size" }, + { 'm', "mediatype", &chfs_opts->mediatype, OPT_INT32, + 0, 1, "type of the media, 0 (nor) or 1 (nand)" }, + { .name = NULL } + }; + + chfs_opts->pagesize = -1; + chfs_opts->eraseblock = -1; + chfs_opts->mediatype = -1; + + fsopts->size = 0; + fsopts->fs_specific = chfs_opts; + fsopts->fs_options = copy_opts(chfs_options); +} + +void +chfs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +chfs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + + assert(option != NULL); + assert(fsopts != NULL); + + return set_option(fsopts->fs_options, option, NULL, 0) != -1; +} + +void +chfs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + TIMER_START(start); + chfs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "chfs_validate"); + + printf("Creating `%s'\n", image); + TIMER_START(start); + if (chfs_create_image(image, fsopts) == -1) { + errx(EXIT_FAILURE, "Image file `%s' not created", image); + } + TIMER_RESULTS(start, "chfs_create_image"); + + fsopts->curinode = CHFS_ROOTINO; + root->inode->ino = CHFS_ROOTINO; + + printf("Populating `%s'\n", image); + TIMER_START(start); + write_eb_header(fsopts); + if (!chfs_populate_dir(dir, root, root, fsopts)) { + errx(EXIT_FAILURE, "Image file `%s' not populated", image); + } + TIMER_RESULTS(start, "chfs_populate_dir"); + + padblock(fsopts); + + if (close(fsopts->fd) == -1) { + err(EXIT_FAILURE, "Closing `%s'", image); + } + fsopts->fd = -1; + + printf("Image `%s' complete\n", image); +} + +static void +chfs_validate(const char* dir, fsnode *root, fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts; + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + chfs_opts = fsopts->fs_specific; + + if (chfs_opts->pagesize == -1) { + chfs_opts->pagesize = DEFAULT_PAGESIZE; + } + if (chfs_opts->eraseblock == -1) { + chfs_opts->eraseblock = DEFAULT_ERASEBLOCK; + } + if (chfs_opts->mediatype == -1) { + chfs_opts->mediatype = DEFAULT_MEDIATYPE; + } +} + +static int +chfs_create_image(const char *image, fsinfo_t *fsopts) +{ + assert(image != NULL); + assert(fsopts != NULL); + + if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + warn("Can't open `%s' for writing", image); + return -1; + } + + return fsopts->fd; +} + +static int +chfs_populate_dir(const char *dir, fsnode *root, fsnode *parent, + fsinfo_t *fsopts) +{ + fsnode *cur; + char path[MAXPATHLEN + 1]; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + for (cur = root->next; cur != NULL; cur = cur->next) { + 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; + + write_vnode(fsopts, cur); + write_dirent(fsopts, cur); + if (!S_ISDIR(cur->type & S_IFMT)) { + write_file(fsopts, cur, 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(EXIT_FAILURE, "Pathname too long"); + } + if (!chfs_populate_dir(path, cur->child, cur, fsopts)) { + return 0; + } + } + + return 1; +} + diff --git a/usr.sbin/makefs/chfs/Makefile.inc b/usr.sbin/makefs/chfs/Makefile.inc new file mode 100644 index 000000000..254c4a3ab --- /dev/null +++ b/usr.sbin/makefs/chfs/Makefile.inc @@ -0,0 +1,11 @@ +CHFS= ${NETBSDSRCDIR}/sys/ufs/chfs + +.PATH: ${.CURDIR}/chfs ${NETBSDSRCDIR}/sys/ufs/chfs + +CPPFLAGS+= -I${CHFS} + +SRCS+= chfs_mkfs.c +.if !defined(HOSTPROG) +LDADD+= -lz +DPADD+= ${LIBZ} +.endif diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.c b/usr.sbin/makefs/chfs/chfs_mkfs.c new file mode 100644 index 000000000..4c96f66b8 --- /dev/null +++ b/usr.sbin/makefs/chfs/chfs_mkfs.c @@ -0,0 +1,307 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__minix) +#include +#endif + +#include "makefs.h" +#include "chfs_makefs.h" + +#include "media.h" +#include "ebh.h" + +#include "chfs/chfs_mkfs.h" + +static uint32_t img_ofs = 0; +static uint64_t version = 0; +static uint64_t max_serial = 0; +static int lebnumber = 0; + +static const unsigned char ffbuf[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static void +buf_write(fsinfo_t *fsopts, const void *buf, size_t len) +{ + ssize_t retval; + const char *charbuf = buf; + + while (len > 0) { + retval = write(fsopts->fd, charbuf, len); + + if (retval == -1) { + err(EXIT_FAILURE, "ERROR while writing"); + } + + len -= retval; + charbuf += retval; + img_ofs += retval; + } +} + +void +padblock(fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts = fsopts->fs_specific; + while (img_ofs % chfs_opts->eraseblock) { + buf_write(fsopts, ffbuf, MIN(sizeof(ffbuf), + chfs_opts->eraseblock - (img_ofs % chfs_opts->eraseblock))); + } +} + +static void +padword(fsinfo_t *fsopts) +{ + if (img_ofs % 4) { + buf_write(fsopts, ffbuf, 4 - img_ofs % 4); + } +} + +static void +pad_block_if_less_than(fsinfo_t *fsopts, int req) +{ + chfs_opt_t *chfs_opts = fsopts->fs_specific; + if ((img_ofs % chfs_opts->eraseblock) + req > + (uint32_t)chfs_opts->eraseblock) { + padblock(fsopts); + write_eb_header(fsopts); + } +} + +void +write_eb_header(fsinfo_t *fsopts) +{ + chfs_opt_t *opts; + struct chfs_eb_hdr ebhdr; + char *buf; + + opts = fsopts->fs_specific; + +#define MINSIZE MAX(MAX(CHFS_EB_EC_HDR_SIZE, CHFS_EB_HDR_NOR_SIZE), \ + CHFS_EB_HDR_NAND_SIZE) + if ((uint32_t)opts->pagesize < MINSIZE) + errx(EXIT_FAILURE, "pagesize cannot be less than %zu", MINSIZE); + buf = emalloc(opts->pagesize); + memset(buf, 0xFF, opts->pagesize); + + ebhdr.ec_hdr.magic = htole32(CHFS_MAGIC_BITMASK); + ebhdr.ec_hdr.erase_cnt = htole32(1); + ebhdr.ec_hdr.crc_ec = htole32(crc32(0, + (uint8_t *)&ebhdr.ec_hdr + 8, 4)); + + memcpy(buf, &ebhdr.ec_hdr, CHFS_EB_EC_HDR_SIZE); + + buf_write(fsopts, buf, opts->pagesize); + + memset(buf, 0xFF, opts->pagesize); + + if (opts->mediatype == TYPE_NAND) { + ebhdr.u.nand_hdr.lid = htole32(lebnumber++); + ebhdr.u.nand_hdr.serial = htole64(++(max_serial)); + ebhdr.u.nand_hdr.crc = htole32(crc32(0, + (uint8_t *)&ebhdr.u.nand_hdr + 4, + CHFS_EB_HDR_NAND_SIZE - 4)); + memcpy(buf, &ebhdr.u.nand_hdr, CHFS_EB_HDR_NAND_SIZE); + } else { + ebhdr.u.nor_hdr.lid = htole32(lebnumber++); + ebhdr.u.nor_hdr.crc = htole32(crc32(0, + (uint8_t *)&ebhdr.u.nor_hdr + 4, + CHFS_EB_HDR_NOR_SIZE - 4)); + memcpy(buf, &ebhdr.u.nor_hdr, CHFS_EB_HDR_NOR_SIZE); + } + + buf_write(fsopts, buf, opts->pagesize); + free(buf); +} + +void +write_vnode(fsinfo_t *fsopts, fsnode *node) +{ + struct chfs_flash_vnode fvnode; + memset(&fvnode, 0, sizeof(fvnode)); + + fvnode.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fvnode.type = htole16(CHFS_NODETYPE_VNODE); + fvnode.length = htole32(CHFS_PAD(sizeof(fvnode))); + fvnode.hdr_crc = htole32(crc32(0, (uint8_t *)&fvnode, + CHFS_NODE_HDR_SIZE - 4)); + fvnode.vno = htole64(node->inode->ino); + fvnode.version = htole64(version++); + fvnode.mode = htole32(node->inode->st.st_mode); + fvnode.dn_size = htole32(node->inode->st.st_size); + fvnode.atime = htole32(node->inode->st.st_atime); + fvnode.ctime = htole32(node->inode->st.st_ctime); + fvnode.mtime = htole32(node->inode->st.st_mtime); + fvnode.gid = htole32(node->inode->st.st_uid); + fvnode.uid = htole32(node->inode->st.st_gid); + fvnode.node_crc = htole32(crc32(0, (uint8_t *)&fvnode, + sizeof(fvnode) - 4)); + + pad_block_if_less_than(fsopts, sizeof(fvnode)); + buf_write(fsopts, &fvnode, sizeof(fvnode)); + padword(fsopts); +} + +void +write_dirent(fsinfo_t *fsopts, fsnode *node) +{ + struct chfs_flash_dirent_node fdirent; + char *name; + + name = emalloc(strlen(node->name)); + memcpy(name, node->name, strlen(node->name)); + + memset(&fdirent, 0, sizeof(fdirent)); + fdirent.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fdirent.type = htole16(CHFS_NODETYPE_DIRENT); + fdirent.length = htole32(CHFS_PAD(sizeof(fdirent) + strlen(name))); + fdirent.hdr_crc = htole32(crc32(0, (uint8_t *)&fdirent, + CHFS_NODE_HDR_SIZE - 4)); + fdirent.vno = htole64(node->inode->ino); + if (node->parent != NULL) { + fdirent.pvno = htole64(node->parent->inode->ino); + } else { + fdirent.pvno = htole64(node->inode->ino); + } + + fdirent.version = htole64(version++); + fdirent.mctime = 0; + fdirent.nsize = htole32(strlen(name)); + fdirent.dtype = htole32(IFTOCHT(node->type & S_IFMT)); + fdirent.name_crc = htole32(crc32(0, (uint8_t *)name, fdirent.nsize)); + fdirent.node_crc = htole32(crc32(0, (uint8_t *)&fdirent, + sizeof(fdirent) - 4)); + + pad_block_if_less_than(fsopts, sizeof(fdirent) + fdirent.nsize); + buf_write(fsopts, &fdirent, sizeof(fdirent)); + buf_write(fsopts, name, fdirent.nsize); + padword(fsopts); +} + +void +write_file(fsinfo_t *fsopts, fsnode *node, const char *dir) +{ + int fd; + ssize_t len; + char *name = node->name; + chfs_opt_t *opts; + unsigned char *buf; + uint32_t fileofs = 0; + + opts = fsopts->fs_specific; + buf = emalloc(opts->pagesize); + if (node->type == S_IFREG || node->type == S_IFSOCK) { + char *longname; + if (asprintf(&longname, "%s/%s", dir, name) == 1) + goto out; + + fd = open(longname, O_RDONLY, 0444); + if (fd == -1) + err(EXIT_FAILURE, "Cannot open `%s'", longname); + + while ((len = read(fd, buf, opts->pagesize))) { + if (len < 0) { + warn("ERROR while reading %s", longname); + free(longname); + free(buf); + close(fd); + return; + } + + write_data(fsopts, node, buf, len, fileofs); + fileofs += len; + } + free(longname); + close(fd); + } else if (node->type == S_IFLNK) { + len = strlen(node->symlink); + memcpy(buf, node->symlink, len); + write_data(fsopts, node, buf, len, 0); + } else if (node->type == S_IFCHR || node->type == S_IFBLK || + node->type == S_IFIFO) { + len = sizeof(dev_t); + memcpy(buf, &(node->inode->st.st_rdev), len); + write_data(fsopts, node, buf, len, 0); + } + + free(buf); + return; +out: + err(EXIT_FAILURE, "Memory allocation failed"); +} + +void +write_data(fsinfo_t *fsopts, fsnode *node, unsigned char *buf, size_t len, + uint32_t ofs) +{ + struct chfs_flash_data_node fdata; + + memset(&fdata, 0, sizeof(fdata)); + if (len == 0) { + return; + } + + pad_block_if_less_than(fsopts, sizeof(fdata) + len); + + fdata.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fdata.type = htole16(CHFS_NODETYPE_DATA); + fdata.length = htole32(CHFS_PAD(sizeof(fdata) + len)); + fdata.hdr_crc = htole32(crc32(0, (uint8_t *)&fdata, + CHFS_NODE_HDR_SIZE - 4)); + fdata.vno = htole64(node->inode->ino); + fdata.data_length = htole32(len); + fdata.offset = htole32(ofs); + fdata.data_crc = htole32(crc32(0, (uint8_t *)buf, len)); + fdata.node_crc = htole32(crc32(0, + (uint8_t *)&fdata, sizeof(fdata) - 4)); + + buf_write(fsopts, &fdata, sizeof(fdata)); + buf_write(fsopts, buf, len); + padword(fsopts); +} diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.h b/usr.sbin/makefs/chfs/chfs_mkfs.h new file mode 100644 index 000000000..83229926a --- /dev/null +++ b/usr.sbin/makefs/chfs/chfs_mkfs.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * 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 _CHFS_MKFS_H +#define _CHFS_MKFS_H + +#include "chfs.h" + +void padblock(fsinfo_t *); +void write_eb_header(fsinfo_t *); +void write_vnode(fsinfo_t *, fsnode *); +void write_dirent(fsinfo_t *, fsnode *); +void write_file(fsinfo_t *, fsnode *, const char *); +void write_data(fsinfo_t *, fsnode *, unsigned char *, size_t, uint32_t); +#endif + diff --git a/usr.sbin/makefs/chfs_makefs.h b/usr.sbin/makefs/chfs_makefs.h new file mode 100644 index 000000000..e722a9f5d --- /dev/null +++ b/usr.sbin/makefs/chfs_makefs.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * 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 _CHFS_MAKEFS_H +#define _CHFS_MAKEFS_H + +#define TYPE_NOR 0 +#define TYPE_NAND 1 + +#define DEFAULT_PAGESIZE 2048 +#define DEFAULT_ERASEBLOCK 131072 +#define DEFAULT_MEDIATYPE TYPE_NAND + +typedef struct { + int pagesize; /* page size */ + int eraseblock; /* eraseblock size */ + int mediatype; /* type of the media, 0 (nor) or 1 (nand) */ +} chfs_opt_t; + +#endif diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c new file mode 100644 index 000000000..723c213b8 --- /dev/null +++ b/usr.sbin/makefs/ffs.c @@ -0,0 +1,1155 @@ +/* $NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland 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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "ffs.h" + +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS +#include +#endif + +#include +#include +#include +#include + +#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', "maxbpf", &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(MAXPHYS, 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; + 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); + fs = ffs_mkfs(image, fsopts); + 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; + + 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; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#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; + /* 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; + + 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; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#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 +#if HAVE_STRUCT_STAT_BIRTHTIME + dinp->di_birthtime = cur->inode->st.st_birthtime; + dinp->di_birthnsec = cur->inode->st.st_birthtimensec; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* 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; + srandom(time(NULL)); + 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 000000000..79223bd54 --- /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 +#include + +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/Makefile.inc b/usr.sbin/makefs/ffs/Makefile.inc new file mode 100644 index 000000000..d13bcb358 --- /dev/null +++ b/usr.sbin/makefs/ffs/Makefile.inc @@ -0,0 +1,7 @@ +# $NetBSD: Makefile.inc,v 1.1 2009/01/16 19:39:52 pooka Exp $ +# + +.PATH: ${.CURDIR}/ffs ${NETBSDSRCDIR}/sys/ufs/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 diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c new file mode 100644 index 000000000..1917c3ac0 --- /dev/null +++ b/usr.sbin/makefs/ffs/buf.c @@ -0,0 +1,220 @@ +/* $NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "buf.h" + +TAILQ_HEAD(buftailhead,buf) buftail; + +int +bread(struct vnode *vp, daddr_t blkno, int size, struct kauth_cred *u1 __unused, + 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(1, "%s: lseek %lld (%lld)", __func__, + (long long)(*bpp)->b_blkno, (long long)offset); + rv = read((*bpp)->b_fs->fd, (*bpp)->b_data, (*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(1, "%s: read %ld (%lld) returned %zd", __func__, + (*bpp)->b_bcount, (long long)offset, rv); + else if (rv != (*bpp)->b_bcount) /* short read */ + err(1, "%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; + int bytes; + fsinfo_t *fs = bp->b_fs; + + assert (bp != NULL); + offset = bp->b_blkno * fs->sectorsize + fs->offset; + bytes = bp->b_bcount; + if (debug & DEBUG_BUF_BWRITE) + printf("bwrite: blkno %lld offset %lld bcount %d\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 == 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); + memset(n, 0, 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 000000000..8fd689334 --- /dev/null +++ b/usr.sbin/makefs/ffs/buf.h @@ -0,0 +1,117 @@ +/* $NetBSD: buf.h,v 1.9 2013/01/30 19:19:19 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 _FFS_BUF_H +#define _FFS_BUF_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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, struct kauth_cred *, + 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 desiredvnodes 10000 +#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 000000000..63ab4638e --- /dev/null +++ b/usr.sbin/makefs/ffs/ffs_alloc.c @@ -0,0 +1,597 @@ +/* $NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland 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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include + +#include "makefs.h" + +#include +#include +#include + +#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(1, "ffs_alloc: bad size: bsize %d size %d", + 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, NULL, 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(1, "blkfree: bad size: bno %lld bsize %d size %ld", + (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, NULL, 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(1, "blkfree: freeing free block %lld", + (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(1, "blkfree: freeing free frag: block %lld", + (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(1, + "ffs_alloccg: map corrupted: start %d len %d offset %d %ld", + 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(1, "ffs_alloccg: block not in map: bno %lld", (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 000000000..e5dd1d38a --- /dev/null +++ b/usr.sbin/makefs/ffs/ffs_balloc.c @@ -0,0 +1,584 @@ +/* $NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland 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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#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, + NULL, 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, + NULL, 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, + NULL, 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, NULL, 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, + NULL, 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, + NULL, 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, + NULL, 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, NULL, 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 000000000..53e9fe2ee --- /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 000000000..c4dbc9b46 --- /dev/null +++ b/usr.sbin/makefs/ffs/mkfs.c @@ -0,0 +1,845 @@ +/* $NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95"; +#else +#ifdef __RCSID +__RCSID("$NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 christos Exp $"); +#endif +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "ffs.h" + +#include +#include +#include + +#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) +{ + 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] = start_time.tv_sec; + 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 = start_time.tv_sec; + if (Oflag <= 1) { + sblock.fs_old_time = start_time.tv_sec; + 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, start_time.tv_sec, 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 = start_time.tv_sec; + 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; + } + if (fsopts->needswap) + sblock.fs_flags |= FS_SWAPPED; + 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, saveflag; + void *space; + char *wrbuf; + + saveflag = fs->fs_flags & FS_INTERNAL; + fs->fs_flags &= ~FS_INTERNAL; + + 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); + fs->fs_flags |= saveflag; +} + +/* + * 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(1, "%s: seek error for sector %lld", __func__, + (long long)bno); + n = read(fsopts->fd, bf, size); + if (n == -1) { + err(1, "%s: read error bno %lld size %d", __func__, + (long long)bno, size); + } + else if (n != size) + errx(1, "%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(1, "%s: seek error for sector %lld", __func__, + (long long)bno); + n = write(fsopts->fd, bf, size); + if (n == -1) + err(1, "%s: write error for sector %lld", __func__, + (long long)bno); + else if (n != size) + errx(1, "%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(1, "%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 000000000..d86b248dd --- /dev/null +++ b/usr.sbin/makefs/ffs/newfs_extern.h @@ -0,0 +1,34 @@ +/* $NetBSD: newfs_extern.h,v 1.3 2009/10/21 01:07:47 snj 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 *); +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 000000000..1135b6505 --- /dev/null +++ b/usr.sbin/makefs/ffs/ufs_bmap.c @@ -0,0 +1,145 @@ +/* $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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#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 000000000..cc2d5893f --- /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/makefs.8 b/usr.sbin/makefs/makefs.8 new file mode 100644 index 000000000..92f8fb40d --- /dev/null +++ b/usr.sbin/makefs/makefs.8 @@ -0,0 +1,460 @@ +.\" $NetBSD: makefs.8,v 1.53 2013/08/06 20:16:54 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 August 6, 2013 +.Dt MAKEFS 8 +.Os +.Sh NAME +.Nm makefs +.Nd create a file system image from a directory tree +.Sh SYNOPSIS +.Nm +.Op Fl rxZ +.Op Fl B Ar endian +.Op Fl b Ar free-blocks +.Op Fl d Ar debug-mask +.Op Fl F Ar mtree-specfile +.Op Fl f Ar free-files +.Op Fl M Ar minimum-size +.Op Fl m Ar maximum-size +.Op Fl N Ar userdb-dir +.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 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 mtree-specfile +Use +.Ar mtree-specfile +as an +.Xr mtree 8 +.Sq specfile +specification. +.Pp +If a specfile entry exists in the underlying file system, its +permissions and modification time will be used unless specifically +overridden by the specfile. +An error will be raised if the type of entry in the specfile +conflicts with that of an existing entry. +.Pp +In the opposite case (where a specfile entry does not have an entry +in the underlying file system) the following occurs: +If the specfile entry is marked +.Sy optional , +the specfile entry is ignored. +Otherwise, the entry will be created in the image, and it is +necessary to specify at least the following parameters in the +specfile: +.Sy type , +.Sy mode , +.Sy gname , +or +.Sy gid , +and +.Sy uname +or +.Sy uid , +.Sy device +(in the case of block or character devices), and +.Sy link +(in the case of symbolic links). +If +.Sy time +isn't provided, the current time will be used. +If +.Sy flags +isn't provided, the current file flags will be used. +Missing regular file entries will be created as zero-length files. +.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 N Ar userdb-dir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar userdb-dir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.It Fl O Ar offset +Instead of creating the filesystem 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 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 chfs +Chip flash file system. +.It Sy msdos +FAT12, FAT16, or FAT32 file system. +.It Sy v7fs +7th Edition(V7) file system. +.It Sy udf +ISO/Ecma UDF file system. +.El +.It Fl x +Exclude file system nodes not explicitly listed in the specfile. +.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 CHFS-specific options +.Sy chfs +images have chfs-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 pagesize +Pagesize. +.It Sy erasesize +Erase block size of the media. +.It Sy mediatype +Type of the media. +NOR: 0 or NAND: 1. +.El +.Ss msdos-specific options +See +.Xr newfs_msdos 8 +for fs specific options. +.Ss V7FS-specific options +The following keywords are supported: +.Pp +.Bl -tag -width optimization -offset indent -compact +.It Sy pdp +PDP endian. +.It Sy progress +Display a progress meter for the file system construction and file +population. +.El +.Ss UDF-specific options +.Sy udf +images have udf-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 -compact +.It Sy disctype +This can have the following values: +.Bl -tag -width cdromXdvdromXbdromXXX -compact +.It Sy cdrom , Sy dvdrom , Sy bdrom +create a read-only fs +.It Sy dvdram , Sy bdre , Sy disk +create a rewritable fs without sparing for defective sectors +.It Sy cdr , Sy dvdr , Sy bdr +create a rewritable fs on once recordable media using a VAT +.It Sy cdrw , Sy dvdrw +create a rewritable fs with sparing for defective sectors +.El +When an optical media is selected here, the sectorsize and the default disc +size is assumed unless given explicitly. +For rom images the disc size is the minimum needed. +.It Sy loglabel +Set the logical volume label of the disc to the specified argument. +.It Sy discid +Set the physical volume label of the disc to the specified argument. +Prepend the physical volume label with a volumeset label separated +with a ':' if wanted. +For strict conformance and interchange, don't set the volumeset label +manually unless it has an unique hex number in the first 8 character +positions. +.It Sy minver +Set the minimum UDF version to be used. +Choose UDF version numbers from 0x102, 0x150, 0x200, and 0x201. +Versions 0x250 and 0x260 are currently not supported +in +.Nm . +.El +.Sh SEE ALSO +.Xr strsuftoll 3 , +.Xr installboot 8 , +.Xr mtree 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 000000000..116eceb45 --- /dev/null +++ b/usr.sbin/makefs/makefs.c @@ -0,0 +1,437 @@ +/* $NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.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(chfs), + ENTRY(v7fs), + ENTRY(msdos), + ENTRY(udf), + { .type = NULL }, +}; + +u_int debug; +struct timespec start_time; + +static fstype_t *get_fstype(const char *); +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; + char *specfile; + + 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); + + specfile = NULL; + if (gettimeofday(&start, NULL) == -1) + err(1, "Unable to get system time"); + + start_time.tv_sec = start.tv_sec; + start_time.tv_nsec = start.tv_usec * 1000; + + while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:O:o:rs:S:t:xZ")) != -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 'F': + specfile = optarg; + break; + + case 'M': + fsoptions.minsize = + strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); + break; + + case 'N': + if (! setup_getid(optarg)) + errx(1, + "Unable to use user and group databases in `%s'", + optarg); + 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 'x': + fsoptions.onlyspec = 1; + 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); + + /* -x must be accompanied by -F */ + if (fsoptions.onlyspec != 0 && specfile == NULL) + errx(1, "-x requires -F mtree-specfile."); + + /* 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 (specfile) { /* apply a specfile */ + TIMER_START(start); + apply_specfile(specfile, argv[1], root, fsoptions.onlyspec); + TIMER_RESULTS(start, "apply_specfile"); + } + + 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 void +usage(fstype_t *fstype, fsinfo_t *fsoptions) +{ + const char *prog; + + prog = getprogname(); + fprintf(stderr, +"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask]\n" +"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n" +"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n" +"\t[-s image-size] [-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 000000000..b856d5232 --- /dev/null +++ b/usr.sbin/makefs/makefs.h @@ -0,0 +1,267 @@ +/* $NetBSD: makefs.h,v 1.35 2013/08/05 14:41:57 reinoud 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 + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#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 1 +#define HAVE_STRUCT_STAT_BIRTHTIME 1 +#define HAVE_FSTATVFS 1 +#endif + +#include +#include + +/* + * 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 */ + int onlyspec; /* only add entries in specfile */ + + + /* 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 apply_specfile(const char *, const char *, fsnode *, int); +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(chfs); +DECLARE_FUN(v7fs); +DECLARE_FUN(msdos); +DECLARE_FUN(udf); + +extern u_int debug; +extern struct timespec start_time; + +/* + * If -x is specified, we want to exclude nodes which do not appear + * in the spec file. + */ +#define FSNODE_EXCLUDE_P(opts, fsnode) \ + ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0) + +#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 + + +/* + * ffs specific settings + * --------------------- + */ + +#define FFS_EI /* for opposite endian support in ffs headers */ + + +#endif /* _MAKEFS_H */ diff --git a/usr.sbin/makefs/msdos.c b/usr.sbin/makefs/msdos.c new file mode 100644 index 000000000..5a7b10310 --- /dev/null +++ b/usr.sbin/makefs/msdos.c @@ -0,0 +1,257 @@ +/* $NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 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. + * 3. Neither the name of The NetBSD Foundation 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 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. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "makefs.h" +#include "msdos.h" +#include "mkfs_msdos.h" + +static int msdos_populate_dir(const char *, struct denode *, fsnode *, + fsnode *, fsinfo_t *); + +void +msdos_prep_opts(fsinfo_t *fsopts) +{ + struct msdos_options *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->_name, \ + .minimum = _min, \ + .maximum = sizeof(_type) == 1 ? 0xff : \ + (sizeof(_type) == 2 ? 0xffff : \ + (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)), \ + .desc = _desc, \ +}, +ALLOPTS +#undef AOPT + { .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 *msdos_opt = fsopts->fs_specific; + struct vnode vp, rootvp; + struct timeval start; + struct msdosfsmount *pmp; + + 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->create_size = MAX(msdos_opt->create_size, fsopts->minsize); + msdos_opt->offset = fsopts->offset; + if (msdos_opt->bytes_per_sector == 0) { + if (fsopts->sectorsize == -1) + fsopts->sectorsize = 512; + msdos_opt->bytes_per_sector = fsopts->sectorsize; + } else if (fsopts->sectorsize == -1) { + fsopts->sectorsize = msdos_opt->bytes_per_sector; + } else if (fsopts->sectorsize != msdos_opt->bytes_per_sector) { + err(1, "inconsistent sectorsize -S %u" + "!= -o bytes_per_sector %u", + fsopts->sectorsize, msdos_opt->bytes_per_sector); + } + + /* create image */ + printf("Creating `%s'\n", image); + TIMER_START(start); + if (mkfs_msdos(image, NULL, msdos_opt) == -1) + return; + TIMER_RESULTS(start, "mkfs_msdos"); + + fsopts->fd = open(image, O_RDWR); + vp.fs = fsopts; + + if ((pmp = msdosfs_mount(&vp, 0)) == 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 000000000..3571bef38 --- /dev/null +++ b/usr.sbin/makefs/msdos.h @@ -0,0 +1,42 @@ +/* $NetBSD: msdos.h,v 1.2 2013/01/26 00:31:49 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. + * 3. Neither the name of The NetBSD Foundation 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 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/Makefile.inc b/usr.sbin/makefs/msdos/Makefile.inc new file mode 100644 index 000000000..fbcce2add --- /dev/null +++ b/usr.sbin/makefs/msdos/Makefile.inc @@ -0,0 +1,15 @@ +# $NetBSD: Makefile.inc,v 1.5 2013/01/26 16:50:46 christos Exp $ +# + +MSDOS= ${NETBSDSRCDIR}/sys/fs/msdosfs +MSDOS_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_msdos + +.PATH: ${.CURDIR}/msdos ${MSDOS} ${MSDOS_NEWFS} + +CPPFLAGS+= -DMSDOS_EI -I${MSDOS} -I${MSDOS_NEWFS} +.if !defined(HOSTPROGNAME) +CPPFLAGS+= -I${NETBSDSRCDIR}/sys +.endif + +SRCS+= mkfs_msdos.c msdosfs_fat.c msdosfs_conv.c msdosfs_vfsops.c +SRCS+= msdosfs_lookup.c msdosfs_denode.c msdosfs_vnops.c diff --git a/usr.sbin/makefs/msdos/msdosfs_denode.c b/usr.sbin/makefs/msdos/msdosfs_denode.c new file mode 100644 index 000000000..47ebee012 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_denode.c @@ -0,0 +1,363 @@ +/* $NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +/* + * 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, NOCRED, 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 000000000..29461c760 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_vfsops.c @@ -0,0 +1,425 @@ +/*- + * 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 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.7 2013/01/30 19:19:19 christos Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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, NULL, 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; + } + } + + 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, NULL, 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 pmp; +} + +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 000000000..53c438c24 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_vnops.c @@ -0,0 +1,639 @@ +/* $NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 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 + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $"); + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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) +{ +#ifndef HAVE_NBTOOL_CONFIG_H + struct timespec at = st->st_atimespec; + struct timespec mt = st->st_mtimespec; +#else + struct timespec at = { st->st_atime, 0 }; + struct timespec mt = { st->st_mtime, 0 }; +#endif + 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) + 1; + break; + case 3: + olddos = 0; + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 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, NOCRED, + 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); + 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, + NULL, 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/udf.c b/usr.sbin/makefs/udf.c new file mode 100644 index 000000000..ad0136cdc --- /dev/null +++ b/usr.sbin/makefs/udf.c @@ -0,0 +1,1377 @@ +/* $NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +#if !HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_TM_TM_GMTOFF +#endif + +#include "makefs.h" +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + +#undef APP_NAME +#define APP_NAME "*NetBSD makefs" + +/* + * Note: due to the setup of the newfs code, the current state of the program + * and its options are helt in a few global variables. The FS specific parts + * are in a global `context' structure. + */ + +/* global variables describing disc and format requests */ +int fd; /* device: file descriptor */ +char *dev; /* device: name */ +struct mmc_discinfo mmc_discinfo; /* device: disc info */ + +char *format_str; /* format: string representation */ +int format_flags; /* format: attribute flags */ +int media_accesstype; /* derived from current mmc cap */ +int check_surface; /* for rewritables */ +int imagefile_secsize; /* for files */ + +int display_progressbar; + +int wrtrack_skew; +float meta_fract = (float) UDF_META_PERC / 100.0; + +int mmc_profile; /* emulated profile */ +int req_enable, req_disable; + + +/* --------------------------------------------------------------------- */ + +int +udf_write_sector(void *sector, uint64_t location) +{ + uint64_t wpos; + ssize_t ret; + + wpos = (uint64_t) location * context.sector_size; + ret = pwrite(fd, sector, context.sector_size, wpos); + if (ret == -1) + return errno; + if (ret < (int) context.sector_size) + return EIO; + return 0; +} + + +/* not implemented for files */ +int +udf_surface_check(void) +{ + return 0; +} + + +/* + * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main + * code in sys/fs/udf/ + */ + +#ifdef DEBUG +static void +udf_dump_discinfo(struct mmc_discinfo *di) +{ + char bits[128]; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, + (uint64_t) di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); + printf("\n"); + printf("\tlast_possible_lba %d\n", di->last_possible_lba); + printf("\n"); +} +#else +#define udf_dump_discinfo(a); +#endif + +/* --------------------------------------------------------------------- */ + +static int +udf_emulate_discinfo(fsinfo_t *fsopts, struct mmc_discinfo *di, + int mmc_emuprofile) +{ + off_t sectors; + + memset(di, 0, sizeof(*di)); + + /* file support */ + if ((mmc_emuprofile != 0x01) && (fsopts->sectorsize != 2048)) + warnx("cd/dvd/bd sectorsize is not set to default 2048"); + + sectors = fsopts->size / fsopts->sectorsize; + + /* commons */ + di->mmc_profile = mmc_emuprofile; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + di->last_possible_lba = sectors - 1; + di->sector_size = fsopts->sectorsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK; + switch (mmc_emuprofile) { + case 0x00: /* unknown, treat as CDROM */ + case 0x08: /* CDROM */ + case 0x10: /* DVDROM */ + case 0x40: /* BDROM */ + req_enable |= FORMAT_READONLY; + /* FALLTROUGH */ + case 0x01: /* disc */ + /* set up a disc info profile for partitions/files */ + di->mmc_class = MMC_CLASS_DISC; + di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + case 0x09: /* CD-R */ + di->mmc_class = MMC_CLASS_CD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x0a: /* CD-RW + CD-MRW (regretably) */ + di->mmc_class = MMC_CLASS_CD; + di->mmc_cur |= MMC_CAP_REWRITABLE; + break; + case 0x13: /* DVD-RW */ + di->mmc_class = MMC_CLASS_DVD; + di->mmc_cur |= MMC_CAP_REWRITABLE; + break; + case 0x11: /* DVD-R */ + case 0x14: /* DVD-RW sequential */ + case 0x1b: /* DVD+R */ + case 0x2b: /* DVD+R DL */ + case 0x51: /* HD DVD-R */ + di->mmc_class = MMC_CLASS_DVD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x41: /* BD-R */ + di->mmc_class = MMC_CLASS_BD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x43: /* BD-RE */ + di->mmc_class = MMC_CLASS_BD; + di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + default: + err(EINVAL, "makefs_udf: unknown or unimplemented device type"); + return EINVAL; + } + di->mmc_cap = di->mmc_cur; + + udf_dump_discinfo(di); + return 0; +} + + +int +udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti) +{ + /* discs partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = 32; /* take sensible default */ + + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + +#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, &context.field, \ + sizeof(context.field) == 8 ? OPT_INT64 : \ + (sizeof(context.field) == 4 ? OPT_INT32 : \ + (sizeof(context.field) == 2 ? OPT_INT16 : OPT_INT8)), \ + min, max, desc } + +#define OPT_BOOL(letter, name, field, desc) \ + OPT_NUM(letter, name, field, 0, 1, desc) + +void +udf_prep_opts(fsinfo_t *fsopts) +{ + struct tm *tm; + time_t now; + + const option_t udf_options[] = { + OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,bdrom," + "dvdram,bdre,disk,cdr,dvdr,bdr,cdrw,dvdrw)"), + OPT_STR('L', "loglabel", "\"logical volume name\""), + OPT_STR('P', "discid", "\"[volset name ':']" + "physical volume name\""), + OPT_NUM('t', "tz", gmtoff, -24, 24, "timezone"), + OPT_STR('v', "minver", "minimum UDF version in either " + "``0x201'' or ``2.01'' format"), +#if notyet + OPT_STR('V', "maxver", "maximum UDF version in either " + "``0x201'' or ``2.01'' format"), +#endif + { .name = NULL } + }; + + /* initialise */ + format_str = strdup(""); + req_enable = req_disable = 0; + format_flags = FORMAT_INVALID; + fsopts->sectorsize = 512; /* minimum allowed sector size */ + + srandom((unsigned long) time(NULL)); + + mmc_profile = 0x01; /* 'disc'/file */ + + udf_init_create_context(); + context.app_name = "*NetBSD makefs"; + context.app_version_main = APP_VERSION_MAIN; + context.app_version_sub = APP_VERSION_SUB; + context.impl_name = IMPL_NAME; + + /* minimum and maximum UDF versions we advise */ + context.min_udf = 0x102; + context.max_udf = 0x201; /* 0x250 and 0x260 are not ready */ + + /* use user's time zone as default */ + (void)time(&now); + tm = localtime(&now); +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + context.gmtoff = tm->tm_gmtoff; +#else + context.gmtoff = 0; +#endif + + /* return info */ + fsopts->fs_specific = NULL; + fsopts->fs_options = copy_opts(udf_options); +} + + +void +udf_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_options); +} + + +/* ----- included from newfs_udf.c ------ */ +/* ----- */ + + +#define CDRSIZE ((uint64_t) 700*1024*1024) /* small approx */ +#define CDRWSIZE ((uint64_t) 576*1024*1024) /* small approx */ +#define DVDRSIZE ((uint64_t) 4488*1024*1024) /* small approx */ +#define DVDRAMSIZE ((uint64_t) 4330*1024*1024) /* small approx with spare */ +#define DVDRWSIZE ((uint64_t) 4482*1024*1024) /* small approx */ +#define BDRSIZE ((uint64_t) 23866*1024*1024) /* small approx */ +#define BDRESIZE ((uint64_t) 23098*1024*1024) /* small approx */ +int +udf_parse_opts(const char *option, fsinfo_t *fsopts) +{ + option_t *udf_options = fsopts->fs_options; + uint64_t stdsize; + uint32_t set_sectorsize; + char buffer[1024], *buf, *colon; + int i; + + assert(option != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("udf_parse_opts: got `%s'\n", option); + + i = set_option(udf_options, option, buffer, sizeof(buffer)); + if (i == -1) + return 0; + + if (udf_options[i].name == NULL) + abort(); + + set_sectorsize = 0; + stdsize = 0; + + buf = buffer; + switch (udf_options[i].letter) { + case 'T': + if (strcmp(buf, "cdrom") == 0) { + mmc_profile = 0x00; + } else if (strcmp(buf, "dvdrom") == 0) { + mmc_profile = 0x10; + } else if (strcmp(buf, "bdrom") == 0) { + mmc_profile = 0x40; + } else if (strcmp(buf, "dvdram") == 0) { + mmc_profile = 0x12; + stdsize = DVDRAMSIZE; + } else if (strcmp(buf, "bdre") == 0) { + mmc_profile = 0x43; + stdsize = BDRESIZE; + } else if (strcmp(buf, "disk") == 0) { + mmc_profile = 0x01; + } else if (strcmp(buf, "cdr") == 0) { + mmc_profile = 0x09; + stdsize = CDRSIZE; + } else if (strcmp(buf, "dvdr") == 0) { + mmc_profile = 0x1b; + stdsize = DVDRSIZE; + } else if (strcmp(buf, "bdr") == 0) { + mmc_profile = 0x41; + stdsize = BDRSIZE; + } else if (strcmp(buf, "cdrw") == 0) { + mmc_profile = 0x0a; + stdsize = CDRWSIZE; + } else if (strcmp(buf, "dvdrw") == 0) { + mmc_profile = 0x13; + stdsize = DVDRWSIZE; + } else { + errx(EINVAL, "Unknown or unimplemented disc format"); + return 0; + } + if (mmc_profile != 0x01) + set_sectorsize = 2048; + break; + case 'L': + if (context.logvol_name) free(context.logvol_name); + context.logvol_name = strdup(buf); + break; + case 'P': + if ((colon = strstr(buf, ":"))) { + if (context.volset_name) + free(context.volset_name); + *colon = 0; + context.volset_name = strdup(buf); + buf = colon+1; + } + if (context.primary_name) + free(context.primary_name); + if ((strstr(buf, ":"))) { + errx(EINVAL, "primary name can't have ':' in its name"); + return 0; + } + context.primary_name = strdup(buf); + break; + case 'v': + context.min_udf = a_udf_version(buf, "min_udf"); + if (context.min_udf > 0x201) { + errx(EINVAL, "maximum supported version is UDF 2.01"); + return 0; + } + if (context.min_udf > context.max_udf) + context.max_udf = context.min_udf; + break; + } + if (set_sectorsize) + fsopts->sectorsize = set_sectorsize; + if (stdsize) + fsopts->size = stdsize; + return 1; +} + +/* --------------------------------------------------------------------- */ + +struct udf_stats { + uint32_t nfiles; + uint32_t ndirs; + uint32_t ndescr; + uint32_t nmetadatablocks; + uint32_t ndatablocks; +}; + + +/* node reference administration */ +static void +udf_inc_link(union dscrptr *dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1); + } else { + errx(1, "Bad tag passed to udf_inc_link"); + } +} + + +static void +udf_set_link_cnt(union dscrptr *dscr, int num) +{ + struct file_entry *fe; + struct extfile_entry *efe; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + fe->link_cnt = udf_rw16(num); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + efe->link_cnt = udf_rw16(num); + } else { + errx(1, "Bad tag passed to udf_set_link_cnt"); + } +} + + +static uint32_t +udf_datablocks(off_t sz) +{ + /* predictor if it can be written inside the node */ + if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16) + return 0; + + return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size; +} + + +static void +udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb, + uint8_t *dirdata, uint32_t dirdata_size) +{ + struct fileid_desc *fid; + struct long_ad *icb; + uint32_t fidsize, offset; + uint32_t location; + + if (udf_datablocks(dirdata_size) == 0) { + /* going internal */ + icb = dir_icb; + } else { + /* external blocks to write to */ + icb = dirdata_icb; + } + + for (offset = 0; offset < dirdata_size; offset += fidsize) { + /* for each FID: */ + fid = (struct fileid_desc *) (dirdata + offset); + assert(udf_rw16(fid->tag.id) == TAGID_FID); + + location = udf_rw32(icb->loc.lb_num); + location += offset / context.sector_size; + + fid->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + fidsize = udf_fidsize(fid); + } +} + +static int +udf_file_inject_blob(union dscrptr *dscr, uint8_t *blob, off_t size) +{ + struct icb_tag *icb; + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t inf_len, obj_size; + uint32_t l_ea, l_ad; + uint32_t free_space, desc_size; + uint16_t crclen; + uint8_t *data, *pos; + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + icb = &fe->icbtag; + inf_len = udf_rw64(fe->inf_len); + obj_size = 0; + desc_size = sizeof(struct file_entry); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + icb = &efe->icbtag; + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + desc_size = sizeof(struct extfile_entry); + } else { + errx(1, "Bad tag passed to udf_file_inject_blob"); + } + crclen = udf_rw16(dscr->tag.desc_crc_len); + + /* calculate free space */ + free_space = context.sector_size - (l_ea + l_ad) - desc_size; + if (udf_datablocks(size)) { + assert(free_space < size); + return 1; + } + + /* going internal */ + assert(l_ad == 0); + assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == + UDF_ICB_INTERN_ALLOC); + + // assert(free_space >= size); + pos = data + l_ea + l_ad; + memcpy(pos, blob, size); + l_ad += size; + crclen += size; + + inf_len += size; + obj_size += size; + + if (fe) { + fe->l_ad = udf_rw32(l_ad); + fe->inf_len = udf_rw64(inf_len); + } else if (efe) { + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + } + + /* make sure the header sums stays correct */ + dscr->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums(dscr); + + return 0; +} + + +/* XXX no sparse file support */ +static void +udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece) +{ + struct icb_tag *icb; + struct file_entry *fe; + struct extfile_entry *efe; + struct long_ad *last_long, last_piece; + struct short_ad *last_short, new_short; + uint64_t inf_len, obj_size, logblks_rec; + uint32_t l_ea, l_ad, size; + uint32_t last_lb_num, piece_lb_num; + uint64_t last_len, piece_len, last_flags; + uint64_t rest_len, merge_len, last_end; + uint16_t last_part_num, piece_part_num; + uint16_t crclen, cur_alloc; + uint8_t *data, *pos; + const int short_len = sizeof(struct short_ad); + const int long_len = sizeof(struct long_ad); + const int sector_size = context.sector_size; + const int use_shorts = (context.data_part == context.metadata_part); + uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size); + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + icb = &fe->icbtag; + inf_len = udf_rw64(fe->inf_len); + logblks_rec = udf_rw64(fe->logblks_rec); + obj_size = 0; + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + icb = &efe->icbtag; + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + logblks_rec = udf_rw64(efe->logblks_rec); + } else { + errx(1, "Bad tag passed to udf_file_append_blob"); + } + crclen = udf_rw16(dscr->tag.desc_crc_len); + + pos = data + l_ea; + cur_alloc = udf_rw16(icb->flags); + size = UDF_EXT_LEN(udf_rw32(piece->len)); + + /* extract last entry as a long_ad */ + memset(&last_piece, 0, sizeof(last_piece)); + last_len = 0; + last_lb_num = 0; + last_part_num = 0; + last_flags = 0; + last_short = NULL; + last_long = NULL; + if (l_ad != 0) { + if (use_shorts) { + assert(cur_alloc == UDF_ICB_SHORT_ALLOC); + pos += l_ad - short_len; + last_short = (struct short_ad *) pos; + last_lb_num = udf_rw32(last_short->lb_num); + last_part_num = udf_rw16(piece->loc.part_num); + last_len = UDF_EXT_LEN(udf_rw32(last_short->len)); + last_flags = UDF_EXT_FLAGS(udf_rw32(last_short->len)); + } else { + assert(cur_alloc == UDF_ICB_LONG_ALLOC); + pos += l_ad - long_len; + last_long = (struct long_ad *) pos; + last_lb_num = udf_rw32(last_long->loc.lb_num); + last_part_num = udf_rw16(last_long->loc.part_num); + last_len = UDF_EXT_LEN(udf_rw32(last_long->len)); + last_flags = UDF_EXT_FLAGS(udf_rw32(last_long->len)); + } + } + + piece_len = UDF_EXT_LEN(udf_rw32(piece->len)); + piece_lb_num = udf_rw32(piece->loc.lb_num); + piece_part_num = udf_rw16(piece->loc.part_num); + + /* try merging */ + rest_len = max_len - last_len; + + merge_len = MIN(piece_len, rest_len); + last_end = last_lb_num + (last_len / sector_size); + + if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) { + /* we can merge */ + last_len += merge_len; + piece_len -= merge_len; + + /* write back merge result */ + if (use_shorts) { + last_short->len = udf_rw32(last_len | last_flags); + } else { + last_long->len = udf_rw32(last_len | last_flags); + } + piece_lb_num += merge_len / sector_size; + } + + if (piece_len) { + /* append new entry */ + pos = data + l_ea + l_ad; + if (use_shorts) { + icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); + memset(&new_short, 0, short_len); + new_short.len = udf_rw32(piece_len); + new_short.lb_num = udf_rw32(piece_lb_num); + memcpy(pos, &new_short, short_len); + l_ad += short_len; + crclen += short_len; + } else { + icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC); + piece->len = udf_rw32(piece_len); + piece->loc.lb_num = udf_rw32(piece_lb_num); + memcpy(pos, piece, long_len); + l_ad += long_len; + crclen += long_len; + } + } + piece->len = udf_rw32(0); + + inf_len += size; + obj_size += size; + logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size; + + dscr->tag.desc_crc_len = udf_rw16(crclen); + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe->l_ad = udf_rw32(l_ad); + fe->inf_len = udf_rw64(inf_len); + fe->logblks_rec = udf_rw64(logblks_rec); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + efe->logblks_rec = udf_rw64(logblks_rec); + } +} + + +static int +udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb, + uint8_t *fdata, off_t flen) +{ + struct long_ad icb; + uint32_t location; + uint64_t phys; + uint16_t vpart; + uint8_t *bpos; + int cnt, sects; + int error; + + if (udf_file_inject_blob(dscr, fdata, flen) == 0) + return 0; + + /* has to be appended in mappings */ + icb = *data_icb; + icb.len = udf_rw32(flen); + while (udf_rw32(icb.len) > 0) + udf_append_file_mapping(dscr, &icb); + udf_validate_tag_and_crc_sums(dscr); + + /* write out data piece */ + vpart = udf_rw16(data_icb->loc.part_num); + location = udf_rw32(data_icb->loc.lb_num); + sects = udf_datablocks(flen); + for (cnt = 0; cnt < sects; cnt++) { + bpos = fdata + cnt*context.sector_size; + phys = context.vtop_offset[vpart] + location + cnt; + error = udf_write_sector(bpos, phys); + if (error) + return error; + } + return 0; +} + + +static int +udf_create_new_file(struct stat *st, union dscrptr **dscr, + int filetype, struct long_ad *icb) +{ + struct file_entry *fe; + struct extfile_entry *efe; + int error; + + fe = NULL; + efe = NULL; + if (context.dscrver == 2) { + error = udf_create_new_fe(&fe, filetype, st); + if (error) + errx(error, "can't create fe"); + *dscr = (union dscrptr *) fe; + icb->longad_uniqueid = fe->unique_id; + } else { + error = udf_create_new_efe(&efe, filetype, st); + if (error) + errx(error, "can't create fe"); + *dscr = (union dscrptr *) efe; + icb->longad_uniqueid = efe->unique_id; + } + + return 0; +} + + +static void +udf_estimate_walk(fsinfo_t *fsopts, + fsnode *root, char *dir, struct udf_stats *stats) +{ + struct fileid_desc *fid; + struct long_ad dummy_ref; + fsnode *cur; + fsinode *fnode; + size_t pathlen = strlen(dir); + char *mydir = dir + pathlen; + off_t sz; + uint32_t nblk, ddoff; + uint32_t softlink_len; + uint8_t *softlink_buf; + int nentries; + int error; + + stats->ndirs++; + + /* + * Count number of directory entries and count directory size; needed + * for the reservation of enough space for the directory. Pity we + * don't keep the FIDs we created. If it turns out to be a issue we + * can cache it later. + */ + fid = (struct fileid_desc *) malloc(context.sector_size); + assert(fid); + + ddoff = 40; /* '..' entry */ + for (cur = root, nentries = 0; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + break; + case S_IFCHR: + case S_IFBLK: + /* not supported yet */ + continue; + case S_IFDIR: + if (strcmp(cur->name, ".") == 0) + continue; + case S_IFLNK: + case S_IFREG: + /* create dummy FID to see how long name will become */ + udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref); + + nentries++; + ddoff += udf_fidsize(fid); + } + } + sz = ddoff; + + root->inode->st.st_size = sz; /* max now */ + root->inode->flags |= FI_SIZED; + + nblk = udf_datablocks(sz); + stats->nmetadatablocks += nblk; + + /* for each entry in the directory, there needs to be a (E)FE */ + stats->nmetadatablocks += nentries + 1; + + /* recurse */ + for (cur = root; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + break; + case S_IFCHR: + case S_IFBLK: + /* not supported yet */ + // stats->nfiles++; + break; + case S_IFDIR: + if (strcmp(cur->name, ".") == 0) + continue; + /* empty dir? */ + if (!cur->child) + break; + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + udf_estimate_walk(fsopts, cur->child, dir, stats); + mydir[0] = '\0'; + break; + case S_IFREG: + fnode = cur->inode; + /* don't double-count hard-links */ + if (!(fnode->flags & FI_SIZED)) { + sz = fnode->st.st_size; + nblk = udf_datablocks(sz); + stats->ndatablocks += nblk; + /* ... */ + fnode->flags |= FI_SIZED; + } + stats->nfiles++; + break; + case S_IFLNK: + /* softlink */ + fnode = cur->inode; + /* don't double-count hard-links */ + if (!(fnode->flags & FI_SIZED)) { + error = udf_encode_symlink(&softlink_buf, + &softlink_len, cur->symlink); + if (error) { + printf("SOFTLINK error %d\n", error); + break; + } + nblk = udf_datablocks(softlink_len); + stats->ndatablocks += nblk; + fnode->flags |= FI_SIZED; + + free(softlink_buf); + } + stats->nfiles++; + break; + } + } +} + + +#define UDF_MAX_CHUNK_SIZE (4*1024*1024) +static int +udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid, + struct long_ad *icb) +{ + union dscrptr *dscr; + struct long_ad data_icb; + fsinode *fnode; + off_t sz, chunk, rd; + uint8_t *data; + int nblk; + int i, f; + int error; + + fnode = cur->inode; + + f = open(path, O_RDONLY); + if (f < 0) { + warn("Can't open file %s for reading", cur->name); + return errno; + } + + /* claim disc space for the (e)fe descriptor for this file */ + udf_metadata_alloc(1, icb); + udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb); + + sz = fnode->st.st_size; + + chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); + data = malloc(MAX(chunk, context.sector_size)); + assert(data); + + printf(" "); + i = 0; + error = 0; + while (chunk) { + rd = read(f, data, chunk); + if (rd != chunk) { + warn("Short read of file %s\n", cur->name); + error = errno; + break; + } + printf("\b%c", "\\|/-"[i++ % 4]); fflush(stdout);fflush(stderr); + + nblk = udf_datablocks(chunk); + if (nblk > 0) + udf_data_alloc(nblk, &data_icb); + udf_append_file_contents(dscr, &data_icb, data, chunk); + + sz -= chunk; + chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); + } + printf("\b \n"); + close(f); + free(data); + + /* write out dscr (e)fe */ + udf_set_link_cnt(dscr, fnode->nlink); + udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num), + udf_rw16(icb->loc.part_num), 1); + free(dscr); + + /* remember our location for hardlinks */ + cur->inode->fsuse = malloc(sizeof(struct long_ad)); + memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad)); + + return error; +} + + +static int +udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir, + struct long_ad *parent_icb, struct long_ad *dir_icb) +{ + union dscrptr *dir_dscr, *dscr; + struct fileid_desc *fid; + struct long_ad icb, data_icb, dirdata_icb; + fsnode *cur; + fsinode *fnode; + size_t pathlen = strlen(dir); + size_t dirlen; + char *mydir = dir + pathlen; + uint32_t nblk, ddoff; + uint32_t softlink_len; + uint8_t *softlink_buf; + uint8_t *dirdata; + int error, ret, retval; + + /* claim disc space for the (e)fe descriptor for this dir */ + udf_metadata_alloc(1, dir_icb); + + /* create new e(fe) */ + udf_create_new_file(&root->inode->st, &dir_dscr, + UDF_ICB_FILETYPE_DIRECTORY, dir_icb); + + /* claim space for the directory contents */ + dirlen = root->inode->st.st_size; + nblk = udf_datablocks(dirlen); + if (nblk > 0) { + /* claim disc space for the dir contents */ + udf_data_alloc(nblk, &dirdata_icb); + } + + /* allocate memory for the directory contents */ + nblk++; + dirdata = malloc(nblk * context.sector_size); + assert(dirdata); + memset(dirdata, 0, nblk * context.sector_size); + + /* create and append '..' */ + fid = (struct fileid_desc *) dirdata; + ddoff = udf_create_parentfid(fid, parent_icb); + + /* for '..' */ + udf_inc_link(dir_dscr); + + /* recurse */ + retval = 0; + for (cur = root; cur != NULL; cur = cur->next) { + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + + fid = (struct fileid_desc *) (dirdata + ddoff); + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + retval = 2; + break; + case S_IFCHR: + case S_IFBLK: + /* not supported */ + retval = 2; + warnx("device node %s not supported", dir); + break; + case S_IFDIR: + /* not an empty dir? */ + if (strcmp(cur->name, ".") == 0) + break; + assert(cur->child); + if (cur->child) { + ret = udf_populate_walk(fsopts, cur->child, + dir, dir_icb, &icb); + if (ret) + retval = 2; + } + udf_create_fid(ddoff, fid, cur->name, + UDF_FILE_CHAR_DIR, &icb); + udf_inc_link(dir_dscr); + ddoff += udf_fidsize(fid); + break; + case S_IFREG: + fnode = cur->inode; + /* don't re-copy hard-links */ + if (!(fnode->flags & FI_WRITTEN)) { + printf("%s", dir); + error = udf_copy_file(&fnode->st, dir, cur, + fid, &icb); + if (!error) { + fnode->flags |= FI_WRITTEN; + udf_create_fid(ddoff, fid, cur->name, + 0, &icb); + ddoff += udf_fidsize(fid); + } else { + retval = 2; + } + } else { + /* hardlink! */ + printf("%s (hardlink)\n", dir); + udf_create_fid(ddoff, fid, cur->name, + 0, (struct long_ad *) (fnode->fsuse)); + ddoff += udf_fidsize(fid); + } + fnode->nlink--; + if (fnode->nlink == 0) + free(fnode->fsuse); + break; + case S_IFLNK: + /* softlink */ + fnode = cur->inode; + printf("%s -> %s\n", dir, cur->symlink); + error = udf_encode_symlink(&softlink_buf, + &softlink_len, cur->symlink); + if (error) { + printf("SOFTLINK error %d\n", error); + retval = 2; + break; + } + + udf_metadata_alloc(1, &icb); + udf_create_new_file(&fnode->st, &dscr, + UDF_ICB_FILETYPE_SYMLINK, &icb); + + nblk = udf_datablocks(softlink_len); + if (nblk > 0) + udf_data_alloc(nblk, &data_icb); + udf_append_file_contents(dscr, &data_icb, + softlink_buf, softlink_len); + + /* write out dscr (e)fe */ + udf_inc_link(dscr); + udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num), + udf_rw16(icb.loc.part_num), 1); + + free(dscr); + free(softlink_buf); + + udf_create_fid(ddoff, fid, cur->name, 0, &icb); + ddoff += udf_fidsize(fid); + break; + } + mydir[0] = '\0'; + } + + /* writeout directory contents */ + dirlen = ddoff; /* XXX might bite back */ + + udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen); + udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen); + + /* write out dir_dscr (e)fe */ + udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num), + udf_rw16(dir_icb->loc.part_num), 1); + + free(dirdata); + free(dir_dscr); + return retval; +} + + +static int +udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts, + struct udf_stats *stats) +{ + struct long_ad rooticb; + static char path[MAXPATHLEN+1]; + int error; + + /* make sure the root gets the rootdir entry */ + context.metadata_alloc_pos = layout.rootdir; + context.data_alloc_pos = layout.rootdir; + + strncpy(path, dir, sizeof(path)); + error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb); + + return error; +} + + +static void +udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts, + struct udf_stats *stats) +{ + char path[MAXPATHLEN + 1]; + off_t proposed_size; + uint32_t n, nblk; + + strncpy(path, dir, sizeof(path)); + + /* calculate strict minimal size */ + udf_estimate_walk(fsopts, root, path, stats); + printf("ndirs %d\n", stats->ndirs); + printf("nfiles %d\n", stats->nfiles); + printf("ndata_blocks %d\n", stats->ndatablocks); + printf("nmetadata_blocks %d\n", stats->nmetadatablocks); + printf("\n"); + + /* adjust for options : free file nodes */ + if (fsopts->freefiles) { + /* be mercifull and reserve more for the FID */ + stats->nmetadatablocks += fsopts->freefiles * 1.5; + } else if ((n = fsopts->freefilepc)) { + stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n); + } + + /* adjust for options : free data blocks */ + if (fsopts->freeblocks) { + stats->ndatablocks += fsopts->freeblocks; + } else if ((n = fsopts->freeblockpc)) { + stats->ndatablocks += (stats->ndatablocks * n) / (100-n); + } + + /* rough predictor of minimum disc size */ + nblk = stats->ndatablocks + stats->nmetadatablocks; + nblk = (double) nblk * (1.0 + 1.0/8.0); /* free space map */ + nblk += 256; /* pre-volume space */ + nblk += 256; /* post-volume space */ + nblk += 64; /* safeguard */ + + /* try to honour minimum size */ + n = fsopts->minsize / fsopts->sectorsize; + if (nblk < n) { + stats->ndatablocks += (n - nblk); + nblk += n - nblk; + } + proposed_size = (off_t) nblk * fsopts->sectorsize; + /* sanity size */ + if (proposed_size < 512*1024) + proposed_size = 512*1024; + + if (fsopts->size) { + if (fsopts->size < proposed_size) + err(EINVAL, "makefs_udf: won't fit on disc!"); + } else { + fsopts->size = proposed_size; + } + + fsopts->inodes = stats->nfiles + stats->ndirs; +} + + +void +udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct udf_stats stats; + uint64_t truncate_len; + char scrap[255]; + int error; + + /* determine format */ + udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); + printf("req_enable %d, req_disable %d\n", req_enable, req_disable); + + context.sector_size = fsopts->sectorsize; + error = udf_derive_format(req_enable, req_disable, false); + if (error) + err(EINVAL, "makefs_udf: can't determine format"); + + /* names */ + error = udf_proces_names(); + if (error) + err(EINVAL, "makefs_udf: bad names given"); + + /* set return value to 1 indicating error */ + error = 1; + + /* estimate the amount of space needed */ + memset(&stats, 0, sizeof(stats)); + udf_enumerate_and_estimate(dir, root, fsopts, &stats); + + printf("Calculated size of `%s': %lld bytes, %ld inodes\n", + image, (long long)fsopts->size, (long)fsopts->inodes); + + /* create file image */ + if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + err(EXIT_FAILURE, "%s", image); + } + if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, &fd, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + fsopts->fd = fd; + + /* calculate metadata percentage */ + meta_fract = fsopts->size / (stats.nmetadatablocks*fsopts->sectorsize); + meta_fract = ((int) ((meta_fract + 0.005)*100.0)) / 100; + + /* update mmc info but now with correct size */ + udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); + + printf("Building disc compatible with UDF version %x to %x\n\n", + context.min_udf, context.max_udf); + (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS, + (uint64_t) format_flags); + printf("UDF properties %s\n", scrap); + printf("Volume set `%s'\n", context.volset_name); + printf("Primary volume `%s`\n", context.primary_name); + printf("Logical volume `%s`\n", context.logvol_name); + if (format_flags & FORMAT_META) + printf("Metadata percentage %d %%\n", + (int) (100.0*stats.ndatablocks/stats.nmetadatablocks)); + printf("\n"); + udf_do_newfs_prefix(); + + /* update context */ + context.unique_id = 0; + + /* XXX are the next two needed? or should be re-count them? */ + context.num_files = stats.nfiles; + context.num_directories = stats.ndirs; + + error = udf_populate(dir, root, fsopts, &stats); + + udf_do_newfs_postfix(); + + if (format_flags & FORMAT_VAT) { + truncate_len = context.vtop_offset[context.data_part] + + context.data_alloc_pos; + truncate_len *= context.sector_size; + + printf("\nTruncing the disc-image to allow for VAT\n"); + printf("Free space left on this volume approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n", + (fsopts->size - truncate_len)/1024, + (fsopts->size - truncate_len)/1024/1024); + ftruncate(fd, truncate_len); + } + + if (error) { + error = 2; /* some files couldn't be added */ + goto err_exit; + } + + close(fd); + return; + +err_exit: + close(fd); + if (error == 2) { + errx(error, "Not all files could be added"); + } else { + errx(error, "creation of %s failed", image); + } +} + diff --git a/usr.sbin/makefs/udf/Makefile.inc b/usr.sbin/makefs/udf/Makefile.inc new file mode 100644 index 000000000..8d47e9ba7 --- /dev/null +++ b/usr.sbin/makefs/udf/Makefile.inc @@ -0,0 +1,13 @@ +# $NetBSD: Makefile.inc,v 1.2 2013/08/05 18:45:00 reinoud Exp $ +# + +UDF= ${NETBSDSRCDIR}/sys/fs/udf +UDF_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_udf +FSCK= ${NETBSDSRCDIR}/sbin/fsck # use progress meter. + +.PATH: ${.CURDIR}/udf ${UDF} ${UDF_NEWFS} ${FSCK} + +CPPFLAGS+= -I${UDF} -I${UDF_NEWFS} -I${FSCK} + +SRCS += udf_create.c udf_write.c udf_osta.c + diff --git a/usr.sbin/makefs/udf/cdio_mmc_structs.h b/usr.sbin/makefs/udf/cdio_mmc_structs.h new file mode 100644 index 000000000..8e5992de7 --- /dev/null +++ b/usr.sbin/makefs/udf/cdio_mmc_structs.h @@ -0,0 +1,163 @@ +/* $NetBSD: cdio_mmc_structs.h,v 1.1 2013/08/05 18:44:16 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * 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. + * + */ + +#ifndef _CDIO_MMC_EMU_H_ +#define _CDIO_MMC_EMU_H_ + +#include + +/* + * MMC device abstraction interface. + * + * It gathers information from GET_CONFIGURATION, READ_DISCINFO, + * READ_TRACKINFO, READ_TOC2, READ_CD_CAPACITY and GET_CONFIGURATION + * SCSI/ATAPI calls regardless if its a legacy CD-ROM/DVD-ROM device or a MMC + * standard recordable device. + */ +struct mmc_discinfo { + uint16_t mmc_profile; + uint16_t mmc_class; + + uint8_t disc_state; + uint8_t last_session_state; + uint8_t bg_format_state; + uint8_t link_block_penalty; /* in sectors */ + + uint64_t mmc_cur; /* current MMC_CAPs */ + uint64_t mmc_cap; /* possible MMC_CAPs */ + + uint32_t disc_flags; /* misc flags */ + + uint32_t disc_id; + uint64_t disc_barcode; + uint8_t application_code; /* 8 bit really */ + + uint8_t unused1[3]; /* padding */ + + uint32_t last_possible_lba; /* last leadout start adr. */ + uint32_t sector_size; + + uint16_t num_sessions; + uint16_t num_tracks; /* derived */ + + uint16_t first_track; + uint16_t first_track_last_session; + uint16_t last_track_last_session; + + uint16_t unused2; /* padding/misc info resv. */ + + uint16_t reserved1[4]; /* MMC-5 track resources */ + uint32_t reserved2[3]; /* MMC-5 POW resources */ + + uint32_t reserved3[8]; /* MMC-5+ */ +}; +#define MMCGETDISCINFO _IOR('c', 28, struct mmc_discinfo) + +#define MMC_CLASS_UNKN 0 +#define MMC_CLASS_DISC 1 +#define MMC_CLASS_CD 2 +#define MMC_CLASS_DVD 3 +#define MMC_CLASS_MO 4 +#define MMC_CLASS_BD 5 +#define MMC_CLASS_FILE 0xffff /* emulation mode */ + +#define MMC_DFLAGS_BARCODEVALID (1 << 0) /* barcode is present and valid */ +#define MMC_DFLAGS_DISCIDVALID (1 << 1) /* discid is present and valid */ +#define MMC_DFLAGS_APPCODEVALID (1 << 2) /* application code valid */ +#define MMC_DFLAGS_UNRESTRICTED (1 << 3) /* restricted, then set app. code */ + +#define MMC_DFLAGS_FLAGBITS \ + "\10\1BARCODEVALID\2DISCIDVALID\3APPCODEVALID\4UNRESTRICTED" + +#define MMC_CAP_SEQUENTIAL (1 << 0) /* sequential writable only */ +#define MMC_CAP_RECORDABLE (1 << 1) /* record-able; i.e. not static */ +#define MMC_CAP_ERASABLE (1 << 2) /* drive can erase sectors */ +#define MMC_CAP_BLANKABLE (1 << 3) /* media can be blanked */ +#define MMC_CAP_FORMATTABLE (1 << 4) /* media can be formatted */ +#define MMC_CAP_REWRITABLE (1 << 5) /* media can be rewritten */ +#define MMC_CAP_MRW (1 << 6) /* Mount Rainier formatted */ +#define MMC_CAP_PACKET (1 << 7) /* using packet recording */ +#define MMC_CAP_STRICTOVERWRITE (1 << 8) /* only writes a packet at a time */ +#define MMC_CAP_PSEUDOOVERWRITE (1 << 9) /* overwrite through replacement */ +#define MMC_CAP_ZEROLINKBLK (1 << 10) /* zero link block length capable */ +#define MMC_CAP_HW_DEFECTFREE (1 << 11) /* hardware defect management */ + +#define MMC_CAP_FLAGBITS \ + "\10\1SEQUENTIAL\2RECORDABLE\3ERASABLE\4BLANKABLE\5FORMATTABLE" \ + "\6REWRITABLE\7MRW\10PACKET\11STRICTOVERWRITE\12PSEUDOOVERWRITE" \ + "\13ZEROLINKBLK\14HW_DEFECTFREE" + +#define MMC_STATE_EMPTY 0 +#define MMC_STATE_INCOMPLETE 1 +#define MMC_STATE_FULL 2 +#define MMC_STATE_CLOSED 3 + +#define MMC_BGFSTATE_UNFORM 0 +#define MMC_BGFSTATE_STOPPED 1 +#define MMC_BGFSTATE_RUNNING 2 +#define MMC_BGFSTATE_COMPLETED 3 + + +struct mmc_trackinfo { + uint16_t tracknr; /* IN/OUT */ + uint16_t sessionnr; + + uint8_t track_mode; + uint8_t data_mode; + + uint16_t flags; + + uint32_t track_start; + uint32_t next_writable; + uint32_t free_blocks; + uint32_t packet_size; + uint32_t track_size; + uint32_t last_recorded; +}; +#define MMCGETTRACKINFO _IOWR('c', 29, struct mmc_trackinfo) + +#define MMC_TRACKINFO_COPY (1 << 0) +#define MMC_TRACKINFO_DAMAGED (1 << 1) +#define MMC_TRACKINFO_FIXED_PACKET (1 << 2) +#define MMC_TRACKINFO_INCREMENTAL (1 << 3) +#define MMC_TRACKINFO_BLANK (1 << 4) +#define MMC_TRACKINFO_RESERVED (1 << 5) +#define MMC_TRACKINFO_NWA_VALID (1 << 6) +#define MMC_TRACKINFO_LRA_VALID (1 << 7) +#define MMC_TRACKINFO_DATA (1 << 8) +#define MMC_TRACKINFO_AUDIO (1 << 9) +#define MMC_TRACKINFO_AUDIO_4CHAN (1 << 10) +#define MMC_TRACKINFO_PRE_EMPH (1 << 11) + +#define MMC_TRACKINFO_FLAGBITS \ + "\10\1COPY\2DAMAGED\3FIXEDPACKET\4INCREMENTAL\5BLANK" \ + "\6RESERVED\7NWA_VALID\10LRA_VALID\11DATA\12AUDIO" \ + "\13AUDIO_4CHAN\14PRE_EMPH" + +#endif /* _CDIO_MMC_EMU_H_ */ + diff --git a/usr.sbin/makefs/v7fs.c b/usr.sbin/makefs/v7fs.c new file mode 100644 index 000000000..7c479a35e --- /dev/null +++ b/usr.sbin/makefs/v7fs.c @@ -0,0 +1,191 @@ +/* $NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_makefs.h" +#include "newfs_v7fs.h" + + +#ifndef HAVE_NBTOOL_CONFIG_H +#include "progress.h" +static bool progress_bar_enable; +#endif +int v7fs_newfs_verbose; + +void +v7fs_prep_opts(fsinfo_t *fsopts) +{ + v7fs_opt_t *v7fs_opts = ecalloc(1, sizeof(*v7fs_opts)); + const option_t v7fs_options[] = { + { 'p', "pdp", &v7fs_opts->pdp_endian, OPT_INT32, false, true, + "PDP endian" }, + { 'P', "progress", &v7fs_opts->progress, OPT_INT32, false, true, + "Progress bar" }, + { .name = NULL } + }; + + fsopts->fs_specific = v7fs_opts; + fsopts->fs_options = copy_opts(v7fs_options); +} + +void +v7fs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +v7fs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + + return set_option_var(fsopts->fs_options, option, "1", NULL, 0) != -1; +} + +void +v7fs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct v7fs_mount_device v7fs_mount; + int fd, endian, error = 1; + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + + v7fs_newfs_verbose = debug; +#ifndef HAVE_NBTOOL_CONFIG_H + if ((progress_bar_enable = v7fs_opts->progress)) { + progress_switch(progress_bar_enable); + progress_init(); + progress(&(struct progress_arg){ .cdev = image }); + } +#endif + + /* Determine filesystem image size */ + v7fs_estimate(dir, root, fsopts); + printf("Calculated size of `%s': %lld bytes, %ld inodes\n", + image, (long long)fsopts->size, (long)fsopts->inodes); + + if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + err(EXIT_FAILURE, "%s", image); + } + if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, &fd, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + fsopts->fd = fd; + v7fs_mount.device.fd = fd; + +#if !defined BYTE_ORDER +#error +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + if (fsopts->needswap) + endian = BIG_ENDIAN; + else + endian = LITTLE_ENDIAN; +#else + if (fsopts->needswap) + endian = LITTLE_ENDIAN; + else + endian = BIG_ENDIAN; +#endif + if (v7fs_opts->pdp_endian) { + endian = PDP_ENDIAN; + } + + v7fs_mount.endian = endian; + v7fs_mount.sectors = fsopts->size >> V7FS_BSHIFT; + if (v7fs_newfs(&v7fs_mount, fsopts->inodes) != 0) { + goto err_exit; + } + + if (v7fs_populate(dir, root, fsopts, &v7fs_mount) != 0) { + error = 2; /* some files couldn't add */ + goto err_exit; + } + + close(fd); + return; + + err_exit: + close(fd); + err(error, "%s", image); +} + +void +progress(const struct progress_arg *p) +{ +#ifndef HAVE_NBTOOL_CONFIG_H + static struct progress_arg Progress; + static char cdev[32]; + static char label[32]; + + if (!progress_bar_enable) + return; + + if (p) { + Progress = *p; + if (p->cdev) + strcpy(cdev, p->cdev); + if (p->label) + strcpy(label, p->label); + } + + if (!Progress.tick) + return; + if (++Progress.cnt > Progress.tick) { + Progress.cnt = 0; + Progress.total++; + progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE); + } +#endif +} diff --git a/usr.sbin/makefs/v7fs/Makefile.inc b/usr.sbin/makefs/v7fs/Makefile.inc new file mode 100644 index 000000000..6c8163d1c --- /dev/null +++ b/usr.sbin/makefs/v7fs/Makefile.inc @@ -0,0 +1,20 @@ +# $NetBSD: Makefile.inc,v 1.5 2013/01/23 21:03:15 christos Exp $ +# + +V7FS= ${NETBSDSRCDIR}/sys/fs/v7fs +V7FS_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_v7fs +FSCK= ${NETBSDSRCDIR}/sbin/fsck # use progress meter. + +.PATH: ${.CURDIR}/v7fs ${V7FS} ${V7FS_NEWFS} ${FSCK} + +CPPFLAGS+= -DV7FS_EI -I${V7FS} -I${V7FS_NEWFS} -I${FSCK} + +SRCS += v7fs_endian.c v7fs_superblock.c v7fs_superblock_util.c v7fs_inode.c \ +v7fs_inode_util.c v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_file.c \ +v7fs_file_util.c v7fs_io_user.c +SRCS += main.c # newfs +.if !defined(HOSTPROG) +SRCS += progress.c # progress bar (fsck) +.endif + +SRCS += v7fs_estimate.c v7fs_populate.c diff --git a/usr.sbin/makefs/v7fs/v7fs_estimate.c b/usr.sbin/makefs/v7fs/v7fs_estimate.c new file mode 100644 index 000000000..74b7e25a6 --- /dev/null +++ b/usr.sbin/makefs/v7fs/v7fs_estimate.c @@ -0,0 +1,275 @@ +/* $NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include /*MAXPATHLEN */ +#endif + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" +#include "v7fs_makefs.h" + +struct v7fs_geometry { + v7fs_daddr_t ndatablock; + v7fs_ino_t ninode; + v7fs_daddr_t npuredatablk; +}; + +#define VPRINTF(fmt, args...) { if (v7fs_newfs_verbose) printf(fmt, ##args); } + +static int +v7fs_datablock_size(off_t sz, v7fs_daddr_t *nblk) +{ + struct v7fs_daddr_map map; + int error = 0; + + if (sz == 0) { + *nblk = 0; + return 0; + } + + if ((error = v7fs_datablock_addr(sz, &map))) { + return error; + } + switch (map.level) { + case 0: /* Direct */ + *nblk = map.index[0] + 1; + break; + case 1: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + map.index[0] + 1; + break; + case 2: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + + V7FS_DADDR_PER_BLOCK +/*idx1 */ + 1/*addr[V7FS_NADDR_INDEX2]*/ + + map.index[0] + /* # of idx2 index block(filled) */ + map.index[0] * V7FS_DADDR_PER_BLOCK + /* of its datablocks*/ + 1 + /*current idx2 indexblock */ + map.index[1] + 1; + break; + case 3: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + + V7FS_DADDR_PER_BLOCK +/*idx1 */ + 1/*addr[V7FS_NADDR_INDEX2]*/ + + V7FS_DADDR_PER_BLOCK + /* # of idx2 index block */ + V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK + + /*idx2 datablk */ + 1/*addr[v7FS_NADDR_INDEX3*/ + + map.index[0] + /* # of lv1 index block(filled) */ + map.index[0] * V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK + + 1 + /*lv1 */ + map.index[1] + /* #of lv2 index block(filled) */ + map.index[1] * V7FS_DADDR_PER_BLOCK + /*lv2 datablock */ + 1 + /* current lv2 index block */ + map.index[2] + 1; /*filled datablock */ + break; + default: + *nblk = 0; + error = EINVAL; + break; + } + + return error; +} + +static int +estimate_size_walk(fsnode *root, char *dir, struct v7fs_geometry *geom) +{ + fsnode *cur; + int nentries; + size_t pathlen = strlen(dir); + char *mydir = dir + pathlen; + fsinode *fnode; + v7fs_daddr_t nblk; + int n; + off_t sz; + + for (cur = root, nentries = 0; cur != NULL; cur = cur->next, + nentries++, geom->ninode++) { + switch (cur->type & S_IFMT) { + default: + break; + case S_IFDIR: + if (!cur->child) + break; + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + n = estimate_size_walk(cur->child, dir, geom); + sz = (n + 1/*..*/) * sizeof(struct v7fs_dirent); + v7fs_datablock_size(sz, &nblk); + mydir[0] = '\0'; + geom->ndatablock += nblk; + geom->npuredatablk += + V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT; + break; + case S_IFREG: + fnode = cur->inode; + if (!(fnode->flags & FI_SIZED)) { /*Skip hard-link */ + sz = fnode->st.st_size; + v7fs_datablock_size(sz, &nblk); + geom->ndatablock += nblk; + geom->npuredatablk += + V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT; + fnode->flags |= FI_SIZED; + } + + break; + case S_IFLNK: + nblk = V7FSBSD_MAXSYMLINKLEN >> V7FS_BSHIFT; + geom->ndatablock += nblk; + geom->npuredatablk += nblk; + break; + } + } + + return nentries; +} + +static v7fs_daddr_t +calculate_fs_size(const struct v7fs_geometry *geom) +{ + v7fs_daddr_t fs_blk, ilist_blk; + + ilist_blk = V7FS_ROUND_BSIZE(geom->ninode * + sizeof(struct v7fs_inode_diskimage)) >> V7FS_BSHIFT; + fs_blk = geom->ndatablock + ilist_blk + V7FS_ILIST_SECTOR; + + VPRINTF("datablock:%d ilistblock:%d total:%d\n", geom->ndatablock, + ilist_blk, fs_blk); + + return fs_blk; +} + +static void +determine_fs_size(fsinfo_t *fsopts, struct v7fs_geometry *geom) +{ + v7fs_daddr_t nblk = geom->ndatablock; + v7fs_daddr_t fsblk, n; + int32_t nfiles = geom->ninode; + + VPRINTF("size=%lld inodes=%lld fd=%d superb=%p onlyspec=%d\n", + (long long)fsopts->size, (long long)fsopts->inodes, fsopts->fd, + fsopts->superblock, fsopts->onlyspec); + VPRINTF("minsize=%lld maxsize=%lld freefiles=%lld freefilepc=%d " + "freeblocks=%lld freeblockpc=%d sectorseize=%d\n", + (long long)fsopts->minsize, (long long)fsopts->maxsize, + (long long)fsopts->freefiles, fsopts->freefilepc, + (long long)fsopts->freeblocks, fsopts->freeblockpc, + fsopts->sectorsize); + + if ((fsopts->sectorsize > 0) && (fsopts->sectorsize != V7FS_BSIZE)) + warnx("v7fs sector size is 512byte only. '-S %d' is ignored.", + fsopts->sectorsize); + + /* Free inode */ + if (fsopts->freefiles) { + nfiles += fsopts->freefiles; + } else if ((n = fsopts->freefilepc)) { + nfiles += (nfiles * n) / (100 - n); + } + if (nfiles >= V7FS_INODE_MAX) { + errx(EXIT_FAILURE, "# of files(%d) over v7fs limit(%d).", + nfiles, V7FS_INODE_MAX); + } + + /* Free datablock */ + if (fsopts->freeblocks) { + nblk += fsopts->freeblocks; + } else if ((n = fsopts->freeblockpc)) { + nblk += (nblk * n) / (100 - n); + } + + /* Total size */ + geom->ndatablock = nblk; + geom->ninode = nfiles; + fsblk = calculate_fs_size(geom); + + if (fsblk >= V7FS_DADDR_MAX) { + errx(EXIT_FAILURE, "filesystem size(%d) over v7fs limit(%d).", + fsblk, V7FS_DADDR_MAX); + } + + n = fsopts->minsize >> V7FS_BSHIFT; + if (fsblk < n) + geom->ndatablock += (n - fsblk); + + n = fsopts->maxsize >> V7FS_BSHIFT; + if (fsopts->maxsize > 0 && fsblk > n) { + errx(EXIT_FAILURE, "# of datablocks %d > %d", fsblk, n); + } +} + +void +v7fs_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + char path[MAXPATHLEN + 1]; + int ndir; + off_t sz; + v7fs_daddr_t nblk; + struct v7fs_geometry geom; + + memset(&geom , 0, sizeof(geom)); + strncpy(path, dir, sizeof(path)); + + /* Calculate strict size. */ + ndir = estimate_size_walk(root, path, &geom); + sz = (ndir + 1/*..*/) * sizeof(struct v7fs_dirent); + v7fs_datablock_size(sz, &nblk); + geom.ndatablock += nblk; + + /* Consider options. */ + determine_fs_size(fsopts, &geom); + + fsopts->size = calculate_fs_size(&geom) << V7FS_BSHIFT; + fsopts->inodes = geom.ninode; + v7fs_opts->npuredatablk = geom.npuredatablk; /* for progress bar */ +} diff --git a/usr.sbin/makefs/v7fs/v7fs_populate.c b/usr.sbin/makefs/v7fs/v7fs_populate.c new file mode 100644 index 000000000..5b6215858 --- /dev/null +++ b/usr.sbin/makefs/v7fs/v7fs_populate.c @@ -0,0 +1,298 @@ +/* $NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" +#include "v7fs_datablock.h" +#include "v7fs_endian.h" +#include "v7fs_file.h" +#include "v7fs_makefs.h" +#include "newfs_v7fs.h" + +#define VPRINTF(fmt, args...) { if (v7fs_newfs_verbose) printf(fmt, ##args); } + +static void +attr_setup(fsnode *node, struct v7fs_fileattr *attr) +{ + struct stat *st = &node->inode->st; + + attr->mode = node->type | st->st_mode; + attr->uid = st->st_uid; + attr->gid = st->st_gid; + attr->device = 0; + attr->ctime = st->st_ctime; + attr->atime = st->st_atime; + attr->mtime = st->st_mtime; +} + +static int +allocate(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, + dev_t dev, struct v7fs_inode *inode) +{ + int error; + v7fs_ino_t ino; + struct v7fs_fileattr attr; + + memset(inode, 0, sizeof(*inode)); + + attr_setup(node, &attr); + attr.device = dev; + if ((error = v7fs_file_allocate(fs, parent_inode, node->name, &attr, + &ino))) { + errno = error; + warn("%s", node->name); + return error; + } + node->inode->ino = ino; + node->inode->flags |= FI_ALLOCATED; + if ((error = v7fs_inode_load(fs, inode, ino))) { + errno = error; + warn("%s", node->name); + return error; + } + + return 0; +} + +struct copy_arg { + int fd; + uint8_t buf[V7FS_BSIZE]; +}; + +static int +copy_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct copy_arg *p = ctx; + + if (read(p->fd, p->buf, sz) != (ssize_t)sz) { + return V7FS_ITERATOR_ERROR; + } + + if (!fs->io.write(fs->io.cookie, p->buf, blk)) { + errno = EIO; + return V7FS_ITERATOR_ERROR; + } + progress(0); + + return 0; +} + +static int +file_copy(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, + const char *filepath) +{ + struct v7fs_inode inode; + const char *errmsg; + fsinode *fnode = node->inode; + int error = 0; + int fd; + + /* Check hard-link */ + if ((fnode->nlink > 1) && (fnode->flags & FI_ALLOCATED)) { + if ((error = v7fs_inode_load(fs, &inode, fnode->ino))) { + errmsg = "inode load"; + goto err_exit; + } + if ((error = v7fs_file_link(fs, parent_inode, &inode, + node->name))) { + errmsg = "hard link"; + goto err_exit; + } + return 0; + } + + /* Allocate file */ + if ((error = allocate(fs, parent_inode, node, 0, &inode))) { + errmsg = "file allocate"; + goto err_exit; + } + if ((error = v7fs_datablock_expand(fs, &inode, fnode->st.st_size))) { + errmsg = "datablock expand"; + goto err_exit; + } + + /* Data copy */ + if ((fd = open(filepath, O_RDONLY)) == -1) { + error = errno; + errmsg = "source file"; + goto err_exit; + } + + error = v7fs_datablock_foreach(fs, &inode, copy_subr, + &(struct copy_arg){ .fd = fd }); + if (error != V7FS_ITERATOR_END) { + errmsg = "data copy"; + close(fd); + goto err_exit; + } else { + error = 0; + } + close(fd); + + return error; + +err_exit: + errno = error; + warn("%s %s", node->name, errmsg); + + return error; +} + +static int +populate_walk(struct v7fs_self *fs, struct v7fs_inode *parent_inode, + fsnode *root, char *dir) +{ + fsnode *cur; + char *mydir = dir + strlen(dir); + char srcpath[MAXPATHLEN + 1]; + struct v7fs_inode inode; + int error = 0; + bool failed = false; + + for (cur = root; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + VPRINTF("%x\n", cur->flags & S_IFMT); + break; + case S_IFCHR: + /*FALLTHROUGH*/ + case S_IFBLK: + if ((error = allocate(fs, parent_inode, cur, + cur->inode->st.st_rdev, &inode))) { + errno = error; + warn("%s", cur->name); + } + break; + case S_IFDIR: + if (!cur->child) /*'.'*/ + break; + /* Allocate this directory. */ + if ((error = allocate(fs, parent_inode, cur, 0, + &inode))) { + errno = error; + warn("%s", cur->name); + } else { + /* Populate children. */ + mydir[0] = '/'; + strcpy(&mydir[1], cur->name); + error = populate_walk(fs, &inode, cur->child, + dir); + mydir[0] = '\0'; + } + break; + case S_IFREG: + snprintf(srcpath, sizeof(srcpath), "%s/%s", dir, + cur->name); + error = file_copy(fs, parent_inode, cur, srcpath); + break; + case S_IFLNK: + if ((error = allocate(fs, parent_inode, cur, 0, + &inode))) { + errno = error; + warn("%s", cur->name); + } else { + v7fs_file_symlink(fs, &inode, cur->symlink); + } + break; + } + if (error) + failed = true; + } + + return failed ? 2 : 0; +} + +int +v7fs_populate(const char *dir, fsnode *root, fsinfo_t *fsopts, + const struct v7fs_mount_device *device) +{ + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + static char path[MAXPATHLEN + 1]; + struct v7fs_inode root_inode; + struct v7fs_self *fs; + int error; + + if ((error = v7fs_io_init(&fs, device, V7FS_BSIZE))) { + errno = error; + warn("I/O setup failed."); + return error; + } + fs->endian = device->endian; + v7fs_endian_init(fs); + + if ((error = v7fs_superblock_load(fs))) { + errno = error; + warn("Can't load superblock."); + return error; + } + fsopts->superblock = &fs->superblock; /* not used. */ + + if ((error = v7fs_inode_load(fs, &root_inode, V7FS_ROOT_INODE))) { + errno = error; + warn("Can't load root inode."); + return error; + } + + progress(&(struct progress_arg){ .label = "populate", .tick = + v7fs_opts->npuredatablk / PROGRESS_BAR_GRANULE }); + + strncpy(path, dir, sizeof(path)); + error = populate_walk(fs, &root_inode, root, path); + + v7fs_inode_writeback(fs, &root_inode); + v7fs_superblock_writeback(fs); + + return error; +} diff --git a/usr.sbin/makefs/v7fs_makefs.h b/usr.sbin/makefs/v7fs_makefs.h new file mode 100644 index 000000000..1c047caa4 --- /dev/null +++ b/usr.sbin/makefs/v7fs_makefs.h @@ -0,0 +1,49 @@ +/* $NetBSD: v7fs_makefs.h,v 1.2 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * 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 _MAKEFS_V7FS_H_ +#define _MAKEFS_V7FS_H_ + +typedef struct { + int pdp_endian; + int progress; + v7fs_daddr_t npuredatablk; /* for progress bar */ +} v7fs_opt_t; + +__BEGIN_DECLS +void v7fs_estimate(const char *, fsnode *, fsinfo_t *); +int v7fs_populate(const char *, fsnode *, fsinfo_t *, + const struct v7fs_mount_device *); +struct progress_arg; +void progress(const struct progress_arg *); +extern int v7fs_newfs_verbose; +__END_DECLS +#endif /*!_MAKEFS_V7FS_H_*/ diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c new file mode 100644 index 000000000..5e585ac83 --- /dev/null +++ b/usr.sbin/makefs/walk.c @@ -0,0 +1,691 @@ +/* $NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" + +static void apply_specdir(const char *, NODE *, fsnode *, int); +static void apply_specentry(const char *, NODE *, fsnode *); +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; + 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); + } +} + +/* + * apply_specfile -- + * read in the mtree(8) specfile, and apply it to the tree + * at dir,parent. parameters in parent on equivalent types + * will be changed to those found in specfile, and missing + * entries will be added. + */ +void +apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) +{ + struct timeval start; + FILE *fp; + NODE *root; + + assert(specfile != NULL); + assert(parent != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); + + /* read in the specfile */ + if ((fp = fopen(specfile, "r")) == NULL) + err(1, "Can't open `%s'", specfile); + TIMER_START(start); + root = spec(fp); + TIMER_RESULTS(start, "spec"); + if (fclose(fp) == EOF) + err(1, "Can't close `%s'", specfile); + + /* perform some sanity checks */ + if (root == NULL) + errx(1, "Specfile `%s' did not contain a tree", specfile); + assert(strcmp(root->name, ".") == 0); + assert(root->type == F_DIR); + + /* merge in the changes */ + apply_specdir(dir, root, parent, speconly); + + free_nodes(root); +} + +static void +apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) +{ + char path[MAXPATHLEN + 1]; + NODE *curnode; + fsnode *curfsnode; + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); + + if (specnode->type != F_DIR) + errx(1, "Specfile node `%s/%s' is not a directory", + dir, specnode->name); + if (dirnode->type != S_IFDIR) + errx(1, "Directory node `%s/%s' is not a directory", + dir, dirnode->name); + + apply_specentry(dir, specnode, dirnode); + + /* Remove any filesystem nodes not found in specfile */ + /* XXX inefficient. This is O^2 in each dir and it would + * have been better never to have walked this part of the tree + * to begin with + */ + if (speconly) { + fsnode *next; + assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0'); + for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) { + next = curfsnode->next; + for (curnode = specnode->child; curnode != NULL; + curnode = curnode->next) { + if (strcmp(curnode->name, curfsnode->name) == 0) + break; + } + if (curnode == NULL) { + if (debug & DEBUG_APPLY_SPECONLY) { + printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode); + } + free_fsnodes(curfsnode); + } + } + } + + /* now walk specnode->child matching up with dirnode */ + for (curnode = specnode->child; curnode != NULL; + curnode = curnode->next) { + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: spec %s\n", + curnode->name); + for (curfsnode = dirnode->next; curfsnode != NULL; + curfsnode = curfsnode->next) { +#if 0 /* too verbose for now */ + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: dirent %s\n", + curfsnode->name); +#endif + if (strcmp(curnode->name, curfsnode->name) == 0) + break; + } + if ((size_t)snprintf(path, sizeof(path), "%s/%s", + dir, curnode->name) >= sizeof(path)) + errx(1, "Pathname too long."); + if (curfsnode == NULL) { /* need new entry */ + struct stat stbuf; + + /* + * don't add optional spec entries + * that lack an existing fs entry + */ + if ((curnode->flags & F_OPT) && + lstat(path, &stbuf) == -1) + continue; + + /* check that enough info is provided */ +#define NODETEST(t, m) \ + if (!(t)) \ + errx(1, "`%s': %s not provided", path, m) + NODETEST(curnode->flags & F_TYPE, "type"); + NODETEST(curnode->flags & F_MODE, "mode"); + /* XXX: require F_TIME ? */ + NODETEST(curnode->flags & F_GID || + curnode->flags & F_GNAME, "group"); + NODETEST(curnode->flags & F_UID || + curnode->flags & F_UNAME, "user"); + if (curnode->type == F_BLOCK || curnode->type == F_CHAR) + NODETEST(curnode->flags & F_DEV, + "device number"); +#undef NODETEST + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: adding %s\n", + curnode->name); + /* build minimal fsnode */ + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_mode = nodetoino(curnode->type); + stbuf.st_nlink = 1; + stbuf.st_mtime = stbuf.st_atime = + stbuf.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + stbuf.st_mtimensec = stbuf.st_atimensec = + stbuf.st_ctimensec = start_time.tv_nsec; +#endif + curfsnode = create_fsnode(".", ".", curnode->name, + &stbuf); + curfsnode->parent = dirnode->parent; + curfsnode->first = dirnode; + curfsnode->next = dirnode->next; + dirnode->next = curfsnode; + if (curfsnode->type == S_IFDIR) { + /* for dirs, make "." entry as well */ + curfsnode->child = create_fsnode(".", ".", ".", + &stbuf); + curfsnode->child->parent = curfsnode; + curfsnode->child->first = curfsnode->child; + } + if (curfsnode->type == S_IFLNK) { + assert(curnode->slink != NULL); + /* for symlinks, copy the target */ + curfsnode->symlink = estrdup(curnode->slink); + } + } + apply_specentry(dir, curnode, curfsnode); + if (curnode->type == F_DIR) { + if (curfsnode->type != S_IFDIR) + errx(1, "`%s' is not a directory", path); + assert (curfsnode->child != NULL); + apply_specdir(path, curnode, curfsnode->child, speconly); + } + } +} + +static void +apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) +{ + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (nodetoino(specnode->type) != dirnode->type) + errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", + dir, specnode->name, inode_type(nodetoino(specnode->type)), + inode_type(dirnode->type)); + + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specentry: %s/%s\n", dir, dirnode->name); + +#define ASEPRINT(t, b, o, n) \ + if (debug & DEBUG_APPLY_SPECENTRY) \ + printf("\t\t\tchanging %s from " b " to " b "\n", \ + t, o, n) + + if (specnode->flags & (F_GID | F_GNAME)) { + ASEPRINT("gid", "%d", + dirnode->inode->st.st_gid, specnode->st_gid); + dirnode->inode->st.st_gid = specnode->st_gid; + } + if (specnode->flags & F_MODE) { + ASEPRINT("mode", "%#o", + dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); + dirnode->inode->st.st_mode &= ~ALLPERMS; + dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); + } + /* XXX: ignoring F_NLINK for now */ + if (specnode->flags & F_SIZE) { + ASEPRINT("size", "%lld", + (long long)dirnode->inode->st.st_size, + (long long)specnode->st_size); + dirnode->inode->st.st_size = specnode->st_size; + } + if (specnode->flags & F_SLINK) { + assert(dirnode->symlink != NULL); + assert(specnode->slink != NULL); + ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); + free(dirnode->symlink); + dirnode->symlink = estrdup(specnode->slink); + } + if (specnode->flags & F_TIME) { + ASEPRINT("time", "%ld", + (long)dirnode->inode->st.st_mtime, + (long)specnode->st_mtimespec.tv_sec); + dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_ctimensec = start_time.tv_nsec; +#endif + } + if (specnode->flags & (F_UID | F_UNAME)) { + ASEPRINT("uid", "%d", + dirnode->inode->st.st_uid, specnode->st_uid); + dirnode->inode->st.st_uid = specnode->st_uid; + } +#if HAVE_STRUCT_STAT_ST_FLAGS + if (specnode->flags & F_FLAGS) { + ASEPRINT("flags", "%#lX", + (unsigned long)dirnode->inode->st.st_flags, + (unsigned long)specnode->st_flags); + dirnode->inode->st.st_flags = specnode->st_flags; + } +#endif + if (specnode->flags & F_DEV) { + ASEPRINT("rdev", "%#llx", + (unsigned long long)dirnode->inode->st.st_rdev, + (unsigned long long)specnode->st_rdev); + dirnode->inode->st.st_rdev = specnode->st_rdev; + } +#undef ASEPRINT + + dirnode->flags |= FSNODE_F_HASSPEC; +} + + +/* + * 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) +{ + + if (S_ISLNK(mode)) + return ("symlink"); /* inotype() returns "link"... */ + return (inotype(mode)); +} + + +/* + * 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; +} From bcaa0d3ac02073415bd7c3c22a3c0e43b93968c4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Thu, 23 Apr 2015 17:30:57 +0200 Subject: [PATCH 3/4] makefs: apply NetBSD PR kern/48852 patch It's a fix for correcting cd9660 device node creation in makefs. This fix was commited in NetBSD on May 30, 2014, after the current NetBSD source code import, hence the cherrypicking. --- usr.sbin/makefs/cd9660/iso9660_rrip.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c index 9189be049..9f7170dff 100644 --- a/usr.sbin/makefs/cd9660/iso9660_rrip.c +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c @@ -1,4 +1,4 @@ -/* $NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $ */ +/* $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 @@ -44,7 +44,7 @@ #include #if defined(__RCSID) && !defined(__lint) -__RCSID("$NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $"); +__RCSID("$NetBSD: iso9660_rrip.c,v 1.14 2014/05/30 13:14:47 martin Exp $"); #endif /* !__lint */ static void cd9660_rrip_initialize_inode(cd9660node *); @@ -657,13 +657,14 @@ 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_dev) > 32) - cd9660_bothendian_dword((uint64_t)fnode->inode->st.st_dev >> 32, + 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_dev & 0xffffffff, + cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff, pn_field->attr.rr_entry.PN.low); return 1; } From 6716cfaba09f58844b5ed6e6e9d416ee42159b9f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Wed, 16 Sep 2015 14:36:11 +0200 Subject: [PATCH 4/4] isofs: reworked for better performance isofs now uses an in-memory directory listing built on-the-fly instead of parsing the ISO 9660 data structures over and over for almost every request. This yields huge performance improvements. The directory listing is allocated dynamically, but Minix servers aren't normally supposed to do that because critical servers would crash if the system runs out of memory. isofs is quite frugal, won't allocate memory after having the whole directory tree cached and is not that critical (its most important job is to serve as a root file system during installation). The benefits and elegance of this scheme far outweights this small problem in practice. --- etc/system.conf | 18 +- minix/fs/isofs/const.h | 12 +- minix/fs/isofs/inode.c | 572 +++++++++--------- minix/fs/isofs/inode.h | 22 +- minix/fs/isofs/link.c | 8 +- minix/fs/isofs/mount.c | 4 +- minix/fs/isofs/path.c | 78 +-- minix/fs/isofs/proto.h | 27 +- minix/fs/isofs/read.c | 36 +- minix/fs/isofs/stadir.c | 2 +- minix/fs/isofs/super.c | 44 +- minix/fs/isofs/susp.c | 3 + minix/fs/isofs/susp_rock_ridge.c | 106 ++-- minix/fs/isofs/uthash.h | 960 +++++++++++++++++++++++++++++++ minix/fs/isofs/utility.c | 72 +-- minix/tests/testisofs.sh | 148 ++++- 16 files changed, 1598 insertions(+), 514 deletions(-) create mode 100644 minix/fs/isofs/uthash.h diff --git a/etc/system.conf b/etc/system.conf index aab9934ff..25b5426ac 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -399,7 +399,11 @@ service isofs { system UMAP # 14 - ; + ; + vm MAPCACHEPAGE + SETCACHEPAGE + CLEARCACHE + ; uid 0; }; @@ -494,12 +498,12 @@ service osscore DEVIO # 21 SDEVIO # 22 ; - pci class + pci class 4/1 # Multimedia / Audio device ; ipc SYSTEM pm rs tty ds vfs vm - pci inet lwip amddev + pci inet lwip amddev ; uid 0; }; @@ -567,14 +571,14 @@ service mmc IRQCTL # 19 ; # Interrupts allowed - irq + irq 64 83 ; # IRQs allowed priority 4; # priority queue 4 }; -service fb +service fb { system UMAP # 14 @@ -597,7 +601,7 @@ service gpio SETCACHEPAGE CLEARCACHE ; - irq + irq 29 # GPIO module 1 (dm37xx) 30 # GPIO module 2 (dm37xx) 31 # GPIO module 3 (dm37xx) @@ -610,7 +614,7 @@ service gpio 97 # GPIO module 0b (am335x) 98 # GPIO module 1a (am335x) 99 # GPIO module 1b (am335x) - ; + ; }; diff --git a/minix/fs/isofs/const.h b/minix/fs/isofs/const.h index 81e80b5ac..516342bd9 100644 --- a/minix/fs/isofs/const.h +++ b/minix/fs/isofs/const.h @@ -3,14 +3,10 @@ #define ISO9660_STANDARD_ID "CD001" /* Standard code for ISO9660 FS */ -#define NR_INODE_RECORDS 64 -#define NR_DIR_EXTENT_RECORDS NR_INODE_RECORDS * 16 - -#define NO_ADDRESS (-1) /* Error constants */ -#define NO_FREE_INODES (-1) - -#define PATH_PENULTIMATE 001 /* parse_path stops at last but one name */ -#define PATH_NONSYMBOLIC 004 /* parse_path scans final name if symbolic */ +/* Filesystem options support */ +#define ISO9660_OPTION_ROCKRIDGE +/* TODO: Make MODE3 working. */ +/*#define ISO9660_OPTION_MODE3*/ /* Below there are constant of the ISO9660 fs */ #define ISO9660_SUPER_BLOCK_POSITION 32768 diff --git a/minix/fs/isofs/inode.c b/minix/fs/isofs/inode.c index 1e4474bac..595ad897c 100644 --- a/minix/fs/isofs/inode.c +++ b/minix/fs/isofs/inode.c @@ -6,23 +6,48 @@ #include "inc.h" -static struct inode inodes[NR_INODE_RECORDS]; -static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset); +#include "uthash.h" + +struct inode_cache { + ino_t key; + struct inode *value; + UT_hash_handle hh; +} ; + +struct inode_cache *icache = NULL; + +void read_inode_iso9660(struct inode_dir_entry *i, + const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, + size_t offset, int name_only); + +#ifdef ISO9660_OPTION_MODE3 +static void read_inode_extents(struct inode_dir_entry *i, + const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, + size_t *offset); +#endif + +#ifdef ISO9660_OPTION_ROCKRIDGE +void read_inode_susp(struct inode_dir_entry *i, + const struct iso9660_dir_record *dir_rec, struct buf *bp, size_t offset, + int name_only); +#endif + +static int check_dir_record(const struct iso9660_dir_record *d, size_t offset); int fs_putnode(ino_t ino_nr, unsigned int count) { - /* - * Find the inode specified by the request message and decrease its - * counter. - */ + /* + * Find the inode specified by the request message and decrease its + * counter. + */ struct inode *i_node; - if ((i_node = find_inode(ino_nr)) == NULL) { - printf("ISOFS: trying to free unused inode\n"); + if ((i_node = get_inode(ino_nr)) == NULL) { + puts("ISOFS: trying to free unused inode"); return EINVAL; } if (count > i_node->i_count) { - printf("ISOFS: put_node count too high\n"); + puts("ISOFS: put_node count too high"); return EINVAL; } @@ -31,211 +56,225 @@ int fs_putnode(ino_t ino_nr, unsigned int count) return OK; } -struct inode* alloc_inode(void) -{ - /* - * Return a free inode from the pool. - */ - static int i; - int end = i; - struct inode *i_node; - i = (i + 1) % NR_INODE_RECORDS; - do { - i_node = &inodes[i]; +struct inode* get_inode(ino_t ino_nr) { + /* Return an already opened inode from cache. */ + struct inode *i_node = inode_cache_get(ino_nr); - if (i_node->i_count == 0) { - free_extent(i_node->extent); - - memset(i_node, 0, sizeof(*i_node)); - i_node->i_count = 1; - - return i_node; - } - - i = (i + 1) % NR_INODE_RECORDS; - } - while(i != end); - - panic("No free inodes in cache"); -} - -struct inode* find_inode(ino_t i) -{ - /* Get inode from cache. */ - int cpt; - struct inode *i_node; - - if (i == 0) + if (i_node == NULL) return NULL; - for (cpt = 0; cpt < NR_INODE_RECORDS; cpt++) { - i_node = &inodes[cpt]; - - if ((i_node->i_stat.st_ino == i) && (i_node->i_count > 0)) - return i_node; - } - - return NULL; -} - -struct inode* get_inode(ino_t i) -{ - struct inode *i_node; - struct dir_extent *extent; - - if (i == 0) + if (i_node->i_count == 0) return NULL; - /* Try to get inode from cache. */ - i_node = find_inode(i); - if (i_node != NULL) { - dup_inode(i_node); - return i_node; - } - - /* - * Inode wasn't in cache, try to load it. - * FIXME: a fake extent of one logical block is created for - * read_inode(). Reading a inode this way could be problematic if - * additional extents are stored behind the block boundary. - */ - i_node = alloc_inode(); - extent = alloc_extent(); - extent->location = i / v_pri.logical_block_size_l; - extent->length = 1; - - if (read_inode(i_node, extent, i % v_pri.logical_block_size_l, - NULL) != OK) { - free_extent(extent); - put_inode(i_node); - return NULL; - } - - free_extent(extent); return i_node; } -void put_inode(struct inode *i_node) -{ +struct inode* open_inode(ino_t ino_nr) { + /* Return an inode from cache. */ + struct inode *i_node = inode_cache_get(ino_nr); + if (i_node == NULL) + return NULL; + + i_node->i_count++; + + return i_node; +} + +void put_inode(struct inode *i_node) { if (i_node == NULL) return; assert(i_node->i_count > 0); - i_node->i_count--; + + if(i_node->i_count == 0) + i_node->i_mountpoint = FALSE; } -void dup_inode(struct inode *i_node) -{ +void dup_inode(struct inode *i_node) { assert(i_node != NULL); + assert(i_node->i_count > 0); i_node->i_count++; } -static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset) -{ - struct iso9660_dir_record *dir_rec; - struct buf *bp; +int read_directory(struct inode *dir) { +#define MAX_ENTRIES 4096 + /* Read all entries in a directory. */ + size_t pos = 0, cur_entry = 0, cpt; + struct inode_dir_entry entries[MAX_ENTRIES]; + int status = OK; - /* - * Directory entries aren't allowed to cross a logical block boundary in - * ISO 9660, so we keep searching until we find something or reach the - * end of the extent. - */ - bp = read_extent_block(extent, *offset / v_pri.logical_block_size_l); - while (bp != NULL) { - dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % - v_pri.logical_block_size_l); - if (dir_rec->length == 0) { - *offset -= *offset % v_pri.logical_block_size_l; - *offset += v_pri.logical_block_size_l; - } - else { + if (dir->dir_contents) + return OK; + + if (!S_ISDIR(dir->i_stat.st_mode)) + return ENOTDIR; + + for (cur_entry = 0; status == OK && cur_entry < MAX_ENTRIES; cur_entry++) { + memset(&entries[cur_entry], 0, sizeof(struct inode_dir_entry)); + + status = read_inode(&entries[cur_entry], &dir->extent, &pos); + if (status != OK) break; - } - lmfs_put_block(bp); - bp = read_extent_block(extent, *offset / - v_pri.logical_block_size_l); + /* Dump the entry if it's not to be exported to userland. */ + if (entries[cur_entry].i_node->skip) { + free_inode_dir_entry(&entries[cur_entry]); + continue; + } } - return bp; + /* Resize dynamic array to correct size */ + dir->dir_contents = alloc_mem(sizeof(struct inode_dir_entry) * cur_entry); + memcpy(dir->dir_contents, entries, sizeof(struct inode_dir_entry) * cur_entry); + dir->dir_size = cur_entry; + + /* The name pointer has to point to the new memory location. */ + for (cpt = 0; cpt < cur_entry; cpt++) { + if (dir->dir_contents[cpt].r_name == NULL) + dir->dir_contents[cpt].name = + dir->dir_contents[cpt].i_name; + else + dir->dir_contents[cpt].name = + dir->dir_contents[cpt].r_name; + } + + return (status == EOF) ? OK : status; } -int read_inode(struct inode *i_node, struct dir_extent *extent, size_t offset, - size_t *new_offset) +int check_inodes(void) { + /* Check whether there are no more inodes in use. Called on unmount. */ + int i; + + /* XXX: actually check for inodes in use. */ + return TRUE; +} + +int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent, + size_t *offset) { struct iso9660_dir_record *dir_rec; struct buf *bp; + struct inode *i_node; + ino_t ino_nr; + int name_only = FALSE; /* Find inode. */ - bp = fetch_inode(extent, &offset); - if (bp == NULL) + bp = read_extent_block(extent, *offset); + if (bp == NULL) { return EOF; + } - dir_rec = (struct iso9660_dir_record*)(b_data(bp) + offset % + /* Check if we are crossing a sector boundary. */ + dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % v_pri.logical_block_size_l); + if (dir_rec->length == 0) { + *offset = ((*offset / v_pri.logical_block_size_l) + 1) * + v_pri.logical_block_size_l; + + lmfs_put_block(bp); + bp = read_extent_block(extent, *offset); + if (bp == NULL) { + return EOF; + } + + dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset % + v_pri.logical_block_size_l); + } + /* Parse basic ISO 9660 specs. */ - if (check_dir_record(dir_rec, - offset % v_pri.logical_block_size_l) != OK) { + if (check_dir_record(dir_rec, *offset % v_pri.logical_block_size_l) + != OK) { lmfs_put_block(bp); return EINVAL; } - memset(&i_node->i_stat, 0, sizeof(struct stat)); + /* Get inode */ + if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) { + ino_nr = dir_rec->loc_extent_l; + } + else { + ino_nr = get_extent_absolute_block_id(extent, *offset) + * v_pri.logical_block_size_l + + *offset % v_pri.logical_block_size_l; + } - i_node->i_stat.st_ino = get_extent_absolute_block_id(extent, - offset / v_pri.logical_block_size_l) * v_pri.logical_block_size_l + - offset % v_pri.logical_block_size_l; + i_node = inode_cache_get(ino_nr); + if (i_node) { + /* Inode was already loaded, parse file names only. */ + dir_entry->i_node = i_node; + i_node->i_refcount++; - read_inode_iso9660(i_node, dir_rec); + memset(&dir_entry->i_name[0], 0, sizeof(dir_entry->i_name)); + + name_only = TRUE; + } + else { + /* Inode wasn't in memory, parse it. */ + i_node = alloc_mem(sizeof(struct inode)); + dir_entry->i_node = i_node; + i_node->i_refcount = 1; + i_node->i_stat.st_ino = ino_nr; + inode_cache_add(ino_nr, i_node); + } + + dir_entry->i_node = i_node; + read_inode_iso9660(dir_entry, dir_rec, extent, *offset, name_only); /* Parse extensions. */ - read_inode_susp(i_node, dir_rec, bp, - offset % v_pri.logical_block_size_l); +#ifdef ISO9660_OPTION_ROCKRIDGE + read_inode_susp(dir_entry, dir_rec, bp, + *offset % v_pri.logical_block_size_l, name_only); +#endif - offset += dir_rec->length; - read_inode_extents(i_node, dir_rec, extent, &offset); + *offset += dir_rec->length; + if (dir_rec->length % 2) + (*offset)++; + +#ifdef ISO9660_OPTION_MODE3 + read_inode_extents(dir_entry, dir_rec, extent, offset); +#endif lmfs_put_block(bp); - if (new_offset != NULL) - *new_offset = offset; + return OK; } -void read_inode_iso9660(struct inode *i, - const struct iso9660_dir_record *dir_rec) -{ - char *cp; +struct inode* inode_cache_get(ino_t ino_nr) { + struct inode_cache *i_node; + HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), i_node); - /* Parse first extent. */ - if (dir_rec->data_length_l > 0) { - assert(i->extent == NULL); - i->extent = alloc_extent(); - i->extent->location = dir_rec->loc_extent_l + - dir_rec->ext_attr_rec_length; - i->extent->length = dir_rec->data_length_l / - v_pri.logical_block_size_l; - if (dir_rec->data_length_l % v_pri.logical_block_size_l) - i->extent->length++; + if (i_node) + return i_node->value; + else + return NULL; +} - i->i_stat.st_size = dir_rec->data_length_l; - } +void inode_cache_add(ino_t ino_nr, struct inode *i_node) { + struct inode_cache *c_check; + struct inode_cache *c_entry; - /* Parse timestamps (record date). */ - i->i_stat.st_atime = i->i_stat.st_mtime = i->i_stat.st_ctime = - i->i_stat.st_birthtime = date7_to_time_t(dir_rec->rec_date); + HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), c_check); - if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) { - i->i_stat.st_mode = S_IFDIR; - i->i_stat.st_ino = - i->extent->location * v_pri.logical_block_size_l; + if (c_check == NULL) { + c_entry = alloc_mem(sizeof(struct inode_cache)); + c_entry->key = ino_nr; + c_entry->value = i_node; + + HASH_ADD(hh, icache, key, sizeof(ino_t), c_entry); } else - i->i_stat.st_mode = S_IFREG; - i->i_stat.st_mode |= 0555; + panic("Trying to insert inode into cache twice"); +} + +void read_inode_iso9660(struct inode_dir_entry *i, + const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, + size_t offset, int name_only) +{ + char *cp; /* Parse file name. */ if (dir_rec->file_id[0] == 0) @@ -247,89 +286,57 @@ void read_inode_iso9660(struct inode *i, /* Truncate/ignore file version suffix. */ cp = strchr(i->i_name, ';'); - if (cp != NULL) + if (cp != NULL) { *cp = '\0'; - /* Truncate dot if file has no extension. */ - if (strchr(i->i_name, '.') + 1 == cp) - *(cp-1) = '\0'; + /* Truncate dot if file has no extension. */ + if (strchr(i->i_name, '.') + 1 == cp) + *(cp-1) = '\0'; + } } - /* Initialize stat. */ - i->i_stat.st_dev = fs_dev; - i->i_stat.st_blksize = v_pri.logical_block_size_l; - i->i_stat.st_blocks = - dir_rec->data_length_l / v_pri.logical_block_size_l; - i->i_stat.st_nlink = 1; -} - -void read_inode_extents(struct inode *i, - const struct iso9660_dir_record *dir_rec, - struct dir_extent *extent, size_t *offset) -{ - struct buf *bp; - struct iso9660_dir_record *extent_rec; - struct dir_extent *cur_extent = i->extent; - int done = FALSE; - - /* - * No need to search extents if file is empty or has final directory - * record flag set. - */ - if (cur_extent == NULL || - ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0)) + if (name_only == TRUE) return; - while (!done) { - bp = fetch_inode(extent, offset); - if (bp == NULL) - return; + /* Parse first extent. */ + if (dir_rec->data_length_l > 0) { + i->i_node->extent.location = dir_rec->loc_extent_l + + dir_rec->ext_attr_rec_length; + i->i_node->extent.length = dir_rec->data_length_l / + v_pri.logical_block_size_l; - bp = read_extent_block(extent, - *offset / v_pri.logical_block_size_l); - extent_rec = (struct iso9660_dir_record*)(b_data(bp) + - *offset % v_pri.logical_block_size_l); + if (dir_rec->data_length_l % v_pri.logical_block_size_l) + i->i_node->extent.length++; - if (check_dir_record(dir_rec, - *offset % v_pri.logical_block_size_l) != OK) { - lmfs_put_block(bp); - return; - } - - /* Extent entries should share the same name. */ - if ((dir_rec->length_file_id == extent_rec->length_file_id) && - (memcmp(dir_rec->file_id, extent_rec->file_id, - dir_rec->length_file_id) == 0)) { - /* Add the extent at the end of the linked list. */ - assert(cur_extent->next == NULL); - cur_extent->next = alloc_extent(); - cur_extent->next->location = dir_rec->loc_extent_l + - dir_rec->ext_attr_rec_length; - cur_extent->next->length = dir_rec->data_length_l / - v_pri.logical_block_size_l; - if (dir_rec->data_length_l % v_pri.logical_block_size_l) - cur_extent->next->length++; - - i->i_stat.st_size += dir_rec->data_length_l; - i->i_stat.st_blocks += cur_extent->next->length; - - cur_extent = cur_extent->next; - *offset += extent_rec->length; - } - else - done = TRUE; - - /* Check if not last extent bit is not set. */ - if ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0) - done = TRUE; - - lmfs_put_block(bp); + i->i_node->i_stat.st_size = dir_rec->data_length_l; } + + /* Parse timestamps (record date). */ + i->i_node->i_stat.st_atime = i->i_node->i_stat.st_mtime = + i->i_node->i_stat.st_ctime = i->i_node->i_stat.st_birthtime = + date7_to_time_t(dir_rec->rec_date); + + if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) + i->i_node->i_stat.st_mode = S_IFDIR; + else + i->i_node->i_stat.st_mode = S_IFREG; + + i->i_node->i_stat.st_mode |= 0555; + + /* Initialize stat. */ + i->i_node->i_stat.st_dev = fs_dev; + i->i_node->i_stat.st_blksize = v_pri.logical_block_size_l; + i->i_node->i_stat.st_blocks = + dir_rec->data_length_l / v_pri.logical_block_size_l; + i->i_node->i_stat.st_nlink = 1; } -void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec, - struct buf *bp, size_t offset) +#ifdef ISO9660_OPTION_ROCKRIDGE + +void read_inode_susp(struct inode_dir_entry *i, + const struct iso9660_dir_record *dir_rec, struct buf *bp, size_t offset, + int name_only) { - int susp_offset, susp_size; + int susp_offset, susp_size, name_length; struct rrii_dir_record rrii_data; susp_offset = 33 + dir_rec->length_file_id; @@ -338,48 +345,79 @@ void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec, susp_offset++; } - if(dir_rec->length - susp_offset >= 4) { - susp_size = dir_rec->length - susp_offset; + if(dir_rec->length - susp_offset < 4) + return; - /* Initialize record with known, sane data. */ - memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); - memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7); - memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7); - memcpy(rrii_data.birthtime, dir_rec->rec_date, - ISO9660_SIZE_DATE7); + susp_size = dir_rec->length - susp_offset; - rrii_data.d_mode = i->i_stat.st_mode; - rrii_data.uid = 0; - rrii_data.gid = 0; - rrii_data.rdev = NO_DEV; - rrii_data.file_id_rrip[0] = '\0'; - rrii_data.slink_rrip[0] = '\0'; + /* Initialize record with known, sane data. */ + memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); + memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7); + memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7); + memcpy(rrii_data.birthtime, dir_rec->rec_date, ISO9660_SIZE_DATE7); - parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, - susp_size); + rrii_data.d_mode = i->i_node->i_stat.st_mode; + rrii_data.uid = SYS_UID; + rrii_data.gid = SYS_GID; + rrii_data.rdev = NO_DEV; + rrii_data.file_id_rrip[0] = '\0'; + rrii_data.slink_rrip[0] = '\0'; + rrii_data.reparented_inode = NULL; - /* Copy back data from rrii_dir_record structure. */ - i->i_stat.st_atime = date7_to_time_t(rrii_data.atime); - i->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime); - i->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime); - i->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime); + parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, susp_size); - i->i_stat.st_mode = rrii_data.d_mode; - i->i_stat.st_uid = rrii_data.uid; - i->i_stat.st_gid = rrii_data.gid; - i->i_stat.st_rdev = rrii_data.rdev; - - if (rrii_data.file_id_rrip[0] != '\0') - strlcpy(i->i_name, rrii_data.file_id_rrip, - sizeof(i->i_name)); - if (rrii_data.slink_rrip[0] != '\0') - strlcpy(i->s_link, rrii_data.slink_rrip, - sizeof(i->s_link)); + /* Copy back data from rrii_dir_record structure. */ + if (rrii_data.file_id_rrip[0] != '\0') { + name_length = strlen(rrii_data.file_id_rrip); + i->r_name = alloc_mem(name_length + 1); + memcpy(i->r_name, rrii_data.file_id_rrip, name_length); } + + if (rrii_data.slink_rrip[0] != '\0') { + name_length = strlen(rrii_data.slink_rrip); + i->i_node->s_name = alloc_mem(name_length + 1); + memcpy(i->i_node->s_name, rrii_data.slink_rrip, name_length); + } + + if (rrii_data.reparented_inode) { + /* Recycle the inode already parsed. */ + i->i_node = rrii_data.reparented_inode; + return; + } + + /* XXX: not the correct way to ignore reparented directory holder... */ + if (strcmp(rrii_data.file_id_rrip, ".rr_moved") == 0) + i->i_node->skip = 1; + + if (name_only == TRUE) + return; + + /* Write back all Rock Ridge properties. */ + i->i_node->i_stat.st_atime = date7_to_time_t(rrii_data.atime); + i->i_node->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime); + i->i_node->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime); + i->i_node->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime); + + i->i_node->i_stat.st_mode = rrii_data.d_mode; + i->i_node->i_stat.st_uid = rrii_data.uid; + i->i_node->i_stat.st_gid = rrii_data.gid; + i->i_node->i_stat.st_rdev = rrii_data.rdev; } -int check_dir_record(const struct iso9660_dir_record *d, size_t offset) +#endif + +#ifdef ISO9660_OPTION_MODE3 + +void read_inode_extents(struct inode *i, + const struct iso9660_dir_record *dir_rec, + struct dir_extent *extent, size_t *offset) { + panic("read_inode_extents() isn't implemented yet!"); +} + +#endif + +int check_dir_record(const struct iso9660_dir_record *d, size_t offset) { /* Run some consistency check on a directory entry. */ if ((d->length < 33) || (d->length_file_id < 1)) return EINVAL; @@ -390,15 +428,3 @@ int check_dir_record(const struct iso9660_dir_record *d, size_t offset) return OK; } - -int check_inodes(void) -{ - /* Check whether there are no more inodes in use. Called on unmount. */ - int i; - - for (i = 0; i < NR_INODE_RECORDS; i++) - if (inodes[i].i_count > 0) - return FALSE; - - return TRUE; -} diff --git a/minix/fs/isofs/inode.h b/minix/fs/isofs/inode.h index 19ce1953e..d0635f8aa 100644 --- a/minix/fs/isofs/inode.h +++ b/minix/fs/isofs/inode.h @@ -32,30 +32,40 @@ struct rrii_dir_record { mode_t d_mode; /* file mode */ uid_t uid; /* user ID of the file's owner */ gid_t gid; /* group ID of the file's group */ - dev_t rdev; /* device ID */ + dev_t rdev; /* device major/minor */ char file_id_rrip[ISO9660_RRIP_MAX_FILE_ID_LEN]; /* file name */ char slink_rrip[ISO9660_RRIP_MAX_FILE_ID_LEN]; /* symbolic link */ + + struct inode *reparented_inode; } ; struct dir_extent { /* * Extent (contiguous array of logical sectors). */ - char in_use; u32_t location; u32_t length; struct dir_extent *next; } ; +struct inode_dir_entry { + struct inode *i_node; + char *name; /* Pointer to real name */ + char i_name[ISO9660_MAX_FILE_ID_LEN+1]; /* ISO 9660 name */ + char *r_name; /* Rock Ridge name */ +} ; + struct inode { int i_count; /* usage counter of this inode */ + int i_refcount; /* reference counter of this inode */ int i_mountpoint; /* flag for inode being used as a mount point */ - int ea_length; /* total size of extended attributes in bytes */ struct stat i_stat; /* inode properties */ - struct dir_extent *extent; /* first extent of file */ - char i_name[NAME_MAX]; /* inode name */ - char s_link[NAME_MAX]; /* symbolic link target */ + struct dir_extent extent; /* first extent of file */ + struct inode_dir_entry *dir_contents; /* contents of directory */ + size_t dir_size; /* number of inodes in this directory */ + char *s_name; /* Rock Ridge symbolic link */ + int skip; /* skip inode because of reparenting */ } ; struct opt { diff --git a/minix/fs/isofs/link.c b/minix/fs/isofs/link.c index 5f5f8bbfb..c6f7e37c5 100644 --- a/minix/fs/isofs/link.c +++ b/minix/fs/isofs/link.c @@ -3,21 +3,21 @@ ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes) { struct inode *i_node; - size_t len; + size_t len = 0; int r; /* Try to get inode according to its index */ - if ((i_node = find_inode(ino_nr)) == NULL) + if ((i_node = get_inode(ino_nr)) == NULL) return EINVAL; /* no inode found */ if (!S_ISLNK(i_node->i_stat.st_mode)) return EACCES; - len = strlen(i_node->s_link); + len = strlen(i_node->s_name); if (len > bytes) len = bytes; - if ((r = fsdriver_copyout(data, 0, i_node->s_link, len)) != OK) + if ((r = fsdriver_copyout(data, 0, i_node->s_name, len)) != OK) return r; return len; diff --git a/minix/fs/isofs/mount.c b/minix/fs/isofs/mount.c index 5b9266f9b..3e4429531 100644 --- a/minix/fs/isofs/mount.c +++ b/minix/fs/isofs/mount.c @@ -40,7 +40,7 @@ int fs_mountpt(ino_t ino_nr) */ struct inode *rip; - if ((rip = find_inode(ino_nr)) == NULL) + if ((rip = get_inode(ino_nr)) == NULL) return EINVAL; if (rip->i_mountpoint) @@ -62,5 +62,5 @@ void fs_unmount(void) bdev_close(fs_dev); if (check_inodes() == FALSE) - printf("ISOFS: unmounting with in-use inodes!\n"); + puts("ISOFS: unmounting with in-use inodes!\n"); } diff --git a/minix/fs/isofs/path.c b/minix/fs/isofs/path.c index 04af43cdf..cd2ad25f1 100644 --- a/minix/fs/isofs/path.c +++ b/minix/fs/isofs/path.c @@ -1,17 +1,16 @@ #include "inc.h" static int search_dir( - struct inode *ldir_ptr, /* dir record parent */ + struct inode *ldir_ptr, /* dir record parent */ char string[NAME_MAX], /* component to search for */ ino_t *numb /* pointer to new dir record */ ) { - /* The search_dir function performs the operation of searching for the + /* + * The search_dir function performs the operation of searching for the * component ``string" in ldir_ptr. It returns the response and the * number of the inode in numb. */ - struct inode *dir_tmp; - size_t pos = 0; - int r; + int r, i; /* * This function search a particular element (in string) in a inode and @@ -21,69 +20,24 @@ static int search_dir( if ((ldir_ptr->i_stat.st_mode & S_IFMT) != S_IFDIR) return ENOTDIR; - if (strcmp(string, ".") == 0) { + r = read_directory(ldir_ptr); + if (r != OK) + return r; + + if (strcmp(".", string) == 0) { *numb = ldir_ptr->i_stat.st_ino; return OK; } - /* - * Parent directories need special attention to make sure directory - * inodes stay consistent. - */ - if (strcmp(string, "..") == 0) { - if (ldir_ptr->i_stat.st_ino == - v_pri.inode_root->i_stat.st_ino) { - *numb = v_pri.inode_root->i_stat.st_ino; - return OK; - } - else { - dir_tmp = alloc_inode(); - r = read_inode(dir_tmp, ldir_ptr->extent, pos, &pos); - if ((r != OK) || (pos >= ldir_ptr->i_stat.st_size)) { - put_inode(dir_tmp); - return ENOENT; - } - /* Temporary fix for extent spilling */ - put_inode(dir_tmp); - dir_tmp = alloc_inode(); - /* End of fix */ - r = read_inode(dir_tmp, ldir_ptr->extent, pos, &pos); - if ((r != OK) || (pos >= ldir_ptr->i_stat.st_size)) { - put_inode(dir_tmp); - return ENOENT; - } - *numb = dir_tmp->i_stat.st_ino; - put_inode(dir_tmp); + /* Walk the directory listing. */ + for (i = 0; i < ldir_ptr->dir_size; i++) { + if (strcmp(string, ldir_ptr->dir_contents[i].name) == 0) { + *numb = ldir_ptr->dir_contents[i].i_node->i_stat.st_ino; return OK; } } - /* Read the dir's content */ - while (TRUE) { - dir_tmp = alloc_inode(); - r = read_inode(dir_tmp, ldir_ptr->extent, pos, &pos); - if ((r != OK) || (pos >= ldir_ptr->i_stat.st_size)) { - put_inode(dir_tmp); - return ENOENT; - } - - if ((strcmp(dir_tmp->i_name, string) == 0) || - (strcmp(dir_tmp->i_name, "..") && - strcmp(string, "..") == 0)) { - if (dir_tmp->i_stat.st_ino == - v_pri.inode_root->i_stat.st_ino) { - *numb = v_pri.inode_root->i_stat.st_ino; - put_inode(dir_tmp); - return OK; - } - - *numb = dir_tmp->i_stat.st_ino; - put_inode(dir_tmp); - return OK; - } - - put_inode(dir_tmp); - } + return ENOENT; } int fs_lookup(ino_t dir_nr, char *name, struct fsdriver_node *node, @@ -97,7 +51,7 @@ int fs_lookup(ino_t dir_nr, char *name, struct fsdriver_node *node, int r; /* Find the starting inode. */ - if ((dirp = find_inode(dir_nr)) == NULL) + if ((dirp = get_inode(dir_nr)) == NULL) return EINVAL; /* Look up the directory entry. */ @@ -105,7 +59,7 @@ int fs_lookup(ino_t dir_nr, char *name, struct fsdriver_node *node, return r; /* The component has been found in the directory. Get the inode. */ - if ((rip = get_inode(ino_nr)) == NULL) + if ((rip = open_inode(ino_nr)) == NULL) return EIO; /* FIXME: this could have multiple causes */ /* Return its details to the caller. */ diff --git a/minix/fs/isofs/proto.h b/minix/fs/isofs/proto.h index 051eb052f..8fabfcdb0 100644 --- a/minix/fs/isofs/proto.h +++ b/minix/fs/isofs/proto.h @@ -6,27 +6,23 @@ struct rrii_dir_record; struct iso9660_dir_record; struct iso9660_vol_pri_desc; struct inode; +struct inode_dir_entry; /* inode.c */ int fs_putnode(ino_t ino_nr, unsigned int count); -struct inode* alloc_inode(void); -struct inode* find_inode(ino_t i); +struct inode* get_inode(ino_t ino_nr); +struct inode* open_inode(ino_t ino_nr); void put_inode(struct inode *i); void dup_inode(struct inode *i_node); -struct inode* get_inode(ino_t i); -int read_inode(struct inode *i_node, struct dir_extent *extent, size_t offset, - size_t *new_offset); -void read_inode_iso9660(struct inode *i, - const struct iso9660_dir_record *dir_rec); -void read_inode_extents(struct inode *i, - const struct iso9660_dir_record *dir_rec, struct dir_extent *extent, - size_t *offset); -void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec, - struct buf *bp, size_t offset); +int read_directory(struct inode *dir); -int check_dir_record(const struct iso9660_dir_record *d, size_t offset); +int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent, + size_t *offset); + +struct inode* inode_cache_get(ino_t ino_nr); +void inode_cache_add(ino_t ino_nr, struct inode *i_node); int check_inodes(void); @@ -54,7 +50,7 @@ int fs_stat(ino_t ino_nr, struct stat *statbuf); int fs_statvfs(struct statvfs *st); /* super.c */ -int release_vol_pri_desc(struct iso9660_vol_pri_desc *v_pri); +int release_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri); int read_vds(struct iso9660_vol_pri_desc *v_pri, dev_t dev); /* susp.c */ @@ -67,9 +63,10 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer); /* utility.c */ -struct dir_extent* alloc_extent(void); +void free_inode_dir_entry(struct inode_dir_entry *e); void free_extent(struct dir_extent *extent); struct buf* read_extent_block(struct dir_extent *e, size_t block); size_t get_extent_absolute_block_id(struct dir_extent *e, size_t block); time_t date7_to_time_t(const u8_t *date); +void* alloc_mem(size_t s); \ No newline at end of file diff --git a/minix/fs/isofs/read.c b/minix/fs/isofs/read.c index e5f6dd45d..b28650065 100644 --- a/minix/fs/isofs/read.c +++ b/minix/fs/isofs/read.c @@ -12,7 +12,7 @@ ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, int r; /* Try to get inode according to its index. */ - if ((i_node = find_inode(ino_nr)) == NULL) + if ((i_node = get_inode(ino_nr)) == NULL) return EINVAL; /* No inode found. */ f_size = i_node->i_stat.st_size; @@ -37,7 +37,7 @@ ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, chunk = bytes; /* Read 'chunk' bytes. */ - bp = read_extent_block(i_node->extent, pos / block_size); + bp = read_extent_block(&i_node->extent, pos); if (bp == NULL) panic("bp not valid in rw_chunk; this can't happen"); @@ -61,42 +61,38 @@ ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *pos) { struct fsdriver_dentry fsdentry; - struct inode *i_node, *i_node_tmp; - size_t cur_pos, new_pos; + struct inode *i_node; + off_t cur_pos; int r, len; char *cp; - if ((i_node = find_inode(ino_nr)) == NULL) + if ((i_node = get_inode(ino_nr)) == NULL) return EINVAL; if (*pos < 0 || *pos > SSIZE_MAX) return EINVAL; + r = read_directory(i_node); + if (r != OK) + return r; + fsdriver_dentry_init(&fsdentry, data, bytes, getdents_buf, sizeof(getdents_buf)); r = OK; - for (cur_pos = (size_t)*pos; ; cur_pos = new_pos) { - i_node_tmp = alloc_inode(); - r = read_inode(i_node_tmp, i_node->extent, cur_pos, &new_pos); - if ((r != OK) || (new_pos >= i_node->i_stat.st_size)) { - put_inode(i_node_tmp); - break; - } - + for (cur_pos = *pos; cur_pos < i_node->dir_size; cur_pos++) { /* Compute the length of the name */ - cp = memchr(i_node_tmp->i_name, '\0', NAME_MAX); + cp = memchr(i_node->dir_contents[cur_pos].name, '\0', NAME_MAX); if (cp == NULL) len = NAME_MAX; else - len = cp - i_node_tmp->i_name; + len = cp - i_node->dir_contents[cur_pos].name; - r = fsdriver_dentry_add(&fsdentry, i_node_tmp->i_stat.st_ino, - i_node_tmp->i_name, len, - IFTODT(i_node_tmp->i_stat.st_mode)); - - put_inode(i_node_tmp); + r = fsdriver_dentry_add(&fsdentry, + i_node->dir_contents[cur_pos].i_node->i_stat.st_ino, + i_node->dir_contents[cur_pos].name, len, + IFTODT(i_node->dir_contents[cur_pos].i_node->i_stat.st_mode)); if (r <= 0) break; diff --git a/minix/fs/isofs/stadir.c b/minix/fs/isofs/stadir.c index 6825b1031..926b98c25 100644 --- a/minix/fs/isofs/stadir.c +++ b/minix/fs/isofs/stadir.c @@ -6,7 +6,7 @@ int fs_stat(ino_t ino_nr, struct stat *statbuf) { struct inode *rip; - if ((rip = find_inode(ino_nr)) == NULL) + if ((rip = get_inode(ino_nr)) == NULL) return EINVAL; *statbuf = rip->i_stat; diff --git a/minix/fs/isofs/super.c b/minix/fs/isofs/super.c index b8d7aa5a9..e6f208bfc 100644 --- a/minix/fs/isofs/super.c +++ b/minix/fs/isofs/super.c @@ -21,16 +21,16 @@ int release_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri) return OK; } -static int create_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri, char *buf, - size_t __unused address) +static int create_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri, char *buf) { /* * This function fullfill the super block data structure using the * information contained in the buffer. */ struct iso9660_dir_record *root_record; - struct inode *root; - struct dir_extent *extent; + struct inode_dir_entry dir_entry; + struct dir_extent extent; + size_t dummy_offset = 0; if (vol_pri->i_count > 0) release_vol_pri_desc(vol_pri); @@ -39,10 +39,9 @@ static int create_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri, char *buf, /* Check various fields for consistency. */ if ((memcmp(vol_pri->standard_id, "CD001", - ISO9660_SIZE_STANDARD_ID) != 0) || - (vol_pri->vd_version != 1) || - (vol_pri->logical_block_size_l < 2048) || - (vol_pri->file_struct_ver != 1)) + ISO9660_SIZE_STANDARD_ID) != 0) + || (vol_pri->vd_version != 1) + || (vol_pri->logical_block_size_l < 2048)) return EINVAL; lmfs_set_blocksize(vol_pri->logical_block_size_l); @@ -51,24 +50,24 @@ static int create_vol_pri_desc(struct iso9660_vol_pri_desc *vol_pri, char *buf, /* Read root directory record. */ root_record = (struct iso9660_dir_record *)vol_pri->root_directory; - root = alloc_inode(); - extent = alloc_extent(); - extent->location = + extent.location = root_record->loc_extent_l + root_record->ext_attr_rec_length; - extent->length = + extent.length = root_record->data_length_l / vol_pri->logical_block_size_l; - if (root_record->data_length_l % vol_pri->logical_block_size_l) - extent->length++; + extent.next = NULL; - if (read_inode(root, extent, 0, NULL) != OK) { - free_extent(extent); - put_inode(root); + if (root_record->data_length_l % vol_pri->logical_block_size_l) + extent.length++; + + memset(&dir_entry, 0, sizeof(struct inode_dir_entry)); + if (read_inode(&dir_entry, &extent, &dummy_offset) != OK) { return EINVAL; } - free_extent(extent); - vol_pri->inode_root = root; + dir_entry.i_node->i_count = 1; + + vol_pri->inode_root = dir_entry.i_node; vol_pri->i_count = 1; return OK; @@ -99,8 +98,13 @@ int read_vds(struct iso9660_vol_pri_desc *vol_pri, dev_t dev) } if ((sbbuf[0] & BYTE) == VD_PRIMARY) { + /* Free already parsed descriptor, if any. */ + if (vol_pri_flag == TRUE) { + release_vol_pri_desc(vol_pri); + vol_pri_flag = FALSE; + } /* Copy the buffer in the data structure. */ - if (create_vol_pri_desc(vol_pri, sbbuf, offset) == OK) { + if (create_vol_pri_desc(vol_pri, sbbuf) == OK) { vol_pri_flag = TRUE; } } diff --git a/minix/fs/isofs/susp.c b/minix/fs/isofs/susp.c index 80436e991..78005b48a 100644 --- a/minix/fs/isofs/susp.c +++ b/minix/fs/isofs/susp.c @@ -6,6 +6,8 @@ #include "inc.h" #include +#ifdef ISO9660_OPTION_ROCKRIDGE + int parse_susp(struct rrii_dir_record *dir, char *buffer) { /* Parse fundamental SUSP entries */ @@ -127,3 +129,4 @@ void parse_susp_buffer(struct rrii_dir_record *dir, char *buffer, u32_t size) } } +#endif diff --git a/minix/fs/isofs/susp_rock_ridge.c b/minix/fs/isofs/susp_rock_ridge.c index d68bb94c1..b98456227 100644 --- a/minix/fs/isofs/susp_rock_ridge.c +++ b/minix/fs/isofs/susp_rock_ridge.c @@ -6,6 +6,43 @@ #include "inc.h" #include +#ifdef ISO9660_OPTION_ROCKRIDGE + +void parse_susp_rock_ridge_plcl(struct rrii_dir_record *dir, u32_t block) { + struct inode *rep_inode; + struct buf *bp; + struct iso9660_dir_record *dir_rec; + struct dir_extent extent; + struct inode_dir_entry dummy_dir_entry; + size_t dummy_offset = 0; + + /* Check if inode wasn't already parsed. */ + rep_inode = inode_cache_get(block); + if (rep_inode != NULL) { + rep_inode->i_refcount++; + dir->reparented_inode = rep_inode; + return; + } + + /* Peek ahead to build extent for read_inode. */ + if (lmfs_get_block(&bp, fs_dev, block, NORMAL) != OK) + return; + + dir_rec = (struct iso9660_dir_record*)b_data(bp); + + extent.location = block; + extent.length = dir_rec->data_length_l / v_pri.logical_block_size_l; + if (dir_rec->data_length_l % v_pri.logical_block_size_l) + extent.length++; + extent.next = NULL; + lmfs_put_block(bp); + + memset(&dummy_dir_entry, 0, sizeof(struct inode_dir_entry)); + read_inode(&dummy_dir_entry, &extent, &dummy_offset); + free(dummy_dir_entry.r_name); + dir->reparented_inode = dummy_dir_entry.i_node; +} + void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int length) { /* Parse a Rock Ridge SUSP symbolic link entry (SL). */ @@ -18,13 +55,14 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int len component_length = *((u8_t*)(buffer + offset + 1)); /* Add directory separator if necessary. */ - if (dir->slink_rrip[0] != '\0') { + if (strcmp(dir->slink_rrip, "") != 0 && + strcmp(dir->slink_rrip, "/") != 0) { slink_size = strlen(dir->slink_rrip); if (slink_size + 2 >= ISO9660_RRIP_MAX_FILE_ID_LEN) return; - dir->slink_rrip[slink_size] = '/'; - slink_size++; + dir->slink_rrip[slink_size++] = '/'; + dir->slink_rrip[slink_size] = '\0'; } else slink_size = strlen(dir->slink_rrip); @@ -43,8 +81,9 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int len return; } - strlcpy(dir->slink_rrip + slink_size, - buffer + offset + 2, component_length+1); + strlcat(&dir->slink_rrip[slink_size], + buffer + offset + 2, + component_length + 1); break; } @@ -55,7 +94,7 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int len return; } - strcat(dir->slink_rrip + slink_size, "."); + strcat(&dir->slink_rrip[slink_size], "."); break; } @@ -66,7 +105,7 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int len return; } - strcat(dir->slink_rrip + slink_size, ".."); + strcat(&dir->slink_rrip[slink_size], ".."); break; } @@ -78,7 +117,7 @@ void parse_susp_rock_ridge_sl(struct rrii_dir_record *dir, char *buffer, int len return; } - strcat(dir->slink_rrip + slink_size, "/"); + strcat(&dir->slink_rrip[slink_size], "/"); break; } @@ -106,6 +145,7 @@ int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer) u32_t rrii_pn_rdev_major; u32_t rrii_pn_rdev_minor; mode_t rrii_px_posix_mode; + u32_t rrii_pcl_block; susp_signature[0] = buffer[0]; susp_signature[1] = buffer[1]; @@ -115,26 +155,7 @@ int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer) if ((susp_signature[0] == 'P') && (susp_signature[1] == 'X') && (susp_length >= 36) && (susp_version >= 1)) { /* POSIX file mode, UID and GID. */ - rrii_px_posix_mode = *((u32_t*)(buffer + 4)); - - /* Check if file mode is supported by isofs. */ - switch (rrii_px_posix_mode & _S_IFMT) { - case S_IFCHR: - case S_IFBLK: - case S_IFREG: - case S_IFDIR: - case S_IFLNK: { - dir->d_mode = rrii_px_posix_mode & _S_IFMT; - break; - } - default: { - /* Fall back to what ISO 9660 said. */ - dir->d_mode &= _S_IFMT; - break; - } - } - - dir->d_mode |= rrii_px_posix_mode & 07777; + dir->d_mode = *((u32_t*)(buffer + 4)); dir->uid = *((u32_t*)(buffer + 20)); dir->gid = *((u32_t*)(buffer + 28)); @@ -143,9 +164,18 @@ int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer) else if ((susp_signature[0] == 'P') && (susp_signature[1] == 'N') && (susp_length >= 20) && (susp_version >= 1)) { /* Device ID (for character or block special inode). */ + + /* + * XXX: Specific to how Minix ISO is generated, will have to + * investigate why makefs does that later. + */ +#if 0 rrii_pn_rdev_major = *((u32_t*)(buffer + 4)); rrii_pn_rdev_minor = *((u32_t*)(buffer + 12)); - +#else + rrii_pn_rdev_major = *((u32_t*)(buffer + 12)) >> 8; + rrii_pn_rdev_minor = *((u32_t*)(buffer + 12)) & 0xFF; +#endif dir->rdev = makedev(rrii_pn_rdev_major, rrii_pn_rdev_minor); return OK; @@ -174,16 +204,25 @@ int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer) return OK; } - else if ((susp_signature[0] == 'C') && (susp_signature[1] == 'L')) { - /* Ignored, skip. */ + else if ((susp_signature[0] == 'P') && (susp_signature[1] == 'L') && + (susp_length >= 12) && (susp_version >= 1)) { + /* Reparenting ".." directory entry. */ + rrii_pcl_block = *((u32_t*)(buffer + 4)); + parse_susp_rock_ridge_plcl(dir, rrii_pcl_block); + return OK; } - else if ((susp_signature[0] == 'P') && (susp_signature[1] == 'L')) { - /* Ignored, skip. */ + else if ((susp_signature[0] == 'C') && (susp_signature[1] == 'L') && + (susp_length >= 12) && (susp_version >= 1)) { + /* Reorganize deep directory entry. */ + rrii_pcl_block = *((u32_t*)(buffer + 4)); + parse_susp_rock_ridge_plcl(dir, rrii_pcl_block); + return OK; } else if ((susp_signature[0] == 'R') && (susp_signature[1] == 'E')) { /* Ignored, skip. */ + return OK; } else if ((susp_signature[0] == 'T') && (susp_signature[1] == 'F') && @@ -246,3 +285,4 @@ int parse_susp_rock_ridge(struct rrii_dir_record *dir, char *buffer) return EINVAL; } +#endif diff --git a/minix/fs/isofs/uthash.h b/minix/fs/isofs/uthash.h new file mode 100644 index 000000000..d2255b18b --- /dev/null +++ b/minix/fs/isofs/uthash.h @@ -0,0 +1,960 @@ +/* +Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER +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 UTHASH_H +#define UTHASH_H + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#define DECLTYPE(x) +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined (_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#else +#include +#endif + +#define UTHASH_VERSION 1.9.9 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + out=NULL; \ + if (head) { \ + unsigned _hf_bkt,_hf_hashv; \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + } \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + unsigned _hd_bkt; \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count; \ + char *_prev; \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %u, actual %u\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619; \ + } \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned char *_hj_key=(unsigned char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned char *_sfh_key=(unsigned char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + ((head) ? ( \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN)))) : 0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/minix/fs/isofs/utility.c b/minix/fs/isofs/utility.c index bea356d10..e2d99769b 100644 --- a/minix/fs/isofs/utility.c +++ b/minix/fs/isofs/utility.c @@ -1,67 +1,44 @@ #include "inc.h" -static struct dir_extent dir_extents[NR_DIR_EXTENT_RECORDS]; - -struct dir_extent* alloc_extent(void) -{ - /* Return a free extent from the pool. */ - int i; - struct dir_extent *extent; - - for (i = 0; i < NR_DIR_EXTENT_RECORDS; i++) { - extent = &dir_extents[i]; - - if (extent->in_use == 0) { - memset(extent, 0, sizeof(*extent)); - extent->in_use = 1; - - return extent; - } - } - - panic("No free extents in cache"); -} - -void free_extent(struct dir_extent *e) -{ +void free_extent(struct dir_extent *e) { if (e == NULL) return; - if (e->in_use == 0) - panic("Trying to free unused extent"); - free_extent(e->next); - e->in_use = 0; + free(e); } -struct buf* read_extent_block(struct dir_extent *e, size_t block) -{ - struct buf *bp; - size_t block_id; - int r; +/* Free the contents of an inode dir entry, but not the pointer itself. */ +void free_inode_dir_entry(struct inode_dir_entry *e) { + if (e == NULL) + return; - block_id = get_extent_absolute_block_id(e, block); + free(e->r_name); + e->r_name = NULL; +} + +struct buf* read_extent_block(struct dir_extent *e, size_t block) { + size_t block_id = get_extent_absolute_block_id(e, block); + struct buf *bp; if (block_id == 0 || block_id >= v_pri.volume_space_size_l) return NULL; - /* Not all callers deal well with failure, so panic on I/O error. */ - if ((r = lmfs_get_block(&bp, fs_dev, block_id, NORMAL)) != OK) - panic("ISOFS: error getting block (%llu,%zu): %d", - fs_dev, block_id, r); + if(lmfs_get_block(&bp, fs_dev, block_id, NORMAL) != OK) + return NULL; return bp; } -size_t get_extent_absolute_block_id(struct dir_extent *e, size_t block) -{ +size_t get_extent_absolute_block_id(struct dir_extent *e, size_t block) { size_t extent_offset = 0; + block /= v_pri.logical_block_size_l; if (e == NULL) return 0; /* Retrieve the extent on which the block lies. */ - while(block > extent_offset + e->length) { + while(block >= extent_offset + e->length) { if (e->next == NULL) return 0; @@ -72,9 +49,9 @@ size_t get_extent_absolute_block_id(struct dir_extent *e, size_t block) return e->location + block - extent_offset; } -time_t date7_to_time_t(const u8_t *date) -{ - /* This function converts from the ISO 9660 7-byte time format to a +time_t date7_to_time_t(const u8_t *date) { + /* + * This function converts from the ISO 9660 7-byte time format to a * time_t. */ struct tm ltime; @@ -94,3 +71,10 @@ time_t date7_to_time_t(const u8_t *date) return mktime(<ime); } + +void* alloc_mem(size_t s) { + void *ptr = calloc(1, s); + assert(ptr != NULL); + + return ptr; +} diff --git a/minix/tests/testisofs.sh b/minix/tests/testisofs.sh index 84df95d43..47ac85a8b 100755 --- a/minix/tests/testisofs.sh +++ b/minix/tests/testisofs.sh @@ -6,6 +6,11 @@ set -e echo -n "isofs test " +# testing ISO 9660 Level 3 compliance isn't possible for the time being +# (not possible to store a >4GB ISO file into a ramdisk) +testLevel3=0 +testRockRidge=1 + ramdev=/dev/ram mp=/mnt testdir=isofstest @@ -13,6 +18,97 @@ fsimage=isofsimage contents=CONTENTS out1=v1 out2=v2 + +create_contents_level3() { + # >4GB file + seq 1 1000000000 > $testdir/HUGEFILE +} + +create_contents_rockridge() { + # long filenames + mkdir -p $testdir/rockridge/longnames + echo "this is a test" > $testdir/rockridge/longnames/azertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopaz + echo "this is a test" > $testdir/rockridge/longnames/azertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopaze + echo "this is a test" > $testdir/rockridge/longnames/azertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazertyuiopazer + + # devices + mkdir -p $testdir/rockridge/devices + CURLOC=$(pwd) + cd $testdir/rockridge/devices && MAKEDEV -s + cd $CURLOC + + # symbolic links + mkdir -p $testdir/rockridge/symlinks + ln -s . $testdir/rockridge/symlinks/cur_dir + ln -s .. $testdir/rockridge/symlinks/parent_dir + ln -s / $testdir/rockridge/symlinks/root_dir + ln -s /mnt $testdir/rockridge/symlinks/root_mnt_dir + ln -s ../../rockridge $testdir/rockridge/symlinks/rockridge_dir + ln -s ../../rockridge/symlinks $testdir/rockridge/symlinks/symlinks_dir + ln -s ../../rockridge/symlinks/../symlinks $testdir/rockridge/symlinks/symlinks_dir_bis + ln -s cur_dir $testdir/rockridge/symlinks/to_cur_dir + ln -s rockridge_dir $testdir/rockridge/symlinks/to_rockridge_dir + + # deep directory tree + mkdir -p $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think + mkdir -p $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/yes + mkdir -p $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/no + echo "I agree." > $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/yes/awnser1 + echo "Yes, totally." > $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/yes/awnser2 + echo "Nah." > $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/no/awnser1 + echo "Meh." > $testdir/rockridge/deep_dirs/this/is/a/ridiculously/deep/directory/hierarchy/dont/you/think/no/awnser2 + + # permissions + mkdir -p $testdir/rockridge/permissions + for u in $(seq 0 7); do + for g in $(seq 0 7); do + for o in $(seq 0 7); do + echo "$u$g$o" > $testdir/rockridge/permissions/mode-$u$g$o + chmod $u$g$o $testdir/rockridge/permissions/mode-$u$g$o + done + done + done + echo "uid-gid test" > $testdir/rockridge/permissions/uid-1-gid-2 + chown 1:2 $testdir/rockridge/permissions/uid-1-gid-2 + echo "uid-gid test" > $testdir/rockridge/permissions/uid-128-gid-256 + chown 128:256 $testdir/rockridge/permissions/uid-128-gid-256 + echo "uid-gid test" > $testdir/rockridge/permissions/uid-12345-gid-23456 + chown 12345:23456 $testdir/rockridge/permissions/uid-12345-gid-23456 +} + +create_contents_base() { + # simple file + echo $(date) > $testdir/DATE + + # big file + seq 1 100000 > $testdir/BIGFILE + + # lots of files in a directory + mkdir $testdir/BIGDIR + for i in $(seq 1 250); do + HASH=$(cksum -a SHA1 < $testdir/BIGDIR/$FILE + done + + # lots of directories + mkdir $testdir/SUBDIRS + for i in $(seq 1 1000); do + HASH=$(cksum -a SHA1 < $testdir/SUBDIRS/$DIR1/$DIR2/$FILE + done +} + rm -rf $testdir $fsimage $out1 $out2 if [ -d $testdir ] @@ -21,7 +117,7 @@ then exit 1 fi -mkdir -p $testdir $testdir/$contents +mkdir -p $testdir if [ ! -d $testdir ] then @@ -29,33 +125,47 @@ then exit 1 fi -# Make some small & big & bigger files +# make some small & big & bigger files +OPTIONS= +create_contents_base +if [ "$testLevel3" -eq 1 ] +then + create_contents_level3 +fi +if [ "$testRockRidge" -eq 1 ] +then + create_contents_rockridge + OPTIONS="-o rockridge" +else + # fixups for the fact that bare ISO 9660 isn't POSIX enough + # for mtree + # fix permissions + find $testdir -exec chmod 555 {} ";" +fi -prevf=$testdir/$contents/FILE -echo "Test contents 123" >$prevf -for double in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -do fn=$testdir/$contents/FN.$double - cat $prevf $prevf >$fn - prevf=$fn -done - -# Make an ISO filesystem image out of it -writeisofs -s0x0 -l MINIX $testdir $fsimage >/dev/null 2>&1 +# make image +/usr/sbin/makefs -t cd9660 $OPTIONS $fsimage $testdir # umount previous things umount $ramdev >/dev/null 2>&1 || true umount $mp >/dev/null 2>&1 || true -# Mount it on a RAM disk -ramdisk 50000 $ramdev >/dev/null 2>&1 -cp $fsimage $ramdev +# mount it on a RAM disk +ramdisk $(expr $(wc -c < $fsimage) / 1024) $ramdev >/dev/null 2>&1 +cat < $fsimage > $ramdev mount -t isofs $ramdev $mp >/dev/null 2>&1 # compare contents -(cd $testdir/$contents && sha1 * | sort) >$out1 -(cd $mp/$contents && sha1 * | sort) >$out2 - -diff -u $out1 $out2 +if [ "$testRockRidge" -eq 1 ] +then + # get rid of root directory time + /usr/sbin/mtree -c -p $testdir | sed -e "s/\. *type=dir.*/\. type=dir/" | /usr/sbin/mtree -p $mp +else + # fixups for the fact that bare ISO 9660 isn't POSIX enough + # for mtree + # get rid of time + /usr/sbin/mtree -c -p $testdir | sed -e "s/time=[0-9]*.[0-9]*//" | /usr/sbin/mtree -p $mp +fi umount $ramdev >/dev/null 2>&1