./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
./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
./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
./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
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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+# $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 <bsd.own.mk>
+
+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 <bsd.prog.mk>
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#ifndef MAKEFS
+#include <sys/mount.h>
+#include <sys/disk.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <util.h>
+#include <disktab.h>
+
+#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;
+}
--- /dev/null
+/* $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 <sys/types.h>
+#include <stdbool.h>
+#define ALLOPTS \
+AOPT('@', off_t, offset, 0, "Offset in device") \
+AOPT('B', char *, bootstrap, -1, "Bootstrap file") \
+AOPT('C', off_t, create_size, 0, "Create file") \
+AOPT('F', uint8_t, fat_type, 12, "FAT type (12, 16, or 32)") \
+AOPT('I', uint32_t, volume_id, 0, "Volume ID") \
+AOPT('L', char *, volume_label, -1, "Volume Label") \
+AOPT('N', bool, no_create, -2, "Don't create filesystem, print params only") \
+AOPT('O', char *, OEM_string, -1, "OEM string") \
+AOPT('S', uint16_t, bytes_per_sector, 1, "Bytes per sector") \
+AOPT('a', uint32_t, sectors_per_fat, 1, "Sectors per FAT") \
+AOPT('b', uint32_t, block_size, 1, "Block size") \
+AOPT('c', uint8_t, sectors_per_cluster, 1, "Sectors per cluster") \
+AOPT('e', uint16_t, directory_entries, 1, "Directory entries") \
+AOPT('f', char *, floppy, -1, "Standard format floppies (160,180,320,360,640,720,1200,1232,1440,2880)") \
+AOPT('h', uint16_t, drive_heads, 1, "Drive heads") \
+AOPT('i', uint16_t, info_sector, 1, "Info sector") \
+AOPT('k', uint16_t, backup_sector, 1, "Backup sector") \
+AOPT('m', uint8_t, media_descriptor, 0, "Media descriptor") \
+AOPT('n', uint8_t, num_FAT, 1, "Number of FATs") \
+AOPT('o', uint32_t, hidden_sectors, 0, "Hidden sectors") \
+AOPT('r', uint16_t, reserved_sectors, 1, "Reserved sectors") \
+AOPT('s', uint32_t, size, 1, "File System size") \
+AOPT('u', uint16_t, sectors_per_track, 1, "Sectors per track")
+
+struct msdos_options {
+#define AOPT(_opt, _type, _name, _min, _desc) _type _name;
+ALLOPTS
+#undef AOPT
+ uint32_t volume_id_set:1;
+ uint32_t media_descriptor_set:1;
+ uint32_t hidden_sectors_set:1;
+};
+
+int mkfs_msdos(const char *, const char *, const struct msdos_options *);
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include <errno.h>
+
+#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);
+}
--- /dev/null
+# $NetBSD: Makefile,v 1.4 2013/07/18 12:44:21 reinoud Exp $
+
+.include <bsd.own.mk>
+
+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 <bsd.prog.mk>
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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 <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <util.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdio.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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;
+}
+
+/* --------------------------------------------------------------------- */
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__RCSID("$NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#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;
+}
+
--- /dev/null
+/* $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 <sys/types.h>
+#include <sys/stat.h>
+#if !HAVE_NBTOOL_CONFIG_H
+#include <fs/udf/ecma167-udf.h>
+#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_ */
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+__RCSID("$NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#define _EXPOSE_MMC
+#include <sys/cdio.h>
+#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;
+}
--- /dev/null
+/* $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 <sys/cdio.h>
+#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_ */
--- /dev/null
+/* $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;
+ }
+}
--- /dev/null
+# $NetBSD: Makefile,v 1.4 2012/09/05 23:01:42 riz Exp $
+
+.include <bsd.own.mk>
+
+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 <bsd.prog.mk>
--- /dev/null
+/* $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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <err.h>
+
+#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;
+}
--- /dev/null
+.\" $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+
+#include <fs/v7fs/v7fs.h>
+#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);
+}
--- /dev/null
+/* $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_ */
# $NetBSD: Makefile,v 1.20 2011/06/27 11:52:24 uch Exp $
SUBDIR= cd9660 msdosfs \
- puffs
+ puffs udf v7fs
.include <bsd.kinc.mk>
--- /dev/null
+# $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 <bsd.kinc.mk>
--- /dev/null
+/* $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 * <reinoud@NetBSD.org>
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * 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_ */
+
--- /dev/null
+# $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
+
--- /dev/null
+/* $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 <sys/queue.h>
+#include <sys/rbtree.h>
+#include <sys/uio.h>
+#include <sys/mutex.h>
+
+#include "udf_osta.h"
+#include "ecma167-udf.h"
+#include <sys/cdio.h>
+#include <sys/bufq.h>
+#include <sys/disk.h>
+#include <sys/kthread.h>
+#include <miscfs/genfs/genfs_node.h>
+
+/* 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<<UDF_INODE_HASHBITS)
+#define UDF_INODE_HASHMASK (UDF_INODE_HASHSIZE - 1)
+#define UDF_ECCBUF_HASHBITS 10
+#define UDF_ECCBUF_HASHSIZE (1<<UDF_ECCBUF_HASHBITS)
+#define UDF_ECCBUF_HASHMASK (UDF_ECCBUF_HASHSIZE -1)
+
+#define UDF_ECCLINE_MAXFREE 5 /* picked, needs calculation */
+#define UDF_ECCLINE_MAXBUSY 100 /* picked, needs calculation */
+
+#define UDF_MAX_MAPPINGS (MAXPHYS/DEV_BSIZE) /* 128 */
+#define UDF_VAT_CHUNKSIZE (64*1024) /* picked */
+#define UDF_SYMLINKBUFLEN (64*1024) /* picked */
+
+#define UDF_DISC_SLACK (128) /* picked, at least 64 kb or 128 */
+#define UDF_ISO_VRS_SIZE (32*2048) /* 32 ISO `sectors' */
+
+
+/* structure space */
+#define UDF_ANCHORS 4 /* 256, 512, N-256, N */
+#define UDF_PARTITIONS 4 /* overkill */
+#define UDF_PMAPS 5 /* overkill */
+#define UDF_LVDINT_SEGMENTS 100 /* big overkill */
+#define UDF_LVINT_LOSSAGE 4 /* lose 2 openings */
+#define UDF_MAX_ALLOC_EXTENTS 50 /* overkill */
+
+
+/* constants */
+#define UDF_MAXNAMLEN 255 /* as per SPEC */
+#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 */
+
+
+/* RW content hint for allocation and other purposes */
+#define UDF_C_ABSOLUTE 0 /* blob to write at absolute */
+#define UDF_C_PROCESSED 0 /* not relevant */
+#define UDF_C_USERDATA 1 /* all but userdata is metadata */
+#define UDF_C_DSCR 2 /* update sectornr and CRC */
+#define UDF_C_FLOAT_DSCR 3 /* update sectornr and CRC; sequential */
+#define UDF_C_NODE 4 /* file/dir node, update sectornr and CRC */
+#define UDF_C_FIDS 5 /* update all contained fids */
+#define UDF_C_METADATA_SBM 6 /* space bitmap, update sectornr and CRC */
+#define UDF_C_EXTATTRS 7 /* dunno what to do yet */
+
+/* use unused b_freelistindex for our UDF_C_TYPE */
+#define b_udf_c_type b_freelistindex
+
+
+/* virtual to physical mapping types */
+#define UDF_VTOP_RAWPART UDF_PMAPS /* [0..UDF_PMAPS> 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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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;
+}
+
--- /dev/null
+/* $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 <reinoud@netbsd.org>
+ *
+ */
+
+#ifndef _FS_UDF_UDF_BSWAP_H_
+#define _FS_UDF_UDF_BSWAP_H_
+
+#include <sys/endian.h>
+#include <machine/bswap.h>
+#include <sys/bswap.h>
+
+/* 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_ */
+
--- /dev/null
+/* $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_ */
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <ctype.h>
+#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 <stdio.h> */
+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<EXT_SIZE &&
+ extIndex + Index +1 < udfLen; Index++ ) {
+ current = udfName[extIndex + Index + 1];
+ if (IsIllegal(current) ||
+ !UnicodeIsPrint(current)) {
+ needsCRC = 1;
+ /* Replace Illegal and non-displayable
+ * chars with underscore.
+ */
+ current = ILLEGAL_CHAR_MARK;
+ /* Skip any other illegal or
+ * non-displayable characters.
+ */
+ while(Index + 1 < EXT_SIZE
+ && (IsIllegal(udfName[extIndex +
+ Index + 2]) ||
+ !isprint(udfName[extIndex +
+ Index + 2]))) {
+ Index++;
+ }
+ }
+ ext[localExtIndex++] = current;
+ }
+
+ /* Truncate filename to leave room for extension and
+ * CRC.
+ */
+ maxFilenameLen = ((MAXLEN - 5) - localExtIndex - 1);
+ if (newIndex > 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 */
+
--- /dev/null
+/* $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 <sys/types.h>
+
+#ifndef _KERNEL
+#include <stdbool.h>
+#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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/kauth.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/vnode.h>
+#include <sys/vnode_if.h>
+
+#include <miscfs/genfs/genfs.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#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,
+};
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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
+};
+
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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
+};
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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
+};
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/kthread.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+
+#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
+};
+
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <fs/unicode.h>
+#include <dev/clock_subr.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#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;
+}
+
+/* --------------------------------------------------------------------- */
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/specfs/specdev.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
+#include <sys/module.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#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;
+}
+
+/* --------------------------------------------------------------------- */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#ifndef lint
+__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $");
+#endif /* not lint */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
+#include <sys/kernel.h>
+#include <sys/file.h> /* define FWRITE ... */
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+#include <sys/kauth.h>
+
+#include <miscfs/genfs/genfs.h>
+#include <uvm/uvm_extern.h>
+
+#include <fs/udf/ecma167-udf.h>
+#include <fs/udf/udf_mount.h>
+#include <sys/dirhash.h>
+
+#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
+};
--- /dev/null
+# $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 <bsd.kinc.mk>
--- /dev/null
+# $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
--- /dev/null
+/* $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 <sys/types.h>
+#ifndef _KERNEL
+#include <inttypes.h>
+#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_ */
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/types.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#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';
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/resource.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/module.h>
+
+#include <miscfs/fifofs/fifo.h>
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/genfs/genfs_node.h>
+
+#include <fs/v7fs/v7fs_extern.h>
+
+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;
+ }
+}
--- /dev/null
+/* $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 <fs/v7fs/v7fs_args.h>
+
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/genfs/genfs_node.h>
+#include <miscfs/specfs/specdev.h>
+
+#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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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;
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $");
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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;
+}
--- /dev/null
+/* $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 <stdbool.h>
+#include <assert.h>
+#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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#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
+}
+
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/systm.h>
+#include <sys/param.h>
+#else
+#include <stdio.h>
+#include <time.h>
+#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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/param.h>
+#else
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#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
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/cdefs.h>
+
+__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/kauth.h>
+#include <sys/mutex.h>
+
+#include <fs/v7fs/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 {
+ 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);
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $");
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/mman.h>
+#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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/systm.h>
+#include <sys/param.h> /* errno */
+#else
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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 */
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/systm.h>
+#include <sys/param.h> /* errno */
+#else
+#include <stdio.h>
+#include <time.h>
+#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
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/pool.h>
+#include <sys/time.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/disk.h>
+#include <sys/device.h>
+#include <sys/fcntl.h>
+#include <sys/kmem.h>
+#include <sys/kauth.h>
+#include <sys/proc.h>
+
+/* v-node */
+#include <sys/namei.h>
+#include <sys/vnode.h>
+/* devsw */
+#include <sys/conf.h>
+
+#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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__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 <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/resource.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/dirent.h>
+#include <sys/kmem.h>
+#include <sys/lockf.h>
+#include <sys/unistd.h>
+#include <sys/fcntl.h>
+#include <sys/kauth.h>
+#include <sys/buf.h>
+#include <sys/stat.h> /*APPEND */
+#include <miscfs/genfs/genfs.h>
+
+#include <fs/v7fs/v7fs.h>
+#include <fs/v7fs/v7fs_impl.h>
+#include <fs/v7fs/v7fs_inode.h>
+#include <fs/v7fs/v7fs_dirent.h>
+#include <fs/v7fs/v7fs_file.h>
+#include <fs/v7fs/v7fs_datablock.h>
+#include <fs/v7fs/v7fs_extern.h>
+
+#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;
+}
\
\
link \
- \
+ makefs \
mtree \
\
\
--- /dev/null
+# $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $
+#
+
+WARNS?= 5
+
+.include <bsd.own.mk>
+
+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 <bsd.prog.mk>
+# DO NOT DELETE
--- /dev/null
+$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.
--- /dev/null
+$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.
--- /dev/null
+/* $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 <sys/mount.h>
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $");
+#endif /* !__lint */
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660/iso9660_rrip.h"
+#include "cd9660/cd9660_archimedes.h"
+
+/*
+ * Global variables
+ */
+
+static void cd9660_finalize_PVD(iso9660_disk *);
+static cd9660node *cd9660_allocate_cd9660node(void);
+static void cd9660_set_defaults(iso9660_disk *);
+static int cd9660_arguments_set_string(const char *, const char *, 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;
+}
--- /dev/null
+/* $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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include "makefs.h"
+#include "iso.h"
+#include "iso_rrip.h"
+#include "cd9660/cd9660_eltorito.h"
+
+#ifdef DEBUG
+#define INODE_WARNX(__x) warnx __x
+#else /* DEBUG */
+#define INODE_WARNX(__x)
+#endif /* DEBUG */
+
+#define CD9660MAXPATH 4096
+
+#define ISO_STRING_FILTER_NONE = 0x00
+#define ISO_STRING_FILTER_DCHARS = 0x01
+#define ISO_STRING_FILTER_ACHARS = 0x02
+
+/*
+Extended preferences type, in the spirit of what makefs gives us (only ints)
+*/
+typedef struct {
+ const char *shortName; /* Short option */
+ const char *name; /* option name */
+ char *value; /* where to stuff the value */
+ int minLength; /* minimum for value */
+ int maxLength; /* maximum for value */
+ const char *desc; /* option description */
+ int filterFlags;
+} string_option_t;
+
+/******** STRUCTURES **********/
+
+/*Defaults*/
+#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE"
+#define ISO_DEFAULT_APPID "MAKEFS"
+#define ISO_DEFAULT_PUBLISHER "MAKEFS"
+#define ISO_DEFAULT_PREPARER "MAKEFS"
+
+#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001"
+#define ISO_VOLUME_DESCRIPTOR_BOOT 0
+#define ISO_VOLUME_DESCRIPTOR_PVD 1
+#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255
+
+/*30 for name and extension, as well as version number and padding bit*/
+#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30
+#define ISO_FILENAME_MAXLENGTH 36
+#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37
+
+#define ISO_FLAG_CLEAR 0x00
+#define ISO_FLAG_HIDDEN 0x01
+#define ISO_FLAG_DIRECTORY 0x02
+#define ISO_FLAG_ASSOCIATED 0x04
+#define ISO_FLAG_PERMISSIONS 0x08
+#define ISO_FLAG_RESERVED5 0x10
+#define ISO_FLAG_RESERVED6 0x20
+#define ISO_FLAG_FINAL_RECORD 0x40
+
+#define ISO_PATHTABLE_ENTRY_BASESIZE 8
+
+#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED"
+#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved"
+
+#define CD9660_BLOCKS(__sector_size, __bytes) \
+ howmany((__bytes), (__sector_size))
+
+#define CD9660_MEM_ALLOC_ERROR(_F) \
+ err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__)
+
+#define CD9660_TYPE_FILE 0x01
+#define CD9660_TYPE_DIR 0x02
+#define CD9660_TYPE_DOT 0x04
+#define CD9660_TYPE_DOTDOT 0x08
+#define CD9660_TYPE_VIRTUAL 0x80
+
+#define CD9660_INODE_HASH_SIZE 1024
+#define CD9660_SECTOR_SIZE 2048
+
+#define CD9660_END_PADDING 150
+
+/* Slight modification of the ISO structure in iso.h */
+typedef struct _iso_directory_record_cd9660 {
+ u_char length [ISODCL (1, 1)]; /* 711 */
+ u_char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ u_char extent [ISODCL (3, 10)]; /* 733 */
+ u_char size [ISODCL (11, 18)]; /* 733 */
+ u_char date [ISODCL (19, 25)]; /* 7 by 711 */
+ u_char flags [ISODCL (26, 26)];
+ u_char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ u_char interleave [ISODCL (28, 28)]; /* 711 */
+ u_char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ u_char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} iso_directory_record_cd9660;
+
+/* TODO: Lots of optimization of this structure */
+typedef struct _cd9660node {
+ u_char type;/* Used internally */
+ /* Tree structure */
+ struct _cd9660node *parent; /* parent (NULL if root) */
+ TAILQ_HEAD(cd9660_children_head, _cd9660node) cn_children;
+ TAILQ_ENTRY(_cd9660node) cn_next_child;
+
+ struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */
+ struct _cd9660node *dot_dot_record;
+
+ fsnode *node; /* pointer to fsnode */
+ struct _iso_directory_record_cd9660 *isoDirRecord;
+ struct iso_extended_attributes *isoExtAttributes;
+
+ /***** SIZE CALCULATION *****/
+ /*already stored in isoDirRecord, but this is an int version, and will be
+ copied to isoDirRecord on writing*/
+ uint32_t fileDataSector;
+
+ /*
+ * same thing, though some notes:
+ * If a file, this is the file size
+ * If a directory, this is the size of all its children's
+ * directory records
+ * plus necessary padding
+ */
+ int64_t fileDataLength;
+
+ int64_t fileSectorsUsed;
+ int fileRecordSize;/*copy of a variable, int for quicker calculations*/
+
+ /* Old name, used for renaming - needs to be optimized but low priority */
+ char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+ /***** SPACE RESERVED FOR EXTENSIONS *****/
+ /* For memory efficiency's sake - we should move this to a separate struct
+ and point to null if not needed */
+ /* For Rock Ridge */
+ struct _cd9660node *rr_real_parent, *rr_relocated;
+
+ int64_t susp_entry_size;
+ int64_t susp_dot_entry_size;
+ int64_t susp_dot_dot_entry_size;
+
+ /* Continuation area stuff */
+ int64_t susp_entry_ce_start;
+ int64_t susp_dot_ce_start;
+ int64_t susp_dot_dot_ce_start;
+
+ int64_t susp_entry_ce_length;
+ int64_t susp_dot_ce_length;
+ int64_t susp_dot_dot_ce_length;
+
+ /* Data to put at the end of the System Use field */
+ int64_t su_tail_size;
+ char *su_tail_data;
+
+ /*** PATH TABLE STUFF ***/
+ int level; /*depth*/
+ int ptnumber;
+ struct _cd9660node *ptnext, *ptprev, *ptlast;
+
+ /* SUSP entries */
+ TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head;
+} cd9660node;
+
+typedef struct _path_table_entry
+{
+ u_char length[ISODCL (1, 1)];
+ u_char extended_attribute_length[ISODCL (2, 2)];
+ u_char first_sector[ISODCL (3, 6)];
+ u_char parent_number[ISODCL (7, 8)];
+ u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} path_table_entry;
+
+typedef struct _volume_descriptor
+{
+ u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/
+ int64_t sector;
+ struct _volume_descriptor *next;
+} volume_descriptor;
+
+typedef struct _iso9660_disk {
+ int sectorSize;
+ struct iso_primary_descriptor primaryDescriptor;
+ struct iso_supplementary_descriptor supplementaryDescriptor;
+
+ volume_descriptor *firstVolumeDescriptor;
+
+ cd9660node *rootNode;
+
+ /* Important sector numbers here */
+ /* primaryDescriptor.type_l_path_table*/
+ int64_t primaryBigEndianTableSector;
+
+ /* primaryDescriptor.type_m_path_table*/
+ int64_t primaryLittleEndianTableSector;
+
+ /* primaryDescriptor.opt_type_l_path_table*/
+ int64_t secondaryBigEndianTableSector;
+
+ /* primaryDescriptor.opt_type_m_path_table*/
+ int64_t secondaryLittleEndianTableSector;
+
+ /* primaryDescriptor.path_table_size*/
+ int pathTableLength;
+ int64_t dataFirstSector;
+
+ int64_t totalSectors;
+ /* OPTIONS GO HERE */
+ int isoLevel;
+
+ int include_padding_areas;
+
+ int follow_sym_links;
+ int verbose_level;
+ int displayHelp;
+ int keep_bad_images;
+
+ /* SUSP options and variables */
+ int64_t susp_continuation_area_start_sector;
+ int64_t susp_continuation_area_size;
+ int64_t susp_continuation_area_current_free;
+
+ int rock_ridge_enabled;
+ /* Other Rock Ridge Variables */
+ char *rock_ridge_renamed_dir_name;
+ int rock_ridge_move_count;
+ cd9660node *rr_moved_dir;
+
+ int archimedes_enabled;
+ int chrp_boot;
+
+ /* Spec breaking options */
+ u_char allow_deep_trees;
+ u_char allow_start_dot;
+ u_char allow_max_name; /* Allow 37 char filenames*/
+ u_char allow_illegal_chars; /* ~, !, # */
+ u_char allow_lowercase;
+ u_char allow_multidot;
+ u_char omit_trailing_period;
+
+ /* BOOT INFORMATION HERE */
+ int has_generic_bootimage; /* Default to 0 */
+ char *generic_bootimage;
+
+ int is_bootable;/* Default to 0 */
+ int64_t boot_catalog_sector;
+ boot_volume_descriptor *boot_descriptor;
+ char * boot_image_directory;
+
+ TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images;
+ int image_serialno;
+ LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries;
+
+} iso9660_disk;
+
+/************ FUNCTIONS **************/
+int cd9660_valid_a_chars(const char *);
+int cd9660_valid_d_chars(const char *);
+void cd9660_uppercase_characters(char *, 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
--- /dev/null
+# $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $");
+#endif /* !__lint */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660_archimedes.h"
+
+/*
+ * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC
+ * OS time (non-leap(?) centiseconds since 1900-01-01(?)).
+ */
+
+static u_int64_t
+riscos_date(time_t unixtime)
+{
+ u_int64_t base;
+
+ base = 31536000ULL * 70 + 86400 * 17;
+ return (((u_int64_t)unixtime) + base)*100;
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to a node if that seems appropriate.
+ *
+ * We touch regular files with names matching /,[0-9a-f]{3}$/ and
+ * directories matching /^!/.
+ */
+static void
+archimedes_convert_node(cd9660node *node)
+{
+ struct ISO_ARCHIMEDES *arc;
+ size_t len;
+ int type = -1;
+ uint64_t stamp;
+
+ if (node->su_tail_data != NULL)
+ /* Something else already has the tail. */
+ return;
+
+ len = strlen(node->node->name);
+ if (len < 1) return;
+
+ if (len >= 4 && node->node->name[len-4] == ',')
+ /* XXX should support ,xxx and ,lxa */
+ type = strtoul(node->node->name + len - 3, NULL, 16);
+ if (type == -1 && node->node->name[0] != '!')
+ return;
+ if (type == -1) type = 0;
+
+ assert(sizeof(*arc) == 32);
+ arc = ecalloc(1, sizeof(*arc));
+
+ stamp = riscos_date(node->node->inode->st.st_mtime);
+
+ memcpy(arc->magic, "ARCHIMEDES", 10);
+ cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr);
+ cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr);
+ arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR;
+ arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0;
+ node->su_tail_data = (void *)arc;
+ node->su_tail_size = sizeof(*arc);
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to an entire tree recursively.
+ */
+void
+archimedes_convert_tree(cd9660node *node)
+{
+ cd9660node *cn;
+
+ assert(node != NULL);
+
+ archimedes_convert_node(node);
+
+ /* Recurse on children. */
+ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+ archimedes_convert_tree(cn);
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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);
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+#include <sys/param.h>
+
+#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 <sys/mount.h>
+#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("<ptentry num=\"%i\">\n" ,num);
+ printf("<length>%i</length>\n", pttemp->length[0]);
+ printf("<extended_attribute_length>%i</extended_attribute_length>\n",
+ pttemp->extended_attribute_length[0]);
+ printf("<parent_number>%i</parent_number>\n",
+ debug_get_encoded_number(pttemp->parent_number,mode));
+ debug_dump_to_xml_padded_hex_output("name",
+ pttemp->name, pttemp->length[0]);
+ printf("</ptentry>\n");
+}
+
+void
+debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode)
+{
+ path_table_entry pttemp;
+ int t = 0;
+ int n = 0;
+
+ if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ while (t < size) {
+ /* Read fixed data first */
+ fread(&pttemp, 1, 8, fd);
+ t += 8;
+ /* Read variable */
+ fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd);
+ t += pttemp.length[0];
+ debug_dump_to_xml_ptentry(&pttemp, n, mode);
+ n++;
+ }
+
+}
+
+/*
+ * XML Debug output functions
+ * Dump hierarchy of CD, as well as volume info, to XML
+ * Can be used later to diff against a standard,
+ * or just provide easy to read detailed debug output
+ */
+void
+debug_dump_to_xml(FILE *fd)
+{
+ unsigned char buf[CD9660_SECTOR_SIZE];
+ off_t sector;
+ int t, t2;
+ struct iso_primary_descriptor primaryVD;
+ struct _boot_volume_descriptor bootVD;
+
+ memset(&primaryVD, 0, sizeof(primaryVD));
+ printf("<cd9660dump>\n");
+
+ /* Display Volume Descriptors */
+ sector = 16;
+ do {
+ if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+ err(1, "fseeko");
+ fread(buf, 1, CD9660_SECTOR_SIZE, fd);
+ t = (int)((unsigned char)buf[0]);
+ switch (t) {
+ case 0:
+ memcpy(&bootVD, buf, CD9660_SECTOR_SIZE);
+ break;
+ case 1:
+ memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE);
+ break;
+ }
+ debug_dump_to_xml_volume_descriptor(buf, sector);
+ sector++;
+ } while (t != 255);
+
+ t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table,
+ 731);
+ t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733);
+ printf("Path table 1 located at sector %i and is %i bytes long\n",
+ t,t2);
+ debug_dump_to_xml_path_table(fd, t, t2, 721);
+
+ t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table,
+ 731);
+ debug_dump_to_xml_path_table(fd, t, t2, 722);
+
+ printf("</cd9660dump>\n");
+}
+
+static void
+debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf,
+ int len)
+{
+ int i;
+ int t;
+
+ printf("<%s>",element);
+ for (i = 0; i < len; i++) {
+ t = (unsigned char)buf[i];
+ if (t >= 32 && t < 127)
+ printf("%c",t);
+ }
+ printf("</%s>\n",element);
+
+ printf("<%s:hex>",element);
+ for (i = 0; i < len; i++) {
+ t = (unsigned char)buf[i];
+ printf(" %x",t);
+ }
+ printf("</%s:hex>\n",element);
+}
+
+int
+debug_get_encoded_number(unsigned char* buf, int mode)
+{
+#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</%s>\n", element,
+ debug_get_encoded_number((unsigned char *)buf, mode), element);
+}
+
+void
+debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused)
+{
+
+}
+
+void
+debug_dump_directory_record_9_1(unsigned char* buf)
+{
+ printf("<directoryrecord>\n");
+ debug_dump_integer("length",
+ ((struct iso_directory_record*) buf)->length, 711);
+ debug_dump_integer("ext_attr_length",
+ ((struct iso_directory_record*) buf)->ext_attr_length,711);
+ debug_dump_integer("extent",
+ (char *)((struct iso_directory_record*) buf)->extent, 733);
+ debug_dump_integer("size",
+ (char *)((struct iso_directory_record*) buf)->size, 733);
+ debug_dump_integer("flags",
+ ((struct iso_directory_record*) buf)->flags, 711);
+ debug_dump_integer("file_unit_size",
+ ((struct iso_directory_record*) buf)->file_unit_size,711);
+ debug_dump_integer("interleave",
+ ((struct iso_directory_record*) buf)->interleave, 711);
+ debug_dump_integer("volume_sequence_number",
+ ((struct iso_directory_record*) buf)->volume_sequence_number,
+ 723);
+ debug_dump_integer("name_len",
+ ((struct iso_directory_record*) buf)->name_len, 711);
+ debug_dump_to_xml_padded_hex_output("name",
+ (u_char *)((struct iso_directory_record*) buf)->name,
+ debug_get_encoded_number((u_char *)
+ ((struct iso_directory_record*) buf)->length, 711));
+ printf("</directoryrecord>\n");
+}
+
+
+void
+debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector)
+{
+ printf("<volumedescriptor sector=\"%i\">\n", sector);
+ printf("<vdtype>");
+ switch(buf[0]) {
+ case 0:
+ printf("boot");
+ break;
+
+ case 1:
+ printf("primary");
+ break;
+
+ case 2:
+ printf("supplementary");
+ break;
+
+ case 3:
+ printf("volume partition descriptor");
+ break;
+
+ case 255:
+ printf("terminator");
+ break;
+ }
+
+ printf("</vdtype>\n");
+ switch(buf[0]) {
+ case 1:
+ debug_dump_integer("type",
+ ((struct iso_primary_descriptor*)buf)->type, 711);
+ debug_dump_to_xml_padded_hex_output("id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->id,
+ ISODCL ( 2, 6));
+ debug_dump_integer("version",
+ ((struct iso_primary_descriptor*)buf)->version,
+ 711);
+ debug_dump_to_xml_padded_hex_output("system_id",
+ (u_char *)((struct iso_primary_descriptor*)buf)->system_id,
+ ISODCL(9,40));
+ debug_dump_to_xml_padded_hex_output("volume_id",
+ (u_char *)((struct iso_primary_descriptor*)buf)->volume_id,
+ ISODCL(41,72));
+ debug_dump_integer("volume_space_size",
+ ((struct iso_primary_descriptor*)buf)->volume_space_size,
+ 733);
+ debug_dump_integer("volume_set_size",
+ ((struct iso_primary_descriptor*)buf)->volume_set_size,
+ 733);
+ debug_dump_integer("volume_sequence_number",
+ ((struct iso_primary_descriptor*)buf)->volume_sequence_number,
+ 723);
+ debug_dump_integer("logical_block_size",
+ ((struct iso_primary_descriptor*)buf)->logical_block_size,
+ 723);
+ debug_dump_integer("path_table_size",
+ ((struct iso_primary_descriptor*)buf)->path_table_size,
+ 733);
+ debug_dump_integer("type_l_path_table",
+ ((struct iso_primary_descriptor*)buf)->type_l_path_table,
+ 731);
+ debug_dump_integer("opt_type_l_path_table",
+ ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table,
+ 731);
+ debug_dump_integer("type_m_path_table",
+ ((struct iso_primary_descriptor*)buf)->type_m_path_table,
+ 732);
+ debug_dump_integer("opt_type_m_path_table",
+ ((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732);
+ debug_dump_directory_record_9_1(
+ (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record);
+ debug_dump_to_xml_padded_hex_output("volume_set_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id,
+ ISODCL (191, 318));
+ debug_dump_to_xml_padded_hex_output("publisher_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id,
+ ISODCL (319, 446));
+ debug_dump_to_xml_padded_hex_output("preparer_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id,
+ ISODCL (447, 574));
+ debug_dump_to_xml_padded_hex_output("application_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->application_id,
+ ISODCL (575, 702));
+ debug_dump_to_xml_padded_hex_output("copyright_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id,
+ ISODCL (703, 739));
+ debug_dump_to_xml_padded_hex_output("abstract_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id,
+ ISODCL (740, 776));
+ debug_dump_to_xml_padded_hex_output("bibliographic_file_id",
+ (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id,
+ ISODCL (777, 813));
+
+ debug_dump_to_xml_padded_hex_output("creation_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->creation_date,
+ ISODCL (814, 830));
+ debug_dump_to_xml_padded_hex_output("modification_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->modification_date,
+ ISODCL (831, 847));
+ debug_dump_to_xml_padded_hex_output("expiration_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date,
+ ISODCL (848, 864));
+ debug_dump_to_xml_padded_hex_output("effective_date",
+ (u_char *)((struct iso_primary_descriptor*) buf)->effective_date,
+ ISODCL (865, 881));
+
+ debug_dump_to_xml_padded_hex_output("file_structure_version",
+ (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version,
+ ISODCL(882,882));
+ break;
+ }
+ printf("</volumedescriptor>\n");
+}
+
--- /dev/null
+/* $NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+
+#include "cd9660.h"
+#include "cd9660_eltorito.h"
+#include <sys/bootblock.h>
+#include <util.h>
+
+#include <sys/cdefs.h>
+#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 <util.h>
+
+static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
+ struct cd9660_boot_image *);
+static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+#if 0
+static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
+#endif
+
+int
+cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info)
+{
+ struct stat stbuf;
+ const char *mode_msg;
+ char *temp;
+ char *sysname;
+ char *filename;
+ struct cd9660_boot_image *new_image, *tmp_image;
+
+ assert(boot_info != NULL);
+
+ if (*boot_info == '\0') {
+ warnx("Error: Boot disk information must be in the "
+ "format 'system;filename'");
+ return 0;
+ }
+
+ /* First decode the boot information */
+ temp = estrdup(boot_info);
+
+ sysname = temp;
+ filename = strchr(sysname, ';');
+ if (filename == NULL) {
+ warnx("supply boot disk information in the format "
+ "'system;filename'");
+ free(temp);
+ return 0;
+ }
+
+ *filename++ = '\0';
+
+ if (diskStructure->verbose_level > 0) {
+ printf("Found bootdisk with system %s, and filename %s\n",
+ sysname, filename);
+ }
+ new_image = ecalloc(1, sizeof(*new_image));
+ new_image->loadSegment = 0; /* default for now */
+
+ /* Decode System */
+ if (strcmp(sysname, "i386") == 0)
+ new_image->system = ET_SYS_X86;
+ else if (strcmp(sysname, "powerpc") == 0)
+ new_image->system = ET_SYS_PPC;
+ else if (strcmp(sysname, "macppc") == 0 ||
+ strcmp(sysname, "mac68k") == 0)
+ new_image->system = ET_SYS_MAC;
+ else {
+ warnx("boot disk system must be "
+ "i386, powerpc, macppc, or mac68k");
+ free(temp);
+ free(new_image);
+ return 0;
+ }
+
+
+ new_image->filename = estrdup(filename);
+
+ free(temp);
+
+ /* Get information about the file */
+ if (lstat(new_image->filename, &stbuf) == -1)
+ err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+ new_image->filename);
+
+ switch (stbuf.st_size) {
+ case 1440 * 1024:
+ new_image->targetMode = ET_MEDIA_144FDD;
+ mode_msg = "Assigned boot image to 1.44 emulation mode";
+ break;
+ case 1200 * 1024:
+ new_image->targetMode = ET_MEDIA_12FDD;
+ mode_msg = "Assigned boot image to 1.2 emulation mode";
+ break;
+ case 2880 * 1024:
+ new_image->targetMode = ET_MEDIA_288FDD;
+ mode_msg = "Assigned boot image to 2.88 emulation mode";
+ break;
+ default:
+ new_image->targetMode = ET_MEDIA_NOEM;
+ mode_msg = "Assigned boot image to no emulation mode";
+ break;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("%s\n", mode_msg);
+
+ new_image->size = stbuf.st_size;
+ new_image->num_sectors =
+ howmany(new_image->size, diskStructure->sectorSize) *
+ howmany(diskStructure->sectorSize, 512);
+ if (diskStructure->verbose_level > 0) {
+ printf("New image has size %d, uses %d 512-byte sectors\n",
+ new_image->size, new_image->num_sectors);
+ }
+ new_image->sector = -1;
+ /* Bootable by default */
+ new_image->bootable = ET_BOOTABLE;
+ /* Add boot disk */
+
+ /* Group images for the same platform together. */
+ TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) {
+ if (tmp_image->system != new_image->system)
+ break;
+ }
+
+ if (tmp_image == NULL) {
+ TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image,
+ image_list);
+ } else
+ TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
+
+ new_image->serialno = diskStructure->image_serialno++;
+
+ /* TODO : Need to do anything about the boot image in the tree? */
+ diskStructure->is_bootable = 1;
+
+ return 1;
+}
+
+int
+cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure,
+ const char *option_string, const char *value)
+{
+ char *eptr;
+ struct cd9660_boot_image *image;
+
+ assert(option_string != NULL);
+
+ /* Find the last image added */
+ TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) {
+ if (image->serialno + 1 == diskStructure->image_serialno)
+ break;
+ }
+ if (image == NULL)
+ errx(EXIT_FAILURE, "Attempted to add boot option, "
+ "but no boot images have been specified");
+
+ if (strcmp(option_string, "no-emul-boot") == 0) {
+ image->targetMode = ET_MEDIA_NOEM;
+ } else if (strcmp(option_string, "no-boot") == 0) {
+ image->bootable = ET_NOT_BOOTABLE;
+ } else if (strcmp(option_string, "hard-disk-boot") == 0) {
+ image->targetMode = ET_MEDIA_HDD;
+ } else if (strcmp(option_string, "boot-load-segment") == 0) {
+ image->loadSegment = strtoul(value, &eptr, 16);
+ if (eptr == value || *eptr != '\0' || errno != ERANGE) {
+ warn("%s: strtoul", __func__);
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static struct boot_catalog_entry *
+cd9660_init_boot_catalog_entry(void)
+{
+ return ecalloc(1, sizeof(struct boot_catalog_entry));
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_validation_entry(char sys)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_validation_entry *ve;
+ int16_t checksum;
+ unsigned char *csptr;
+ size_t i;
+ entry = cd9660_init_boot_catalog_entry();
+
+ ve = &entry->entry_data.VE;
+
+ ve->header_id[0] = 1;
+ ve->platform_id[0] = sys;
+ ve->key[0] = 0x55;
+ ve->key[1] = 0xAA;
+
+ /* Calculate checksum */
+ checksum = 0;
+ cd9660_721(0, ve->checksum);
+ csptr = (unsigned char*)ve;
+ for (i = 0; i < sizeof(*ve); i += 2) {
+ checksum += (int16_t)csptr[i];
+ checksum += 256 * (int16_t)csptr[i + 1];
+ }
+ checksum = -checksum;
+ cd9660_721(checksum, ve->checksum);
+
+ ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
+ "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
+ ve->key[0], ve->key[1], checksum));
+ return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
+{
+ struct boot_catalog_entry *default_entry;
+ boot_catalog_initial_entry *ie;
+
+ default_entry = cd9660_init_boot_catalog_entry();
+ if (default_entry == NULL)
+ return NULL;
+
+ ie = &default_entry->entry_data.IE;
+
+ ie->boot_indicator[0] = disk->bootable;
+ ie->media_type[0] = disk->targetMode;
+ cd9660_721(disk->loadSegment, ie->load_segment);
+ ie->system_type[0] = disk->system;
+ cd9660_721(disk->num_sectors, ie->sector_count);
+ cd9660_731(disk->sector, ie->load_rba);
+
+ ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
+ "load segment %04x, system type %d, sector count %d, "
+ "load rba %d\n", __func__, ie->boot_indicator[0],
+ ie->media_type[0], disk->loadSegment, ie->system_type[0],
+ disk->num_sectors, disk->sector));
+ return default_entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_head(char platform)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_section_header *sh;
+
+ entry = cd9660_init_boot_catalog_entry();
+ if (entry == NULL)
+ return NULL;
+
+ sh = &entry->entry_data.SH;
+ /* More by default. The last one will manually be set to 0x91 */
+ sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
+ sh->platform_id[0] = platform;
+ sh->num_section_entries[0] = 0;
+ return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
+{
+ struct boot_catalog_entry *entry;
+ boot_catalog_section_entry *se;
+ if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
+ return NULL;
+
+ se = &entry->entry_data.SE;
+
+ se->boot_indicator[0] = ET_BOOTABLE;
+ se->media_type[0] = disk->targetMode;
+ cd9660_721(disk->loadSegment, se->load_segment);
+ cd9660_721(disk->num_sectors, se->sector_count);
+ cd9660_731(disk->sector, se->load_rba);
+ return entry;
+}
+
+#if 0
+static u_char
+cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
+{
+ /*
+ For hard drive booting, we need to examine the MBR to figure
+ out what the partition type is
+ */
+ return 0;
+}
+#endif
+
+/*
+ * Set up the BVD, Boot catalog, and the boot entries, but do no writing
+ */
+int
+cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector)
+{
+ int sector;
+ int used_sectors;
+ int num_entries = 0;
+ int catalog_sectors;
+ struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
+ *valid_entry, *default_entry, *temp, *head, **headp, *next;
+ struct cd9660_boot_image *tmp_disk;
+
+ headp = NULL;
+ x86_head = mac_head = ppc_head = NULL;
+
+ /* If there are no boot disks, don't bother building boot information */
+ if (TAILQ_EMPTY(&diskStructure->boot_images))
+ return 0;
+
+ /* Point to catalog: For now assume it consumes one sector */
+ ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
+ diskStructure->boot_catalog_sector = first_sector;
+ cd9660_bothendian_dword(first_sector,
+ diskStructure->boot_descriptor->boot_catalog_pointer);
+
+ /* Step 1: Generate boot catalog */
+ /* Step 1a: Validation entry */
+ valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
+ if (valid_entry == NULL)
+ return -1;
+
+ /*
+ * Count how many boot images there are,
+ * and how many sectors they consume.
+ */
+ num_entries = 1;
+ used_sectors = 0;
+
+ TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+ used_sectors += tmp_disk->num_sectors;
+
+ /* One default entry per image */
+ num_entries++;
+ }
+ catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize);
+ used_sectors += catalog_sectors;
+
+ if (diskStructure->verbose_level > 0) {
+ printf("%s: there will be %i entries consuming %i sectors. "
+ "Catalog is %i sectors\n", __func__, num_entries,
+ used_sectors, catalog_sectors);
+ }
+
+ /* Populate sector numbers */
+ sector = first_sector + catalog_sectors;
+ TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
+ tmp_disk->sector = sector;
+ sector += tmp_disk->num_sectors;
+ }
+
+ LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct);
+
+ /* Step 1b: Initial/default entry */
+ /* TODO : PARAM */
+ tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
+ default_entry = cd9660_boot_setup_default_entry(tmp_disk);
+ if (default_entry == NULL) {
+ warnx("Error: memory allocation failed in cd9660_setup_boot");
+ return -1;
+ }
+
+ LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
+
+ /* Todo: multiple default entries? */
+
+ tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+
+ temp = default_entry;
+
+ /* If multiple boot images are given : */
+ while (tmp_disk != NULL) {
+ /* Step 2: Section header */
+ switch (tmp_disk->system) {
+ case ET_SYS_X86:
+ headp = &x86_head;
+ break;
+ case ET_SYS_PPC:
+ headp = &ppc_head;
+ break;
+ case ET_SYS_MAC:
+ headp = &mac_head;
+ break;
+ default:
+ warnx("%s: internal error: unknown system type",
+ __func__);
+ return -1;
+ }
+
+ if (*headp == NULL) {
+ head =
+ cd9660_boot_setup_section_head(tmp_disk->system);
+ if (head == NULL) {
+ warnx("Error: memory allocation failed in "
+ "cd9660_setup_boot");
+ return -1;
+ }
+ LIST_INSERT_AFTER(default_entry, head, ll_struct);
+ *headp = head;
+ } else
+ head = *headp;
+
+ head->entry_data.SH.num_section_entries[0]++;
+
+ /* Step 2a: Section entry and extensions */
+ temp = cd9660_boot_setup_section_entry(tmp_disk);
+ if (temp == NULL) {
+ warn("%s: cd9660_boot_setup_section_entry", __func__);
+ return -1;
+ }
+
+ while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
+ next->entry_type == ET_ENTRY_SE)
+ head = next;
+
+ LIST_INSERT_AFTER(head, temp, ll_struct);
+ tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+ }
+
+ /* TODO: Remaining boot disks when implemented */
+
+ return first_sector + used_sectors;
+}
+
+int
+cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure,
+ volume_descriptor *bvd)
+{
+ boot_volume_descriptor *bvdData =
+ (boot_volume_descriptor*)bvd->volumeDescriptorData;
+
+ bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
+ memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+ bvdData->version[0] = 1;
+ memcpy(bvdData->boot_system_identifier, ET_ID, 23);
+ memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+ diskStructure->boot_descriptor =
+ (boot_volume_descriptor*) bvd->volumeDescriptorData;
+ return 1;
+}
+
+static int
+cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
+ off_t nsectors, int type)
+{
+ uint8_t val;
+ uint32_t lba;
+
+ if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ val = 0x80; /* Bootable */
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = 0xff; /* CHS begin */
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = type; /* Part type */
+ fwrite(&val, sizeof(val), 1, fd);
+
+ val = 0xff; /* CHS end */
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+ fwrite(&val, sizeof(val), 1, fd);
+
+ /* LBA extent */
+ lba = htole32(sector_start);
+ fwrite(&lba, sizeof(lba), 1, fd);
+ lba = htole32(nsectors);
+ fwrite(&lba, sizeof(lba), 1, fd);
+
+ return 0;
+}
+
+static int
+cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
+ off_t sector_start, off_t nsectors, off_t sector_size,
+ const char *part_name, const char *part_type)
+{
+ uint32_t apm32, part_status;
+ uint16_t apm16;
+
+ /* See Apple Tech Note 1189 for the details about the pmPartStatus
+ * flags.
+ * Below the flags which are default:
+ * - IsValid 0x01
+ * - IsAllocated 0x02
+ * - IsReadable 0x10
+ * - IsWritable 0x20
+ */
+ part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE |
+ APPLE_PS_WRITABLE;
+
+ if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ /* Signature */
+ apm16 = htobe16(0x504d);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ apm16 = 0;
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+
+ /* Total number of partitions */
+ apm32 = htobe32(total_partitions);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* Bounds */
+ apm32 = htobe32(sector_start);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ apm32 = htobe32(nsectors);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+
+ fwrite(part_name, strlen(part_name) + 1, 1, fd);
+ fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
+ fwrite(part_type, strlen(part_type) + 1, 1, fd);
+ fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
+
+ apm32 = 0;
+ /* pmLgDataStart */
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* pmDataCnt */
+ apm32 = htobe32(nsectors);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* pmPartStatus */
+ apm32 = htobe32(part_status);
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+
+ return 0;
+}
+
+int
+cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd)
+{
+ struct boot_catalog_entry *e;
+ struct cd9660_boot_image *t;
+ int apm_partitions = 0;
+ int mbr_partitions = 0;
+
+ /* write boot catalog */
+ if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector *
+ diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing boot catalog to sector %" PRId64 "\n",
+ diskStructure->boot_catalog_sector);
+ }
+ LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) {
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing catalog entry of type %d\n",
+ e->entry_type);
+ }
+ /*
+ * It doesnt matter which one gets written
+ * since they are the same size
+ */
+ fwrite(&(e->entry_data.VE), 1, 32, fd);
+ }
+ if (diskStructure->verbose_level > 0)
+ printf("Finished writing boot catalog\n");
+
+ /* copy boot images */
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (diskStructure->verbose_level > 0) {
+ printf("Writing boot image from %s to sectors %d\n",
+ t->filename, t->sector);
+ }
+ cd9660_copy_file(diskStructure, fd, t->sector, t->filename);
+
+ if (t->system == ET_SYS_MAC)
+ apm_partitions++;
+ if (t->system == ET_SYS_PPC)
+ mbr_partitions++;
+ }
+
+ /* some systems need partition tables as well */
+ if (mbr_partitions > 0 || diskStructure->chrp_boot) {
+ uint16_t sig;
+
+ fseek(fd, 0x1fe, SEEK_SET);
+ sig = htole16(0xaa55);
+ fwrite(&sig, sizeof(sig), 1, fd);
+
+ mbr_partitions = 0;
+
+ /* Write ISO9660 descriptor, enclosing the whole disk */
+ if (diskStructure->chrp_boot)
+ cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+ 0, diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512), 0x96);
+
+ /* Write all partition entries */
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (t->system != ET_SYS_PPC)
+ continue;
+ cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+ t->sector * (diskStructure->sectorSize / 512),
+ t->num_sectors * (diskStructure->sectorSize / 512),
+ 0x41 /* PReP Boot */);
+ }
+ }
+
+ if (apm_partitions > 0) {
+ /* Write DDR and global APM info */
+ uint32_t apm32;
+ uint16_t apm16;
+ int total_parts;
+
+ fseek(fd, 0, SEEK_SET);
+ apm16 = htobe16(0x4552);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ /* Device block size */
+ apm16 = htobe16(512);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ /* Device block count */
+ apm32 = htobe32(diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512));
+ fwrite(&apm32, sizeof(apm32), 1, fd);
+ /* Device type/id */
+ apm16 = htobe16(1);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+ fwrite(&apm16, sizeof(apm16), 1, fd);
+
+ /* Count total needed entries */
+ total_parts = 2 + apm_partitions; /* Self + ISO9660 */
+
+ /* Write self-descriptor */
+ cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
+ total_parts, 512, "Apple", "Apple_partition_map");
+
+ /* Write all partition entries */
+ apm_partitions = 0;
+ TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
+ if (t->system != ET_SYS_MAC)
+ continue;
+
+ cd9660_write_apm_partition_entry(fd,
+ 1 + apm_partitions++, total_parts,
+ t->sector * (diskStructure->sectorSize / 512),
+ t->num_sectors * (diskStructure->sectorSize / 512),
+ 512, "CD Boot", "Apple_Bootstrap");
+ }
+
+ /* Write ISO9660 descriptor, enclosing the whole disk */
+ cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
+ total_parts, 0, diskStructure->totalSectors *
+ (diskStructure->sectorSize / 512), 512, "ISO9660",
+ "CD_ROM_Mode_1");
+ }
+
+ return 0;
+}
--- /dev/null
+/* $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_ */
+
--- /dev/null
+/* $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 <sys/mount.h>
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $");
+#endif /* !__lint */
+
+#include <util.h>
+
+static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *);
+static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int);
+static int cd9660_write_path_tables(iso9660_disk *, FILE *);
+static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *);
+static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t,
+ const unsigned char *, int);
+#if 0
+static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *);
+#endif
+static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t);
+
+/*
+ * Write the image
+ * Writes the entire image
+ * @param const char* The filename for the image
+ * @returns int 1 on success, 0 on failure
+ */
+int
+cd9660_write_image(iso9660_disk *diskStructure, const char* image)
+{
+ FILE *fd;
+ int status;
+ char buf[CD9660_SECTOR_SIZE];
+
+ if ((fd = fopen(image, "w+")) == NULL) {
+ err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
+ image);
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Writing image\n");
+
+ if (diskStructure->has_generic_bootimage) {
+ status = cd9660_copy_file(diskStructure, fd, 0,
+ diskStructure->generic_bootimage);
+ if (status == 0) {
+ warnx("%s: Error writing generic boot image",
+ __func__);
+ goto cleanup_bad_image;
+ }
+ }
+
+ /* Write the volume descriptors */
+ status = cd9660_write_volume_descriptors(diskStructure, fd);
+ if (status == 0) {
+ warnx("%s: Error writing volume descriptors to image",
+ __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Volume descriptors written\n");
+
+ /*
+ * Write the path tables: there are actually four, but right
+ * now we are only concearned with two.
+ */
+ status = cd9660_write_path_tables(diskStructure, fd);
+ if (status == 0) {
+ warnx("%s: Error writing path tables to image", __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->verbose_level > 0)
+ printf("Path tables written\n");
+
+ /* Write the directories and files */
+ status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode);
+ if (status == 0) {
+ warnx("%s: Error writing files to image", __func__);
+ goto cleanup_bad_image;
+ }
+
+ if (diskStructure->is_bootable) {
+ cd9660_write_boot(diskStructure, fd);
+ }
+
+ /* Write padding bits. This is temporary */
+ memset(buf, 0, CD9660_SECTOR_SIZE);
+ cd9660_write_filedata(diskStructure, fd,
+ diskStructure->totalSectors - 1, buf, 1);
+
+ if (diskStructure->verbose_level > 0)
+ printf("Files written\n");
+ fclose(fd);
+
+ if (diskStructure->verbose_level > 0)
+ printf("Image closed\n");
+ return 1;
+
+cleanup_bad_image:
+ fclose(fd);
+ if (!diskStructure->keep_bad_images)
+ unlink(image);
+ if (diskStructure->verbose_level > 0)
+ printf("Bad image cleaned up\n");
+ return 0;
+}
+
+static int
+cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd)
+{
+ volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor;
+ while (vd_temp != NULL) {
+ cd9660_write_filedata(diskStructure, fd, vd_temp->sector,
+ vd_temp->volumeDescriptorData, 1);
+ vd_temp = vd_temp->next;
+ }
+ return 1;
+}
+
+/*
+ * Write out an individual path table
+ * Used just to keep redundant code to a minimum
+ * @param FILE *fd Valid file pointer
+ * @param int Sector to start writing path table to
+ * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
+ * @returns int 1 on success, 0 on failure
+ */
+static int
+cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+ int mode)
+{
+ int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize,
+ diskStructure->pathTableLength);
+ unsigned char *buffer;
+ unsigned char *buffer_head;
+ int len;
+ path_table_entry temp_entry;
+ cd9660node *ptcur;
+
+ buffer = ecalloc(path_table_sectors, diskStructure->sectorSize);
+ buffer_head = buffer;
+
+ ptcur = diskStructure->rootNode;
+
+ while (ptcur != NULL) {
+ memset(&temp_entry, 0, sizeof(path_table_entry));
+ temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
+ temp_entry.extended_attribute_length[0] =
+ ptcur->isoDirRecord->ext_attr_length[0];
+ memcpy(temp_entry.name, ptcur->isoDirRecord->name,
+ temp_entry.length[0] + 1);
+
+ /* round up */
+ len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
+
+ /* todo: function pointers instead */
+ if (mode == LITTLE_ENDIAN) {
+ cd9660_731(ptcur->fileDataSector,
+ temp_entry.first_sector);
+ cd9660_721((ptcur->parent == NULL ?
+ 1 : ptcur->parent->ptnumber),
+ temp_entry.parent_number);
+ } else {
+ cd9660_732(ptcur->fileDataSector,
+ temp_entry.first_sector);
+ cd9660_722((ptcur->parent == NULL ?
+ 1 : ptcur->parent->ptnumber),
+ temp_entry.parent_number);
+ }
+
+
+ memcpy(buffer, &temp_entry, len);
+ buffer += len;
+
+ ptcur = ptcur->ptnext;
+ }
+
+ return cd9660_write_filedata(diskStructure, fd, sector, buffer_head,
+ path_table_sectors);
+}
+
+
+/*
+ * Write out the path tables to disk
+ * Each file descriptor should be pointed to by the PVD, so we know which
+ * sector to copy them to. One thing to watch out for: the only path tables
+ * stored are in the endian mode that the application is compiled for. So,
+ * the first thing to do is write out that path table, then to write the one
+ * in the other endian mode requires to convert the endianness of each entry
+ * in the table. The best way to do this would be to create a temporary
+ * path_table_entry structure, then for each path table entry, copy it to
+ * the temporary entry, translate, then copy that to disk.
+ *
+ * @param FILE* Valid file descriptor
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd)
+{
+ if (cd9660_write_path_table(diskStructure, fd,
+ diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
+ return 0;
+
+ if (cd9660_write_path_table(diskStructure, fd,
+ diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0)
+ return 0;
+
+ /* @TODO: handle remaining two path tables */
+ return 1;
+}
+
+/*
+ * Write a file to disk
+ * Writes a file, its directory record, and its data to disk
+ * This file is designed to be called RECURSIVELY, so initially call it
+ * with the root node. All of the records should store what sector the
+ * file goes in, so no computation should be necessary.
+ *
+ * @param int fd Valid file descriptor
+ * @param struct cd9660node* writenode Pointer to the file to be written
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode)
+{
+ char *buf;
+ char *temp_file_name;
+ int ret;
+ off_t working_sector;
+ int cur_sector_offset;
+ iso_directory_record_cd9660 temp_record;
+ cd9660node *temp;
+ int rv = 0;
+
+ /* Todo : clean up variables */
+
+ temp_file_name = ecalloc(CD9660MAXPATH + 1, 1);
+ buf = emalloc(diskStructure->sectorSize);
+ if ((writenode->level != 0) &&
+ !(writenode->node->type & S_IFDIR)) {
+ fsinode *inode = writenode->node->inode;
+ /* Only attempt to write unwritten files that have length. */
+ if ((inode->flags & FI_WRITTEN) != 0) {
+ INODE_WARNX(("%s: skipping written inode %d", __func__,
+ (int)inode->st.st_ino));
+ } else if (writenode->fileDataLength > 0) {
+ INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
+ __func__, (int)inode->st.st_ino, inode->ino));
+ inode->flags |= FI_WRITTEN;
+ cd9660_compute_full_filename(writenode,
+ temp_file_name);
+ ret = cd9660_copy_file(diskStructure, fd,
+ writenode->fileDataSector, temp_file_name);
+ if (ret == 0)
+ goto out;
+ }
+ } else {
+ /*
+ * Here is a new revelation that ECMA didnt explain
+ * (at least not well).
+ * ALL . and .. records store the name "\0" and "\1"
+ * resepctively. So, for each directory, we have to
+ * make a new node.
+ *
+ * This is where it gets kinda messy, since we have to
+ * be careful of sector boundaries
+ */
+ cur_sector_offset = 0;
+ working_sector = writenode->fileDataSector;
+ if (fseeko(fd, working_sector * diskStructure->sectorSize,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ /*
+ * Now loop over children, writing out their directory
+ * records - beware of sector boundaries
+ */
+ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+ /*
+ * Copy the temporary record and adjust its size
+ * if necessary
+ */
+ memcpy(&temp_record, temp->isoDirRecord,
+ sizeof(iso_directory_record_cd9660));
+
+ temp_record.length[0] =
+ cd9660_compute_record_size(diskStructure, temp);
+
+ if (temp_record.length[0] + cur_sector_offset >=
+ diskStructure->sectorSize) {
+ cur_sector_offset = 0;
+ working_sector++;
+
+ /* Seek to the next sector. */
+ if (fseeko(fd, working_sector *
+ diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+ }
+ /* Write out the basic ISO directory record */
+ (void)fwrite(&temp_record, 1,
+ temp->isoDirRecord->length[0], fd);
+ if (diskStructure->rock_ridge_enabled) {
+ cd9660_write_rr(diskStructure, fd, temp,
+ cur_sector_offset, working_sector);
+ }
+ if (fseeko(fd, working_sector *
+ diskStructure->sectorSize + cur_sector_offset +
+ temp_record.length[0] - temp->su_tail_size,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+ if (temp->su_tail_size > 0)
+ fwrite(temp->su_tail_data, 1,
+ temp->su_tail_size, fd);
+ if (ferror(fd)) {
+ warnx("%s: write error", __func__);
+ goto out;
+ }
+ cur_sector_offset += temp_record.length[0];
+
+ }
+
+ /*
+ * Recurse on children.
+ */
+ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+ if ((ret = cd9660_write_file(diskStructure, fd, temp))
+ == 0)
+ goto out;
+ }
+ }
+ rv = 1;
+out:
+ free(temp_file_name);
+ free(buf);
+ return rv;
+}
+
+/*
+ * Wrapper function to write a buffer (one sector) to disk.
+ * Seeks and writes the buffer.
+ * NOTE: You dont NEED to use this function, but it might make your
+ * life easier if you have to write things that align to a sector
+ * (such as volume descriptors).
+ *
+ * @param int fd Valid file descriptor
+ * @param int sector Sector number to write to
+ * @param const unsigned char* Buffer to write. This should be the
+ * size of a sector, and if only a portion
+ * is written, the rest should be set to 0.
+ */
+static int
+cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector,
+ const unsigned char *buf, int numsecs)
+{
+ off_t curpos;
+ size_t success;
+
+ curpos = ftello(fd);
+
+ if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd);
+
+ if (fseeko(fd, curpos, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ if (success == 1)
+ success = diskStructure->sectorSize * numsecs;
+ return success;
+}
+
+#if 0
+static int
+cd9660_write_buffered(FILE *fd, off_t offset, int buff_len,
+ const unsigned char* buffer)
+{
+ static int working_sector = -1;
+ static char buf[CD9660_SECTOR_SIZE];
+
+ return 0;
+}
+#endif
+
+int
+cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector,
+ const char *filename)
+{
+ FILE *rf;
+ int bytes_read;
+ off_t sector = start_sector;
+ int buf_size = diskStructure->sectorSize;
+ char *buf;
+
+ buf = emalloc(buf_size);
+ if ((rf = fopen(filename, "rb")) == NULL) {
+ warn("%s: cannot open %s", __func__, filename);
+ free(buf);
+ return 0;
+ }
+
+ if (diskStructure->verbose_level > 1)
+ printf("Writing file: %s\n",filename);
+
+ if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1)
+ err(1, "fseeko");
+
+ while (!feof(rf)) {
+ bytes_read = fread(buf,1,buf_size,rf);
+ if (ferror(rf)) {
+ warn("%s: fread", __func__);
+ free(buf);
+ (void)fclose(rf);
+ return 0;
+ }
+
+ fwrite(buf,1,bytes_read,fd);
+ if (ferror(fd)) {
+ warn("%s: fwrite", __func__);
+ free(buf);
+ (void)fclose(rf);
+ return 0;
+ }
+ sector++;
+ }
+
+ fclose(rf);
+ free(buf);
+ return 1;
+}
+
+static void
+cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode,
+ off_t offset, off_t sector)
+{
+ int in_ca = 0;
+ struct ISO_SUSP_ATTRIBUTES *myattr;
+
+ offset += writenode->isoDirRecord->length[0];
+ if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) ==
+ -1)
+ err(1, "fseeko");
+ /* Offset now points at the end of the record */
+ TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
+ fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
+
+ if (!in_ca) {
+ offset += CD9660_SUSP_ENTRY_SIZE(myattr);
+ if (myattr->last_in_suf) {
+ /*
+ * Point the offset to the start of this
+ * record's CE area
+ */
+ if (fseeko(fd, ((off_t)diskStructure->
+ susp_continuation_area_start_sector *
+ diskStructure->sectorSize)
+ + writenode->susp_entry_ce_start,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+ in_ca = 1;
+ }
+ }
+ }
+
+ /*
+ * If we had to go to the continuation area, head back to
+ * where we should be.
+ */
+ if (in_ca)
+ if (fseeko(fd, sector * diskStructure->sectorSize + offset,
+ SEEK_SET) == -1)
+ err(1, "fseeko");
+}
--- /dev/null
+/* $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 <sys/queue.h>
+#include <stdio.h>
+#include <util.h>
+
+#include <sys/cdefs.h>
+#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;
+}
--- /dev/null
+/* $NetBSD: iso9660_rrip.h,v 1.6 2013/01/28 21:03:28 christos Exp $ */
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam. All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#ifndef __ISO9660_RRIP_H__
+#define __ISO9660_RRIP_H__
+
+/*
+ * This will hold all the functions needed to
+ * write an ISO 9660 image with Rock Ridge Extensions
+ */
+
+/* For writing must use ISO_RRIP_EXTREF structure */
+
+#include "makefs.h"
+#include <cd9660_rrip.h>
+#include "cd9660.h"
+#include <sys/queue.h>
+
+#define PX_LENGTH 0x2C
+#define PN_LENGTH 0x14
+#define TF_CREATION 0x00
+#define TF_MODIFY 0x01
+#define TF_ACCESS 0x02
+#define TF_ATTRIBUTES 0x04
+#define TF_BACKUP 0x08
+#define TF_EXPIRATION 0x10
+#define TF_EFFECTIVE 0x20
+#define TF_LONGFORM 0x40
+#define NM_CONTINUE 0x80
+#define NM_CURRENT 0x100
+#define NM_PARENT 0x200
+
+
+#define SUSP_LOC_ENTRY 0x01
+#define SUSP_LOC_DOT 0x02
+#define SUSP_LOC_DOTDOT 0x04
+
+#define SUSP_TYPE_SUSP 1
+#define SUSP_TYPE_RRIP 2
+
+#define SUSP_ENTRY_SUSP_CE 1
+#define SUSP_ENTRY_SUSP_PD 2
+#define SUSP_ENTRY_SUSP_SP 3
+#define SUSP_ENTRY_SUSP_ST 4
+#define SUSP_ENTRY_SUSP_ER 5
+#define SUSP_ENTRY_SUSP_ES 6
+
+#define SUSP_ENTRY_RRIP_PX 1
+#define SUSP_ENTRY_RRIP_PN 2
+#define SUSP_ENTRY_RRIP_SL 3
+#define SUSP_ENTRY_RRIP_NM 4
+#define SUSP_ENTRY_RRIP_CL 5
+#define SUSP_ENTRY_RRIP_PL 6
+#define SUSP_ENTRY_RRIP_RE 7
+#define SUSP_ENTRY_RRIP_TF 8
+#define SUSP_ENTRY_RRIP_SF 9
+
+#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282"
+#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."
+#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."
+
+#define SL_FLAGS_NONE 0
+#define SL_FLAGS_CONTINUE 1
+#define SL_FLAGS_CURRENT 2
+#define SL_FLAGS_PARENT 4
+#define SL_FLAGS_ROOT 8
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char mode [ISODCL(5,12)];
+ u_char links [ISODCL(13,20)];
+ u_char uid [ISODCL(21,28)];
+ u_char gid [ISODCL(29,36)];
+ u_char serial [ISODCL(37,44)];/* Not used */
+} ISO_RRIP_PX;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char high [ISODCL(5,12)];
+ u_char low [ISODCL(13,20)];
+} ISO_RRIP_PN;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char component [ISODCL ( 4, 256)];
+ u_int nBytes;
+} ISO_RRIP_SL;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char timestamp [ISODCL ( 5, 256)];
+} ISO_RRIP_TF;
+
+#define RRIP_NM_FLAGS_NONE 0x00
+#define RRIP_NM_FLAGS_CONTINUE 0x01
+#define RRIP_NM_FLAGS_CURRENT 0x02
+#define RRIP_NM_FLAGS_PARENT 0x04
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char flags [ISODCL ( 4, 4)];
+ u_char altname [ISODCL ( 4, 256)];
+} ISO_RRIP_NM;
+
+/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char ca_sector [ISODCL ( 5, 12)];
+ u_char offset [ISODCL ( 13, 20)];
+ u_char length [ISODCL ( 21, 28)];
+} ISO_SUSP_CE;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char padding_area [ISODCL ( 4, 256)];
+} ISO_SUSP_PD;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char check [ISODCL ( 4, 5)];
+ u_char len_skp [ISODCL ( 6, 6)];
+} ISO_SUSP_SP;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+} ISO_SUSP_ST;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char len_id [ISODCL ( 4, 4)];
+ u_char len_des [ISODCL ( 5, 5)];
+ u_char len_src [ISODCL ( 6, 6)];
+ u_char ext_ver [ISODCL ( 7, 7)];
+ u_char ext_data [ISODCL (8,256)];
+/* u_char ext_id [ISODCL ( 8, 256)];
+ u_char ext_des [ISODCL ( 257, 513)];
+ u_char ext_src [ISODCL ( 514, 770)];*/
+} ISO_SUSP_ER;
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char ext_seq [ISODCL ( 4, 4)];
+} ISO_SUSP_ES;
+
+typedef union {
+ ISO_RRIP_PX PX;
+ ISO_RRIP_PN PN;
+ ISO_RRIP_SL SL;
+ ISO_RRIP_NM NM;
+ ISO_RRIP_CLINK CL;
+ ISO_RRIP_PLINK PL;
+ ISO_RRIP_RELDIR RE;
+ ISO_RRIP_TF TF;
+} rrip_entry;
+
+typedef union {
+ ISO_SUSP_CE CE;
+ ISO_SUSP_PD PD;
+ ISO_SUSP_SP SP;
+ ISO_SUSP_ST ST;
+ ISO_SUSP_ER ER;
+ ISO_SUSP_ES ES;
+} susp_entry;
+
+typedef union {
+ susp_entry su_entry;
+ rrip_entry rr_entry;
+} SUSP_ENTRIES;
+
+struct ISO_SUSP_ATTRIBUTES {
+ SUSP_ENTRIES attr;
+ int type;
+ char type_of[2];
+ char last_in_suf; /* last entry in the System Use Field? */
+ /* Dan's addons - will merge later. This allows use of a switch */
+ char susp_type; /* SUSP or RRIP */
+ char entry_type; /* Record type */
+ char write_location;
+ TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll;
+};
+
+#define CD9660_SUSP_ENTRY_SIZE(entry)\
+ ((int) ((entry)->attr.su_entry.SP.h.length[0]))
+
+/* Recursive function - move later to func pointer code*/
+int cd9660_susp_finalize(iso9660_disk *, cd9660node *);
+
+/* These two operate on single nodes */
+int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *);
+int cd9660_rrip_finalize_node(iso9660_disk *, cd9660node *);
+
+/* POSIX File attribute */
+int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Device number */
+int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Symbolic link */
+int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Alternate Name function */
+void cd9660_rrip_NM(cd9660node *);
+void cd9660_rrip_add_NM(cd9660node *,const char *);
+
+/* Parent and child link function */
+int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+
+
+/*
+ * Relocation directory function. I'm not quite sure what
+ * sort of parameters are needed, but personally I don't think
+ * any parameters are needed except for the memory address where
+ * the information needs to be put in
+ */
+int cd9660node_rrip_re(void *, fsnode *);
+
+/*
+ * Don't know if this function is needed because it apparently is an
+ * optional feature that does not really need to be implemented but I
+ * thought I should add it anyway.
+ */
+int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int);
+int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *,
+ const char *, const char *);
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*,
+ cd9660node *);
+
+
+/* Helper functions */
+
+/* Common SUSP/RRIP functions */
+int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *,
+ cd9660node *);
+int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *,
+ int);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *,
+ struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int);
+
+/* RRIP specific functions */
+int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *,
+ cd9660node *);
+void cd9660_createSL(cd9660node *);
+
+/* Functions that probably can be removed */
+/* int cd9660node_initialize_node(int, char *); */
+
+
+#endif
--- /dev/null
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * 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 <sys/param.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#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;
+}
+
--- /dev/null
+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
--- /dev/null
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * 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 <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <util.h>
+
+#if defined(__minix)
+#include <unistd.h>
+#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);
+}
--- /dev/null
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * 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
+
--- /dev/null
+/*-
+ * Copyright (c) 2012 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2012 Tamas Toth <ttoth@inf.u-szeged.hu>
+ * 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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+#include <sys/statvfs.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/ufs_bswap.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/newfs_extern.h"
+#include "ffs/ffs_extern.h"
+
+#undef DIP
+#define DIP(dp, field) \
+ ((ffs_opts->version == 1) ? \
+ (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
+
+/*
+ * Various file system defaults (cribbed from newfs(8)).
+ */
+#define DFL_FRAGSIZE 1024 /* fragment size */
+#define DFL_BLKSIZE 8192 /* block size */
+#define DFL_SECSIZE 512 /* sector size */
+#define DFL_CYLSPERGROUP 65536 /* cylinders per group */
+#define DFL_FRAGSPERINODE 4 /* fragments per inode */
+#define DFL_ROTDELAY 0 /* rotational delay */
+#define DFL_NRPOS 1 /* rotational positions */
+#define DFL_RPM 3600 /* rpm of disk */
+#define DFL_NSECTORS 64 /* # of sectors */
+#define DFL_NTRACKS 16 /* # of tracks */
+
+
+typedef struct {
+ u_char *buf; /* buf for directory */
+ doff_t size; /* full size of buf */
+ doff_t cur; /* offset of current entry */
+} dirbuf_t;
+
+
+static int ffs_create_image(const char *, fsinfo_t *);
+static void ffs_dump_fsinfo(fsinfo_t *);
+static void ffs_dump_dirbuf(dirbuf_t *, const char *, int);
+static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
+static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
+static void ffs_size_dir(fsnode *, fsinfo_t *);
+static void ffs_validate(const char *, fsnode *, fsinfo_t *);
+static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
+static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
+static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+
+
+
+ /* publically visible functions */
+void
+ffs_prep_opts(fsinfo_t *fsopts)
+{
+ ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts));
+
+ const option_t ffs_options[] = {
+ { 'b', "bsize", &ffs_opts->bsize, OPT_INT32,
+ 1, INT_MAX, "block size" },
+ { 'f', "fsize", &ffs_opts->fsize, OPT_INT32,
+ 1, INT_MAX, "fragment size" },
+ { 'd', "density", &ffs_opts->density, OPT_INT32,
+ 1, INT_MAX, "bytes per inode" },
+ { 'm', "minfree", &ffs_opts->minfree, OPT_INT32,
+ 0, 99, "minfree" },
+ { 'M', "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);
+}
--- /dev/null
+/* $NetBSD: ffs.h,v 1.2 2011/10/09 21:33:43 christos Exp $ */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FFS_H
+#define _FFS_H
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+typedef struct {
+ char label[MAXVOLLEN]; /* volume name/label */
+ int bsize; /* block size */
+ int fsize; /* fragment size */
+ int cpg; /* cylinders per group */
+ int cpgflg; /* cpg was specified by user */
+ int density; /* bytes per inode */
+ int ntracks; /* number of tracks */
+ int nsectors; /* number of sectors */
+ int rpm; /* rpm */
+ int minfree; /* free space threshold */
+ int optimization; /* optimization (space or time) */
+ int maxcontig; /* max contiguous blocks to allocate */
+ int rotdelay; /* rotational delay between blocks */
+ int maxbpg; /* maximum blocks per file in a cyl group */
+ int nrpos; /* # of distinguished rotational positions */
+ int avgfilesize; /* expected average file size */
+ int avgfpdir; /* expected # of files per directory */
+ int version; /* filesystem version (1 = FFS, 2 = UFS2) */
+ int maxbsize; /* maximum extent size */
+ int maxblkspercg; /* max # of blocks per cylinder group */
+ /* XXX: support `old' file systems ? */
+} ffs_opt_t;
+
+#endif /* _FFS_H */
--- /dev/null
+# $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "buf.h"
+
+TAILQ_HEAD(buftailhead,buf) buftail;
+
+int
+bread(struct vnode *vp, daddr_t blkno, int size, 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);
+}
--- /dev/null
+/* $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 <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <err.h>
+
+struct componentname {
+ char *cn_nameptr;
+ size_t cn_namelen;
+};
+
+struct makefs_fsinfo;
+struct vnode {
+ struct makefs_fsinfo *fs;
+ void *v_data;
+};
+
+#define vput(a) ((void)(a))
+
+struct buf {
+ void * b_data;
+ long b_bufsize;
+ long b_bcount;
+ daddr_t b_blkno;
+ daddr_t b_lblkno;
+ struct makefs_fsinfo * b_fs;
+
+ TAILQ_ENTRY(buf) b_tailq;
+};
+
+struct kauth_cred;
+void bcleanup(void);
+int bread(struct vnode *, daddr_t, int, 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 */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+
+static int scanc(u_int, const u_char *, const u_char *, int);
+
+static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
+static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int,
+ daddr_t (*)(struct inode *, int, daddr_t, int));
+static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
+
+/* in ffs_tables.c */
+extern const int inside[], around[];
+extern const u_char * const fragtbl[];
+
+/*
+ * Allocate a block in the file system.
+ *
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate a block in the same cylinder group.
+ * 4) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ * If no block preference is given the following hierarchy is used
+ * to allocate a block:
+ * 1) allocate a block in the cylinder group that contains the
+ * inode for the file.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ */
+int
+ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size,
+ daddr_t *bnp)
+{
+ struct fs *fs = ip->i_fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0) {
+ errx(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);
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **);
+static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **);
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ *
+ * Assume: flags == B_SYNC | B_CLRBUF
+ */
+
+int
+ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
+ return ffs_balloc_ufs2(ip, offset, bufsize, bpp);
+ else
+ return ffs_balloc_ufs1(ip, offset, bufsize, bpp);
+}
+
+static int
+ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ int32_t nb;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[UFS_NIADDR + 2];
+ daddr_t newb, pref;
+ int32_t *bap;
+ int osize, nsize, num, i, error;
+ int32_t *allocblk, allociblk[UFS_NIADDR + 1];
+ int32_t *allocib;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ lbn = ffs_lblkno(fs, offset);
+ size = ffs_blkoff(fs, offset) + bufsize;
+ if (bpp != NULL) {
+ *bpp = NULL;
+ }
+
+ assert(size <= fs->fs_bsize);
+ if (lbn < 0)
+ return (EFBIG);
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+
+ lastlbn = ffs_lblkno(fs, ip->i_ffs1_size);
+ if (lastlbn < UFS_NDADDR && lastlbn < lbn) {
+ nb = lastlbn;
+ osize = ffs_blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ }
+
+ /*
+ * The first UFS_NDADDR blocks are direct blocks
+ */
+
+ if (lbn < UFS_NDADDR) {
+ nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs1_size >= ffs_lblktosize(fs, lbn + 1)) {
+
+ /*
+ * The block is an already-allocated direct block
+ * and the file already extends past this block,
+ * thus this must be a whole block.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_devvp, lbn, fs->fs_bsize,
+ 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);
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+#include "ffs/newfs_extern.h"
+
+static void initcg(int, time_t, const fsinfo_t *);
+static int ilog2(int);
+
+static int count_digits(int);
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define UMASK 0755
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+union {
+ struct fs fs;
+ char pad[SBLOCKSIZE];
+} fsun;
+#define sblock fsun.fs
+struct csum *fscs;
+
+union {
+ struct cg cg;
+ char pad[FFS_MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+char *iobuf;
+int iobufsize;
+
+char writebuf[FFS_MAXBSIZE];
+
+static int Oflag; /* format as an 4.3BSD file system */
+static int64_t fssize; /* file system size */
+static int sectorsize; /* bytes/sector */
+static int fsize; /* fragment size */
+static int bsize; /* block size */
+static int maxbsize; /* maximum clustering */
+static int maxblkspercg;
+static int minfree; /* free space threshold */
+static int opt; /* optimization preference (space or time) */
+static int density; /* number of bytes per inode */
+static int maxcontig; /* max contiguous blocks to allocate */
+static int maxbpg; /* maximum blocks per file in a cyl group */
+static int bbsize; /* boot block size */
+static int sbsize; /* superblock size */
+static int avgfilesize; /* expected average file size */
+static int avgfpdir; /* expected number of files per directory */
+
+struct fs *
+ffs_mkfs(const char *fsys, const fsinfo_t *fsopts)
+{
+ 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);
+}
--- /dev/null
+/* $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufs_bswap.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ffs_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump)
+{
+ daddr_t metalbn, realbn;
+ int64_t blockcnt;
+ int lbc;
+ int i, numlevels, off;
+ u_long lognindir;
+
+ lognindir = ffs(FFS_NINDIR(ip->i_fs)) - 1;
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ assert (bn >= UFS_NDADDR);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and UFS_NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+
+ bn -= UFS_NDADDR;
+ for (lbc = 0, i = UFS_NIADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+
+ lbc += lognindir;
+ blockcnt = (int64_t)1 << lbc;
+
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ metalbn = -((realbn >= 0 ? realbn : -realbn) - bn + UFS_NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = UFS_NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= UFS_NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ lbc -= lognindir;
+ blockcnt = (int64_t)1 << lbc;
+ off = (bn >> lbc) & (FFS_NINDIR(ip->i_fs) - 1);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + (off << lbc);
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
--- /dev/null
+/* $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)
--- /dev/null
+.\" $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).
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $");
+#endif /* !__lint */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "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);
+}
--- /dev/null
+/* $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 <sys/stat.h>
+#include <err.h>
+
+/*
+ * fsnode -
+ * a component of the tree; contains a filename, a pointer to
+ * fsinode, optional symlink name, and tree pointers
+ *
+ * fsinode -
+ * equivalent to an inode, containing target file system inode number,
+ * refcount (nlink), and stat buffer
+ *
+ * A tree of fsnodes looks like this:
+ *
+ * name "." "bin" "netbsd"
+ * type S_IFDIR S_IFDIR S_IFREG
+ * next > > NULL
+ * parent NULL NULL NULL
+ * child NULL v
+ *
+ * name "." "ls"
+ * type S_IFDIR S_IFREG
+ * next > NULL
+ * parent ^ ^ (to "bin")
+ * child NULL NULL
+ *
+ * Notes:
+ * - first always points to first entry, at current level, which
+ * must be "." when the tree has been built; during build it may
+ * not be if "." hasn't yet been found by readdir(2).
+ */
+
+enum fi_flags {
+ FI_SIZED = 1<<0, /* inode sized */
+ FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */
+ FI_WRITTEN = 1<<2, /* inode written */
+};
+
+typedef struct {
+ uint32_t ino; /* inode number used on target fs */
+ uint32_t nlink; /* number of links to this entry */
+ enum fi_flags flags; /* flags used by fs specific code */
+ struct stat st; /* stat entry */
+ void *fsuse; /* for storing FS dependent info */
+} fsinode;
+
+typedef struct _fsnode {
+ struct _fsnode *parent; /* parent (NULL if root) */
+ struct _fsnode *child; /* child (if type == S_IFDIR) */
+ struct _fsnode *next; /* next */
+ struct _fsnode *first; /* first node of current level (".") */
+ uint32_t type; /* type of entry */
+ fsinode *inode; /* actual inode data */
+ char *symlink; /* symlink target */
+ const char *root; /* root path */
+ char *path; /* directory name */
+ char *name; /* file name */
+ int flags; /* misc flags */
+} fsnode;
+
+#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */
+
+/*
+ * option_t - contains option name, description, pointer to location to store
+ * result, and range checks for the result. Used to simplify fs specific
+ * option setting
+ */
+typedef enum {
+ OPT_STRARRAY,
+ OPT_STRPTR,
+ OPT_STRBUF,
+ OPT_BOOL,
+ OPT_INT8,
+ OPT_INT16,
+ OPT_INT32,
+ OPT_INT64
+} opttype_t;
+
+typedef struct {
+ char letter; /* option letter NUL for none */
+ const char *name; /* option name */
+ void *value; /* where to stuff the value */
+ opttype_t type; /* type of entry */
+ long long minimum; /* minimum for value */
+ long long maximum; /* maximum for value */
+ const char *desc; /* option description */
+} option_t;
+
+/*
+ * fsinfo_t - contains various settings and parameters pertaining to
+ * the image, including current settings, global options, and fs
+ * specific options
+ */
+typedef struct makefs_fsinfo {
+ /* current settings */
+ off_t size; /* total size */
+ off_t inodes; /* number of inodes */
+ uint32_t curinode; /* current inode */
+
+ /* image settings */
+ int fd; /* file descriptor of image */
+ void *superblock; /* superblock */
+ 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 */
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <util.h>
+
+#include <ffs/buf.h>
+#include <fs/msdosfs/denode.h>
+#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;
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+# $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $");
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/fat.h>
+
+#include <util.h>
+
+/*
+ * 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;
+}
--- /dev/null
+/*-
+ * 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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.7 2013/01/30 19:19:19 christos Exp $");
+
+#include <sys/param.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/bootsect.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include "makefs.h"
+#include "msdos.h"
+#include "mkfs_msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+
+struct msdosfsmount *
+msdosfs_mount(struct vnode *devvp, int flags)
+{
+ struct msdosfsmount *pmp = NULL;
+ struct buf *bp;
+ union bootsector *bsp;
+ struct byte_bpb33 *b33;
+ struct byte_bpb50 *b50;
+ struct byte_bpb710 *b710;
+ uint8_t SecPerClust;
+ int ronly = 0, error, tmp;
+ int bsize;
+ struct msdos_options *m = devvp->fs->fs_specific;
+ uint64_t psize = m->create_size;
+ unsigned secsize = 512;
+
+ DPRINTF(("%s(bread 0)\n", __func__));
+ if ((error = bread(devvp, 0, secsize, 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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <ffs/buf.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/direntry.h>
+#include <fs/msdosfs/denode.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/fat.h>
+
+#include "makefs.h"
+#include "msdos.h"
+
+#ifdef MSDOSFS_DEBUG
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+/*
+ * Some general notes:
+ *
+ * In the ufs filesystem the inodes, superblocks, and indirect blocks are
+ * read/written using the vnode for the filesystem. Blocks that represent
+ * the contents of a file are read/written using the vnode for the file
+ * (including directories when they are read/written as files). This
+ * presents problems for the dos filesystem because data that should be in
+ * an inode (if dos had them) resides in the directory itself. Since we
+ * must update directory entries without the benefit of having the vnode
+ * for the directory we must use the vnode for the filesystem. This means
+ * that when a directory is actually read/written (via read, write, or
+ * readdir, or seek) we must use the vnode for the filesystem instead of
+ * the vnode for the directory as would happen in ufs. This is to insure we
+ * retrieve the correct block from the buffer cache since the hash value is
+ * based upon the vnode address and the desired block number.
+ */
+
+static int msdosfs_wfile(const char *, struct denode *, fsnode *);
+
+static void
+msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
+ const struct stat *st)
+{
+#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;
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+__RCSID("$NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <util.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#define _EXPOSE_MMC
+#include <sys/cdio.h>
+#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);
+ }
+}
+
--- /dev/null
+# $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
+
--- /dev/null
+/* $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 <sys/types.h>
+
+/*
+ * 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_ */
+
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <util.h>
+
+#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
+}
--- /dev/null
+# $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
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h> /*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 */
+}
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $");
+#endif /* !__lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/mount.h>
+#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;
+}
--- /dev/null
+/* $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_*/
--- /dev/null
+/* $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 <sys/cdefs.h>
+#if defined(__RCSID) && !defined(__lint)
+__RCSID("$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $");
+#endif /* !__lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "makefs.h"
+#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;
+}